package com.hypnoticocelot.jaxrs.doclet.parser;
import com.hypnoticocelot.jaxrs.doclet.DocletOptions;
import com.hypnoticocelot.jaxrs.doclet.model.*;
import com.hypnoticocelot.jaxrs.doclet.translator.Translator;
import com.sun.javadoc.*;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import static com.google.common.base.Objects.firstNonNull;
import static com.google.common.collect.Collections2.filter;
import static com.hypnoticocelot.jaxrs.doclet.parser.AnnotationHelper.parsePath;
public class ApiMethodParser {
private final DocletOptions options;
private final Translator translator;
private final String parentPath;
private final MethodDoc methodDoc;
private final Set<Model> models;
private final HttpMethod httpMethod;
private final Method parentMethod;
public ApiMethodParser(DocletOptions options, String parentPath, MethodDoc methodDoc) {
this.options = options;
this.translator = options.getTranslator();
this.parentPath = parentPath;
this.methodDoc = methodDoc;
this.models = new LinkedHashSet<Model>();
this.httpMethod = HttpMethod.fromMethod(methodDoc);
this.parentMethod = null;
}
public ApiMethodParser(DocletOptions options, Method parentMethod, MethodDoc methodDoc) {
this.options = options;
this.translator = options.getTranslator();
this.methodDoc = methodDoc;
this.models = new LinkedHashSet<Model>();
this.httpMethod = HttpMethod.fromMethod(methodDoc);
this.parentPath = parentMethod.getPath();
this.parentMethod = parentMethod;
}
public Method parse() {
String methodPath = firstNonNull(parsePath(methodDoc.annotations()), "");
if (httpMethod == null && methodPath.isEmpty()) {
return null;
}
String path = parentPath + methodPath;
// parameters
List<ApiParameter> parameters = new LinkedList<ApiParameter>();
for (Parameter parameter : methodDoc.parameters()) {
if (!shouldIncludeParameter(httpMethod, parameter)) {
continue;
}
if (options.isParseModels()) {
models.addAll(new ApiModelParser(options, translator, parameter.type()).parse());
}
parameters.add(new ApiParameter(
AnnotationHelper.paramTypeOf(parameter),
AnnotationHelper.paramNameOf(parameter),
commentForParameter(methodDoc, parameter),
translator.typeName(parameter.type()).value()
));
}
// parent method parameters are inherited
if (parentMethod != null)
parameters.addAll(parentMethod.getParameters());
// response messages
Pattern pattern = Pattern.compile("(\\d+) (.+)"); // matches "<code><space><text>"
List<ApiResponseMessage> responseMessages = new LinkedList<ApiResponseMessage>();
for (String tagName : options.getErrorTags()) {
for (Tag tagValue : methodDoc.tags(tagName)) {
Matcher matcher = pattern.matcher(tagValue.text());
if (matcher.find()) {
responseMessages.add(new ApiResponseMessage(Integer.valueOf(matcher.group(1)),
matcher.group(2)));
}
}
}
// return type
Type type = methodDoc.returnType();
String returnType = translator.typeName(type).value();
if (options.isParseModels()) {
models.addAll(new ApiModelParser(options, translator, type).parse());
}
// First Sentence of Javadoc method description
Tag[] fst = methodDoc.firstSentenceTags();
StringBuilder sentences = new StringBuilder();
for (Tag tag : fst) {
sentences.append(tag.text());
}
String firstSentences = sentences.toString();
return new Method(
httpMethod,
methodDoc.name(),
path,
parameters,
responseMessages,
firstSentences,
methodDoc.commentText().replace(firstSentences, ""),
returnType
);
}
public Set<Model> models() {
return models;
}
private boolean shouldIncludeParameter(HttpMethod httpMethod, Parameter parameter) {
List<AnnotationDesc> allAnnotations = Arrays.asList(parameter.annotations());
Collection<AnnotationDesc> excluded = filter(allAnnotations, new AnnotationHelper.ExcludedAnnotations(options));
if (!excluded.isEmpty()) {
return false;
}
Collection<AnnotationDesc> jaxRsAnnotations = filter(allAnnotations, new AnnotationHelper.JaxRsAnnotations());
if (!jaxRsAnnotations.isEmpty()) {
return true;
}
return (allAnnotations.isEmpty() || httpMethod == HttpMethod.POST);
}
private String commentForParameter(MethodDoc method, Parameter parameter) {
for (ParamTag tag : method.paramTags()) {
if (tag.parameterName().equals(parameter.name())) {
return tag.parameterComment();
}
}
return "";
}
}