Skip to content

Commit

Permalink
Merge branch 'master' of https://github.com/Inxmail/jaxrs-analyzer in…
Browse files Browse the repository at this point in the history
…to Inxmail-master
  • Loading branch information
sdaschner committed Jul 12, 2018
2 parents e49d937 + cfb6bbd commit 1ba40af
Show file tree
Hide file tree
Showing 16 changed files with 781 additions and 43 deletions.
4 changes: 4 additions & 0 deletions Changelog.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@
Sebastian Daschner

// new versions are placed on the top
== v0.17-SNAPSHOT
- Generating description also in Plaintext, Asciidoc and Markdown
- Added Markdown support
- Added possibility to ignore JAX-RS boundary classes

== v0.14
- Fixed test related execution phase
Expand Down
11 changes: 7 additions & 4 deletions Documentation.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ The `projectPath` entries may be directories or jar files containing the classes

Following available options:

* `-b <backend>` The backend to choose: `swagger` (default), `plaintext`, `asciidoc`
* `-b <backend>` The backend to choose: `swagger` (default), `plaintext`, `asciidoc`, `markdown`
* `-cp <class path>[:class paths...]` The additional class paths containing classes which are used in the project (separated by colon); this may be directories or jar-files
* `-X` Debug enabled (prints error debugging information on Standard error out)
* `-n <project name>` The name of the project
Expand All @@ -57,12 +57,15 @@ Following available backend specific options (only have effect if the correspond
* `--swaggerSchemes <scheme>[,schemes]` The Swagger schemes: `http` (default), `https`, `ws`, `wss`
* `--renderSwaggerTags` Enables rendering of Swagger tags (default tag will be used per default)
* `--swaggerTagsPathOffset <path offset>` The number at which path position the Swagger tags will be extracted (0 will be used per default)
* `--ignoredBoundaryClasses <fully qualified classname [class,...]>` Boundary classes which should be ignored by analyze (empty per default)

== Backends
The Analyzer supports Plaintext, AsciiDoc and Swagger as output format.
The latter two can be processed further.
The Analyzer supports Plaintext, AsciiDoc, Markdown and Swagger as output format.
The latter three can be processed further.

AsciiDoc is a human-readable, lightweight markup language and can be converted to PDF, HTML (and many more) using tools like https://github.com/asciidoctor/asciidoctor[Asciidoctor].
Markdown is a human-readable, lightweight markup langugae.

AsciiDoc is like Markdown. But it can be easily converted to PDF, HTML (and many more) using tools like https://github.com/asciidoctor/asciidoctor[Asciidoctor].

The Swagger JSON file can be used with Swagger UI or the http://editor.swagger.io[Swagger Editor].

Expand Down
2 changes: 1 addition & 1 deletion README.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ Instead of using the Maven plugin, the tool can also run directly from the jar f
You can download the latest version https://github.com/sdaschner/jaxrs-analyzer/releases[here].

== Backends
The Analyzer supports Plaintext, AsciiDoc and Swagger as output format.
The Analyzer supports Plaintext, AsciiDoc, Markdown and Swagger as output format.

== Documentation / Feature list
+...+ can be found https://github.com/sdaschner/jaxrs-analyzer/blob/master/Documentation.adoc[here].
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ public class JAXRSAnalyzer {
private final String projectVersion;
private final Path outputLocation;
private final Backend backend;
private Set<String> ignoredBoundaryClasses = new HashSet<>();


/**
* Constructs a JAX-RS Analyzer.
Expand Down Expand Up @@ -62,11 +64,15 @@ public JAXRSAnalyzer(final Set<Path> projectClassPaths, final Set<Path> projectS
this.backend = backend;
}

public void setIgnoredBoundaryClasses( Set<String> ignoredBoundaryClasses ) {
this.ignoredBoundaryClasses = ignoredBoundaryClasses;
}

/**
* Analyzes the JAX-RS project at the class path and produces the output as configured.
*/
public void analyze() {
final Resources resources = new ProjectAnalyzer(classPaths).analyze(projectClassPaths, projectSourcePaths);
final Resources resources = new ProjectAnalyzer( classPaths, ignoredBoundaryClasses ).analyze(projectClassPaths, projectSourcePaths);

if (resources.isEmpty()) {
LogProvider.info("Empty JAX-RS analysis result, omitting output");
Expand Down
20 changes: 18 additions & 2 deletions src/main/java/com/sebastian_daschner/jaxrs_analyzer/Main.java
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ public class Main {
private static String name = DEFAULT_NAME;
private static String version = DEFAULT_VERSION;
private static String backendType = "swagger";
private static Set<String> ignoredBoundaryClasses = new HashSet<>();
private static Path outputFileLocation;

/**
Expand All @@ -54,7 +55,7 @@ public class Main {
* <p>
* Following available options:
* <ul>
* <li>{@code -b backend} The backend to choose: {@code swagger} (default), {@code plaintext}, {@code asciidoc}</li>
* <li>{@code -b backend} The backend to choose: {@code swagger} (default), {@code plaintext}, {@code asciidoc}, {@code markdown}</li>
* <li>{@code -cp class path[:class paths...]} The additional class paths which contain classes which are used in the project</li>
* <li>{@code -sp source path[:source paths...]} The optional source paths needed for JavaDoc analysis</li>
* <li>{@code -X} Debug enabled (prints error debugging information on Standard error out)</li>
Expand All @@ -70,6 +71,7 @@ public class Main {
* <li>{@code --swaggerSchemes scheme[,schemes]} The Swagger schemes: {@code http} (default), {@code https}, {@code ws}, {@code wss}")</li>
* <li>{@code --renderSwaggerTags} Enables rendering of Swagger tags (will not be rendered per default)</li>
* <li>{@code --swaggerTagsPathOffset path offset} The number at which path position the Swagger tags should be extracted ({@code 0} per default)</li>
* <li>{@code --ignoredBoundaryClasses class[,classes]} Boundary classes which should be ignored by analyze (empty per default)</li>
* </ul>
*
* @param args The arguments
Expand All @@ -92,6 +94,7 @@ public static void main(final String... args) {
backend.configure(attributes);

final JAXRSAnalyzer jaxrsAnalyzer = new JAXRSAnalyzer(projectClassPaths, projectSourcePaths, classPaths, name, version, backend, outputFileLocation);
jaxrsAnalyzer.setIgnoredBoundaryClasses( ignoredBoundaryClasses );
jaxrsAnalyzer.analyze();
}

Expand Down Expand Up @@ -139,6 +142,9 @@ private static void extractArgs(String[] args) {
case "--noInlinePrettify":
attributes.put(StringBackend.INLINE_PRETTIFY, "false");
break;
case "--ignoredBoundaryClasses":
ignoredBoundaryClasses = buildSetOfCommaList( args[++i] );
break;
case "-a":
addAttribute(args[++i]);
break;
Expand Down Expand Up @@ -187,6 +193,15 @@ private static List<Path> extractClassPaths(final String classPaths) {
return paths;
}

private static Set<String> buildSetOfCommaList( String commaList ) {
Set<String> set = new HashSet<>();
if( commaList==null || commaList.trim().isEmpty() )
return set;

set.addAll( Arrays.asList( commaList.split( "," ) ) );
return set;
}

private static void validateArgs() {
if (projectClassPaths.isEmpty()) {
System.err.println("Please provide at least one project path\n");
Expand All @@ -198,7 +213,7 @@ private static void printUsageAndExit() {
System.err.println("Usage: java -jar jaxrs-analyzer.jar [options] classPath [classPaths...]");
System.err.println("The classPath entries may be directories or jar-files containing the classes to be analyzed\n");
System.err.println("Following available options:\n");
System.err.println(" -b <backend> The backend to choose: swagger (default), plaintext, asciidoc");
System.err.println(" -b <backend> The backend to choose: swagger (default), plaintext, asciidoc, mardown");
System.err.println(" -cp <class path>[:class paths] Additional class paths (separated with colon) which contain classes used in the project (may be directories or jar-files)");
System.err.println(" -sp <source path>[:source paths] Optional source paths (separated with colon) needed for JavaDoc analysis (may be directories or jar-files)");
System.err.println(" -X Debug enabled (enabled error debugging information)");
Expand All @@ -212,6 +227,7 @@ private static void printUsageAndExit() {
System.err.println(" --swaggerSchemes <scheme>[,schemes] The Swagger schemes: http (default), https, ws, wss");
System.err.println(" --renderSwaggerTags Enables rendering of Swagger tags (default tag will be used per default)");
System.err.println(" --swaggerTagsPathOffset <path offset> The number at which path position the Swagger tags will be extracted (0 will be used per default)");
System.err.println(" --ignoredBoundaryClasses <fully qualified classname [class,...]> Boundary classes which should be ignored by analyze (empty per default)");
System.err.println(" --noPrettyPrint Don't pretty print inline JSON body representations (will be pretty printed per default)");
System.err.println("\nExample: java -jar jaxrs-analyzer.jar -b swagger -n \"My Project\" -cp ~/libs/lib1.jar:~/libs/project/bin ~/project/target/classes");
System.exit(1);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

import com.sebastian_daschner.jaxrs_analyzer.utils.RestrictedStringHashSet;

import static com.sebastian_daschner.jaxrs_analyzer.model.JavaUtils.isAnnotationPresent;

/**
Expand All @@ -58,7 +60,7 @@ public class ProjectAnalyzer {
// b should have result

private final Lock lock = new ReentrantLock();
private final Set<String> classes = new HashSet<>();
private final RestrictedStringHashSet classes = new RestrictedStringHashSet();
private final ResultInterpreter resultInterpreter = new ResultInterpreter();
private final BytecodeAnalyzer bytecodeAnalyzer = new BytecodeAnalyzer();
private final JavaDocAnalyzer javaDocAnalyzer = new JavaDocAnalyzer();
Expand All @@ -69,6 +71,18 @@ public class ProjectAnalyzer {
* @param classPaths The locations of additional class paths (can be directories or jar-files)
*/
public ProjectAnalyzer(final Set<Path> classPaths) {
this( classPaths, new HashSet<>() );
}

/**
* Creates a project analyzer with given class path locations where to search for classes.
* Additionally a blacklist of skipped classes is provided.
*
* @param classPaths The locations of additional class paths (can be directories or jar-files)
* @param ignoredBoundaryClassnames Set of full qualified classnames to be skipped on analyze
*/
public ProjectAnalyzer(final Set<Path> classPaths, final Set<String> ignoredBoundaryClassnames ) {
classes.getIgnored().addAll( ignoredBoundaryClassnames );
classPaths.forEach(this::addToClassPool);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,19 @@
package com.sebastian_daschner.jaxrs_analyzer.backend.asciidoc;

import com.sebastian_daschner.jaxrs_analyzer.backend.StringBackend;
import com.sebastian_daschner.jaxrs_analyzer.model.Types;
import com.sebastian_daschner.jaxrs_analyzer.model.rest.*;
import com.sebastian_daschner.jaxrs_analyzer.utils.StringUtils;

import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;

import com.sebastian_daschner.jaxrs_analyzer.backend.StringBackend;
import com.sebastian_daschner.jaxrs_analyzer.model.Types;
import com.sebastian_daschner.jaxrs_analyzer.model.rest.MethodParameter;
import com.sebastian_daschner.jaxrs_analyzer.model.rest.ParameterType;
import com.sebastian_daschner.jaxrs_analyzer.model.rest.ResourceMethod;
import com.sebastian_daschner.jaxrs_analyzer.model.rest.Response;
import com.sebastian_daschner.jaxrs_analyzer.model.rest.TypeIdentifier;
import com.sebastian_daschner.jaxrs_analyzer.model.rest.TypeRepresentation;
import com.sebastian_daschner.jaxrs_analyzer.utils.StringUtils;

import static com.sebastian_daschner.jaxrs_analyzer.backend.ComparatorUtils.mapKeyComparator;
import static com.sebastian_daschner.jaxrs_analyzer.backend.ComparatorUtils.parameterComparator;
import static com.sebastian_daschner.jaxrs_analyzer.model.JavaUtils.toReadableType;
Expand All @@ -30,6 +35,8 @@ protected void appendMethod(final String baseUri, final String resource, final R
if (!StringUtils.isBlank(baseUri))
builder.append(baseUri).append('/');
builder.append(resource).append("`\n\n");
if( !StringUtils.isBlank( resourceMethod.getDescription() ) )
builder.append( "=== Description: " ).append( resourceMethod.getDescription() ).append( "\n\n" );
if (resourceMethod.isDeprecated())
builder.append("CAUTION: deprecated\n\n");
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
package com.sebastian_daschner.jaxrs_analyzer.backend.mardown;

import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;

import com.sebastian_daschner.jaxrs_analyzer.backend.StringBackend;
import com.sebastian_daschner.jaxrs_analyzer.model.Types;
import com.sebastian_daschner.jaxrs_analyzer.model.rest.MethodParameter;
import com.sebastian_daschner.jaxrs_analyzer.model.rest.ParameterType;
import com.sebastian_daschner.jaxrs_analyzer.model.rest.ResourceMethod;
import com.sebastian_daschner.jaxrs_analyzer.model.rest.Response;
import com.sebastian_daschner.jaxrs_analyzer.model.rest.TypeIdentifier;
import com.sebastian_daschner.jaxrs_analyzer.model.rest.TypeRepresentation;
import com.sebastian_daschner.jaxrs_analyzer.utils.StringUtils;

import static com.sebastian_daschner.jaxrs_analyzer.backend.ComparatorUtils.mapKeyComparator;
import static com.sebastian_daschner.jaxrs_analyzer.backend.ComparatorUtils.parameterComparator;
import static com.sebastian_daschner.jaxrs_analyzer.model.JavaUtils.toReadableType;


/**
* A backend implementation which produces an Markdown representation of the JAX-RS project.
*
* @author Sven Ehnert
*/
public class MarkdownBackend extends StringBackend {

private static final String NAME = "Markdown";
private static final String DOCUMENT_TITLE = "# REST resources of ";
private static final String TYPE_WILDCARD = "\\*/*";

@Override
protected void appendMethod(final String baseUri, final String resource, final ResourceMethod resourceMethod) {
builder.append("## `").append(resourceMethod.getMethod()).append(' ');
if (!StringUtils.isBlank(baseUri))
builder.append(baseUri).append('/');
builder.append(resource).append("`\n\n");
if( !StringUtils.isBlank( resourceMethod.getDescription() ) )
builder.append( "### Description: " ).append( resourceMethod.getDescription() ).append( "\n\n" );
if (resourceMethod.isDeprecated())
builder.append("CAUTION: deprecated\n\n");
}

@Override
protected void appendRequest(final ResourceMethod resourceMethod) {
builder.append("### Request\n");

if (resourceMethod.getRequestBody() != null) {
builder.append("*Content-Type*: `");
builder.append(resourceMethod.getRequestMediaTypes().isEmpty() ? TYPE_WILDCARD : toString(resourceMethod.getRequestMediaTypes()));
builder.append("` + \n");

builder.append("*Request Body*: (").append(toTypeOrCollection(resourceMethod.getRequestBody())).append(")");
Optional.ofNullable(resources.getTypeRepresentations().get(resourceMethod.getRequestBody())).ifPresent(
this::generateSample);
builder.append("\n");
} else {
builder.append("_No body_ + \n");
}

final Set<MethodParameter> parameters = resourceMethod.getMethodParameters();

appendParams("Path Param", parameters, ParameterType.PATH);
appendParams("Query Param", parameters, ParameterType.QUERY);
appendParams("Form Param", parameters, ParameterType.FORM);
appendParams("Header Param", parameters, ParameterType.HEADER);
appendParams("Cookie Param", parameters, ParameterType.COOKIE);
appendParams("Matrix Param", parameters, ParameterType.MATRIX);

builder.append('\n');
}

private void appendParams(final String name, final Set<MethodParameter> parameters, final ParameterType parameterType) {
parameters.stream().filter(p -> p.getParameterType() == parameterType)
.sorted(parameterComparator()).forEach(p -> builder
.append('*')
.append(name)
.append("*: `")
.append(p.getName())
.append("`, `")
.append(toReadableType(p.getType().getType()))
// TODO add default value
.append("` + \n"));
}

@Override
protected void appendResponse(final ResourceMethod resourceMethod) {
builder.append("### Response\n");

builder.append("*Content-Type*: `");
builder.append(resourceMethod.getResponseMediaTypes().isEmpty() ? TYPE_WILDCARD : toString(resourceMethod.getResponseMediaTypes()));
builder.append("`\n\n");

resourceMethod.getResponses().entrySet().stream().sorted(mapKeyComparator()).forEach(e -> {
builder.append("#### `").append(e.getKey()).append(' ')
.append(javax.ws.rs.core.Response.Status.fromStatusCode(e.getKey()).getReasonPhrase()).append("`\n");
final Response response = e.getValue();
response.getHeaders().forEach(h -> builder.append("*Header*: `").append(h).append("` + \n"));

if (response.getResponseBody() != null) {
builder.append("*Response Body*: ").append('(').append(toTypeOrCollection(response.getResponseBody())).append(")");
Optional.ofNullable(resources.getTypeRepresentations().get(response.getResponseBody())).ifPresent(
this::generateSample);
builder.append("\n");
}

builder.append('\n');
});
}

private void generateSample(TypeRepresentation r) {
builder.append("\n\n```javascript\n");
builder.append(doVisit(r));
builder.append("\n```\n\n");
}

private String toTypeOrCollection(final TypeIdentifier type) {
final TypeRepresentation representation = resources.getTypeRepresentations().get(type);
if (representation != null && !representation.getComponentType().equals(type) && !type.getType().equals(Types.JSON)) {
return "Collection of `" + toReadableType(representation.getComponentType().getType()) + '`';
}
return '`' + toReadableType(type.getType()) + '`';
}

private static String toString(final Set<String> set) {
return set.stream().sorted().map(Object::toString).collect(Collectors.joining(", "));
}

@Override
public String getName() {
return NAME;
}

@Override
protected void appendFirstLine() {
builder.append(DOCUMENT_TITLE).append(projectName).append("\n\n");
}

}
Loading

0 comments on commit 1ba40af

Please sign in to comment.