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;
}
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;
}
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;
}
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;
}
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());
}
}
Aggregations