Skip to content
This repository was archived by the owner on Nov 11, 2017. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .settings/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/org.eclipse.m2e.core.prefs
2 changes: 2 additions & 0 deletions jaxrs-doclet-sample-dropwizard/.settings/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
/org.eclipse.jdt.core.prefs
/org.eclipse.m2e.core.prefs
2 changes: 2 additions & 0 deletions jaxrs-doclet/.settings/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
/org.eclipse.jdt.core.prefs
/org.eclipse.m2e.core.prefs
1 change: 1 addition & 0 deletions jaxrs-doclet/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
</parent>

<artifactId>jaxrs-doclet</artifactId>
<version>0.0.5-Acrolinx</version>

<dependencies>
<dependency>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ public class DocletOptions {

public DocletOptions() {
excludeAnnotationClasses = new ArrayList<String>();
excludeAnnotationClasses.add("javax.ws.rs.HeaderParam");
excludeAnnotationClasses.add("javax.ws.rs.core.Context");
errorTags = new ArrayList<String>();
errorTags.add("errorResponse"); // swagger 1.1
Expand Down
Original file line number Diff line number Diff line change
@@ -1,38 +1,46 @@

package com.hypnoticocelot.jaxrs.doclet.parser;

import java.util.ArrayList;
import java.util.List;

import com.google.common.base.Predicate;
import com.hypnoticocelot.jaxrs.doclet.DocletOptions;
import com.sun.javadoc.AnnotationDesc;
import com.sun.javadoc.Parameter;
import com.sun.javadoc.Type;

import java.util.ArrayList;
import java.util.List;

public class AnnotationHelper {
public class AnnotationHelper
{

private static final String JAX_RS_ANNOTATION_PACKAGE = "javax.ws.rs";
private static final String JAX_RS_PATH = "javax.ws.rs.Path";
private static final String JAX_RS_PATH_PARAM = "javax.ws.rs.PathParam";
private static final String JAX_RS_HEADER_PARAM = "javax.ws.rs.HeaderParam";
private static final String JAX_RS_QUERY_PARAM = "javax.ws.rs.QueryParam";
private static final String JERSEY_MULTIPART_FORM_PARAM = "com.sun.jersey.multipart.FormDataParam";

private static final String DROPWIZARD_AUTH = "io.dropwizard.auth.Auth";

@SuppressWarnings("serial")
static final List<String> PRIMITIVES = new ArrayList<String>() {{
add("byte");
add("boolean");
add("int");
add("long");
add("float");
add("double");
add("string");
add("Date");
}};

public static String parsePath(AnnotationDesc[] annotations) {
for (AnnotationDesc annotationDesc : annotations) {
static final List<String> PRIMITIVES = new ArrayList<String>()
{
{
add("byte");
add("boolean");
add("int");
add("long");
add("float");
add("double");
add("string");
add("Date");
}
};

public static String parsePath(final AnnotationDesc[] annotations)
{
for (final AnnotationDesc annotationDesc : annotations) {
if (annotationDesc.annotationType().qualifiedTypeName().equals(JAX_RS_PATH)) {
for (AnnotationDesc.ElementValuePair pair : annotationDesc.elementValues()) {
for (final AnnotationDesc.ElementValuePair pair : annotationDesc.elementValues()) {
if (pair.element().name().equals("value")) {
String path = pair.value().value().toString();
if (path.endsWith("/")) {
Expand All @@ -49,17 +57,18 @@ public static String parsePath(AnnotationDesc[] annotations) {
/**
* Determines the String representation of the object Type.
*/
public static String typeOf(String javaType) {
public static String typeOf(final String javaType)
{
String type;
if (javaType.startsWith("java.lang.")) {
int i = javaType.lastIndexOf(".");
final int i = javaType.lastIndexOf(".");
type = javaType.substring(i + 1).toLowerCase();
} else if (PRIMITIVES.contains(javaType.toLowerCase())) {
type = javaType.toLowerCase();
} else if (javaType.equals("java.util.Date")) {
type = "Date";
} else {
int i = javaType.lastIndexOf(".");
final int i = javaType.lastIndexOf(".");
if (i >= 0) {
type = javaType.substring(i + 1);
} else {
Expand All @@ -77,56 +86,72 @@ public static String typeOf(String javaType) {
/**
* Determines the string representation of the parameter type.
*/
public static String paramTypeOf(Parameter parameter) {
AnnotationParser p = new AnnotationParser(parameter);
public static String paramTypeOf(final Parameter parameter)
{
final AnnotationParser p = new AnnotationParser(parameter);
if (p.isAnnotatedBy(JAX_RS_PATH_PARAM)) {
return "path";
} else if (p.isAnnotatedBy(JAX_RS_HEADER_PARAM)) {
return "header";
} else if (p.isAnnotatedBy(JAX_RS_QUERY_PARAM)) {
return "query";
} else if(p.isAnnotatedBy(JERSEY_MULTIPART_FORM_PARAM)) {
return "form";
} else if (p.isAnnotatedBy(JERSEY_MULTIPART_FORM_PARAM)) {
return "form";
} else if (p.isAnnotatedBy(DROPWIZARD_AUTH)) {
return "auth";
}

return "body";
}

/**
* Determines the string representation of the parameter name.
*/
public static String paramNameOf(Parameter parameter) {
public static String paramNameOf(final Parameter parameter)
{
// TODO (DL): make this part of Translator?
AnnotationParser p = new AnnotationParser(parameter);
final AnnotationParser p = new AnnotationParser(parameter);
String name = p.getAnnotationValue(JAX_RS_PATH_PARAM, "value");
if (name == null) {
name = p.getAnnotationValue(JAX_RS_QUERY_PARAM, "value");
}
if (name == null) {
name = p.getAnnotationValue(JAX_RS_HEADER_PARAM, "value");
}
if (name == null) {
name = parameter.name();
}
return name;
}

public static boolean isPrimitive(Type type) {
public static boolean isPrimitive(final Type type)
{
return PRIMITIVES.contains(typeOf(type.qualifiedTypeName()));
}

public static class ExcludedAnnotations implements Predicate<AnnotationDesc> {
public static class ExcludedAnnotations implements Predicate<AnnotationDesc>
{
private final DocletOptions options;

public ExcludedAnnotations(DocletOptions options) {
public ExcludedAnnotations(final DocletOptions options)
{
this.options = options;
}

@Override
public boolean apply(AnnotationDesc annotationDesc) {
String annotationClass = annotationDesc.annotationType().qualifiedTypeName();
public boolean apply(final AnnotationDesc annotationDesc)
{
final String annotationClass = annotationDesc.annotationType().qualifiedTypeName();
return options.getExcludeAnnotationClasses().contains(annotationClass);
}
}

public static class JaxRsAnnotations implements Predicate<AnnotationDesc> {
public static class JaxRsAnnotations implements Predicate<AnnotationDesc>
{
@Override
public boolean apply(AnnotationDesc annotationDesc) {
String annotationClass = annotationDesc.annotationType().qualifiedTypeName();
public boolean apply(final AnnotationDesc annotationDesc)
{
final String annotationClass = annotationDesc.annotationType().qualifiedTypeName();
return annotationClass.startsWith(JAX_RS_ANNOTATION_PACKAGE);
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,20 @@

package com.hypnoticocelot.jaxrs.doclet.parser;

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;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import com.google.common.base.Function;
import com.hypnoticocelot.jaxrs.doclet.DocletOptions;
import com.hypnoticocelot.jaxrs.doclet.model.Api;
Expand All @@ -10,13 +25,8 @@
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 {
public class ApiClassParser
{

private final DocletOptions options;
private final ClassDoc classDoc;
Expand All @@ -25,7 +35,8 @@ public class ApiClassParser {
private final Collection<ClassDoc> classes;
private final Method parentMethod;

public ApiClassParser(DocletOptions options, ClassDoc classDoc, Collection<ClassDoc> classes) {
public ApiClassParser(final DocletOptions options, final ClassDoc classDoc, final Collection<ClassDoc> classes)
{
this.options = options;
this.classDoc = classDoc;
this.rootPath = firstNonNull(parsePath(classDoc.annotations()), "");
Expand All @@ -36,9 +47,12 @@ public ApiClassParser(DocletOptions options, ClassDoc classDoc, Collection<Class

/**
* Creates sub-resource class parser.
*
* @param parentMethod method that creates the sub-resource.
*/
public ApiClassParser(DocletOptions options, ClassDoc classDoc, Collection<ClassDoc> classes, Method parentMethod) {
public ApiClassParser(final DocletOptions options, final ClassDoc classDoc, final Collection<ClassDoc> classes,
final Method parentMethod)
{
this.options = options;
this.classDoc = classDoc;
this.rootPath = firstNonNull(parsePath(classDoc.annotations()), "");
Expand All @@ -47,77 +61,88 @@ public ApiClassParser(DocletOptions options, ClassDoc classDoc, Collection<Class
this.parentMethod = parentMethod;
}

public String getRootPath() {
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());
public Collection<Api> parse()
{
final List<Api> apis = new ArrayList<Api>();
final Map<String, Collection<Method>> apiMethods = new HashMap<String, Collection<Method>>();

ClassDoc currentClassDoc = classDoc;
while (currentClassDoc != null) {
for (final MethodDoc method : currentClassDoc.methods()) {
final ApiMethodParser methodParser = parentMethod == null ? new ApiMethodParser(options, rootPath,
method) : new ApiMethodParser(options, parentMethod, method);
final Method parsedMethod = methodParser.parse();
if (parsedMethod == null) {
continue;
}
continue;
}
models.addAll(methodParser.models());
if (parsedMethod.isSubResource()) {
final ClassDoc subResourceClassDoc = lookUpClassDoc(method.returnType());
if (subResourceClassDoc != null) {
// delete class from the dictionary to handle recursive sub-resources
final Collection<ClassDoc> shrunkClasses = new ArrayList<ClassDoc>(classes);
shrunkClasses.remove(currentClassDoc);
// recursively parse the sub-resource class
final 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);
final String realPath = parsedMethod.getPath();
Collection<Method> matchingMethods = apiMethods.get(realPath);
if (matchingMethods == null) {
matchingMethods = new ArrayList<Method>();
apiMethods.put(realPath, matchingMethods);
}
matchingMethods.add(parsedMethod);
}
matchingMethods.add(parsedMethod);
currentClassDoc = currentClassDoc.superclass();
}

for (Map.Entry<String, Collection<Method>> apiEntries : apiMethods.entrySet()) {
Collection<Operation> operations = new ArrayList<Operation>(
transform(apiEntries.getValue(), new Function<Method, Operation>() {
for (final Map.Entry<String, Collection<Method>> apiEntries : apiMethods.entrySet()) {
final Collection<Operation> operations = new ArrayList<Operation>(transform(apiEntries.getValue(),
new Function<Method, Operation>()
{
@Override
public Operation apply(Method method) {
public Operation apply(final Method method)
{
return new Operation(method);
}
})
);
}));
apis.add(new Api(apiEntries.getKey(), "", operations));
}
Collections.sort(apis, new Comparator<Api>() {
Collections.sort(apis, new Comparator<Api>()
{
@Override
public int compare(Api o1, Api o2) {
public int compare(final Api o1, final Api o2)
{
return o1.getPath().compareTo(o2.getPath());
}
});
return apis;
}

private ClassDoc lookUpClassDoc(Type type) {
for (ClassDoc subResourceClassDoc : classes) {
private ClassDoc lookUpClassDoc(final Type type)
{
for (final ClassDoc subResourceClassDoc : classes) {
if (subResourceClassDoc.qualifiedTypeName().equals(type.qualifiedTypeName())) {
return subResourceClassDoc;
}
}
return null;
}

public Collection<Model> models() {
public Collection<Model> models()
{
return models;
}

}
}
Loading