/*
* Copyright (c) 2015 EMC Corporation
* All Rights Reserved
*/
package com.emc.apidocs.processing;
import com.emc.apidocs.AnnotationUtils;
import com.emc.apidocs.KnownAnnotations;
import com.emc.apidocs.Utils;
import com.emc.apidocs.model.ApiClass;
import com.emc.apidocs.model.ApiField;
import com.sun.javadoc.*;
/**
* Converts a Java JAXB class into an ApiClass definition
*/
public class JaxbClassProcessor {
public static ApiClass convertToApiClass(ClassDoc classDoc) {
ApiClass classDescriptor = new ApiClass();
classDescriptor.name = AnnotationUtils.getAnnotationValue(classDoc, KnownAnnotations.XMLElement_Annotation, "name", null);
if (classDescriptor.name == null) {
classDescriptor.name = AnnotationUtils.getAnnotationValue(classDoc, KnownAnnotations.XMLRoot_Annotation, "name",
classDoc.simpleTypeName());
}
ClassDoc currentClass = classDoc;
while (currentClass.qualifiedName().startsWith("com.emc")) {
String xmlAccessType = getXmlAccessType(classDoc);
// Read Fields
for (FieldDoc field : currentClass.fields()) {
if (shouldIncludeField(field, xmlAccessType)) {
ApiField fieldDescriptor = new ApiField();
fieldDescriptor.name = AnnotationUtils.getAnnotationValue(field, KnownAnnotations.XMLElement_Annotation, "name",
field.name());
fieldDescriptor.required = AnnotationUtils.getAnnotationValue(field, KnownAnnotations.XMLElement_Annotation,
"required", false);
fieldDescriptor.description = field.commentText();
if (AnnotationUtils.hasAnnotation(field, KnownAnnotations.XMLElementWrapper_Annotation)) {
fieldDescriptor.wrapperName = AnnotationUtils.getAnnotationValue(field,
KnownAnnotations.XMLElementWrapper_Annotation, "name", Utils.lowerCaseFirstChar(field.name()));
}
addFieldType(field.type(), fieldDescriptor);
addValidValues(field, fieldDescriptor);
classDescriptor.addField(fieldDescriptor);
}
if (AnnotationUtils.hasAnnotation(field, KnownAnnotations.XMLAttribute_Annotation)) {
ApiField attributeDescriptor = new ApiField();
attributeDescriptor.name = AnnotationUtils.getAnnotationValue(field, KnownAnnotations.XMLAttribute_Annotation, "name",
field.name());
attributeDescriptor.required = AnnotationUtils.getAnnotationValue(field, KnownAnnotations.XMLAttribute_Annotation,
"required", false);
attributeDescriptor.description = field.commentText();
addFieldType(field.type(), attributeDescriptor);
addValidValues(field, attributeDescriptor);
classDescriptor.addAttribute(attributeDescriptor);
}
}
// Read Public Property Methods
for (MethodDoc method : currentClass.methods()) {
if (shouldIncludeMethod(method, xmlAccessType, currentClass)) {
ApiField methodDescriptor = new ApiField();
methodDescriptor.name = AnnotationUtils
.getAnnotationValue(method, KnownAnnotations.XMLElement_Annotation, "name", null);
if (methodDescriptor.name == null) {
if (method.name().startsWith("get")) {
methodDescriptor.name = Utils.lowerCaseFirstChar(method.name().substring(3));
}
else {
methodDescriptor.name = method.name();
}
}
methodDescriptor.required = AnnotationUtils.getAnnotationValue(method, KnownAnnotations.XMLElement_Annotation,
"required", false);
methodDescriptor.description = method.commentText();
if (AnnotationUtils.hasAnnotation(method, KnownAnnotations.XMLElementWrapper_Annotation)) {
methodDescriptor.wrapperName = AnnotationUtils.getAnnotationValue(method,
KnownAnnotations.XMLElementWrapper_Annotation, "name", null);
if (methodDescriptor.wrapperName == null) {
if (method.name().startsWith("get")) {
methodDescriptor.wrapperName = Utils.lowerCaseFirstChar(method.name().substring(3));
}
else if (method.name().startsWith("is")) {
methodDescriptor.wrapperName = Utils.lowerCaseFirstChar(method.name().substring(2));
}
else {
throw new RuntimeException("Unable to work out JavaBean property name " + method.qualifiedName());
}
}
}
// process JsonProperty annotation
String jsonName = AnnotationUtils.getAnnotationValue(method, KnownAnnotations.JsonProperty_Annotation,
KnownAnnotations.Value_Element, null);
if (jsonName != null) {
methodDescriptor.jsonName = jsonName;
}
addFieldType(method.returnType(), methodDescriptor);
addValidValues(method, methodDescriptor);
classDescriptor.addField(methodDescriptor);
}
}
currentClass = currentClass.superclass();
}
return classDescriptor;
}
/** Returns true of false if the fields should be included based on the accessType */
private static boolean shouldIncludeField(FieldDoc field, String accessType) {
if (field.isStatic() ||
AnnotationUtils.hasAnnotation(field, KnownAnnotations.XMLTransient_Annotation) ||
AnnotationUtils.hasAnnotation(field, KnownAnnotations.XMLAttribute_Annotation)) {
return false;
}
if (accessType.equals("FIELD")) {
// Every non static, non transient field in a JAXB-bound class will be automatically bound to XML, unless annotated by
// XmlTransient.
return !field.isStatic() && !field.isTransient();
}
else if (accessType.equals("PUBLIC_MEMBER")) {
// Every public getter/setter pair and every public field will be automatically bound to XML, unless annotated by XmlTransient.
return field.isPublic();
}
else if (accessType.equals("PROPERTY")) {
// Every getter/setter pair in a JAXB-bound class will be automatically bound to XML, unless annotated by XmlTransient.
return false;
}
else if (accessType.equals("NONE")) {
// None of the fields or properties is bound to XML unless they are specifically annotated with some of the JAXB annotations.
return false;
}
return false;
}
/** Returns true of false if the fields should be included based on the accessType */
private static boolean shouldIncludeMethod(MethodDoc method, String accessType, ClassDoc classDoc) {
if (method.isStatic() ||
AnnotationUtils.hasAnnotation(method, KnownAnnotations.XMLTransient_Annotation) ||
AnnotationUtils.hasAnnotation(method, KnownAnnotations.XMLAttribute_Annotation)) {
return false;
}
if (accessType.equals("FIELD")) {
// Every non static, non transient field in a JAXB-bound class will be automatically bound to XML, unless annotated by
// XmlTransient.
return false;
}
else if (accessType.equals("PUBLIC_MEMBER")) {
// Every public getter/setter pair and every public field will be automatically bound to XML, unless annotated by XmlTransient.
return method.isPublic() &&
method.name().startsWith("get") &&
hasMatchingSetter(method.name(), classDoc);
}
else if (accessType.equals("PROPERTY")) {
// Every getter/setter pair in a JAXB-bound class will be automatically bound to XML, unless annotated by XmlTransient.
return method.isPublic() &&
method.name().startsWith("get") &&
hasMatchingSetter(method.name(), classDoc);
}
else if (accessType.equals("NONE")) {
// None of the fields or properties is bound to XML unless they are specifically annotated with some of the JAXB annotations.
return false;
}
return false;
}
private static boolean hasMatchingSetter(String getterName, ClassDoc classDoc) {
String setterName = "set" + getterName.substring(3);
// Search for a matching setter
for (MethodDoc m : classDoc.methods()) {
if (m.name().equals(setterName)) {
return true;
}
}
return false;
}
public static void addFieldType(Type type, ApiField descriptor) {
// Default Values?
// Look for @Length and @Range
if (TypeUtils.isCollectionType(type)) {
descriptor.collection = true;
Type parameterisedType = type.asParameterizedType().typeArguments()[0];
if (TypeUtils.isPrimitiveType(parameterisedType)) {
descriptor.primitiveType = parameterisedType.simpleTypeName();
} else {
descriptor.type = convertToApiClass(parameterisedType.asClassDoc());
}
}
else if (TypeUtils.isPrimitiveType(type)) {
descriptor.primitiveType = type.simpleTypeName();
// Convert to XML types
if (descriptor.primitiveType.equals("Calendar")) {
descriptor.primitiveType = "DateTime";
}
if (descriptor.primitiveType.equals("int")) {
descriptor.primitiveType = "Integer";
}
descriptor.primitiveType = Utils.upperCaseFirstChar(descriptor.primitiveType);
}
else {
descriptor.type = convertToApiClass(type.asClassDoc());
}
}
public static void addValidValues(ProgramElementDoc field, ApiField apiField) {
for (Tag tag : field.tags()) {
if (tag.name().equals("@valid")) {
if (!tag.text().toLowerCase().equals("none")) {
String tagText = tag.text();
apiField.validValues.add(tagText.trim());
}
}
}
AnnotationDesc lengthAnnotation = AnnotationUtils.getAnnotation(field, KnownAnnotations.Length_Annotation);
if (lengthAnnotation != null) {
int min = 0;
int max = Integer.MAX_VALUE;
for (AnnotationDesc.ElementValuePair pair : lengthAnnotation.elementValues()) {
if (pair.element().name().equals("min")) {
min = (Integer) pair.value().value();
}
if (pair.element().name().equals("max")) {
max = (Integer) pair.value().value();
}
}
apiField.validValues.add("Length: " + min + ".." + max);
}
AnnotationDesc rangeAnnotation = AnnotationUtils.getAnnotation(field, KnownAnnotations.Range_Annotation);
if (rangeAnnotation != null) {
long min = 0;
long max = Long.MAX_VALUE;
for (AnnotationDesc.ElementValuePair pair : rangeAnnotation.elementValues()) {
if (pair.element().name().equals("min")) {
min = (Long) pair.value().value();
}
if (pair.element().name().equals("max")) {
max = (Long) pair.value().value();
}
}
apiField.validValues.add("Range: " + min + ".." + max);
}
}
/** Searches the Super Classes for an inherited XmlAccessType annotation */
private static String getXmlAccessType(ClassDoc classDoc) {
String xmlAccessType = "";
ClassDoc currentDoc = classDoc;
while (currentDoc.qualifiedName().startsWith("com.emc") && xmlAccessType.equals("")) {
FieldDoc xmlAccessTypeEnum = AnnotationUtils.getAnnotationValue(currentDoc, KnownAnnotations.XMLAccessorType_Annotation,
KnownAnnotations.Value_Element, null);
if (xmlAccessTypeEnum != null) {
return xmlAccessTypeEnum.name();
}
currentDoc = currentDoc.superclass();
}
return "PUBLIC_MEMBER"; // Default AccessType
}
}