package com.hypnoticocelot.jaxrs.doclet.parser;
import com.google.common.base.Function;
import com.hypnoticocelot.jaxrs.doclet.DocletOptions;
import com.hypnoticocelot.jaxrs.doclet.model.Api;
import com.hypnoticocelot.jaxrs.doclet.model.Method;
import com.hypnoticocelot.jaxrs.doclet.model.Model;
import com.hypnoticocelot.jaxrs.doclet.model.Operation;
import com.sun.javadoc.ClassDoc;
import com.sun.javadoc.MethodDoc;
import com.sun.javadoc.Type;
import java.util.*;
import static com.google.common.base.Objects.firstNonNull;
import static com.google.common.collect.Collections2.transform;
import static com.hypnoticocelot.jaxrs.doclet.parser.AnnotationHelper.parsePath;
public class ApiClassParser {
private final DocletOptions options;
private final ClassDoc classDoc;
private final String rootPath;
private final Set<Model> models;
private final Collection<ClassDoc> classes;
private final Method parentMethod;
public ApiClassParser(DocletOptions options, ClassDoc classDoc, Collection<ClassDoc> classes) {
this.options = options;
this.classDoc = classDoc;
this.rootPath = firstNonNull(parsePath(classDoc.annotations()), "");
this.models = new LinkedHashSet<Model>();
this.classes = classes;
this.parentMethod = null;
}
/**
* Creates sub-resource class parser.
* @param parentMethod method that creates the sub-resource.
*/
public ApiClassParser(DocletOptions options, ClassDoc classDoc, Collection<ClassDoc> classes, Method parentMethod) {
this.options = options;
this.classDoc = classDoc;
this.rootPath = firstNonNull(parsePath(classDoc.annotations()), "");
this.models = new LinkedHashSet<Model>();
this.classes = classes;
this.parentMethod = parentMethod;
}
public String getRootPath() {
return rootPath;
}
public Collection<Api> parse() {
List<Api> apis = new ArrayList<Api>();
Map<String, Collection<Method>> apiMethods = new HashMap<String, Collection<Method>>();
for (MethodDoc method : classDoc.methods()) {
ApiMethodParser methodParser = parentMethod == null ?
new ApiMethodParser(options, rootPath, method) :
new ApiMethodParser(options, parentMethod, method);
Method parsedMethod = methodParser.parse();
if (parsedMethod == null) {
continue;
}
if (parsedMethod.isSubResource()) {
ClassDoc subResourceClassDoc = lookUpClassDoc(method.returnType());
if (subResourceClassDoc != null) {
// delete class from the dictionary to handle recursive sub-resources
Collection<ClassDoc> shrunkClasses = new ArrayList<ClassDoc>(classes);
shrunkClasses.remove(classDoc);
// recursively parse the sub-resource class
ApiClassParser subResourceParser = new ApiClassParser(options, subResourceClassDoc, shrunkClasses, parsedMethod);
apis.addAll(subResourceParser.parse());
models.addAll(subResourceParser.models());
}
continue;
}
models.addAll(methodParser.models());
String realPath = parsedMethod.getPath();
Collection<Method> matchingMethods = apiMethods.get(realPath);
if (matchingMethods == null) {
matchingMethods = new ArrayList<Method>();
apiMethods.put(realPath, matchingMethods);
}
matchingMethods.add(parsedMethod);
}
for (Map.Entry<String, Collection<Method>> apiEntries : apiMethods.entrySet()) {
Collection<Operation> operations = new ArrayList<Operation>(
transform(apiEntries.getValue(), new Function<Method, Operation>() {
@Override
public Operation apply(Method method) {
return new Operation(method);
}
})
);
apis.add(new Api(apiEntries.getKey(), "", operations));
}
Collections.sort(apis, new Comparator<Api>() {
@Override
public int compare(Api o1, Api o2) {
return o1.getPath().compareTo(o2.getPath());
}
});
return apis;
}
private ClassDoc lookUpClassDoc(Type type) {
for (ClassDoc subResourceClassDoc : classes) {
if (subResourceClassDoc.qualifiedTypeName().equals(type.qualifiedTypeName())) {
return subResourceClassDoc;
}
}
return null;
}
public Collection<Model> models() {
return models;
}
}