Camel apt improved html doc generation. And working on generating json schema as well.
Project: http://git-wip-us.apache.org/repos/asf/camel/repo Commit: http://git-wip-us.apache.org/repos/asf/camel/commit/34230db8 Tree: http://git-wip-us.apache.org/repos/asf/camel/tree/34230db8 Diff: http://git-wip-us.apache.org/repos/asf/camel/diff/34230db8 Branch: refs/heads/master Commit: 34230db8d5a4518eff8eeaf575333f6696339e74 Parents: 59322fd Author: Claus Ibsen <davscl...@apache.org> Authored: Thu Nov 6 08:44:32 2014 +0100 Committer: Claus Ibsen <davscl...@apache.org> Committed: Fri Nov 7 13:25:17 2014 +0100 ---------------------------------------------------------------------- .../apache/camel/util/StringQuoteHelper.java | 1 - .../tools/apt/EndpointAnnotationProcessor.java | 113 ++++++++++------ .../tools/apt/util/CollectionStringBuffer.java | 59 +++++++++ .../camel/tools/apt/util/JsonSchemaHelper.java | 128 +++++++++++++++++++ .../apache/camel/tools/apt/util/Strings.java | 29 +++++ 5 files changed, 288 insertions(+), 42 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/camel/blob/34230db8/camel-core/src/main/java/org/apache/camel/util/StringQuoteHelper.java ---------------------------------------------------------------------- diff --git a/camel-core/src/main/java/org/apache/camel/util/StringQuoteHelper.java b/camel-core/src/main/java/org/apache/camel/util/StringQuoteHelper.java index 4a02dd1..8837c3a 100644 --- a/camel-core/src/main/java/org/apache/camel/util/StringQuoteHelper.java +++ b/camel-core/src/main/java/org/apache/camel/util/StringQuoteHelper.java @@ -53,7 +53,6 @@ public final class StringQuoteHelper { return quote + text + quote; } - /** * Splits the input safely honoring if values is enclosed in quotes. * <p/> http://git-wip-us.apache.org/repos/asf/camel/blob/34230db8/tooling/apt/src/main/java/org/apache/camel/tools/apt/EndpointAnnotationProcessor.java ---------------------------------------------------------------------- diff --git a/tooling/apt/src/main/java/org/apache/camel/tools/apt/EndpointAnnotationProcessor.java b/tooling/apt/src/main/java/org/apache/camel/tools/apt/EndpointAnnotationProcessor.java index 501527b..467070f 100644 --- a/tooling/apt/src/main/java/org/apache/camel/tools/apt/EndpointAnnotationProcessor.java +++ b/tooling/apt/src/main/java/org/apache/camel/tools/apt/EndpointAnnotationProcessor.java @@ -23,13 +23,9 @@ import java.io.PrintWriter; import java.io.StringWriter; import java.io.Writer; import java.net.URI; -import java.util.ArrayList; +import java.util.LinkedHashSet; import java.util.List; -import java.util.Map; import java.util.Set; -import java.util.SortedMap; -import java.util.TreeMap; - import javax.annotation.processing.AbstractProcessor; import javax.annotation.processing.Filer; import javax.annotation.processing.RoundEnvironment; @@ -57,7 +53,7 @@ import org.apache.camel.tools.apt.util.Strings; import static org.apache.camel.tools.apt.util.Strings.canonicalClassName; /** - * Processes all Camel endpoints + * Processes all Camel {@link UriEndpoint}s and generate json schema and html documentation for the endpoint/component. */ @SupportedAnnotationTypes({"org.apache.camel.spi.*"}) @SupportedSourceVersion(SourceVersion.RELEASE_7) @@ -87,6 +83,7 @@ public class EndpointAnnotationProcessor extends AbstractProcessor { @Override public Void call(PrintWriter writer) { writeHtmlDocumentation(writer, roundEnv, classElement, uriEndpoint); + writeJSonSchemeDocumentation(writer, roundEnv, classElement, uriEndpoint); return null; } }; @@ -95,6 +92,10 @@ public class EndpointAnnotationProcessor extends AbstractProcessor { } } + protected void writeJSonSchemeDocumentation(PrintWriter writer, RoundEnvironment roundEnv, TypeElement classElement, UriEndpoint uriEndpoint) { + // todo + } + protected void writeHtmlDocumentation(PrintWriter writer, RoundEnvironment roundEnv, TypeElement classElement, UriEndpoint uriEndpoint) { writer.println("<html>"); writer.println("<header>"); @@ -138,36 +139,33 @@ public class EndpointAnnotationProcessor extends AbstractProcessor { protected void showDocumentationAndFieldInjections(PrintWriter writer, RoundEnvironment roundEnv, TypeElement classElement, String prefix) { String classDoc = processingEnv.getElementUtils().getDocComment(classElement); if (!Strings.isNullOrEmpty(classDoc)) { - writer.println("<p>" + classDoc.trim() + "</p>"); + // remove dodgy @version that we may have in class javadoc + classDoc = classDoc.replaceFirst("\\@version", ""); + classDoc = classDoc.trim(); + writer.println("<p>" + classDoc + "</p>"); } - SortedMap<String, List<String>> sortedMap = new TreeMap<String, List<String>>(); - findClassProperties(roundEnv, sortedMap, classElement, prefix); - if (!sortedMap.isEmpty()) { + Set<EndpointOption> endpointOptions = new LinkedHashSet<>(); + findClassProperties(roundEnv, endpointOptions, classElement, prefix); + if (!endpointOptions.isEmpty()) { writer.println("<table class='table'>"); writer.println(" <tr>"); writer.println(" <th>Name</th>"); writer.println(" <th>Type</th>"); writer.println(" <th>Description</th>"); - // see defaultValue above - // writer.println(" <th>Default Value</th>"); writer.println(" </tr>"); - Set<Map.Entry<String, List<String>>> entries = sortedMap.entrySet(); - for (Map.Entry<String, List<String>> entry : entries) { - String name = entry.getKey(); - List<String> values = entry.getValue(); + for (EndpointOption option : endpointOptions) { writer.println(" <tr>"); - writer.println(" <td>" + name + "</td>"); - for (String value : values) { - writer.println(value); - } + writer.println(" <td>" + option.getName() + "</td>"); + writer.println(" <td>" + option.getType() + "</td>"); + writer.println(" <td>" + option.getDocumentation() + "</td>"); writer.println(" </tr>"); } writer.println("</table>"); } } - protected void findClassProperties(RoundEnvironment roundEnv, SortedMap<String, List<String>> sortedMap, TypeElement classElement, String prefix) { + protected void findClassProperties(RoundEnvironment roundEnv, Set<EndpointOption> endpointOptions, TypeElement classElement, String prefix) { Elements elementUtils = processingEnv.getElementUtils(); while (true) { List<VariableElement> fieldElements = ElementFilter.fieldsIn(classElement.getEnclosedElements()); @@ -197,7 +195,7 @@ public class EndpointAnnotationProcessor extends AbstractProcessor { if (!Strings.isNullOrEmpty(extraPrefix)) { nestedPrefix += extraPrefix; } - findClassProperties(roundEnv, sortedMap, fieldTypeElement, nestedPrefix); + findClassProperties(roundEnv, endpointOptions, fieldTypeElement, nestedPrefix); } else { String docComment = elementUtils.getDocComment(fieldElement); if (Strings.isNullOrEmpty(docComment)) { @@ -221,21 +219,9 @@ public class EndpointAnnotationProcessor extends AbstractProcessor { if (docComment == null) { docComment = ""; } - List<String> values = new ArrayList<String>(); - values.add(" <td>" + fieldTypeName + "</td>"); - values.add(" <td>" + docComment.trim() + "</td>"); - - // TODO would be nice here to create a default endpoint/consumer object - // and return the default value of the field so we can put it into the docs - Object defaultValue = null; - if (defaultValue != null) { - values.add(" <td>" + defaultValue + "</td>"); - } - if (sortedMap.containsKey(name)) { - error("Duplicate parameter annotation named '" + name + "' on class " + classElement.getQualifiedName()); - } else { - sortedMap.put(name, values); - } + + EndpointOption option = new EndpointOption(name, fieldTypeName, docComment.trim()); + endpointOptions.add(option); } } } @@ -253,7 +239,6 @@ public class EndpointAnnotationProcessor extends AbstractProcessor { } } - protected TypeElement findTypeElement(RoundEnvironment roundEnv, String className) { if (!Strings.isNullOrEmpty(className) && !"java.lang.Object".equals(className)) { Set<? extends Element> rootElements = roundEnv.getRootElements(); @@ -270,20 +255,19 @@ public class EndpointAnnotationProcessor extends AbstractProcessor { return null; } - /** * Helper method to produce class output text file using the given handler */ protected void processFile(String packageName, String scheme, String fileName, Func1<PrintWriter, Void> handler) { PrintWriter writer = null; try { - Writer out = null; + Writer out; Filer filer = processingEnv.getFiler(); FileObject resource; try { resource = filer.getResource(StandardLocation.CLASS_OUTPUT, packageName, fileName); } catch (Throwable e) { - resource = filer.createResource(StandardLocation.CLASS_OUTPUT, packageName, fileName, new Element[0]); + resource = filer.createResource(StandardLocation.CLASS_OUTPUT, packageName, fileName); } URI uri = resource.toUri(); File file = null; @@ -330,6 +314,53 @@ public class EndpointAnnotationProcessor extends AbstractProcessor { e.printStackTrace(writer); writer.close(); processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, buffer.toString()); + } + + private static final class EndpointOption { + + private String name; + private String type; + private String documentation; + + private EndpointOption(String name, String type, String documentation) { + this.name = name; + this.type = type; + this.documentation = documentation; + } + + public String getName() { + return name; + } + + public String getType() { + return type; + } + + public String getDocumentation() { + return documentation; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + EndpointOption that = (EndpointOption) o; + + if (!name.equals(that.name)) { + return false; + } + + return true; + } + + @Override + public int hashCode() { + return name.hashCode(); + } } } http://git-wip-us.apache.org/repos/asf/camel/blob/34230db8/tooling/apt/src/main/java/org/apache/camel/tools/apt/util/CollectionStringBuffer.java ---------------------------------------------------------------------- diff --git a/tooling/apt/src/main/java/org/apache/camel/tools/apt/util/CollectionStringBuffer.java b/tooling/apt/src/main/java/org/apache/camel/tools/apt/util/CollectionStringBuffer.java new file mode 100644 index 0000000..01fbddd --- /dev/null +++ b/tooling/apt/src/main/java/org/apache/camel/tools/apt/util/CollectionStringBuffer.java @@ -0,0 +1,59 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.camel.tools.apt.util; + +/** + /** + * A little helper class for converting a collection of values to a (usually comma separated) string. + */ +public class CollectionStringBuffer { + + private final StringBuilder buffer = new StringBuilder(); + private String separator; + private boolean first = true; + + public CollectionStringBuffer() { + this(", "); + } + + public CollectionStringBuffer(String separator) { + this.separator = separator; + } + + @Override + public String toString() { + return buffer.toString(); + } + + public void append(Object value) { + if (first) { + first = false; + } else { + buffer.append(separator); + } + buffer.append(value); + } + + public String getSeparator() { + return separator; + } + + public void setSeparator(String separator) { + this.separator = separator; + } + +} http://git-wip-us.apache.org/repos/asf/camel/blob/34230db8/tooling/apt/src/main/java/org/apache/camel/tools/apt/util/JsonSchemaHelper.java ---------------------------------------------------------------------- diff --git a/tooling/apt/src/main/java/org/apache/camel/tools/apt/util/JsonSchemaHelper.java b/tooling/apt/src/main/java/org/apache/camel/tools/apt/util/JsonSchemaHelper.java new file mode 100644 index 0000000..6a4f4c4 --- /dev/null +++ b/tooling/apt/src/main/java/org/apache/camel/tools/apt/util/JsonSchemaHelper.java @@ -0,0 +1,128 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.camel.tools.apt.util; + +import static org.apache.camel.tools.apt.util.Strings.doubleQuote; + +/** + * A helper class for <a href="http://json-schema.org/">JSON schema</a>. + */ +public final class JsonSchemaHelper { + + private JsonSchemaHelper() { + } + + public static String toJson(String name, String type, String description) { +// if (type.isEnum()) { +// String typeName = "string"; +// CollectionStringBuffer sb = new CollectionStringBuffer(); +// for (Object value : parameterType.getEnumConstants()) { +// sb.append(doubleQuote(value.toString())); +// } +// return doubleQuote(name) + ": { \"type\": " + doubleQuote(type) + ", \"enum\": [ " + sb.toString() + " ] }"; +// } else if (parameterType.isArray()) { +// String typeName = "array"; +// return doubleQuote(name) + ": { \"type\": " + doubleQuote(type) + " }"; +// } else { + String typeName = JsonSchemaHelper.getType(type); + if ("object".equals(typeName)) { + // for object then include the javaType as a description so we know that + return doubleQuote(name) + ": { \"type\": " + doubleQuote(typeName) + + ", \"properties\": { \"javaType\": { \"description\": \"" + type + "\", \"type\": \"string\" } } }"; + } else { + return doubleQuote(name) + ": { \"type\": " + doubleQuote(typeName) + " }"; + } +// } + + } + + /** + * Gets the JSon schema type. + * + * @param type the java type + * @return the json schema type, is never null, but returns <tt>object</tt> as the generic type + */ + public static String getType(String type) { + // TODO: +// if (type.isEnum()) { +// return "enum"; +// } else if (type.isArray()) { +// return "array"; +// } + + String primitive = getPrimitiveType(type); + if (primitive != null) { + return primitive; + } + + return "object"; + } + + /** + * Gets the JSon schema primitive type. + * + * @param name the java type + * @return the json schema primitive type, or <tt>null</tt> if not a primitive + */ + public static String getPrimitiveType(String name) { + + // special for byte[] or Object[] as its common to use + if ("java.lang.byte[]".equals(name) || "byte[]".equals(name)) { + return "string"; + } else if ("java.lang.Byte[]".equals(name) || "Byte[]".equals(name)) { + return "array"; + } else if ("java.lang.Object[]".equals(name) || "Object[]".equals(name)) { + return "array"; + } else if ("java.lang.String[]".equals(name) || "String[]".equals(name)) { + return "array"; + // and these is common as well + } else if ("java.lang.String".equals(name) || "String".equals(name)) { + return "string"; + } else if ("java.lang.Boolean".equals(name) || "Boolean".equals(name)) { + return "boolean"; + } else if ("boolean".equals(name)) { + return "boolean"; + } else if ("java.lang.Integer".equals(name) || "Integer".equals(name)) { + return "integer"; + } else if ("int".equals(name)) { + return "integer"; + } else if ("java.lang.Long".equals(name) || "Long".equals(name)) { + return "integer"; + } else if ("long".equals(name)) { + return "integer"; + } else if ("java.lang.Short".equals(name) || "Short".equals(name)) { + return "integer"; + } else if ("short".equals(name)) { + return "integer"; + } else if ("java.lang.Byte".equals(name) || "Byte".equals(name)) { + return "integer"; + } else if ("byte".equals(name)) { + return "integer"; + } else if ("java.lang.Float".equals(name) || "Float".equals(name)) { + return "number"; + } else if ("float".equals(name)) { + return "number"; + } else if ("java.lang.Double".equals(name) || "Double".equals(name)) { + return "number"; + } else if ("double".equals(name)) { + return "number"; + } + + return null; + } + +} http://git-wip-us.apache.org/repos/asf/camel/blob/34230db8/tooling/apt/src/main/java/org/apache/camel/tools/apt/util/Strings.java ---------------------------------------------------------------------- diff --git a/tooling/apt/src/main/java/org/apache/camel/tools/apt/util/Strings.java b/tooling/apt/src/main/java/org/apache/camel/tools/apt/util/Strings.java index bfa8c6b..f07bb79 100644 --- a/tooling/apt/src/main/java/org/apache/camel/tools/apt/util/Strings.java +++ b/tooling/apt/src/main/java/org/apache/camel/tools/apt/util/Strings.java @@ -20,9 +20,11 @@ package org.apache.camel.tools.apt.util; * Some String helper methods */ public final class Strings { + private Strings() { //Helper class } + /** * Returns true if the given text is null or empty string */ @@ -49,4 +51,31 @@ public final class Strings { return className; } } + + /** + * Returns the text wrapped double quotes + */ + public static String doubleQuote(String text) { + return quote(text, "\""); + } + + /** + * Returns the text wrapped single quotes + */ + public static String singleQuote(String text) { + return quote(text, "'"); + } + + /** + * Wraps the text in the given quote text + * + * @param text the text to wrap in quotes + * @param quote the quote text added to the prefix and postfix of the text + * + * @return the text wrapped in the given quotes + */ + public static String quote(String text, String quote) { + return quote + text + quote; + } + }