Search in sources :

Example 6 with ApiComment

use of com.terran4j.commons.api2doc.annotations.ApiComment in project commons by terran4j.

the class ApiResultObject method parseResultType.

/**
 * 找到一个方法返回类型中字段,收集它的 Api2Doc 信息。
 *
 * @param method
 * @param totalResults
 * @return
 */
public static final ApiResultObject parseResultType(Method method, KeyedList<String, ApiResultObject> totalResults) {
    if (method == null) {
        return null;
    }
    if (totalResults == null) {
        totalResults = new KeyedList<>();
    }
    final Class<?> clazz = method.getReturnType();
    final ApiDataType dataType = ApiDataType.toDataType(clazz);
    if (dataType == null) {
        return null;
    }
    String typeName = getTypeName(clazz, dataType);
    // 基本类型,直接处理。
    if (dataType.isSimpleType()) {
        return createSimple(clazz, clazz, dataType, typeName);
    }
    // 子类型。
    Class<?> elementType = null;
    // 数组类型,找到它的元素的具体类型,然后处理具体类型。
    if (dataType.isArrayType()) {
        elementType = Api2DocUtils.getArrayElementClass(method);
        if (elementType == null) {
            log.warn("Can't find element class by method: {}", method);
            return null;
        }
        ApiDataType elementDataType = ApiDataType.toDataType(elementType);
        typeName = getTypeName(elementType, elementDataType) + "[]";
        // 数组类型,但元素是基本类型的,也直接处理。
        if (elementDataType != null && elementDataType.isSimpleType()) {
            return createSimple(elementType, clazz, elementDataType, typeName);
        }
    }
    // 复杂类型的情况。
    ApiResultObject result = new ApiResultObject();
    result.setDataType(dataType);
    result.setSourceType(clazz);
    result.setTypeName(typeName);
    result.setId("");
    if (dataType.isObjectType()) {
        elementType = method.getReturnType();
    }
    // TODO:  暂时不解析 Map 内部的类型。
    if (elementType == null || Map.class.equals(elementType)) {
        return result;
    }
    result.setSourceType(elementType);
    // 没有子类型,直接返回。
    PropertyDescriptor[] props = PropertyUtils.getPropertyDescriptors(elementType);
    if (props == null || props.length == 0) {
        return result;
    }
    // 根据类型生成字段集的 id 和 name 。
    String groupId = getGroupId(elementType);
    result.setGroupId(groupId);
    String groupName = elementType.getSimpleName();
    result.setGroupName(groupName);
    // 加入到结果字段集中。
    if (totalResults.containsKey(groupId)) {
        return result;
    } else {
        totalResults.add(groupId, result);
    }
    // 有子类型,补充子类型信息。
    for (PropertyDescriptor prop : props) {
        if (Api2DocUtils.isFilter(prop, elementType)) {
            continue;
        }
        String fieldName = prop.getName();
        Method subMethod = prop.getReadMethod();
        // 处理子类型。
        ApiResultObject childPropResult;
        try {
            childPropResult = parseResultType(subMethod, totalResults);
        } catch (Exception e) {
            String msg = String.format("解析类[ %s ]的属性[ %s ]出错: %s", elementType.getName(), fieldName, e.getMessage());
            throw new RuntimeException(msg);
        }
        // 补充子类型信息。
        if (childPropResult != null) {
            // 补充到当前节点中。
            result.addChild(childPropResult);
            String id = prop.getName();
            childPropResult.setId(id);
            childPropResult.setName(id);
            Class<?> childPropClass = subMethod.getReturnType();
            ApiDataType childPropDataType = ApiDataType.toDataType(childPropClass);
            childPropResult.setDataType(childPropDataType);
            Api2Doc childApi2Doc;
            ApiComment childApiComment;
            String childName;
            Field field = Classes.getField(id, elementType);
            if (field != null) {
                childApiComment = field.getAnnotation(ApiComment.class);
                childApi2Doc = field.getAnnotation(Api2Doc.class);
                childName = field.getName();
            } else {
                childApiComment = subMethod.getAnnotation(ApiComment.class);
                childApi2Doc = subMethod.getAnnotation(Api2Doc.class);
                childName = subMethod.getName();
            }
            ApiComment elementApiComment = elementType.getAnnotation(ApiComment.class);
            Class<?> defaultSeeClass = ApiCommentUtils.getDefaultSeeClass(elementApiComment, null);
            String comment = ApiCommentUtils.getComment(childApiComment, defaultSeeClass, childName);
            if (comment == null) {
                comment = "";
            }
            childPropResult.insertComment(comment);
            String sample = ApiCommentUtils.getSample(childApiComment, defaultSeeClass, childName);
            if (sample == null) {
                sample = "";
            }
            childPropResult.setSample(sample);
            if (childApi2Doc != null) {
                childPropResult.setOrder(childApi2Doc.order());
            }
            // 记录所引用的类型。
            Class<?> childSubType = null;
            if (childPropDataType != null) {
                if (childPropDataType.isArrayType()) {
                    childSubType = Api2DocUtils.getArrayElementClass(subMethod);
                } else if (childPropDataType.isObjectType()) {
                    childSubType = subMethod.getReturnType();
                }
            }
            if (childSubType != null) {
                String refGroupId = getGroupId(childSubType);
                childPropResult.setRefGroupId(refGroupId);
            }
        }
    }
    Collections.sort(result.getChildren());
    return result;
}
Also used : PropertyDescriptor(java.beans.PropertyDescriptor) Method(java.lang.reflect.Method) Field(java.lang.reflect.Field) Api2Doc(com.terran4j.commons.api2doc.annotations.Api2Doc) ApiComment(com.terran4j.commons.api2doc.annotations.ApiComment) Map(java.util.Map)

Example 7 with ApiComment

use of com.terran4j.commons.api2doc.annotations.ApiComment in project commons by terran4j.

the class Api2DocCollector method toApiParams.

public List<ApiParamObject> toApiParams(Method method, Class<?> defaultSeeClass) {
    List<ApiParamObject> result = new ArrayList<>();
    Set<String> paramIds = new HashSet<>();
    Parameter[] params = method.getParameters();
    if (params != null && params.length > 0) {
        String[] paramNames = parameterNameDiscoverer.getParameterNames(method);
        for (int i = 0; i < params.length; i++) {
            Parameter param = params[i];
            Class<?> paramClass = param.getType();
            ApiComment paramClassComment = paramClass.getAnnotation(ApiComment.class);
            if (paramClassComment != null) {
                // 从参数的类的属性中获取注释信息。
                List<ApiParamObject> paramsFromClass = toApiParams(paramClass, defaultSeeClass);
                for (ApiParamObject paramFromClass : paramsFromClass) {
                    if (paramIds.contains(paramFromClass.getId())) {
                        continue;
                    }
                    paramIds.add(paramFromClass.getId());
                    result.add(paramFromClass);
                }
            } else {
                // 从参数本身中获取注释信息。
                String paramName;
                if (paramNames != null) {
                    paramName = paramNames[i];
                } else {
                    paramName = param.getName();
                }
                ApiParamObject apiParamObject = toApiParam(param, paramName, param.getType(), defaultSeeClass);
                if (apiParamObject == null) {
                    continue;
                }
                String paramId = apiParamObject.getId();
                if (paramIds.contains(paramId)) {
                    String msg = "参数id值重复: " + paramId + ",所在方法: " + method;
                    throw new BeanDefinitionStoreException(msg);
                }
                paramIds.add(paramId);
                result.add(apiParamObject);
            }
        }
    }
    return result;
}
Also used : BeanDefinitionStoreException(org.springframework.beans.factory.BeanDefinitionStoreException) ApiComment(com.terran4j.commons.api2doc.annotations.ApiComment) Parameter(java.lang.reflect.Parameter)

Example 8 with ApiComment

use of com.terran4j.commons.api2doc.annotations.ApiComment in project commons by terran4j.

the class Api2DocCollector method getApiDoc.

ApiDocObject getApiDoc(MappingMethod mappingMethod, String[] basePaths, String beanName, Api2Doc classApi2Doc, Class<?> defaultSeeClass) throws BusinessException {
    Method method = mappingMethod.getMethod();
    // 只要有 @ApiDoc 注解(无论是本方法上,还是类上),有会生成文档,没有这个注解就不会。
    Api2Doc api2Doc = method.getAnnotation(Api2Doc.class);
    if (api2Doc == null && classApi2Doc == null) {
        return null;
    }
    ApiDocObject doc = new ApiDocObject();
    doc.setSourceMethod(method);
    // 获取文档的 id,以 @Api2Doc、方法名 为顺序获取。
    String id = method.getName();
    if (api2Doc != null && StringUtils.hasText(api2Doc.value())) {
        id = api2Doc.value();
    }
    if (api2Doc != null && StringUtils.hasText(api2Doc.id())) {
        id = api2Doc.id();
    }
    doc.setId(id);
    checkId(id);
    // 获取文档的排序。
    if (api2Doc != null) {
        doc.setOrder(api2Doc.order());
    }
    // 获取文档名称,按 @Api2Doc 、@Mapping、方法名的顺序获取。
    String name = method.getName();
    String mappingName = mappingMethod.getName();
    if (StringUtils.hasText(mappingName)) {
        name = mappingName;
    }
    if (api2Doc != null && StringUtils.hasText(api2Doc.name())) {
        name = api2Doc.name();
    }
    doc.setName(name);
    // 获取 API 的注释信息。
    ApiComment apiComment = method.getAnnotation(ApiComment.class);
    defaultSeeClass = ApiCommentUtils.getDefaultSeeClass(apiComment, defaultSeeClass);
    doc.setComment(ApiCommentUtils.getComment(apiComment, defaultSeeClass, name));
    doc.setSample(ApiCommentUtils.getSample(apiComment, defaultSeeClass, name));
    // 获取 API 的访问路径。
    String[] paths = mappingMethod.getPath();
    paths = combine(basePaths, paths);
    doc.setPaths(paths);
    // 获取 HTTP 方法。
    doc.setMethods(mappingMethod.getRequestMethod());
    // 收集参数信息。
    List<ApiParamObject> apiParams = toApiParams(method, defaultSeeClass);
    if (apiParams != null && apiParams.size() > 0) {
        for (ApiParamObject apiParam : apiParams) {
            doc.addParam(apiParam);
        }
    }
    // 收集返回值信息。
    KeyedList<String, ApiResultObject> totalResults = new KeyedList<>();
    ApiResultObject.parseResultType(method, totalResults);
    doc.setResults(totalResults.getAll());
    // 确定返回类型的描述。
    String returnTypeDesc = null;
    List<ApiResultObject> results = doc.getResults();
    if (results != null && results.size() > 0) {
        ApiResultObject result = results.get(0);
        ApiDataType dataType = result.getDataType();
        if (dataType != null) {
            if (dataType == ApiDataType.ARRAY) {
                returnTypeDesc = result.getSourceType().getSimpleName() + "[]";
            } else {
                returnTypeDesc = result.getSourceType().getSimpleName();
            }
        }
    }
    if (returnTypeDesc == null) {
        Class<?> returnType = doc.getSourceMethod().getReturnType();
        if (returnType != null && returnType != void.class) {
            returnTypeDesc = returnType.getSimpleName();
        }
    }
    doc.setReturnTypeDesc(returnTypeDesc);
    // 收集错误码信息。
    ApiErrors errorCodes = method.getAnnotation(ApiErrors.class);
    if (errorCodes != null && errorCodes.value() != null && errorCodes.value().length > 0) {
        for (ApiError errorCode : errorCodes.value()) {
            ApiErrorObject error = getError(errorCode);
            if (error == null) {
                continue;
            }
            doc.addError(error);
        }
    } else {
        ApiError errorCode = method.getAnnotation(ApiError.class);
        ApiErrorObject error = getError(errorCode);
        if (error != null) {
            doc.addError(error);
        }
    }
    return doc;
}
Also used : KeyedList(com.terran4j.commons.util.value.KeyedList) ApiErrors(com.terran4j.commons.api2doc.annotations.ApiErrors) Method(java.lang.reflect.Method) Api2Doc(com.terran4j.commons.api2doc.annotations.Api2Doc) ApiComment(com.terran4j.commons.api2doc.annotations.ApiComment) ApiError(com.terran4j.commons.api2doc.annotations.ApiError)

Example 9 with ApiComment

use of com.terran4j.commons.api2doc.annotations.ApiComment in project commons by terran4j.

the class Api2DocCollector method toApiParam.

ApiParamObject toApiParam(AnnotatedElement element, String elementName, Class<?> elementType, Class<?> defaultSeeClass) {
    ApiParamObject apiParamObject = new ApiParamObject();
    ApiParamLocation.collects(apiParamObject, element);
    String id = apiParamObject.getId();
    checkId(id);
    String name = apiParamObject.getName();
    if (StringUtils.isEmpty(name)) {
        name = elementName;
    }
    apiParamObject.setName(name);
    apiParamObject.setId(name);
    ApiComment apiComment = element.getAnnotation(ApiComment.class);
    ApiCommentUtils.setApiComment(apiComment, defaultSeeClass, apiParamObject);
    apiParamObject.setSourceType(elementType);
    ApiDataType dataType = convertType(elementType);
    apiParamObject.setDataType(dataType);
    return apiParamObject;
}
Also used : ApiComment(com.terran4j.commons.api2doc.annotations.ApiComment)

Example 10 with ApiComment

use of com.terran4j.commons.api2doc.annotations.ApiComment in project commons by terran4j.

the class Api2DocObjectFactory method fillField.

private static void fillField(String name, Object bean, Stack<Class<?>> classStack) {
    Class<?> clazz = Classes.getTargetClass(bean);
    PropertyDescriptor fieldProp = null;
    try {
        fieldProp = PropertyUtils.getPropertyDescriptor(bean, name);
    } catch (IllegalAccessException e) {
        throw new RuntimeException(e);
    } catch (InvocationTargetException e) {
        throw new RuntimeException(e);
    } catch (NoSuchMethodException e) {
        throw new RuntimeException(e);
    }
    if (fieldProp == null) {
        throw new RuntimeException("field[" + name + "] NOT found in class: " + clazz);
    }
    if (Api2DocUtils.isFilter(fieldProp, clazz)) {
        return;
    }
    ApiComment classApiComment = clazz.getAnnotation(ApiComment.class);
    Class<?> defaultSeeClass = ApiCommentUtils.getDefaultSeeClass(classApiComment, null);
    String fieldName = fieldProp.getName();
    Method getMethod = fieldProp.getReadMethod();
    if (getMethod == null) {
        log.warn("没有 getter 方法, class = {}, fieldName = {}", clazz, fieldName);
        return;
    }
    Method setMethod = fieldProp.getWriteMethod();
    if (setMethod == null) {
        log.warn("没有 setter 方法, class = {}, fieldName = {}", clazz, fieldName);
        return;
    }
    Class<?> fieldClass = getMethod.getReturnType();
    Field field = Classes.getField(fieldName, clazz);
    if (field == null) {
        log.warn("找不到字段定义, class = {}, fieldName = {}", clazz, fieldName);
        return;
    }
    ApiDataType fieldDataType = ApiDataType.toDataType(fieldClass);
    if (fieldDataType == null) {
        log.warn("未知字段类型");
        return;
    }
    Object fieldValue = null;
    if (fieldDataType.isSimpleType()) {
        ApiComment apiComment = field.getAnnotation(ApiComment.class);
        String defaultValue = ApiCommentUtils.getSample(apiComment, defaultSeeClass, fieldName);
        fieldValue = createBean(fieldClass, defaultValue, classStack);
        Class<?> paramType = setMethod.getParameterTypes()[0];
        fieldValue = adaptSimpleType(fieldValue, paramType);
    } else if (fieldDataType.isObjectType()) {
        fieldValue = createBean(fieldClass, null, classStack);
    } else if (fieldDataType.isArrayType()) {
        int size = 1;
        ApiComment apiComment = field.getAnnotation(ApiComment.class);
        if (apiComment != null) {
            String sizeText = ApiCommentUtils.getSample(apiComment, defaultSeeClass, field.getName());
            if (StringUtils.hasText(sizeText)) {
                try {
                    size = Integer.parseInt(sizeText);
                } catch (Exception e) {
                    log.warn("List 或 Array 类型的字段上," + "@ApiComment 注解的 sample 属性应该是数字" + "(代表它在 mock 时元素的个数), sample = {}", sizeText);
                }
            }
        }
        Class<?> elementClass = getArrayElementClass(field);
        if (fieldClass.isArray()) {
            fieldValue = doCreateArray(elementClass, size, classStack);
        } else if (List.class.equals(fieldClass)) {
            fieldValue = doCreateList(elementClass, size, classStack);
        } else {
            log.warn("不支持的集合类型,目前只支持 Array OR List, class = {}, fieldName = {}" + ", fieldClass = {}", clazz, fieldName, fieldClass);
        }
    }
    try {
        setMethod.invoke(bean, fieldValue);
    } catch (Exception e) {
        log.warn("调用 setter 方法失败, \n" + "clazz = {}, \n" + "setMethod = {}, \n" + "fieldValue = {}, \n" + "失败原因: {}", clazz, setMethod, fieldValue, e.getMessage());
    }
}
Also used : PropertyDescriptor(java.beans.PropertyDescriptor) ApiComment(com.terran4j.commons.api2doc.annotations.ApiComment) ArrayList(java.util.ArrayList) List(java.util.List) ApiDataType(com.terran4j.commons.api2doc.domain.ApiDataType)

Aggregations

ApiComment (com.terran4j.commons.api2doc.annotations.ApiComment)12 Field (java.lang.reflect.Field)5 Api2Doc (com.terran4j.commons.api2doc.annotations.Api2Doc)3 Method (java.lang.reflect.Method)3 ArrayList (java.util.ArrayList)3 PropertyDescriptor (java.beans.PropertyDescriptor)2 BeanDefinitionStoreException (org.springframework.beans.factory.BeanDefinitionStoreException)2 ApiError (com.terran4j.commons.api2doc.annotations.ApiError)1 ApiErrors (com.terran4j.commons.api2doc.annotations.ApiErrors)1 ApiDataType (com.terran4j.commons.api2doc.domain.ApiDataType)1 ApiObject (com.terran4j.commons.api2doc.domain.ApiObject)1 FlexibleString (com.terran4j.commons.api2doc.impl.FlexibleString)1 KeyedList (com.terran4j.commons.util.value.KeyedList)1 IOException (java.io.IOException)1 Parameter (java.lang.reflect.Parameter)1 HashMap (java.util.HashMap)1 List (java.util.List)1 Map (java.util.Map)1 Test (org.junit.Test)1 Resource (org.springframework.core.io.Resource)1