| /** |
| * 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; |
| |
| import java.io.PrintWriter; |
| import java.util.HashMap; |
| import java.util.LinkedHashSet; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| import javax.annotation.processing.RoundEnvironment; |
| import javax.annotation.processing.SupportedAnnotationTypes; |
| import javax.annotation.processing.SupportedSourceVersion; |
| import javax.lang.model.SourceVersion; |
| import javax.lang.model.element.Element; |
| import javax.lang.model.element.ElementKind; |
| import javax.lang.model.element.ExecutableElement; |
| import javax.lang.model.element.TypeElement; |
| import javax.lang.model.element.VariableElement; |
| import javax.lang.model.type.MirroredTypeException; |
| import javax.lang.model.type.TypeKind; |
| import javax.lang.model.type.TypeMirror; |
| import javax.lang.model.util.ElementFilter; |
| import javax.lang.model.util.Elements; |
| |
| import org.apache.camel.spi.Metadata; |
| import org.apache.camel.spi.UriEndpoint; |
| import org.apache.camel.spi.UriParam; |
| import org.apache.camel.spi.UriParams; |
| import org.apache.camel.spi.UriPath; |
| |
| import static org.apache.camel.tools.apt.JsonSchemaHelper.sanitizeDescription; |
| import static org.apache.camel.tools.apt.Strings.canonicalClassName; |
| import static org.apache.camel.tools.apt.Strings.getOrElse; |
| import static org.apache.camel.tools.apt.Strings.isNullOrEmpty; |
| import static org.apache.camel.tools.apt.Strings.safeNull; |
| |
| /** |
| * 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) |
| public class EndpointAnnotationProcessor extends AbstractAnnotationProcessor { |
| |
| private static final String HEADER_FILTER_STRATEGY_JAVADOC = "To use a custom HeaderFilterStrategy to filter header to and from Camel message."; |
| |
| public boolean process(Set<? extends TypeElement> annotations, final RoundEnvironment roundEnv) { |
| if (roundEnv.processingOver()) { |
| return true; |
| } |
| Set<? extends Element> elements = roundEnv.getElementsAnnotatedWith(UriEndpoint.class); |
| for (Element element : elements) { |
| if (element instanceof TypeElement) { |
| processEndpointClass(roundEnv, (TypeElement) element); |
| } |
| } |
| return true; |
| } |
| |
| protected void processEndpointClass(final RoundEnvironment roundEnv, final TypeElement classElement) { |
| final UriEndpoint uriEndpoint = classElement.getAnnotation(UriEndpoint.class); |
| if (uriEndpoint != null) { |
| String scheme = uriEndpoint.scheme(); |
| String title = uriEndpoint.title(); |
| final String label = uriEndpoint.label(); |
| if (!isNullOrEmpty(scheme)) { |
| // support multiple schemes separated by comma, which maps to the exact same component |
| // for example camel-mail has a bunch of component schema names that does that |
| String[] schemes = scheme.split(","); |
| String[] titles = title.split(","); |
| for (int i = 0; i < schemes.length; i++) { |
| final String alias = schemes[i]; |
| final String aliasTitle = i < titles.length ? titles[i] : titles[0]; |
| // write html documentation |
| String name = canonicalClassName(classElement.getQualifiedName().toString()); |
| String packageName = name.substring(0, name.lastIndexOf(".")); |
| String fileName = alias + ".html"; |
| Func1<PrintWriter, Void> handler = new Func1<PrintWriter, Void>() { |
| @Override |
| public Void call(PrintWriter writer) { |
| writeHtmlDocumentation(writer, roundEnv, classElement, uriEndpoint, aliasTitle, alias, label); |
| return null; |
| } |
| }; |
| processFile(packageName, fileName, handler); |
| |
| // write json schema |
| fileName = alias + ".json"; |
| handler = new Func1<PrintWriter, Void>() { |
| @Override |
| public Void call(PrintWriter writer) { |
| writeJSonSchemeDocumentation(writer, roundEnv, classElement, uriEndpoint, aliasTitle, alias, label); |
| return null; |
| } |
| }; |
| processFile(packageName, fileName, handler); |
| } |
| } |
| } |
| } |
| |
| protected void writeHtmlDocumentation(PrintWriter writer, RoundEnvironment roundEnv, TypeElement classElement, UriEndpoint uriEndpoint, |
| String title, String scheme, String label) { |
| writer.println("<html>"); |
| writer.println("<header>"); |
| writer.println("<title>" + title + "</title>"); |
| writer.println("</header>"); |
| writer.println("<body>"); |
| writer.println("<h1>" + title + "</h1>"); |
| writer.println("<b>Scheme: " + scheme + "</b>"); |
| |
| if (label != null) { |
| String[] labels = label.split(","); |
| writer.println("<ul>"); |
| for (String text : labels) { |
| writer.println("<li>" + text + "</li>"); |
| } |
| writer.println("</ul>"); |
| } |
| |
| showDocumentationAndFieldInjections(writer, roundEnv, classElement, ""); |
| |
| // This code is not my fault, it seems to honestly be the hacky way to find a class name in APT :) |
| TypeMirror consumerType = null; |
| try { |
| uriEndpoint.consumerClass(); |
| } catch (MirroredTypeException mte) { |
| consumerType = mte.getTypeMirror(); |
| } |
| |
| boolean found = false; |
| String consumerClassName = null; |
| String consumerPrefix = getOrElse(uriEndpoint.consumerPrefix(), ""); |
| if (consumerType != null) { |
| consumerClassName = consumerType.toString(); |
| TypeElement consumerElement = findTypeElement(roundEnv, consumerClassName); |
| if (consumerElement != null) { |
| writer.println("<h2>" + scheme + " consumer" + "</h2>"); |
| showDocumentationAndFieldInjections(writer, roundEnv, consumerElement, consumerPrefix); |
| found = true; |
| } |
| } |
| if (!found && consumerClassName != null) { |
| warning("APT could not find consumer class " + consumerClassName); |
| } |
| writer.println("</body>"); |
| writer.println("</html>"); |
| } |
| |
| protected void writeJSonSchemeDocumentation(PrintWriter writer, RoundEnvironment roundEnv, TypeElement classElement, UriEndpoint uriEndpoint, |
| String title, String scheme, String label) { |
| // gather component information |
| ComponentModel componentModel = findComponentProperties(roundEnv, uriEndpoint, title, scheme, label); |
| |
| // get endpoint information which is divided into paths and options (though there should really only be one path) |
| Set<EndpointPath> endpointPaths = new LinkedHashSet<EndpointPath>(); |
| Set<EndpointOption> endpointOptions = new LinkedHashSet<EndpointOption>(); |
| Set<ComponentOption> componentOptions = new LinkedHashSet<ComponentOption>(); |
| |
| TypeElement componentClassElement = findTypeElement(roundEnv, componentModel.getJavaType()); |
| if (componentClassElement != null) { |
| findComponentClassProperties(writer, roundEnv, componentOptions, componentClassElement, ""); |
| } |
| |
| findClassProperties(writer, roundEnv, endpointPaths, endpointOptions, classElement, ""); |
| |
| String json = createParameterJsonSchema(componentModel, componentOptions, endpointPaths, endpointOptions); |
| writer.println(json); |
| } |
| |
| public String createParameterJsonSchema(ComponentModel componentModel, Set<ComponentOption> componentOptions, |
| Set<EndpointPath> endpointPaths, Set<EndpointOption> endpointOptions) { |
| StringBuilder buffer = new StringBuilder("{"); |
| // component model |
| buffer.append("\n \"component\": {"); |
| buffer.append("\n \"kind\": \"").append("component").append("\","); |
| buffer.append("\n \"scheme\": \"").append(componentModel.getScheme()).append("\","); |
| buffer.append("\n \"syntax\": \"").append(componentModel.getSyntax()).append("\","); |
| buffer.append("\n \"title\": \"").append(componentModel.getTitle()).append("\","); |
| buffer.append("\n \"description\": \"").append(componentModel.getDescription()).append("\","); |
| buffer.append("\n \"label\": \"").append(getOrElse(componentModel.getLabel(), "")).append("\","); |
| if (componentModel.isConsumerOnly()) { |
| buffer.append("\n \"consumerOnly\": \"").append("true").append("\","); |
| } else if (componentModel.isProducerOnly()) { |
| buffer.append("\n \"producerOnly\": \"").append("true").append("\","); |
| } |
| buffer.append("\n \"javaType\": \"").append(componentModel.getJavaType()).append("\","); |
| buffer.append("\n \"groupId\": \"").append(componentModel.getGroupId()).append("\","); |
| buffer.append("\n \"artifactId\": \"").append(componentModel.getArtifactId()).append("\","); |
| buffer.append("\n \"version\": \"").append(componentModel.getVersionId()).append("\""); |
| buffer.append("\n },"); |
| |
| // and component properties |
| buffer.append("\n \"componentProperties\": {"); |
| boolean first = true; |
| for (ComponentOption entry : componentOptions) { |
| if (first) { |
| first = false; |
| } else { |
| buffer.append(","); |
| } |
| buffer.append("\n "); |
| // as its json we need to sanitize the docs |
| String doc = entry.getDocumentationWithNotes(); |
| doc = sanitizeDescription(doc, false); |
| Boolean required = entry.getRequired() != null ? Boolean.valueOf(entry.getRequired()) : null; |
| String defaultValue = entry.getDefaultValue(); |
| if (Strings.isNullOrEmpty(defaultValue) && "boolean".equals(entry.getType())) { |
| // fallback as false for boolean types |
| defaultValue = "false"; |
| } |
| |
| buffer.append(JsonSchemaHelper.toJson(entry.getName(), "property", required, entry.getType(), defaultValue, doc, |
| entry.isDeprecated(), entry.getLabel(), entry.isEnumType(), entry.getEnums(), false, null)); |
| } |
| buffer.append("\n },"); |
| |
| buffer.append("\n \"properties\": {"); |
| first = true; |
| |
| // include paths in the top |
| for (EndpointPath entry : endpointPaths) { |
| String label = entry.getLabel(); |
| if (label != null) { |
| // skip options which are either consumer or producer labels but the component does not support them |
| if (label.contains("consumer") && componentModel.isProducerOnly()) { |
| continue; |
| } else if (label.contains("producer") && componentModel.isConsumerOnly()) { |
| continue; |
| } |
| } |
| |
| if (first) { |
| first = false; |
| } else { |
| buffer.append(","); |
| } |
| buffer.append("\n "); |
| String doc = entry.getDocumentation(); |
| doc = sanitizeDescription(doc, false); |
| Boolean required = entry.getRequired() != null ? Boolean.valueOf(entry.getRequired()) : null; |
| String defaultValue = entry.getDefaultValue(); |
| if (Strings.isNullOrEmpty(defaultValue) && "boolean".equals(entry.getType())) { |
| // fallback as false for boolean types |
| defaultValue = "false"; |
| } |
| |
| buffer.append(JsonSchemaHelper.toJson(entry.getName(), "path", required, entry.getType(), defaultValue, doc, |
| entry.isDeprecated(), label, entry.isEnumType(), entry.getEnums(), false, null)); |
| } |
| |
| // and then regular parameter options |
| for (EndpointOption entry : endpointOptions) { |
| String label = entry.getLabel(); |
| if (label != null) { |
| // skip options which are either consumer or producer labels but the component does not support them |
| if (label.contains("consumer") && componentModel.isProducerOnly()) { |
| continue; |
| } else if (label.contains("producer") && componentModel.isConsumerOnly()) { |
| continue; |
| } |
| } |
| |
| if (first) { |
| first = false; |
| } else { |
| buffer.append(","); |
| } |
| buffer.append("\n "); |
| // as its json we need to sanitize the docs |
| String doc = entry.getDocumentationWithNotes(); |
| doc = sanitizeDescription(doc, false); |
| Boolean required = entry.getRequired() != null ? Boolean.valueOf(entry.getRequired()) : null; |
| String defaultValue = entry.getDefaultValue(); |
| if (Strings.isNullOrEmpty(defaultValue) && "boolean".equals(entry.getType())) { |
| // fallback as false for boolean types |
| defaultValue = "false"; |
| } |
| |
| buffer.append(JsonSchemaHelper.toJson(entry.getName(), "parameter", required, entry.getType(), defaultValue, |
| doc, entry.isDeprecated(), label, entry.isEnumType(), entry.getEnums(), false, null)); |
| } |
| buffer.append("\n }"); |
| |
| buffer.append("\n}\n"); |
| return buffer.toString(); |
| } |
| |
| protected void showDocumentationAndFieldInjections(PrintWriter writer, RoundEnvironment roundEnv, TypeElement classElement, String prefix) { |
| String classDoc = processingEnv.getElementUtils().getDocComment(classElement); |
| if (!isNullOrEmpty(classDoc)) { |
| // remove dodgy @version that we may have in class javadoc |
| classDoc = classDoc.replaceFirst("\\@version", ""); |
| classDoc = classDoc.trim(); |
| writer.println("<p>" + classDoc + "</p>"); |
| } |
| |
| Set<EndpointPath> endpointPaths = new LinkedHashSet<EndpointPath>(); |
| Set<EndpointOption> endpointOptions = new LinkedHashSet<EndpointOption>(); |
| findClassProperties(writer, roundEnv, endpointPaths, endpointOptions, classElement, prefix); |
| |
| if (!endpointOptions.isEmpty() || !endpointPaths.isEmpty()) { |
| writer.println("<table class='table'>"); |
| writer.println(" <tr>"); |
| writer.println(" <th>Name</th>"); |
| writer.println(" <th>Kind</th>"); |
| writer.println(" <th>Type</th>"); |
| writer.println(" <th>Required</th>"); |
| writer.println(" <th>Deprecated</th>"); |
| writer.println(" <th>Default Value</th>"); |
| writer.println(" <th>Enum Values</th>"); |
| writer.println(" <th>Description</th>"); |
| writer.println(" </tr>"); |
| // include paths in the top |
| for (EndpointPath path : endpointPaths) { |
| writer.println(" <tr>"); |
| writer.println(" <td>" + path.getName() + "</td>"); |
| writer.println(" <td>" + "path" + "</td>"); |
| writer.println(" <td>" + path.getType() + "</td>"); |
| writer.println(" <td>" + safeNull(path.getRequired()) + "</td>"); |
| writer.println(" <td>" + path.isDeprecated() + "</td>"); |
| writer.println(" <td>" + path.getEnumValuesAsHtml() + "</td>"); |
| writer.println(" <td>" + path.getDocumentation() + "</td>"); |
| writer.println(" </tr>"); |
| } |
| // and then regular parameter options |
| for (EndpointOption option : endpointOptions) { |
| writer.println(" <tr>"); |
| writer.println(" <td>" + option.getName() + "</td>"); |
| writer.println(" <td>" + "parameter" + "</td>"); |
| writer.println(" <td>" + option.getType() + "</td>"); |
| writer.println(" <td>" + safeNull(option.getRequired()) + "</td>"); |
| writer.println(" <td>" + option.isDeprecated() + "</td>"); |
| writer.println(" <td>" + option.getDefaultValue() + "</td>"); |
| writer.println(" <td>" + option.getEnumValuesAsHtml() + "</td>"); |
| writer.println(" <td>" + option.getDocumentationWithNotes() + "</td>"); |
| writer.println(" </tr>"); |
| } |
| writer.println("</table>"); |
| } |
| } |
| |
| protected ComponentModel findComponentProperties(RoundEnvironment roundEnv, UriEndpoint uriEndpoint, |
| String title, String scheme, String label) { |
| ComponentModel model = new ComponentModel(scheme); |
| |
| // if the scheme is an alias then replace the scheme name from the syntax with the alias |
| String syntax = scheme + ":" + Strings.after(uriEndpoint.syntax(), ":"); |
| |
| model.setSyntax(syntax); |
| model.setTitle(title); |
| model.setLabel(label); |
| model.setConsumerOnly(uriEndpoint.consumerOnly()); |
| model.setProducerOnly(uriEndpoint.producerOnly()); |
| |
| String data = loadResource("META-INF/services/org/apache/camel/component", scheme); |
| if (data != null) { |
| Map<String, String> map = parseAsMap(data); |
| model.setJavaType(map.get("class")); |
| } |
| |
| data = loadResource("META-INF/services/org/apache/camel", "component.properties"); |
| if (data != null) { |
| Map<String, String> map = parseAsMap(data); |
| // now we have a lot more data, so we need to load it as key/value |
| // need to sanitize the description first |
| String doc = map.get("projectDescription"); |
| if (doc != null) { |
| model.setDescription(sanitizeDescription(doc, true)); |
| } else { |
| model.setDescription(""); |
| } |
| if (map.containsKey("groupId")) { |
| model.setGroupId(map.get("groupId")); |
| } else { |
| model.setGroupId(""); |
| } |
| if (map.containsKey("artifactId")) { |
| model.setArtifactId(map.get("artifactId")); |
| } else { |
| model.setArtifactId(""); |
| } |
| if (map.containsKey("version")) { |
| model.setVersionId(map.get("version")); |
| } else { |
| model.setVersionId(""); |
| } |
| } |
| |
| // favor to use class javadoc of component as description |
| if (model.getJavaType() != null) { |
| Elements elementUtils = processingEnv.getElementUtils(); |
| TypeElement typeElement = findTypeElement(roundEnv, model.getJavaType()); |
| if (typeElement != null) { |
| String doc = elementUtils.getDocComment(typeElement); |
| if (doc != null) { |
| // need to sanitize the description first (we only want a summary) |
| doc = sanitizeDescription(doc, true); |
| // the javadoc may actually be empty, so only change the doc if we got something |
| if (!Strings.isNullOrEmpty(doc)) { |
| model.setDescription(doc); |
| } |
| } |
| } |
| } |
| |
| return model; |
| } |
| |
| protected void findComponentClassProperties(PrintWriter writer, RoundEnvironment roundEnv, Set<ComponentOption> componentOptions, TypeElement classElement, String prefix) { |
| Elements elementUtils = processingEnv.getElementUtils(); |
| while (true) { |
| List<ExecutableElement> methods = ElementFilter.methodsIn(classElement.getEnclosedElements()); |
| for (ExecutableElement method : methods) { |
| String methodName = method.getSimpleName().toString(); |
| boolean deprecated = method.getAnnotation(Deprecated.class) != null; |
| Metadata metadata = method.getAnnotation(Metadata.class); |
| |
| // must be the setter |
| boolean isSetter = methodName.startsWith("set") && method.getParameters().size() == 1 & method.getReturnType().getKind().equals(TypeKind.VOID); |
| if (!isSetter) { |
| continue; |
| } |
| |
| // skip unwanted methods as they are inherited from default component and are not intended for end users to configure |
| if ("setEndpointClass".equals(methodName) || "setCamelContext".equals(methodName) || "setEndpointHeaderFilterStrategy".equals(methodName)) { |
| continue; |
| } |
| |
| // must be a getter/setter pair |
| String fieldName = methodName.substring(3); |
| fieldName = fieldName.substring(0, 1).toLowerCase() + fieldName.substring(1); |
| |
| ExecutableElement setter = method; |
| String name = fieldName; |
| name = prefix + name; |
| TypeMirror fieldType = setter.getParameters().get(0).asType(); |
| String fieldTypeName = fieldType.toString(); |
| TypeElement fieldTypeElement = findTypeElement(roundEnv, fieldTypeName); |
| |
| String required = metadata != null ? metadata.required() : null; |
| String label = metadata != null ? metadata.label() : null; |
| |
| // we do not yet have default values / notes / as no annotation support yet |
| // String defaultValueNote = param.defaultValueNote(); |
| String defaultValue = metadata != null ? metadata.defaultValue() : null; |
| String defaultValueNote = null; |
| |
| String docComment = findJavaDoc(elementUtils, method, fieldName, name, classElement, false); |
| if (docComment == null) { |
| // apt cannot grab javadoc from camel-core, only from annotations |
| if ("setHeaderFilterStrategy".equals(methodName)) { |
| docComment = HEADER_FILTER_STRATEGY_JAVADOC; |
| } else { |
| docComment = ""; |
| } |
| } |
| |
| // gather enums |
| Set<String> enums = new LinkedHashSet<String>(); |
| boolean isEnum = fieldTypeElement != null && fieldTypeElement.getKind() == ElementKind.ENUM; |
| if (isEnum) { |
| TypeElement enumClass = findTypeElement(roundEnv, fieldTypeElement.asType().toString()); |
| // find all the enum constants which has the possible enum value that can be used |
| List<VariableElement> fields = ElementFilter.fieldsIn(enumClass.getEnclosedElements()); |
| for (VariableElement var : fields) { |
| if (var.getKind() == ElementKind.ENUM_CONSTANT) { |
| String val = var.toString(); |
| enums.add(val); |
| } |
| } |
| } |
| |
| ComponentOption option = new ComponentOption(name, fieldTypeName, required, defaultValue, defaultValueNote, |
| docComment.trim(), deprecated, label, isEnum, enums); |
| componentOptions.add(option); |
| } |
| |
| // check super classes which may also have fields |
| TypeElement baseTypeElement = null; |
| TypeMirror superclass = classElement.getSuperclass(); |
| if (superclass != null) { |
| String superClassName = canonicalClassName(superclass.toString()); |
| baseTypeElement = findTypeElement(roundEnv, superClassName); |
| } |
| if (baseTypeElement != null) { |
| classElement = baseTypeElement; |
| } else { |
| break; |
| } |
| } |
| } |
| |
| protected void findClassProperties(PrintWriter writer, RoundEnvironment roundEnv, Set<EndpointPath> endpointPaths, Set<EndpointOption> endpointOptions, TypeElement classElement, String prefix) { |
| Elements elementUtils = processingEnv.getElementUtils(); |
| while (true) { |
| List<VariableElement> fieldElements = ElementFilter.fieldsIn(classElement.getEnclosedElements()); |
| for (VariableElement fieldElement : fieldElements) { |
| |
| Metadata metadata = fieldElement.getAnnotation(Metadata.class); |
| boolean deprecated = fieldElement.getAnnotation(Deprecated.class) != null; |
| |
| UriPath path = fieldElement.getAnnotation(UriPath.class); |
| String fieldName = fieldElement.getSimpleName().toString(); |
| if (path != null) { |
| String name = path.name(); |
| if (isNullOrEmpty(name)) { |
| name = fieldName; |
| } |
| name = prefix + name; |
| |
| String defaultValue = path.defaultValue(); |
| if (defaultValue == null && metadata != null) { |
| defaultValue = metadata.defaultValue(); |
| } |
| String defaultValueNote = path.defaultValueNote(); |
| String required = metadata != null ? metadata.required() : null; |
| String label = path.label(); |
| |
| TypeMirror fieldType = fieldElement.asType(); |
| String fieldTypeName = fieldType.toString(); |
| TypeElement fieldTypeElement = findTypeElement(roundEnv, fieldTypeName); |
| |
| String docComment = findJavaDoc(elementUtils, fieldElement, fieldName, name, classElement, false); |
| if (isNullOrEmpty(docComment)) { |
| docComment = path.description(); |
| } |
| |
| // gather enums |
| Set<String> enums = new LinkedHashSet<String>(); |
| |
| boolean isEnum; |
| if (!Strings.isNullOrEmpty(path.enums())) { |
| isEnum = true; |
| String[] values = path.enums().split(","); |
| for (String val : values) { |
| enums.add(val); |
| } |
| } else { |
| isEnum = fieldTypeElement != null && fieldTypeElement.getKind() == ElementKind.ENUM; |
| if (isEnum) { |
| TypeElement enumClass = findTypeElement(roundEnv, fieldTypeElement.asType().toString()); |
| // find all the enum constants which has the possible enum value that can be used |
| List<VariableElement> fields = ElementFilter.fieldsIn(enumClass.getEnclosedElements()); |
| for (VariableElement var : fields) { |
| if (var.getKind() == ElementKind.ENUM_CONSTANT) { |
| String val = var.toString(); |
| enums.add(val); |
| } |
| } |
| } |
| } |
| |
| EndpointPath ep = new EndpointPath(name, fieldTypeName, required, defaultValue, docComment, deprecated, label, isEnum, enums); |
| endpointPaths.add(ep); |
| } |
| |
| UriParam param = fieldElement.getAnnotation(UriParam.class); |
| fieldName = fieldElement.getSimpleName().toString(); |
| if (param != null) { |
| String name = param.name(); |
| if (isNullOrEmpty(name)) { |
| name = fieldName; |
| } |
| name = prefix + name; |
| |
| String defaultValue = param.defaultValue(); |
| if (defaultValue == null && metadata != null) { |
| defaultValue = metadata.defaultValue(); |
| } |
| String defaultValueNote = param.defaultValueNote(); |
| String required = metadata != null ? metadata.required() : null; |
| String label = param.label(); |
| |
| // if the field type is a nested parameter then iterate through its fields |
| TypeMirror fieldType = fieldElement.asType(); |
| String fieldTypeName = fieldType.toString(); |
| TypeElement fieldTypeElement = findTypeElement(roundEnv, fieldTypeName); |
| UriParams fieldParams = null; |
| if (fieldTypeElement != null) { |
| fieldParams = fieldTypeElement.getAnnotation(UriParams.class); |
| } |
| if (fieldParams != null) { |
| String nestedPrefix = prefix; |
| String extraPrefix = fieldParams.prefix(); |
| if (!isNullOrEmpty(extraPrefix)) { |
| nestedPrefix += extraPrefix; |
| } |
| findClassProperties(writer, roundEnv, endpointPaths, endpointOptions, fieldTypeElement, nestedPrefix); |
| } else { |
| String docComment = findJavaDoc(elementUtils, fieldElement, fieldName, name, classElement, false); |
| if (isNullOrEmpty(docComment)) { |
| docComment = param.description(); |
| } |
| |
| // gather enums |
| Set<String> enums = new LinkedHashSet<String>(); |
| |
| boolean isEnum; |
| if (!Strings.isNullOrEmpty(param.enums())) { |
| isEnum = true; |
| String[] values = param.enums().split(","); |
| for (String val : values) { |
| enums.add(val); |
| } |
| } else { |
| isEnum = fieldTypeElement != null && fieldTypeElement.getKind() == ElementKind.ENUM; |
| if (isEnum) { |
| TypeElement enumClass = findTypeElement(roundEnv, fieldTypeElement.asType().toString()); |
| // find all the enum constants which has the possible enum value that can be used |
| List<VariableElement> fields = ElementFilter.fieldsIn(enumClass.getEnclosedElements()); |
| for (VariableElement var : fields) { |
| if (var.getKind() == ElementKind.ENUM_CONSTANT) { |
| String val = var.toString(); |
| enums.add(val); |
| } |
| } |
| } |
| } |
| |
| EndpointOption option = new EndpointOption(name, fieldTypeName, required, defaultValue, defaultValueNote, |
| docComment.trim(), deprecated, label, isEnum, enums); |
| endpointOptions.add(option); |
| } |
| } |
| } |
| |
| // check super classes which may also have @UriParam fields |
| TypeElement baseTypeElement = null; |
| TypeMirror superclass = classElement.getSuperclass(); |
| if (superclass != null) { |
| String superClassName = canonicalClassName(superclass.toString()); |
| baseTypeElement = findTypeElement(roundEnv, superClassName); |
| } |
| if (baseTypeElement != null) { |
| classElement = baseTypeElement; |
| } else { |
| break; |
| } |
| } |
| } |
| |
| protected Map<String, String> parseAsMap(String data) { |
| Map<String, String> answer = new HashMap<String, String>(); |
| String[] lines = data.split("\n"); |
| for (String line : lines) { |
| int idx = line.indexOf('='); |
| String key = line.substring(0, idx); |
| String value = line.substring(idx + 1); |
| // remove ending line break for the values |
| value = value.trim().replaceAll("\n", ""); |
| answer.put(key.trim(), value); |
| } |
| return answer; |
| } |
| |
| private static final class ComponentModel { |
| |
| private String scheme; |
| private String syntax; |
| private String javaType; |
| private String title; |
| private String description; |
| private String groupId; |
| private String artifactId; |
| private String versionId; |
| private String label; |
| private boolean consumerOnly; |
| private boolean producerOnly; |
| |
| private ComponentModel(String scheme) { |
| this.scheme = scheme; |
| } |
| |
| public String getScheme() { |
| return scheme; |
| } |
| |
| public String getSyntax() { |
| return syntax; |
| } |
| |
| public void setSyntax(String syntax) { |
| this.syntax = syntax; |
| } |
| |
| public String getJavaType() { |
| return javaType; |
| } |
| |
| public void setJavaType(String javaType) { |
| this.javaType = javaType; |
| } |
| |
| public String getTitle() { |
| return title; |
| } |
| |
| public void setTitle(String title) { |
| this.title = title; |
| } |
| |
| public String getDescription() { |
| return description; |
| } |
| |
| public void setDescription(String description) { |
| this.description = description; |
| } |
| |
| public String getGroupId() { |
| return groupId; |
| } |
| |
| public void setGroupId(String groupId) { |
| this.groupId = groupId; |
| } |
| |
| public String getArtifactId() { |
| return artifactId; |
| } |
| |
| public void setArtifactId(String artifactId) { |
| this.artifactId = artifactId; |
| } |
| |
| public String getVersionId() { |
| return versionId; |
| } |
| |
| public void setVersionId(String versionId) { |
| this.versionId = versionId; |
| } |
| |
| public String getLabel() { |
| return label; |
| } |
| |
| public void setLabel(String label) { |
| this.label = label; |
| } |
| |
| public boolean isConsumerOnly() { |
| return consumerOnly; |
| } |
| |
| public void setConsumerOnly(boolean consumerOnly) { |
| this.consumerOnly = consumerOnly; |
| } |
| |
| public boolean isProducerOnly() { |
| return producerOnly; |
| } |
| |
| public void setProducerOnly(boolean producerOnly) { |
| this.producerOnly = producerOnly; |
| } |
| } |
| |
| private static final class ComponentOption { |
| |
| private String name; |
| private String type; |
| private String required; |
| private String defaultValue; |
| private String defaultValueNote; |
| private String documentation; |
| private boolean deprecated; |
| private String label; |
| private boolean enumType; |
| private Set<String> enums; |
| |
| private ComponentOption(String name, String type, String required, String defaultValue, String defaultValueNote, |
| String documentation, boolean deprecated, String label, boolean enumType, Set<String> enums) { |
| this.name = name; |
| this.type = type; |
| this.required = required; |
| this.defaultValue = defaultValue; |
| this.defaultValueNote = defaultValueNote; |
| this.documentation = documentation; |
| this.deprecated = deprecated; |
| this.label = label; |
| this.enumType = enumType; |
| this.enums = enums; |
| } |
| |
| public String getName() { |
| return name; |
| } |
| |
| public String getType() { |
| return type; |
| } |
| |
| public String getRequired() { |
| return required; |
| } |
| |
| public String getDefaultValue() { |
| return defaultValue; |
| } |
| |
| public String getDocumentation() { |
| return documentation; |
| } |
| |
| public boolean isDeprecated() { |
| return deprecated; |
| } |
| |
| public String getEnumValuesAsHtml() { |
| CollectionStringBuffer csb = new CollectionStringBuffer("<br/>"); |
| if (enums != null && enums.size() > 0) { |
| for (String e : enums) { |
| csb.append(e); |
| } |
| } |
| return csb.toString(); |
| } |
| |
| public String getDocumentationWithNotes() { |
| StringBuilder sb = new StringBuilder(); |
| sb.append(documentation); |
| |
| if (!isNullOrEmpty(defaultValueNote)) { |
| sb.append(". Default value notice: ").append(defaultValueNote); |
| } |
| |
| return sb.toString(); |
| } |
| |
| public boolean isEnumType() { |
| return enumType; |
| } |
| |
| public Set<String> getEnums() { |
| return enums; |
| } |
| |
| public String getLabel() { |
| return label; |
| } |
| |
| @Override |
| public boolean equals(Object o) { |
| if (this == o) { |
| return true; |
| } |
| if (o == null || getClass() != o.getClass()) { |
| return false; |
| } |
| |
| ComponentOption that = (ComponentOption) o; |
| |
| if (!name.equals(that.name)) { |
| return false; |
| } |
| |
| return true; |
| } |
| |
| @Override |
| public int hashCode() { |
| return name.hashCode(); |
| } |
| } |
| |
| private static final class EndpointOption { |
| |
| private String name; |
| private String type; |
| private String required; |
| private String defaultValue; |
| private String defaultValueNote; |
| private String documentation; |
| private boolean deprecated; |
| private String label; |
| private boolean enumType; |
| private Set<String> enums; |
| |
| private EndpointOption(String name, String type, String required, String defaultValue, String defaultValueNote, |
| String documentation, boolean deprecated, String label, boolean enumType, Set<String> enums) { |
| this.name = name; |
| this.type = type; |
| this.required = required; |
| this.defaultValue = defaultValue; |
| this.defaultValueNote = defaultValueNote; |
| this.documentation = documentation; |
| this.deprecated = deprecated; |
| this.label = label; |
| this.enumType = enumType; |
| this.enums = enums; |
| } |
| |
| public String getName() { |
| return name; |
| } |
| |
| public String getType() { |
| return type; |
| } |
| |
| public String getRequired() { |
| return required; |
| } |
| |
| public String getDefaultValue() { |
| return defaultValue; |
| } |
| |
| public String getDocumentation() { |
| return documentation; |
| } |
| |
| public boolean isDeprecated() { |
| return deprecated; |
| } |
| |
| public String getEnumValuesAsHtml() { |
| CollectionStringBuffer csb = new CollectionStringBuffer("<br/>"); |
| if (enums != null && enums.size() > 0) { |
| for (String e : enums) { |
| csb.append(e); |
| } |
| } |
| return csb.toString(); |
| } |
| |
| public String getDocumentationWithNotes() { |
| StringBuilder sb = new StringBuilder(); |
| sb.append(documentation); |
| |
| if (!isNullOrEmpty(defaultValueNote)) { |
| sb.append(". Default value notice: ").append(defaultValueNote); |
| } |
| |
| return sb.toString(); |
| } |
| |
| public boolean isEnumType() { |
| return enumType; |
| } |
| |
| public Set<String> getEnums() { |
| return enums; |
| } |
| |
| public String getLabel() { |
| return label; |
| } |
| |
| @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(); |
| } |
| } |
| |
| private static final class EndpointPath { |
| |
| private String name; |
| private String type; |
| private String required; |
| private String defaultValue; |
| private String documentation; |
| private boolean deprecated; |
| private String label; |
| private boolean enumType; |
| private Set<String> enums; |
| |
| private EndpointPath(String name, String type, String required, String defaultValue, String documentation, boolean deprecated, |
| String label, boolean enumType, Set<String> enums) { |
| this.name = name; |
| this.type = type; |
| this.required = required; |
| this.defaultValue = defaultValue; |
| this.documentation = documentation; |
| this.deprecated = deprecated; |
| this.label = label; |
| this.enumType = enumType; |
| this.enums = enums; |
| } |
| |
| public String getName() { |
| return name; |
| } |
| |
| public String getType() { |
| return type; |
| } |
| |
| public String getRequired() { |
| return required; |
| } |
| |
| public String getDefaultValue() { |
| return defaultValue; |
| } |
| |
| public String getDocumentation() { |
| return documentation; |
| } |
| |
| public boolean isDeprecated() { |
| return deprecated; |
| } |
| |
| public String getEnumValuesAsHtml() { |
| CollectionStringBuffer csb = new CollectionStringBuffer("<br/>"); |
| if (enums != null && enums.size() > 0) { |
| for (String e : enums) { |
| csb.append(e); |
| } |
| } |
| return csb.toString(); |
| } |
| |
| public boolean isEnumType() { |
| return enumType; |
| } |
| |
| public Set<String> getEnums() { |
| return enums; |
| } |
| |
| public String getLabel() { |
| return label; |
| } |
| |
| @Override |
| public boolean equals(Object o) { |
| if (this == o) { |
| return true; |
| } |
| if (o == null || getClass() != o.getClass()) { |
| return false; |
| } |
| |
| EndpointPath that = (EndpointPath) o; |
| |
| if (!name.equals(that.name)) { |
| return false; |
| } |
| |
| return true; |
| } |
| |
| @Override |
| public int hashCode() { |
| return name.hashCode(); |
| } |
| } |
| |
| } |