diff --git a/jaxrs-doclet/src/main/java/com/hypnoticocelot/jaxrs/doclet/parser/ApiClassParser.java b/jaxrs-doclet/src/main/java/com/hypnoticocelot/jaxrs/doclet/parser/ApiClassParser.java index 60d4ba31..8cb8830b 100644 --- a/jaxrs-doclet/src/main/java/com/hypnoticocelot/jaxrs/doclet/parser/ApiClassParser.java +++ b/jaxrs-doclet/src/main/java/com/hypnoticocelot/jaxrs/doclet/parser/ApiClassParser.java @@ -16,6 +16,7 @@ import static com.google.common.collect.Collections2.transform; import static com.hypnoticocelot.jaxrs.doclet.parser.AnnotationHelper.parsePath; + public class ApiClassParser { private final DocletOptions options; @@ -36,6 +37,7 @@ public ApiClassParser(DocletOptions options, ClassDoc classDoc, Collection classes, Method parentMethod) { @@ -55,36 +57,40 @@ public Collection parse() { List apis = new ArrayList(); Map> apiMethods = new HashMap>(); - 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 shrunkClasses = new ArrayList(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()); + ClassDoc currentClassDoc = classDoc; + while (currentClassDoc != null) { + for (MethodDoc method : currentClassDoc.methods()) { + ApiMethodParser methodParser = parentMethod == null ? + new ApiMethodParser(options, rootPath, method) : + new ApiMethodParser(options, parentMethod, method); + Method parsedMethod = methodParser.parse(); + if (parsedMethod == null) { + continue; } - continue; - } - models.addAll(methodParser.models()); + if (parsedMethod.isSubResource()) { + ClassDoc subResourceClassDoc = lookUpClassDoc(method.returnType()); + if (subResourceClassDoc != null) { + // delete class from the dictionary to handle recursive sub-resources + Collection shrunkClasses = new ArrayList(classes); + shrunkClasses.remove(currentClassDoc); + // 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 matchingMethods = apiMethods.get(realPath); - if (matchingMethods == null) { - matchingMethods = new ArrayList(); - apiMethods.put(realPath, matchingMethods); + String realPath = parsedMethod.getPath(); + Collection matchingMethods = apiMethods.get(realPath); + if (matchingMethods == null) { + matchingMethods = new ArrayList(); + apiMethods.put(realPath, matchingMethods); + } + matchingMethods.add(parsedMethod); } - matchingMethods.add(parsedMethod); + currentClassDoc = currentClassDoc.superclass(); } for (Map.Entry> apiEntries : apiMethods.entrySet()) { diff --git a/jaxrs-doclet/src/test/java/com/hypnoticocelot/jaxrs/doclet/apidocs/InheritanceTest.java b/jaxrs-doclet/src/test/java/com/hypnoticocelot/jaxrs/doclet/apidocs/InheritanceTest.java new file mode 100644 index 00000000..b61637f4 --- /dev/null +++ b/jaxrs-doclet/src/test/java/com/hypnoticocelot/jaxrs/doclet/apidocs/InheritanceTest.java @@ -0,0 +1,40 @@ +package com.hypnoticocelot.jaxrs.doclet.apidocs; + +import com.hypnoticocelot.jaxrs.doclet.DocletOptions; +import com.hypnoticocelot.jaxrs.doclet.Recorder; +import com.hypnoticocelot.jaxrs.doclet.model.ApiDeclaration; +import com.hypnoticocelot.jaxrs.doclet.parser.JaxRsAnnotationParser; +import com.sun.javadoc.RootDoc; +import org.junit.Before; +import org.junit.Test; + +import java.io.File; +import java.io.IOException; + +import static com.hypnoticocelot.jaxrs.doclet.apidocs.FixtureLoader.loadFixture; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; + + +public class InheritanceTest { + private Recorder recorderMock; + private DocletOptions options; + + @Before + public void setup() { + recorderMock = mock(Recorder.class); + options = new DocletOptions().setRecorder(recorderMock); + } + + @Test + public void testStart() throws IOException { + final RootDoc rootDoc = RootDocLoader.fromPath("src/test/resources", "fixtures.inheritance"); + new JaxRsAnnotationParser(options, rootDoc).run(); + + final ApiDeclaration api = loadFixture("/fixtures/inheritance/concrete.json", ApiDeclaration.class); + verify(recorderMock).record(any(File.class), eq(api)); + } + +} diff --git a/jaxrs-doclet/src/test/resources/fixtures/inheritance/AbstractResource.java b/jaxrs-doclet/src/test/resources/fixtures/inheritance/AbstractResource.java new file mode 100644 index 00000000..54b69ef9 --- /dev/null +++ b/jaxrs-doclet/src/test/resources/fixtures/inheritance/AbstractResource.java @@ -0,0 +1,17 @@ +package fixtures.inheritance; + +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; + + +public abstract class AbstractResource { + + @GET + @Path("{id}") + public String getById(@PathParam("id") String id) { + return getResourceById(id); + } + + protected abstract String getResourceById(String id); +} diff --git a/jaxrs-doclet/src/test/resources/fixtures/inheritance/ConcreteResource.java b/jaxrs-doclet/src/test/resources/fixtures/inheritance/ConcreteResource.java new file mode 100644 index 00000000..25823bc2 --- /dev/null +++ b/jaxrs-doclet/src/test/resources/fixtures/inheritance/ConcreteResource.java @@ -0,0 +1,20 @@ +package fixtures.inheritance; + +import javax.ws.rs.GET; +import javax.ws.rs.Path; + + +@Path("/foo") +public class ConcreteResource extends AbstractResource { + + @GET + @Path("bar") + public String bar() { + return "bar"; + } + + @Override + protected String getResourceById(String id) { + return "Concrete Resource with id " + id; + } +} diff --git a/jaxrs-doclet/src/test/resources/fixtures/inheritance/concrete.json b/jaxrs-doclet/src/test/resources/fixtures/inheritance/concrete.json new file mode 100644 index 00000000..c207bd08 --- /dev/null +++ b/jaxrs-doclet/src/test/resources/fixtures/inheritance/concrete.json @@ -0,0 +1,37 @@ +{ + "apiVersion": "0", + "swaggerVersion": "1.1", + "basePath": "http://localhost:8080", + "resourcePath": "/foo", + "apis": [ + { + "path": "/foo/bar", + "description": "", + "operations": [ + { + "httpMethod": "GET", + "nickname": "bar", + "responseClass": "string" + } + ] + }, + { + "path": "/foo/{id}", + "description": "", + "operations": [ + { + "httpMethod": "GET", + "nickname": "getById", + "responseClass": "string", + "parameters": [ + { + "paramType": "path", + "name": "id", + "dataType": "string" + } + ] + } + ] + } + ] +}