org.apache.camel.maven.packaging.SchemaGeneratorMojo Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of camel-package-maven-plugin Show documentation
Show all versions of camel-package-maven-plugin Show documentation
Maven plugin to help package Camel components and plugins
The newest version!
/*
* 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.maven.packaging;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Type;
import java.net.URI;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import java.util.stream.Collectors;
import jakarta.xml.bind.annotation.XmlAttribute;
import jakarta.xml.bind.annotation.XmlElement;
import jakarta.xml.bind.annotation.XmlElementRef;
import jakarta.xml.bind.annotation.XmlElementWrapper;
import jakarta.xml.bind.annotation.XmlElements;
import jakarta.xml.bind.annotation.XmlRootElement;
import jakarta.xml.bind.annotation.XmlType;
import jakarta.xml.bind.annotation.XmlValue;
import javax.inject.Inject;
import org.apache.camel.maven.packaging.generics.GenericsUtil;
import org.apache.camel.maven.packaging.generics.PackagePluginUtils;
import org.apache.camel.spi.AsPredicate;
import org.apache.camel.spi.Metadata;
import org.apache.camel.tooling.model.EipModel;
import org.apache.camel.tooling.model.EipModel.EipOptionModel;
import org.apache.camel.tooling.model.JsonMapper;
import org.apache.camel.tooling.util.JavadocHelper;
import org.apache.camel.tooling.util.PackageHelper;
import org.apache.camel.tooling.util.Strings;
import org.apache.camel.tooling.util.srcgen.GenericType;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugins.annotations.LifecyclePhase;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.plugins.annotations.ResolutionScope;
import org.apache.maven.project.MavenProjectHelper;
import org.codehaus.plexus.build.BuildContext;
import org.jboss.forge.roaster.Roaster;
import org.jboss.forge.roaster.model.source.FieldSource;
import org.jboss.forge.roaster.model.source.JavaClassSource;
import org.jboss.forge.roaster.model.source.MethodSource;
import org.jboss.jandex.AnnotationTarget.Kind;
import org.jboss.jandex.AnnotationValue;
import org.jboss.jandex.ClassInfo;
import org.jboss.jandex.ClassInfo.NestingType;
import org.jboss.jandex.DotName;
import org.jboss.jandex.IndexView;
import static java.lang.reflect.Modifier.isStatic;
@Mojo(name = "generate-schema", threadSafe = true, requiresDependencyResolution = ResolutionScope.COMPILE_PLUS_RUNTIME,
defaultPhase = LifecyclePhase.PROCESS_CLASSES)
public class SchemaGeneratorMojo extends AbstractGeneratorMojo {
public static final DotName XML_ROOT_ELEMENT = DotName.createSimple(XmlRootElement.class.getName());
public static final DotName XML_TYPE = DotName.createSimple(XmlType.class.getName());
// special for app package
private static final DotName APP_PACKAGE = DotName.createSimple("org.apache.camel.model.app");
private static final Map APP_NAME_MAPPINGS = Map.of(
"BeanConstructorDefinition", "constructor",
"BeanConstructorsDefinition", "constructors",
"BeanPropertiesDefinition", "properties",
"BeanPropertyDefinition", "property",
"RegistryBeanDefinition", "bean");
// special when using expression/predicates in the model
private static final String ONE_OF_TYPE_NAME = "org.apache.camel.model.ExpressionSubElementDefinition";
private static final String[] ONE_OF_LANGUAGES = new String[] {
"org.apache.camel.model.language.ExpressionDefinition",
"org.apache.camel.model.language.NamespaceAwareExpression" };
// special for inputs (these classes have sub classes, so we use this to find all classes)
private static final String[] ONE_OF_INPUTS
= new String[] { "org.apache.camel.model.ProcessorDefinition", "org.apache.camel.model.rest.VerbDefinition" };
// special for outputs (these classes have sub classes, so we use this to
// find all classes - and not in particular if they support outputs or not)
private static final String[] ONE_OF_OUTPUTS = new String[] {
"org.apache.camel.model.ProcessorDefinition", "org.apache.camel.model.NoOutputDefinition",
"org.apache.camel.model.OutputDefinition", "org.apache.camel.model.OutputExpressionNode",
"org.apache.camel.model.NoOutputExpressionNode", "org.apache.camel.model.SendDefinition",
"org.apache.camel.model.InterceptDefinition", "org.apache.camel.model.WhenDefinition",
"org.apache.camel.model.ToDynamicDefinition" };
// special for verbs (these classes have sub classes, so we use this to find all classes)
private static final String[] ONE_OF_VERBS = new String[] { "org.apache.camel.model.rest.VerbDefinition" };
private static final String[] ONE_OF_ABSTRACTS = new String[] {
"org.apache.camel.model.InterceptDefinition",
"org.apache.camel.model.InterceptFromDefinition",
"org.apache.camel.model.InterceptSendToEndpointDefinition",
"org.apache.camel.model.OnCompletionDefinition",
"org.apache.camel.model.OnExceptionDefinition",
"org.apache.camel.model.PolicyDefinition",
"org.apache.camel.model.SagaDefinition",
"org.apache.camel.model.TransactedDefinition" };
@Parameter(defaultValue = "${project.build.outputDirectory}")
protected File classesDirectory;
@Parameter(defaultValue = "${project.basedir}/src/generated/java")
protected File sourcesOutputDir;
@Parameter(defaultValue = "${project.basedir}/src/generated/resources")
protected File resourcesOutputDir;
private IndexView indexView;
private final Map sources = new HashMap<>();
@Inject
public SchemaGeneratorMojo(MavenProjectHelper projectHelper, BuildContext buildContext) {
super(projectHelper, buildContext);
}
@Override
public void execute() throws MojoExecutionException, MojoFailureException {
if (classesDirectory == null) {
classesDirectory = new File(project.getBuild().getOutputDirectory());
}
if (sourcesOutputDir == null) {
sourcesOutputDir = new File(project.getBasedir(), "src/generated/java");
}
if (resourcesOutputDir == null) {
resourcesOutputDir = new File(project.getBasedir(), "src/generated/resources");
}
if (!classesDirectory.isDirectory()) {
return;
}
IndexView index = getIndex();
index.getAnnotations(XML_ROOT_ELEMENT);
//
// @XmlRootElement
// core elements
//
Set coreElements = index.getAnnotations(XML_ROOT_ELEMENT).stream()
.filter(cpa -> cpa.target().kind() == Kind.CLASS)
.filter(cpa -> cpa.target().asClass().nestingType() == NestingType.TOP_LEVEL)
.filter(cpa -> cpa.target().asClass().name().toString().startsWith("org.apache.camel.model."))
.map(cpa -> cpa.target().asClass())
.collect(Collectors.toSet());
if (!coreElements.isEmpty()) {
getLog().info(String.format("Found %d core elements", coreElements.size()));
}
// add special for bean DSL in app package
Set appElements = index.getClassesInPackage(APP_PACKAGE).stream()
.filter(cpa -> cpa.hasAnnotation(XML_TYPE))
.filter(cpa -> cpa.kind() == Kind.CLASS)
.collect(Collectors.toSet());
if (!coreElements.isEmpty()) {
getLog().info(String.format("Found %d app elements", appElements.size()));
}
Set classes = new TreeSet<>(Comparator.comparing(ClassInfo::name));
classes.addAll(coreElements);
classes.addAll(appElements);
// we want them to be sorted
for (ClassInfo element : classes) {
processModelClass(element);
}
// spring elements
Set springElements = index.getAnnotations(XML_ROOT_ELEMENT).stream()
.filter(cpa -> cpa.target().kind() == Kind.CLASS)
.filter(cpa -> cpa.target().asClass().nestingType() == NestingType.TOP_LEVEL)
.filter(cpa -> {
String javaTypeName = cpa.target().asClass().name().toString();
return javaTypeName.startsWith("org.apache.camel.spring.")
|| javaTypeName.startsWith("org.apache.camel.core.xml.");
})
.map(cpa -> cpa.target().asClass())
.collect(Collectors.toSet());
if (!springElements.isEmpty()) {
getLog().info(String.format("Found %d spring elements", springElements.size()));
}
for (ClassInfo element : springElements) {
processModelClass(element);
}
}
private void processModelClass(ClassInfo element) {
// skip abstract classes
if (Modifier.isAbstract(element.flags())) {
return;
}
// skip unwanted classes which are "abstract" holders
if (element.name().toString().equals(ONE_OF_TYPE_NAME)) {
return;
}
String aName = null;
if (element.hasAnnotation(XML_ROOT_ELEMENT)) {
AnnotationValue av = element.declaredAnnotation(XML_ROOT_ELEMENT).value("name");
aName = av != null ? av.asString() : null;
}
if (Strings.isNullOrEmpty(aName) || "##default".equals(aName)) {
if (element.hasAnnotation(XML_TYPE)) {
AnnotationValue av = element.declaredAnnotation(XML_TYPE).value("name");
aName = av != null ? av.asString() : null;
}
}
if (aName == null) {
// special for app package
String sn = element.name().withoutPackagePrefix();
aName = APP_NAME_MAPPINGS.get(sn);
}
if (aName == null) {
getLog().warn(
"Class is not annotated with @XmlRootElement or @XmlType. Skipping class: " + element.name().toString());
return;
}
final String name = aName;
// lets use the xsd name as the file name
String fileName;
if (Strings.isNullOrEmpty(name) || "##default".equals(name)) {
fileName = element.simpleName() + PackageHelper.JSON_SUFIX;
} else {
fileName = name + PackageHelper.JSON_SUFIX;
}
String javaTypeName = element.name().toString();
Class> classElement = loadClass(javaTypeName);
// gather eip information and what metadata the EIP can store as exchange properties
EipModel eipModel = findEipModelProperties(classElement, name);
findEipModelExchangeProperties(classElement, name, eipModel);
// get endpoint information which is divided into paths and options
// (though there should really only be one path)
Set eipOptions = new TreeSet<>(new EipOptionComparator(eipModel));
findClassProperties(eipOptions, classElement, classElement, "", name);
eipOptions.forEach(eipModel::addOption);
eipOptions.forEach(o -> {
// compute group based on label for each option
String group = EndpointHelper.labelAsGroupName(o.getLabel(), false, false);
o.setGroup(group);
});
// after we have found all the options then figure out if the model
// accepts input/output
eipModel.setAbstractModel(hasAbstract(classElement));
eipModel.setInput(hasInput(classElement));
eipModel.setOutput(hasOutput(eipModel));
if (Strings.isNullOrEmpty(eipModel.getTitle())) {
eipModel.setTitle(Strings.asTitle(eipModel.getName()));
}
if (!eipModel.isOutput()) {
// filter out outputs if we do not support it
eipModel.getOptions().removeIf(o -> "outputs".equals(o.getName()));
}
if ("route".equals(eipModel.getName())) {
// route should not have disabled
eipModel.getOptions().removeIf(o -> "disabled".equals(o.getName()));
}
// write json schema file
String packageName = javaTypeName.substring(0, javaTypeName.lastIndexOf('.'));
String json = JsonMapper.createParameterJsonSchema(eipModel);
updateResource(
resourcesOutputDir.toPath(),
"META-INF/" + packageName.replace('.', '/') + "/" + fileName,
json);
}
private IndexView getIndex() {
if (indexView == null) {
indexView = PackagePluginUtils.readJandexIndexQuietly(project);
}
return indexView;
}
protected EipModel findEipModelProperties(Class> classElement, String name) {
EipModel model = new EipModel();
model.setJavaType(classElement.getName());
model.setName(name);
boolean deprecated = classElement.getAnnotation(Deprecated.class) != null;
model.setDeprecated(deprecated);
Metadata metadata = classElement.getAnnotation(Metadata.class);
if (metadata != null) {
if (!Strings.isNullOrEmpty(metadata.label())) {
model.setLabel(metadata.label());
}
if (!Strings.isNullOrEmpty(metadata.title())) {
model.setTitle(metadata.title());
}
if (!Strings.isNullOrEmpty(metadata.firstVersion())) {
model.setFirstVersion(metadata.firstVersion());
}
}
// favor to use class javadoc of component as description
String doc = getDocComment(classElement);
if (doc != null) {
// need to sanitize the description first (we only want a
// summary)
doc = JavadocHelper.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 findEipModelExchangeProperties(Class> classElement, String name, EipModel eipModel) {
// load Exchange and find options for this EIP
Class> clazz;
if ("circuitBreaker".equals(name)) {
// special for circuit breaker
clazz = loadClass("org.apache.camel.spi.CircuitBreakerConstants");
} else {
clazz = loadClass("org.apache.camel.Exchange");
}
for (Field field : clazz.getFields()) {
if ((isStatic(field.getModifiers()) && field.getType() == String.class)
&& field.isAnnotationPresent(Metadata.class)) {
Metadata metadata = field.getAnnotation(Metadata.class);
String labels = metadata.label();
for (String lab : labels.split(",")) {
if (lab.equals(name)) {
EipOptionModel o = new EipOptionModel();
o.setKind("exchangeProperty");
// SPLIT_INDEX => CamelSplitIndex
String n = field.getName().toLowerCase().replace('_', '-');
String n2 = SchemaHelper.dashToCamelCase(n);
String valueName = "Camel" + Character.toUpperCase(n2.charAt(0)) + n2.substring(1);
o.setName(valueName);
o.setDescription(metadata.description());
if (!metadata.displayName().isEmpty()) {
o.setDisplayName(metadata.displayName());
} else {
o.setDisplayName(Strings.asTitle(o.getName().substring(5)));
}
o.setRequired(metadata.required());
o.setDefaultValue(metadata.defaultValue());
o.setDeprecated(field.isAnnotationPresent(Deprecated.class));
if (!metadata.deprecationNote().isEmpty()) {
o.setDeprecationNote(metadata.deprecationNote());
}
o.setSecret(metadata.secret());
o.setJavaType(metadata.javaType());
// special if the property is for input (such as AGGREGATION_COMPLETE_CURRENT_GROUP)
if (labels.startsWith("consumer,")) {
o.setLabel("consumer");
} else {
o.setLabel("producer");
}
eipModel.addExchangeProperty(o);
break;
}
}
}
}
}
protected void findClassProperties(
Set eipOptions,
Class> originalClassType, Class> classElement,
String prefix, String modelName) {
while (true) {
for (Field fieldElement : classElement.getDeclaredFields()) {
String fieldName = fieldElement.getName();
XmlAttribute attribute = fieldElement.getAnnotation(XmlAttribute.class);
if (attribute != null) {
boolean skip = processAttribute(originalClassType, classElement, fieldElement, fieldName, attribute,
eipOptions, prefix);
if (skip) {
continue;
}
}
XmlValue value = fieldElement.getAnnotation(XmlValue.class);
if (value != null) {
processValue(originalClassType, classElement, fieldElement, fieldName, eipOptions, prefix,
modelName);
}
XmlElements elements = fieldElement.getAnnotation(XmlElements.class);
if (elements != null) {
processElements(originalClassType, classElement, elements, fieldElement, eipOptions, prefix);
}
XmlElement element = fieldElement.getAnnotation(XmlElement.class);
if (element != null) {
processElement(originalClassType, classElement, element, fieldElement, eipOptions, prefix);
}
// special for eips which has outputs or requires an expressions
XmlElementRef elementRef = fieldElement.getAnnotation(XmlElementRef.class);
if (elementRef != null) {
// special for routes
processRoutes(originalClassType, fieldElement, fieldName, eipOptions);
// special for outputs
processOutputs(originalClassType, elementRef, fieldElement, null, fieldName, eipOptions, prefix);
// special for when clauses (choice eip)
processRefWhenClauses(originalClassType, elementRef, fieldElement, fieldName, eipOptions, prefix);
// special for rests (rest-dsl)
processRests(originalClassType, fieldElement, fieldName, eipOptions);
// special for verbs (rest-dsl)
processVerbs(originalClassType, elementRef, fieldElement, fieldName, eipOptions, prefix);
// special for expression
processRefExpression(originalClassType, classElement, elementRef, fieldElement, fieldName, eipOptions,
prefix);
// special for setHeaders/setVariables
processSetHeadersOrSetVariables(modelName, originalClassType, elementRef, fieldElement, fieldName,
eipOptions, prefix);
}
}
for (Method methodElement : classElement.getDeclaredMethods()) {
String methodName = methodElement.getName();
if (methodName.startsWith("set")) {
methodName = Character.toLowerCase(methodName.charAt(3)) + methodName.substring(4);
}
// special for eips which has outputs or requires an expressions
XmlElementRef elementRef = methodElement.getAnnotation(XmlElementRef.class);
if (elementRef != null) {
if ("RouteDefinition".equals(classElement.getSimpleName())) {
// special for route as we handle this specially
continue;
}
// special for outputs
processOutputs(originalClassType, elementRef, null, methodElement, methodName, eipOptions, prefix);
}
}
// special when we process these nodes as they do not use JAXB
// annotations on fields, but on methods
if ("OptionalIdentifiedDefinition".equals(classElement.getSimpleName())) {
processIdentified(classElement, eipOptions);
} else if ("RouteDefinition".equals(classElement.getSimpleName())) {
processRoute(classElement, eipOptions);
}
// check super classes which may also have fields
Class> superclass = classElement.getSuperclass();
if (superclass != null) {
classElement = superclass;
} else {
break;
}
}
}
private boolean processAttribute(
Class> originalClassType, Class> classElement,
Field fieldElement, String fieldName,
XmlAttribute attribute, Set eipOptions,
String prefix) {
String name = attribute.name();
if (Strings.isNullOrEmpty(name) || "##default".equals(name)) {
name = fieldName;
}
// we want to skip inheritErrorHandler which is only applicable for
// the load-balancer
boolean loadBalancer = "LoadBalanceDefinition".equals(originalClassType.getSimpleName());
if (!loadBalancer && "inheritErrorHandler".equals(name)) {
return true;
}
Metadata metadata = fieldElement.getAnnotation(Metadata.class);
name = prefix + name;
Class> fieldTypeElement = fieldElement.getType();
String fieldTypeName = getTypeName(GenericsUtil.resolveType(originalClassType, fieldElement));
boolean isDuration = false;
if (metadata != null && !Strings.isNullOrEmpty(metadata.javaType())) {
String jt = metadata.javaType();
if ("java.time.Duration".equals(jt)) {
isDuration = true;
} else {
fieldTypeName = jt;
}
}
String defaultValue = findDefaultValue(fieldElement, fieldTypeName);
String docComment = findJavaDoc(fieldElement, fieldName, name, classElement, true);
boolean required = attribute.required();
// metadata may overrule element required
required = findRequired(fieldElement, required);
// gather enums
Set enums;
boolean isEnum;
if (metadata != null && !Strings.isNullOrEmpty(metadata.enums())) {
// use the order from the metadata
enums = new LinkedHashSet<>();
isEnum = true;
String[] values = metadata.enums().split(",");
for (String val : values) {
enums.add(val.trim());
}
} else {
enums = new TreeSet<>(); // sort the enums A..Z
isEnum = fieldTypeElement.isEnum();
if (isEnum) {
for (Object val : fieldTypeElement.getEnumConstants()) {
String str = val.toString();
enums.add(str);
}
}
}
String displayName = null;
if (metadata != null) {
displayName = metadata.displayName();
}
boolean deprecated = fieldElement.getAnnotation(Deprecated.class) != null;
String deprecationNote = null;
if (metadata != null) {
deprecationNote = metadata.deprecationNote();
}
String label = null;
if (metadata != null) {
label = metadata.label();
}
EipOptionModel ep = createOption(name, displayName, "attribute", fieldTypeName,
required, defaultValue, label, docComment, deprecated, deprecationNote, isEnum, enums,
null, false, isDuration);
eipOptions.add(ep);
return false;
}
private void processValue(
Class> originalClassType, Class> classElement, Field fieldElement,
String fieldName, Set eipOptions, String prefix, String modelName) {
// XmlValue has no name attribute
String name = fieldName;
if ("expression".equals(name) && !expressionRequired(modelName)) {
// skip expression attribute on these three languages as they are
// solely configured using attributes
return;
}
name = prefix + name;
String fieldTypeName = getTypeName(GenericsUtil.resolveType(originalClassType, fieldElement));
String defaultValue = findDefaultValue(fieldElement, fieldTypeName);
String docComment = findJavaDoc(fieldElement, fieldName, name, classElement, true);
boolean required = true;
// metadata may overrule element required
required = findRequired(fieldElement, required);
String displayName = null;
boolean isDuration = false;
Metadata metadata = fieldElement.getAnnotation(Metadata.class);
if (metadata != null && !Strings.isNullOrEmpty(metadata.javaType())) {
String jt = metadata.javaType();
if ("java.time.Duration".equals(jt)) {
isDuration = true;
} else {
fieldTypeName = jt;
}
}
if (metadata != null) {
displayName = metadata.displayName();
}
boolean deprecated = fieldElement.getAnnotation(Deprecated.class) != null;
String deprecationNote = null;
if (metadata != null) {
deprecationNote = metadata.deprecationNote();
}
String label = null;
if (metadata != null) {
label = metadata.label();
}
EipOptionModel ep = createOption(name, displayName, "value", fieldTypeName, required,
defaultValue, label, docComment, deprecated, deprecationNote, false, null,
null, false, isDuration);
eipOptions.add(ep);
}
private void processElement(
Class> originalClassType, Class> classElement, XmlElement element, Field fieldElement,
Set eipOptions, String prefix) {
String fieldName = fieldElement.getName();
if (element != null) {
Metadata metadata = fieldElement.getAnnotation(Metadata.class);
String name = fetchElementName(element, fieldElement, prefix);
Class> fieldTypeElement = fieldElement.getType();
String fieldTypeName = getTypeName(GenericsUtil.resolveType(originalClassType, fieldElement));
boolean isDuration = false;
if (metadata != null && !Strings.isNullOrEmpty(metadata.javaType())) {
String jt = metadata.javaType();
if ("java.time.Duration".equals(jt)) {
isDuration = true;
} else {
fieldTypeName = jt;
}
}
String defaultValue = findDefaultValue(fieldElement, fieldTypeName);
String docComment = findJavaDoc(fieldElement, fieldName, name, classElement, true);
boolean required = element.required();
// metadata may overrule element required
required = findRequired(fieldElement, required);
// is it used as predicate (check field first and then fallback to
// its class)
boolean asPredicate = fieldElement.getAnnotation(AsPredicate.class) != null;
if (!asPredicate) {
asPredicate = classElement.getAnnotation(AsPredicate.class) != null;
}
// gather enums
Set enums = new TreeSet<>();
boolean isEnum;
if (metadata != null && !Strings.isNullOrEmpty(metadata.enums())) {
isEnum = true;
String[] values = metadata.enums().split(",");
for (String val : values) {
enums.add(val.trim());
}
} else {
isEnum = fieldTypeElement.isEnum();
if (isEnum) {
for (Object val : fieldTypeElement.getEnumConstants()) {
String str = val.toString();
enums.add(str);
}
}
}
String kind = "element";
// gather oneOf expression/predicates which uses language
Set oneOfTypes = new TreeSet<>();
boolean isOneOf = ONE_OF_TYPE_NAME.equals(fieldTypeName);
if (isOneOf) {
// okay its actually an language expression, so favor using that
// in the eip option
kind = "expression";
oneOfTypes = getOneOfs(ONE_OF_LANGUAGES);
}
// special for otherwise as we want to indicate that the element is
if ("otherwise".equals(name)) {
oneOfTypes.add("otherwise");
}
String displayName = null;
if (metadata != null) {
displayName = metadata.displayName();
}
boolean deprecated = fieldElement.getAnnotation(Deprecated.class) != null;
String deprecationNote = null;
if (metadata != null) {
deprecationNote = metadata.deprecationNote();
}
String label = null;
if (metadata != null) {
label = metadata.label();
}
EipOptionModel ep = createOption(name, displayName, kind, fieldTypeName, required, defaultValue, label,
docComment, deprecated, deprecationNote, isEnum, enums,
oneOfTypes, asPredicate, isDuration);
eipOptions.add(ep);
}
}
private void processElements(
Class> originalClassType, Class> classElement, XmlElements elements, Field fieldElement,
Set eipOptions, String prefix) {
String fieldName = fieldElement.getName();
if (elements != null) {
String name = fieldName;
name = prefix + name;
String fieldTypeName = getTypeName(GenericsUtil.resolveType(originalClassType, fieldElement));
String defaultValue = findDefaultValue(fieldElement, fieldTypeName);
String docComment = findJavaDoc(fieldElement, fieldName, name, classElement, true);
boolean required = true;
required = findRequired(fieldElement, required);
// gather oneOf of the elements
Set oneOfTypes = new TreeSet<>();
for (XmlElement element : elements.value()) {
String child = element.name();
oneOfTypes.add(child);
}
String displayName = null;
Metadata metadata = fieldElement.getAnnotation(Metadata.class);
if (metadata != null) {
displayName = metadata.displayName();
}
boolean deprecated = fieldElement.getAnnotation(Deprecated.class) != null;
String deprecationNote = null;
if (metadata != null) {
deprecationNote = metadata.deprecationNote();
}
String label = null;
if (metadata != null) {
label = metadata.label();
}
final String kind = "element";
EipOptionModel ep = createOption(name, displayName, kind, fieldTypeName, required, defaultValue, label, docComment,
deprecated, deprecationNote, false, null, oneOfTypes,
false, false);
eipOptions.add(ep);
}
}
private void processRoute(Class> classElement, Set eipOptions) {
// group
String docComment = findJavaDoc(null, "group", null, classElement, true);
EipOptionModel ep
= createOption("group", "Group", "attribute", "java.lang.String", false, "", "", docComment, false, null,
false, null, null, false, false);
eipOptions.add(ep);
// nodePrefixId
docComment = findJavaDoc(null, "nodePrefixId", null, classElement, true);
ep = createOption("nodePrefixId", "Node Prefix Id", "attribute", "java.lang.String", false, "", "", docComment, false,
null,
false, null, null, false, false);
eipOptions.add(ep);
// routeConfigurationId
docComment = findJavaDoc(null, "routeConfigurationId", null, classElement, true);
ep = createOption("routeConfigurationId", "Route Configuration Id", "attribute", "java.lang.String", false, "", "",
docComment, false, null,
false, null, null, false, false);
eipOptions.add(ep);
// autoStartup
docComment = findJavaDoc(null, "autoStartup", null, classElement, true);
ep = createOption("autoStartup", "Auto Startup", "attribute", "java.lang.String", false, "true", "", docComment, false,
null, false, null, null, false, false);
eipOptions.add(ep);
// startupOrder
docComment = findJavaDoc(null, "startupOrder", null, classElement, true);
ep = createOption("startupOrder", "Startup Order", "attribute", "java.lang.Integer", false, "", "advanced", docComment,
false,
null,
false, null, null, false, false);
eipOptions.add(ep);
// stream cache
docComment = findJavaDoc(null, "streamCache", null, classElement, true);
ep = createOption("streamCache", "Stream Cache", "attribute", "java.lang.String", false, "", "", docComment, false,
null,
false, null, null, false, false);
eipOptions.add(ep);
// trace
docComment = findJavaDoc(null, "trace", null, classElement, true);
ep = createOption("trace", "Trace", "attribute", "java.lang.String", false, "", "", docComment, false, null, false,
null,
null, false, false);
eipOptions.add(ep);
// message history
docComment = findJavaDoc(null, "messageHistory", null, classElement, true);
ep = createOption("messageHistory", "Message History", "attribute", "java.lang.String", false, "", "", docComment,
false, null, false, null, null, false, false);
eipOptions.add(ep);
// log mask
docComment = findJavaDoc(null, "logMask", null, classElement, true);
ep = createOption("logMask", "Log Mask", "attribute", "java.lang.String", false, "false", "", docComment, false, null,
false, null, null, false, false);
eipOptions.add(ep);
// delayer
docComment = findJavaDoc(null, "delayer", null, classElement, true);
ep = createOption("delayer", "Delayer", "attribute", "java.lang.String", false, "advanced", "", docComment, false, null,
false,
null, null, false, true);
eipOptions.add(ep);
// errorHandlerRef
docComment = findJavaDoc(null, "errorHandlerRef", null, classElement, true);
ep = createOption("errorHandlerRef", "Error Handler", "attribute", "java.lang.String", false, "", "error", docComment,
false,
null, false, null, null, false, false);
eipOptions.add(ep);
// routePolicyRef
docComment = findJavaDoc(null, "routePolicyRef", null, classElement, true);
ep = createOption("routePolicyRef", "Route Policy", "attribute", "java.lang.String", false, "", "", docComment, false,
null,
false, null, null, false, false);
eipOptions.add(ep);
// shutdownRoute
Set enums = new LinkedHashSet<>();
enums.add("Default");
enums.add("Defer");
docComment = findJavaDoc(null, "shutdownRoute", "Default", classElement, true);
ep = createOption("shutdownRoute", "Shutdown Route", "attribute", "org.apache.camel.ShutdownRoute", false, "",
"advanced",
docComment, false, null, true, enums, null, false, false);
eipOptions.add(ep);
// shutdownRunningTask
enums = new LinkedHashSet<>();
enums.add("CompleteCurrentTaskOnly");
enums.add("CompleteAllTasks");
docComment = findJavaDoc(null, "shutdownRunningTask", "CompleteCurrentTaskOnly", classElement, true);
ep = createOption("shutdownRunningTask", "Shutdown Running Task", "attribute", "org.apache.camel.ShutdownRunningTask",
false, "", "advanced", docComment, false, null, true, enums,
null, false, false);
eipOptions.add(ep);
// precondition
docComment = findJavaDoc(null, "precondition", null, classElement, true);
ep = createOption("precondition", "Precondition", "attribute", "java.lang.String", false, "", "advanced", docComment,
false,
null, false, null, null, false, false);
eipOptions.add(ep);
// error handler
docComment = findJavaDoc(null, "errorHandler", null, classElement, true);
ep = createOption("errorHandler", "Error Handler", "element", "org.apache.camel.model.ErrorHandlerDefinition", false,
"",
"error", docComment, false,
null, false, null, null, false, false);
eipOptions.add(ep);
// input type
docComment = findJavaDoc(null, "inputType", null, classElement, true);
ep = createOption("inputType", "Input Type", "element", "org.apache.camel.model.InputTypeDefinition", false, "",
"advanced", docComment, false,
null, false, null, null, false, false);
eipOptions.add(ep);
// output type
docComment = findJavaDoc(null, "outputType", null, classElement, true);
ep = createOption("outputType", "Output Type", "element", "org.apache.camel.model.OutputTypeDefinition", false, "",
"advanced", docComment, false,
null, false, null, null, false, false);
eipOptions.add(ep);
// input
Set oneOfTypes = new TreeSet<>();
oneOfTypes.add("from");
docComment = findJavaDoc(null, "input", null, classElement, true);
ep = createOption("input", "Input", "element", "org.apache.camel.model.FromDefinition", true, "", "", docComment, false,
null, false, null, oneOfTypes, false, false);
eipOptions.add(ep);
// outputs
// gather oneOf which extends any of the output base classes
oneOfTypes = new TreeSet<>();
// find all classes that has that superClassName
for (String superclass : ONE_OF_OUTPUTS) {
for (ClassInfo ci : getIndex().getAllKnownSubclasses(DotName.createSimple(superclass))) {
Class> child = loadClass(ci.name().toString());
XmlRootElement rootElement = child.getAnnotation(XmlRootElement.class);
if (rootElement != null) {
String childName = rootElement.name();
oneOfTypes.add(childName);
}
}
}
// remove some types which are not intended as an output in eips
oneOfTypes.remove("route");
docComment = findJavaDoc(null, "outputs", null, classElement, true);
ep = createOption("outputs", "Outputs", "element", "java.util.List>",
true, "", "", docComment, false, null, false, null,
oneOfTypes, false, false);
eipOptions.add(ep);
}
/**
* Special for process the OptionalIdentifiedDefinition
*/
private void processIdentified(Class> classElement, Set eipOptions) {
// id
String docComment = findJavaDoc(null, "id", null, classElement, true);
EipOptionModel ep
= createOption("id", "Id", "attribute", "java.lang.String", false, "", "", docComment, false, null, false,
null, null, false, false);
eipOptions.add(ep);
// description
docComment = findJavaDoc(null, "description", null, classElement, true);
ep = createOption("description", "Description", "attribute", "java.lang.String", false, "",
"",
docComment, false, null, false, null, null,
false, false);
eipOptions.add(ep);
}
/**
* Special for processing an @XmlElementRef routes field
*/
private void processRoutes(
Class> originalClassType,
Field fieldElement, String fieldName,
Set eipOptions) {
if ("routes".equals(fieldName)) {
String fieldTypeName = getTypeName(GenericsUtil.resolveType(originalClassType, fieldElement));
Set oneOfTypes = new TreeSet<>();
oneOfTypes.add("route");
EipOptionModel ep = createOption("routes", "Routes", "element",
fieldTypeName, false, "", "", "Contains the Camel routes",
false, null, false, null, oneOfTypes,
false, false);
eipOptions.add(ep);
}
}
/**
* Special for processing an @XmlElementRef rests field
*/
private void processRests(
Class> originalClassType,
Field fieldElement, String fieldName,
Set eipOptions) {
if ("rests".equals(fieldName)) {
String fieldTypeName = getTypeName(GenericsUtil.resolveType(originalClassType, fieldElement));
Set oneOfTypes = new TreeSet<>();
oneOfTypes.add("rest");
EipOptionModel ep = createOption("rests", "Rests", "element", fieldTypeName, false, "", "",
"Contains the rest services defined using the rest-dsl", false, null, false,
null, oneOfTypes, false, false);
eipOptions.add(ep);
}
}
/**
* Special for processing an SetHeaders/SetVariables
*/
private void processSetHeadersOrSetVariables(
String modelName,
Class> originalClassType,
XmlElementRef elementRef,
Field fieldElement, String fieldName,
Set eipOptions,
String prefix) {
if (!"setHeaders".equals(modelName) && !"setVariables".equals(modelName)) {
// only for these two EIPs
return;
}
if ("headers".equals(fieldName) || "variables".equals(fieldName)) {
String name = fetchName(elementRef.name(), fieldName, prefix);
String typeName = getTypeName(GenericsUtil.resolveType(originalClassType, fieldElement));
Set oneOfTypes;
if ("headers".equals(fieldName)) {
oneOfTypes = Set.of("setHeader");
} else {
oneOfTypes = Set.of("setVariable");
}
String displayName = null;
Metadata metadata = fieldElement.getAnnotation(Metadata.class);
if (metadata != null) {
displayName = metadata.displayName();
}
boolean deprecated = fieldElement.getAnnotation(Deprecated.class) != null;
String deprecationNote = null;
if (metadata != null) {
deprecationNote = metadata.deprecationNote();
}
String label = null;
if (metadata != null) {
label = metadata.label();
}
String kind = "element";
EipOptionModel ep
= createOption(name, displayName, kind, typeName, true, "", label,
"Contains the " + fieldName + " to be set", deprecated, deprecationNote,
false, null, oneOfTypes, false, false);
eipOptions.add(ep);
}
}
/**
* Special for processing an @XmlElementRef outputs field
*/
private void processOutputs(
Class> originalClassType, XmlElementRef elementRef,
Field fieldElement, Method methodElement, String fieldOrMethodName, Set eipOptions, String prefix) {
if ("outputs".equals(fieldOrMethodName) && supportOutputs(originalClassType)) {
String name = fetchName(elementRef.name(), fieldOrMethodName, prefix);
String typeName;
if (fieldElement != null) {
typeName = getTypeName(GenericsUtil.resolveType(originalClassType, fieldElement));
} else {
typeName = getTypeName(GenericsUtil.resolveSetterType(originalClassType, methodElement));
}
// gather oneOf which extends any of the output base classes
Set oneOfTypes = getOneOfs(ONE_OF_OUTPUTS);
// remove some types which are not intended as an output in eips
oneOfTypes.remove("route");
String displayName = null;
Metadata metadata = fieldElement != null ? fieldElement.getAnnotation(Metadata.class) : null;
if (metadata == null) {
metadata = methodElement != null ? methodElement.getAnnotation(Metadata.class) : null;
}
if (metadata != null) {
displayName = metadata.displayName();
}
boolean deprecated = false;
if (fieldElement != null && fieldElement.getAnnotation(Deprecated.class) != null
|| methodElement != null && methodElement.getAnnotation(Deprecated.class) != null) {
deprecated = true;
}
String deprecationNote = null;
if (metadata != null) {
deprecationNote = metadata.deprecationNote();
}
String label = null;
if (metadata != null) {
label = metadata.label();
}
String kind = "element";
EipOptionModel ep
= createOption(name, displayName, kind, typeName, true, "", label, "", deprecated, deprecationNote,
false, null, oneOfTypes, false, false);
eipOptions.add(ep);
}
}
/**
* Special for processing an @XmlElementRef verbs field (rest-dsl)
*/
private void processVerbs(
Class> originalClassType, XmlElementRef elementRef, Field fieldElement,
String fieldName, Set eipOptions, String prefix) {
if ("verbs".equals(fieldName) && supportOutputs(originalClassType)) {
String name = fetchName(elementRef.name(), fieldName, prefix);
String fieldTypeName = getTypeName(GenericsUtil.resolveType(originalClassType, fieldElement));
String docComment = findJavaDoc(fieldElement, fieldName, name, originalClassType, true);
// gather oneOf which extends any of the output base classes
Set oneOfTypes = getOneOfs(ONE_OF_VERBS);
String displayName = null;
Metadata metadata = fieldElement.getAnnotation(Metadata.class);
if (metadata != null) {
displayName = metadata.displayName();
}
boolean deprecated = fieldElement.getAnnotation(Deprecated.class) != null;
String deprecationNote = null;
if (metadata != null) {
deprecationNote = metadata.deprecationNote();
}
String label = null;
if (metadata != null) {
label = metadata.label();
}
String kind = "element";
EipOptionModel ep = createOption(name, displayName, kind, fieldTypeName, true, "", label, docComment, deprecated,
deprecationNote, false, null, oneOfTypes, false, false);
eipOptions.add(ep);
}
}
/**
* Special for processing an @XmlElementRef expression field
*/
private void processRefExpression(
Class> originalClassType, Class> classElement,
XmlElementRef elementRef, Field fieldElement,
String fieldName, Set eipOptions, String prefix) {
if ("expression".equals(fieldName)) {
String name = fetchName(elementRef.name(), fieldName, prefix);
String fieldTypeName = getTypeName(GenericsUtil.resolveType(originalClassType, fieldElement));
// find javadoc from original class as it will override the
// setExpression method where we can provide the javadoc for the
// given EIP
String docComment = findJavaDoc(fieldElement, fieldName, name, originalClassType, true);
// is it used as predicate (check field first and then fallback to
// its class / original class)
boolean asPredicate = fieldElement.getAnnotation(AsPredicate.class) != null;
if (!asPredicate) {
asPredicate = classElement.getAnnotation(AsPredicate.class) != null;
}
if (!asPredicate) {
asPredicate = originalClassType.getAnnotation(AsPredicate.class) != null;
}
// gather oneOf expression/predicates which uses language
Set oneOfTypes = getOneOfs(ONE_OF_LANGUAGES);
String displayName = null;
Metadata metadata = fieldElement.getAnnotation(Metadata.class);
if (metadata != null) {
displayName = metadata.displayName();
}
boolean deprecated = fieldElement.getAnnotation(Deprecated.class) != null;
String deprecationNote = null;
if (metadata != null) {
deprecationNote = metadata.deprecationNote();
}
String label = null;
if (metadata != null) {
label = metadata.label();
}
final String kind = "expression";
final boolean required = expressionRequired(name);
EipOptionModel ep
= createOption(name, displayName, kind, fieldTypeName, required, "", label, docComment, deprecated,
deprecationNote, false, null, oneOfTypes, asPredicate, false);
eipOptions.add(ep);
}
}
private Set getOneOfs(String[] classes) {
Set oneOfTypes = new TreeSet<>();
for (String superclass : classes) {
for (ClassInfo ci : getIndex().getAllKnownSubclasses(DotName.createSimple(superclass))) {
Class> child = loadClass(ci.name().toString());
XmlRootElement rootElement = child.getAnnotation(XmlRootElement.class);
if (rootElement != null) {
String childName = rootElement.name();
oneOfTypes.add(childName);
}
}
}
return oneOfTypes;
}
/**
* Special for processing an @XmlElementRef when field
*/
private void processRefWhenClauses(
Class> originalClassType, XmlElementRef elementRef,
Field fieldElement, String fieldName,
Set eipOptions, String prefix) {
if ("whenClauses".equals(fieldName)) {
String name = fetchName(elementRef.name(), fieldName, prefix);
String fieldTypeName = getTypeName(GenericsUtil.resolveType(originalClassType, fieldElement));
// find javadoc from original class as it will override the
// setExpression method where we can provide the javadoc for the
// given EIP
String docComment = findJavaDoc(fieldElement, fieldName, name, originalClassType, true);
// indicate that this element is one of when
Set oneOfTypes = new HashSet<>();
oneOfTypes.add("when");
// when is predicate
boolean asPredicate = true;
String displayName = null;
Metadata metadata = fieldElement.getAnnotation(Metadata.class);
if (metadata != null) {
displayName = metadata.displayName();
}
boolean deprecated = fieldElement.getAnnotation(Deprecated.class) != null;
String deprecationNote = null;
if (metadata != null) {
deprecationNote = metadata.deprecationNote();
}
String label = null;
if (metadata != null) {
label = metadata.label();
}
final String kind = "element";
EipOptionModel ep = createOption(name, displayName, kind, fieldTypeName, false, "", label, docComment, deprecated,
deprecationNote, false, null, oneOfTypes,
asPredicate, false);
eipOptions.add(ep);
}
}
private String fetchName(String elementRef, String fieldName, String prefix) {
String name = elementRef;
if (Strings.isNullOrEmpty(name) || "##default".equals(name)) {
name = fieldName;
}
name = prefix + name;
return name;
}
private String fetchElementName(XmlElement element, Field fieldElement, String prefix) {
String fieldName = fieldElement.getName();
String name = element.name();
if (Strings.isNullOrEmpty(name) || "##default".equals(name)) {
name = fieldName;
}
// special for value definition which can be wrapped
if ("value".equals(name)) {
XmlElementWrapper wrapper = fieldElement.getAnnotation(XmlElementWrapper.class);
String n = wrapper.name();
if (!"##default".equals(n)) {
name = n;
}
}
name = prefix + name;
return name;
}
/**
* Whether the class supports outputs.
*
* There are some classes which does not support outputs, even though they have a outputs element.
*/
private boolean supportOutputs(Class> classElement) {
return loadClass("org.apache.camel.model.OutputNode").isAssignableFrom(classElement);
}
private String findDefaultValue(Field fieldElement, String fieldTypeName) {
String defaultValue = null;
Metadata metadata = fieldElement.getAnnotation(Metadata.class);
if (metadata != null) {
if (!Strings.isNullOrEmpty(metadata.defaultValue())) {
defaultValue = metadata.defaultValue();
}
}
if (defaultValue == null) {
// if its a boolean type, then we use false as the default
if ("boolean".equals(fieldTypeName) || "java.lang.Boolean".equals(fieldTypeName)) {
defaultValue = "false";
}
}
return defaultValue;
}
private boolean expressionRequired(String modelName) {
if ("method".equals(modelName) || "tokenize".equals(modelName)) {
// skip expression attribute on these two languages as they are
// solely configured using attributes
return false;
}
return true;
}
private boolean findRequired(Field fieldElement, boolean defaultValue) {
Metadata metadata = fieldElement.getAnnotation(Metadata.class);
if (metadata != null) {
return metadata.required();
}
return defaultValue;
}
private boolean hasAbstract(Class> classElement) {
for (String name : ONE_OF_ABSTRACTS) {
if (hasSuperClass(classElement, name)) {
return true;
}
}
return false;
}
private boolean hasInput(Class> classElement) {
for (String name : ONE_OF_INPUTS) {
if (hasSuperClass(classElement, name)) {
return true;
}
}
return false;
}
private boolean hasOutput(EipModel model) {
switch (model.getName()) {
// if we are route/rest then we accept output
case "route":
case "routeTemplate":
case "rest":
return true;
// special for transacted/policy which should not have output
case "policy":
case "transacted":
return false;
default:
return model.getOptions().stream().anyMatch(option -> "outputs".equals(option.getName()));
}
}
private EipOptionModel createOption(
String name, String displayName, String kind, String type, boolean required, String defaultValue, String label,
String description, boolean deprecated,
String deprecationNote, boolean enumType, Set enums, Set oneOfs, boolean asPredicate,
boolean isDuration) {
EipOptionModel option = new EipOptionModel();
option.setName(name);
option.setDisplayName(Strings.isNullOrEmpty(displayName) ? Strings.asTitle(name) : displayName);
option.setKind(kind);
option.setRequired(required);
option.setDefaultValue("java.lang.Boolean".equals(type) && !Strings.isNullOrEmpty(defaultValue)
? Boolean.parseBoolean(defaultValue) : defaultValue);
if (!Strings.isNullOrEmpty(label)) {
option.setLabel(label);
}
option.setDescription(JavadocHelper.sanitizeDescription(description, false));
option.setDeprecated(deprecated);
option.setDeprecationNote(Strings.isNullOrEmpty(deprecationNote) ? null : deprecationNote);
option.setType(getType(type, enumType, isDuration));
option.setJavaType(type);
option.setEnums(enums != null && !enums.isEmpty() ? new ArrayList<>(enums) : null);
option.setOneOfs(oneOfs != null && !oneOfs.isEmpty() ? new ArrayList<>(oneOfs) : null);
option.setAsPredicate(asPredicate);
return option;
}
private boolean hasSuperClass(Class> classElement, String superClassName) {
return loadClass(superClassName).isAssignableFrom(classElement);
}
private String findJavaDoc(
Field fieldElement, String fieldName, String name, Class> classElement, boolean builderPattern) {
if (fieldElement != null) {
Metadata md = fieldElement.getAnnotation(Metadata.class);
if (md != null) {
String doc = md.description();
if (!Strings.isNullOrEmpty(doc)) {
return doc;
}
}
}
JavaClassSource source = javaClassSource(classElement.getName());
FieldSource field = source.getField(fieldName);
if (field != null) {
String doc = field.getJavaDoc().getFullText();
if (!Strings.isNullOrEmpty(doc)) {
return doc;
}
}
String setterName = "set" + Character.toUpperCase(fieldName.charAt(0)) + fieldName.substring(1);
// special for mdcLoggingKeysPattern
if ("setMdcLoggingKeysPattern".equals(setterName)) {
setterName = "setMDCLoggingKeysPattern";
}
for (MethodSource setter : source.getMethods()) {
if (setter.getParameters().size() == 1
&& setter.getName().equals(setterName)) {
String doc = setter.getJavaDoc().getFullText();
if (!Strings.isNullOrEmpty(doc)) {
return doc;
}
}
}
String getterName = "get" + Character.toUpperCase(fieldName.charAt(0)) + fieldName.substring(1);
for (MethodSource setter : source.getMethods()) {
if (setter.getParameters().isEmpty()
&& setter.getName().equals(getterName)) {
String doc = setter.getJavaDoc().getFullText();
if (!Strings.isNullOrEmpty(doc)) {
return doc;
}
}
}
if (builderPattern) {
if (name != null && !name.equals(fieldName)) {
String doc = getDoc(source, name);
if (doc != null) {
return doc;
}
}
String doc = getDoc(source, fieldName);
if (doc != null) {
return doc;
}
}
return "";
}
private String getDoc(JavaClassSource source, String name) {
for (MethodSource builder : source.getMethods()) {
if (builder.getParameters().size() == 1 && builder.getName().equals(name)) {
String doc = builder.getJavaDoc().getFullText();
if (!Strings.isNullOrEmpty(doc)) {
return doc;
}
}
}
for (MethodSource builder : source.getMethods()) {
if (builder.getParameters().isEmpty() && builder.getName().equals(name)) {
String doc = builder.getJavaDoc().getFullText();
if (!Strings.isNullOrEmpty(doc)) {
return doc;
}
}
}
return null;
}
private String getDocComment(Class> classElement) {
JavaClassSource source = javaClassSource(classElement.getName());
return source.getJavaDoc().getFullText();
}
private JavaClassSource javaClassSource(String className) {
return sources.computeIfAbsent(className, this::doParseJavaClassSource);
}
private JavaClassSource doParseJavaClassSource(String className) {
try {
Path srcDir = project.getBasedir().toPath().resolve("src/main/java");
Path file = srcDir.resolve(className.replace('.', '/') + ".java");
String fileContent = new String(Files.readAllBytes(file));
return (JavaClassSource) Roaster.parse(fileContent);
} catch (IOException e) {
throw new RuntimeException("Unable to parse java class " + className, e);
}
}
private static String getTypeName(Type fieldType) {
String fieldTypeName = new GenericType(fieldType).toString();
fieldTypeName = fieldTypeName.replace('$', '.');
return fieldTypeName;
}
/**
* Gets the JSON schema type.
*
* @param type the java type
* @return the json schema type, is never null, but returns object as the generic type
*/
public static String getType(String type, boolean enumType, boolean isDuration) {
if (enumType) {
return "enum";
} else if (isDuration) {
return "duration";
} else if (type == null) {
// return generic type for unknown type
return "object";
} else if (type.equals(URI.class.getName()) || type.equals(URL.class.getName())) {
return "string";
} else if (type.equals(File.class.getName())) {
return "string";
} else if (type.equals(Date.class.getName())) {
return "string";
} else if (type.startsWith("java.lang.Class")) {
return "string";
} else if (type.startsWith("java.util.List") || type.startsWith("java.util.Collection")) {
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 null 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";
} else if ("java.lang.Character".equals(name) || "Character".equals(name) || "char".equals(name)) {
return "string";
} else if ("java.lang.String".equals(name) || "String".equals(name)) {
return "string";
} else if ("java.lang.Boolean".equals(name) || "Boolean".equals(name) || "boolean".equals(name)) {
return "boolean";
} else if ("java.lang.Integer".equals(name) || "Integer".equals(name) || "int".equals(name)) {
return "integer";
} else if ("java.lang.Long".equals(name) || "Long".equals(name) || "long".equals(name)) {
return "integer";
} else if ("java.lang.Short".equals(name) || "Short".equals(name) || "short".equals(name)) {
return "integer";
} else if ("java.lang.Byte".equals(name) || "Byte".equals(name) || "byte".equals(name)) {
return "integer";
} else if ("java.lang.Float".equals(name) || "Float".equals(name) || "float".equals(name)) {
return "number";
} else if ("java.lang.Double".equals(name) || "Double".equals(name) || "double".equals(name)) {
return "number";
}
return null;
}
private static final class EipOptionComparator implements Comparator {
private final EipModel model;
private final boolean restVerb;
private EipOptionComparator(EipModel model) {
this.model = model;
this.restVerb = isRestVerb(model);
}
@Override
public int compare(EipOptionModel o1, EipOptionModel o2) {
int weight;
int weight2;
if (restVerb) {
weight = weightRestVerb(o1);
weight2 = weightRestVerb(o2);
} else {
weight = weight(o1);
weight2 = weight(o2);
}
if (weight == weight2) {
// keep the current order
return 1;
} else {
// sort according to weight
return weight2 - weight;
}
}
private boolean isRestVerb(EipModel model) {
if ("rest".equals(model.getLabel())) {
String name = model.getName();
return "delete".equals(name) || "get".equals(name) || "head".equals(name) || "patch".equals(name)
|| "post".equals(name) || "put".equals(name);
}
return false;
}
private int weightRestVerb(EipOptionModel o) {
String name = o.getName();
// path is in top
if ("path".equals(name)) {
return 20;
}
// to is after path
if ("to".equals(name)) {
return 19;
}
return weight(o);
}
private int weight(EipOptionModel o) {
String name = o.getName();
// required name/key should be in top
if (o.isRequired() && ("language".equals(name) || "name".equals(name) || "key".equals(name))) {
return 20;
}
// these should be in top
if ("expression".equals(name)) {
return 10;
}
// these should be first
if ("disabled".equals(name)) {
return 98;
} else if ("description".equals(name)) {
return 99;
} else if ("id".equals(name)) {
return 100;
} else if ("pattern".equals(name) && "to".equals(model.getName())) {
// and pattern only for the to model
return -10;
}
return 0;
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy