blob: 7b8d6f17300686c06b952c448dfdf64c66c0d84b [file] [log] [blame]
/*
* 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.FileInputStream;
import java.io.IOError;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import javax.annotation.Generated;
import org.apache.camel.maven.packaging.generics.GenericsUtil;
import org.apache.camel.maven.packaging.model.ComponentModel;
import org.apache.camel.maven.packaging.model.ComponentOptionModel;
import org.apache.camel.maven.packaging.model.EndpointOptionModel;
import org.apache.camel.maven.packaging.srcgen.GenericType;
import org.apache.camel.maven.packaging.srcgen.GenericType.BoundType;
import org.apache.camel.maven.packaging.srcgen.JavaClass;
import org.apache.camel.maven.packaging.srcgen.Method;
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 org.apache.maven.plugin.AbstractMojo;
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.MavenProject;
import org.jboss.forge.roaster.model.util.Strings;
import static org.apache.camel.maven.packaging.AbstractGeneratorMojo.updateResource;
import static org.apache.camel.maven.packaging.JSonSchemaHelper.getSafeValue;
import static org.apache.camel.maven.packaging.PackageHelper.findCamelDirectory;
import static org.apache.camel.maven.packaging.PackageHelper.loadText;
/**
* Generate Spring Boot auto configuration files for Camel components and data
* formats.
*/
@Mojo(name = "generate-endpoint-dsl", threadSafe = true, requiresDependencyResolution = ResolutionScope.COMPILE_PLUS_RUNTIME, defaultPhase = LifecyclePhase.PROCESS_CLASSES)
public class EndpointDslMojo extends AbstractMojo {
private static final Map<String, Class<?>> PRIMITIVEMAP;
static {
PRIMITIVEMAP = new HashMap<>();
PRIMITIVEMAP.put("boolean", java.lang.Boolean.class);
PRIMITIVEMAP.put("char", java.lang.Character.class);
PRIMITIVEMAP.put("long", java.lang.Long.class);
PRIMITIVEMAP.put("int", java.lang.Integer.class);
PRIMITIVEMAP.put("integer", java.lang.Integer.class);
PRIMITIVEMAP.put("byte", java.lang.Byte.class);
PRIMITIVEMAP.put("short", java.lang.Short.class);
PRIMITIVEMAP.put("double", java.lang.Double.class);
PRIMITIVEMAP.put("float", java.lang.Float.class);
}
/**
* The maven project.
*/
@Parameter(property = "project", required = true, readonly = true)
protected MavenProject project;
/**
* The project build directory
*/
@Parameter(defaultValue = "${project.build.directory}")
protected File buildDir;
/**
* The base directory
*/
@Parameter(defaultValue = "${basedir}")
protected File baseDir;
@Parameter
protected File outputDir;
@Parameter
protected String packageName = "org.apache.camel.builder.endpoint.dsl";
DynamicClassLoader projectClassLoader;
@Override
public void execute() throws MojoExecutionException, MojoFailureException {
try {
projectClassLoader = DynamicClassLoader.createDynamicClassLoader(project.getTestClasspathElements());
} catch (org.apache.maven.artifact.DependencyResolutionRequiredException e) {
throw new RuntimeException(e.getMessage(), e);
}
if (outputDir == null) {
outputDir = findCamelDirectory(baseDir, "core/camel-endpointdsl/src/main/java");
}
Map<File, Supplier<String>> files = PackageHelper.findJsonFiles(buildDir, p -> p.isDirectory() || p.getName().endsWith(".json")).stream()
.collect(Collectors.toMap(Function.identity(), s -> cache(() -> loadJson(s))));
executeComponent(files);
}
private static String loadJson(File file) {
try (InputStream is = new FileInputStream(file)) {
return loadText(is);
} catch (IOException e) {
throw new IOError(e);
}
}
private static <T> Supplier<T> cache(Supplier<T> supplier) {
return new Supplier<T>() {
T value;
@Override
public T get() {
if (value == null) {
value = supplier.get();
}
return value;
}
};
}
private void executeComponent(Map<File, Supplier<String>> jsonFiles) throws MojoExecutionException, MojoFailureException {
// find the component names
Set<String> componentNames = new TreeSet<>();
findComponentNames(buildDir, componentNames);
// create auto configuration for the components
if (!componentNames.isEmpty()) {
getLog().debug("Found " + componentNames.size() + " components");
List<ComponentModel> allModels = new LinkedList<>();
for (String componentName : componentNames) {
String json = loadComponentJson(jsonFiles, componentName);
if (json != null) {
ComponentModel model = generateComponentModel(componentName, json);
allModels.add(model);
}
}
// Group the models by implementing classes
Map<String, List<ComponentModel>> grModels = allModels.stream().collect(Collectors.groupingBy(ComponentModel::getJavaType));
for (String componentClass : grModels.keySet()) {
List<ComponentModel> compModels = grModels.get(componentClass);
ComponentModel model = compModels.get(0); // They should be
// equivalent
List<String> aliases = compModels.stream().map(ComponentModel::getScheme).sorted().collect(Collectors.toList());
String overrideComponentName = null;
if (aliases.size() > 1) {
// determine component name when there are multiple ones
overrideComponentName = model.getArtifactId().replace("camel-", "");
}
createEndpointDsl(packageName, model, overrideComponentName);
}
}
}
private void createEndpointDsl(String packageName, ComponentModel model, String overrideComponentName) throws MojoFailureException {
String componentClassName = model.getJavaType();
String builderName = getEndpointName(componentClassName);
String methodName = getMethodName(componentClassName);
Class<?> realComponentClass = loadClass(componentClassName);
Class<?> realEndpointClass = loadClass(findEndpointClassName(componentClassName));
final JavaClass javaClass = new JavaClass(getProjectClassLoader());
javaClass.setPackage(packageName);
javaClass.setName(builderName + "Factory");
javaClass.setClass(false);
javaClass.addImport("org.apache.camel.builder.EndpointConsumerBuilder");
javaClass.addImport("org.apache.camel.builder.EndpointProducerBuilder");
javaClass.addImport("org.apache.camel.builder.endpoint.AbstractEndpointBuilder");
Map<String, JavaClass> enumClasses = new HashMap<>();
boolean advanced = false;
for (EndpointOptionModel option : model.getEndpointOptions()) {
if (option.getLabel().contains("advanced")) {
advanced = true;
}
}
JavaClass consumerClass = null;
JavaClass advancedConsumerClass = null;
JavaClass producerClass = null;
JavaClass advancedProducerClass = null;
if (!realEndpointClass.getAnnotation(UriEndpoint.class).producerOnly() && !realEndpointClass.getAnnotation(UriEndpoint.class).consumerOnly()) {
String consumerName = builderName.replace("Endpoint", "EndpointConsumer");
consumerClass = javaClass.addNestedType().setPublic().setClass(false);
consumerClass.setName(consumerName);
consumerClass.implementInterface("EndpointConsumerBuilder");
generateDummyClass(consumerClass.getCanonicalName());
consumerClass.getJavaDoc().setText("Builder for endpoint consumers for the " + model.getTitle() + " component.");
if (advanced) {
advancedConsumerClass = javaClass.addNestedType().setPublic().setClass(false);
advancedConsumerClass.setName("Advanced" + consumerName);
advancedConsumerClass.implementInterface("EndpointConsumerBuilder");
generateDummyClass(advancedConsumerClass.getCanonicalName());
advancedConsumerClass.getJavaDoc().setText("Advanced builder for endpoint consumers for the " + model.getTitle() + " component.");
consumerClass.addMethod().setName("advanced").setReturnType(loadClass(advancedConsumerClass.getCanonicalName())).setDefault()
.setBody("return (Advanced" + consumerName + ") this;");
advancedConsumerClass.addMethod().setName("basic").setReturnType(loadClass(consumerClass.getCanonicalName())).setDefault()
.setBody("return (" + consumerName + ") this;");
}
String producerName = builderName.replace("Endpoint", "EndpointProducer");
producerClass = javaClass.addNestedType().setPublic().setClass(false);
producerClass.setName(producerName);
producerClass.implementInterface("EndpointProducerBuilder");
generateDummyClass(producerClass.getCanonicalName());
producerClass.getJavaDoc().setText("Builder for endpoint producers for the " + model.getTitle() + " component.");
if (advanced) {
advancedProducerClass = javaClass.addNestedType().setPublic().setClass(false);
advancedProducerClass.setName("Advanced" + producerName);
advancedProducerClass.implementInterface("EndpointProducerBuilder");
generateDummyClass(advancedProducerClass.getCanonicalName());
advancedProducerClass.getJavaDoc().setText("Advanced builder for endpoint producers for the " + model.getTitle() + " component.");
producerClass.addMethod().setName("advanced").setReturnType(loadClass(advancedProducerClass.getCanonicalName())).setDefault()
.setBody("return (Advanced" + producerName + ") this;");
advancedProducerClass.addMethod().setName("basic").setReturnType(loadClass(producerClass.getCanonicalName())).setDefault()
.setBody("return (" + producerName + ") this;");
}
}
JavaClass builderClass;
JavaClass advancedBuilderClass = null;
builderClass = javaClass.addNestedType().setPublic().setClass(false);
builderClass.setName(builderName);
if (realEndpointClass.getAnnotation(UriEndpoint.class).producerOnly()) {
builderClass.implementInterface("EndpointProducerBuilder");
} else if (realEndpointClass.getAnnotation(UriEndpoint.class).consumerOnly()) {
builderClass.implementInterface("EndpointConsumerBuilder");
} else {
builderClass.implementInterface(consumerClass.getName());
builderClass.implementInterface(producerClass.getName());
}
generateDummyClass(builderClass.getCanonicalName());
builderClass.getJavaDoc().setText("Builder for endpoint for the " + model.getTitle() + " component.");
if (advanced) {
advancedBuilderClass = javaClass.addNestedType().setPublic().setClass(false);
advancedBuilderClass.setName("Advanced" + builderName);
if (realEndpointClass.getAnnotation(UriEndpoint.class).producerOnly()) {
advancedBuilderClass.implementInterface("EndpointProducerBuilder");
} else if (realEndpointClass.getAnnotation(UriEndpoint.class).consumerOnly()) {
advancedBuilderClass.implementInterface("EndpointConsumerBuilder");
} else {
advancedBuilderClass.implementInterface(advancedConsumerClass.getName());
advancedBuilderClass.implementInterface(advancedProducerClass.getName());
}
generateDummyClass(advancedBuilderClass.getCanonicalName());
advancedBuilderClass.getJavaDoc().setText("Advanced builder for endpoint for the " + model.getTitle() + " component.");
builderClass.addMethod().setName("advanced").setReturnType(loadClass(advancedBuilderClass.getCanonicalName())).setDefault()
.setBody("return (Advanced" + builderName + ") this;");
advancedBuilderClass.addMethod().setName("basic").setReturnType(loadClass(builderClass.getCanonicalName())).setDefault().setBody("return (" + builderName + ") this;");
}
generateDummyClass(packageName + ".T");
String doc = "Generated by camel-package-maven-plugin - do not edit this file!";
if (!Strings.isBlank(model.getDescription())) {
doc = model.getDescription() + "\n\n" + doc;
}
javaClass.getJavaDoc().setText(doc);
javaClass.addAnnotation(Generated.class.getName()).setStringValue("value", EndpointDslMojo.class.getName());
for (EndpointOptionModel option : model.getEndpointOptions()) {
// skip all @UriPath parameters as the endpoint DSL is for query parameters
if ("path".equals(option.getKind())) {
continue;
}
List<JavaClass> targets = new ArrayList<>();
if (option.getLabel() != null) {
if (option.getLabel().contains("producer")) {
if (option.getLabel().contains("advanced")) {
targets.add(advancedProducerClass);
} else {
targets.add(producerClass);
}
} else if (option.getLabel().contains("consumer")) {
if (option.getLabel().contains("advanced")) {
targets.add(advancedConsumerClass);
} else {
targets.add(consumerClass);
}
} else {
if (option.getLabel().contains("advanced")) {
targets.add(advancedConsumerClass);
targets.add(advancedProducerClass);
targets.add(advancedBuilderClass);
} else {
targets.add(consumerClass);
targets.add(producerClass);
targets.add(builderClass);
}
}
}
GenericType ogtype;
GenericType gtype;
try {
Field field = findField(realComponentClass, realEndpointClass, option);
ogtype = new GenericType(GenericsUtil.resolveType(realEndpointClass, field));
gtype = getType(javaClass, enumClasses, option.getEnums(), ogtype.toString());
} catch (Exception e) {
throw new RuntimeException(e);
}
for (JavaClass target : targets) {
if (target == null) {
continue;
}
Method fluent = target.addMethod().setDefault().setName(option.getName()).setReturnType(new GenericType(loadClass(target.getCanonicalName())))
.addParameter(isPrimitive(ogtype.toString()) ? ogtype : gtype, option.getName())
.setBody("setProperty(\"" + option.getName() + "\", " + option.getName() + ");\n" + "return this;\n");
if ("true".equals(option.getDeprecated())) {
fluent.addAnnotation(Deprecated.class);
}
if (!Strings.isBlank(option.getDescription())) {
String desc = option.getDescription();
if (!desc.endsWith(".")) {
desc += ".";
}
desc += "\n";
desc += "\nThe option is a: <code>" + ogtype.toString().replaceAll("<", "&lt;").replaceAll(">", "&gt;") + "</code> type.";
desc += "\n";
// the Endpoint DSL currently requires to provide the entire context-path and not as individual options
// so lets only mark query parameters that are required as required
if ("parameter".equals(option.getKind()) && "true".equals(option.getRequired())) {
desc += "\nRequired: true";
}
desc += "\nGroup: " + option.getGroup();
fluent.getJavaDoc().setFullText(desc);
}
if (ogtype.getRawClass() != String.class) {
fluent = target.addMethod().setDefault().setName(option.getName()).setReturnType(new GenericType(loadClass(target.getCanonicalName())))
.addParameter(new GenericType(String.class), option.getName())
.setBody("setProperty(\"" + option.getName() + "\", " + option.getName() + ");\n" + "return this;\n");
if ("true".equals(option.getDeprecated())) {
fluent.addAnnotation(Deprecated.class);
}
if (!Strings.isBlank(option.getDescription())) {
String desc = option.getDescription();
if (!desc.endsWith(".")) {
desc += ".";
}
desc += "\n";
desc += "\nThe option will be converted to a <code>" + ogtype.toString().replaceAll("<", "&lt;").replaceAll(">", "&gt;") + "</code> type.";
desc += "\n";
// the Endpoint DSL currently requires to provide the entire context-path and not as individual options
// so lets only mark query parameters that are required as required
if ("parameter".equals(option.getKind()) && "true".equals(option.getRequired())) {
desc += "\nRequired: true";
}
desc += "\nGroup: " + option.getGroup();
fluent.getJavaDoc().setFullText(desc);
}
}
}
}
javaClass.removeImport("T");
Method method = javaClass.addMethod().setDefault().setName(methodName).addParameter(String.class, "path")
.setReturnType(new GenericType(loadClass(builderClass.getCanonicalName())))
.setBody("class " + builderName + "Impl extends AbstractEndpointBuilder implements " + builderName + ", Advanced" + builderName + " {\n" + " public " + builderName
+ "Impl(String path) {\n" + " super(\"" + model.getScheme() + "\", path);\n" + " }\n" + "}\n" + "return new " + builderName + "Impl(path);\n");
if ("true".equals(model.getDeprecated())) {
method.addAnnotation(Deprecated.class);
}
String desc = model.getTitle() + " (" + model.getArtifactId() + ")";
desc += "\n" + model.getDescription();
desc += "\n";
desc += "\nCategory: " + model.getLabel();
desc += "\nAvailable as of version: " + model.getFirstVersionShort();
desc += "\nMaven coordinates: " + project.getGroupId() + ":" + project.getArtifactId();
// include javadoc for all path parameters and mark which are required
desc += "\n";
desc += "\nSyntax: <code>" + model.getSyntax() + "</code>";
for (EndpointOptionModel option : model.getEndpointOptions()) {
if ("path".equals(option.getKind())) {
desc += "\n";
desc += "\nPath parameter: " + option.getName();
if ("true".equals(option.getRequired())) {
desc += " (required)";
}
if ("true".equals(option.getDeprecated())) {
desc += " <strong>deprecated</strong>";
}
desc += "\n" + option.getDescription();
if (!StringHelper.isEmpty(option.getDefaultValue())) {
desc += "\nDefault value: " + option.getDefaultValue();
}
if (!StringHelper.isEmpty(option.getEnumValues())) {
desc += "\nThe value can be one of: " + wrapEnumValues(option.getEnumValues(), 120);
}
}
}
method.getJavaDoc().setText(desc);
String fileName = packageName.replaceAll("\\.", "\\/") + "/" + builderName + "Factory.java";
writeSourceIfChanged(javaClass, fileName, false);
}
private String wrapEnumValues(String enumValues, int watermark) {
// comma to space so we can wrap words (which uses space)
String text = enumValues.replace(',', ' ');
String wrapped = StringHelper.wrapWords(text, "\n", watermark, true);
// back to comma again
wrapped = wrapped.replaceAll("\\s", ", ");
return wrapped;
}
private String getMethodName(String type) {
String builderName = getEndpointName(type);
String methodName = builderName.replace("EndpointBuilder", "");
methodName = methodName.substring(0, 1).toLowerCase() + methodName.substring(1);
switch (type) {
case "org.apache.camel.component.rest.RestComponent":
return "restEndpoint";
case "org.apache.camel.component.beanclass.ClassComponent":
return "classEndpoint";
default:
return methodName;
}
}
private String getEndpointName(String type) {
int pos = type.lastIndexOf(".");
String name = type.substring(pos + 1).replace("Component", "EndpointBuilder");
//
// HACKS
//
switch (type) {
case "org.apache.camel.component.atmosphere.websocket.WebsocketComponent":
return "AtmosphereWebsocketEndpointBuilder";
case "org.apache.camel.component.zookeepermaster.MasterComponent":
return "ZooKeeperMasterEndpointBuilder";
default:
return name;
}
}
private String findEndpointClassName(String type) {
String endpointName = type.replaceFirst("Component", "Endpoint");
//
// HACKS
//
switch (type) {
case "org.apache.camel.component.disruptor.vm.DisruptorVmComponent":
return "org.apache.camel.component.disruptor.DisruptorEndpoint";
case "org.apache.camel.component.etcd.EtcdComponent":
return "org.apache.camel.component.etcd.AbstractEtcdPollingEndpoint";
case "org.apache.camel.websocket.jsr356.JSR356WebSocketComponent":
return "org.apache.camel.websocket.jsr356.JSR356Endpoint";
default:
return endpointName;
}
}
private Field findField(Class<?> realComponentClass, Class<?> realEndpointClass, EndpointOptionModel option) throws NoSuchFieldException {
Field field = null;
List<Class<?>> classes = new ArrayList<>();
classes.add(realComponentClass);
classes.add(realEndpointClass);
while (!classes.isEmpty()) {
Class cl = classes.remove(0);
for (Field f : cl.getDeclaredFields()) {
String n = f.getName();
UriPath path = f.getAnnotation(UriPath.class);
if (path != null && !Strings.isBlank(path.name())) {
n = path.name();
}
UriParam param = f.getAnnotation(UriParam.class);
if (param != null && !Strings.isBlank(param.name())) {
n = param.name();
}
if (n.equals(option.getName())) {
field = f;
break;
}
if (f.getType().isAnnotationPresent(UriParams.class)) {
classes.add(f.getType());
}
}
if (field != null) {
break;
}
cl = cl.getSuperclass();
if (cl != null) {
classes.add(cl);
}
}
if (field == null) {
throw new NoSuchFieldException("Could not find field for option " + option.getName());
}
return field;
}
static boolean isPrimitive(String type) {
return PRIMITIVEMAP.containsKey(type);
}
private Class<?> loadClass(String loadClassName) {
Class<?> optionClass;
String org = loadClassName;
while (true) {
try {
optionClass = getProjectClassLoader().loadClass(loadClassName);
break;
} catch (ClassNotFoundException e) {
int dotIndex = loadClassName.lastIndexOf('.');
if (dotIndex == -1) {
throw new IllegalArgumentException(org);
} else {
loadClassName = loadClassName.substring(0, dotIndex) + "$" + loadClassName.substring(dotIndex + 1);
}
}
}
return optionClass;
}
private GenericType getType(JavaClass javaClass, Map<String, JavaClass> enumClasses, String enums, String type) {
type = type.trim();
// Check if this is an array
if (type.endsWith("[]")) {
GenericType t = getType(javaClass, enumClasses, enums, type.substring(0, type.length() - 2));
return new GenericType(Array.newInstance(t.getRawClass(), 0).getClass(), t);
}
// Check if this is a generic
int genericIndex = type.indexOf('<');
if (genericIndex > 0) {
if (!type.endsWith(">")) {
throw new IllegalArgumentException("Can not load type: " + type);
}
GenericType base = getType(javaClass, enumClasses, enums, type.substring(0, genericIndex));
if (base.getRawClass() == Object.class) {
return base;
}
String[] params = splitParams(type.substring(genericIndex + 1, type.length() - 1));
GenericType[] types = new GenericType[params.length];
for (int i = 0; i < params.length; i++) {
types[i] = getType(javaClass, enumClasses, enums, params[i]);
}
return new GenericType(base.getRawClass(), types);
}
// Primitive
if (isPrimitive(type)) {
return new GenericType(PRIMITIVEMAP.get(type));
}
// Extends
if (type.startsWith("? extends ")) {
String raw = type.substring("? extends ".length());
return new GenericType(loadClass(raw), BoundType.Extends);
}
// Super
if (type.startsWith("? super ")) {
String raw = type.substring("? extends ".length());
return new GenericType(loadClass(raw), BoundType.Super);
}
// Wildcard
if (type.equals("?")) {
return new GenericType(Object.class, BoundType.Extends);
}
if (loadClass(type).isEnum() && !isCamelCoreType(type)) {
String enumClassName = type.substring(type.lastIndexOf('.') + 1);
if (enumClassName.contains("$")) {
enumClassName = enumClassName.substring(enumClassName.indexOf('$') + 1);
}
JavaClass enumClass = enumClasses.get(enumClassName);
if (enumClass == null) {
enumClass = javaClass.addNestedType().setPackagePrivate().setName(enumClassName).setEnum(true);
enumClass.getJavaDoc().setText("Proxy enum for <code>" + type + "</code> enum.");
enumClasses.put(enumClassName, enumClass);
for (Object value : loadClass(type).getEnumConstants()) {
enumClass.addValue(value.toString().replace('.', '_').replace('-', '_'));
}
}
type = javaClass.getPackage() + "." + javaClass.getName() + "$" + enumClassName;
return new GenericType(generateDummyClass(type));
}
if (!isCamelCoreType(type)) {
getLog().debug("Substituting java.lang.Object to " + type);
return new GenericType(Object.class);
}
return new GenericType(loadClass(type));
}
private String[] splitParams(String string) {
List<String> params = new ArrayList<>();
int cur = 0;
int start = 0;
int opened = 0;
while (true) {
int nextComma = string.indexOf(',', cur);
int nextOpen = string.indexOf('<', cur);
int nextClose = string.indexOf('>', cur);
if (nextComma < 0) {
params.add(string.substring(start));
return params.toArray(new String[0]);
} else if ((nextOpen < 0 || nextComma < nextOpen) && (nextClose < 0 || nextComma < nextClose) && opened == 0) {
params.add(string.substring(start, nextComma));
start = cur = nextComma + 1;
} else if (nextOpen < 0) {
if (--opened < 0) {
throw new IllegalStateException();
}
cur = nextClose + 1;
} else if (nextClose < 0 || nextOpen < nextClose) {
++opened;
cur = nextOpen + 1;
} else {
if (--opened < 0) {
throw new IllegalStateException();
}
cur = nextClose + 1;
}
}
}
private boolean isCamelCoreType(String type) {
return type.startsWith("java.") || type.matches("org\\.apache\\.camel\\.(spi\\.)?([A-Za-z]+)");
}
private Class generateDummyClass(String clazzName) {
return getProjectClassLoader().generateDummyClass(clazzName);
}
private DynamicClassLoader getProjectClassLoader() {
return projectClassLoader;
}
private static String loadComponentJson(Map<File, Supplier<String>> jsonFiles, String componentName) {
return loadJsonOfType(jsonFiles, componentName, "component");
}
private static String loadJsonOfType(Map<File, Supplier<String>> jsonFiles, String modelName, String type) {
for (Map.Entry<File, Supplier<String>> entry : jsonFiles.entrySet()) {
if (entry.getKey().getName().equals(modelName + ".json")) {
String json = entry.getValue().get();
if (json.contains("\"kind\": \"" + type + "\"")) {
return json;
}
}
}
return null;
}
private static ComponentModel generateComponentModel(String componentName, String json) {
List<Map<String, String>> rows = JSonSchemaHelper.parseJsonSchema("component", json, false);
ComponentModel component = new ComponentModel(true);
component.setScheme(getSafeValue("scheme", rows));
component.setSyntax(getSafeValue("syntax", rows));
component.setAlternativeSyntax(getSafeValue("alternativeSyntax", rows));
component.setTitle(getSafeValue("title", rows));
component.setDescription(getSafeValue("description", rows));
component.setFirstVersion(JSonSchemaHelper.getSafeValue("firstVersion", rows));
component.setLabel(getSafeValue("label", rows));
component.setDeprecated(getSafeValue("deprecated", rows));
component.setDeprecationNote(getSafeValue("deprecationNote", rows));
component.setConsumerOnly(getSafeValue("consumerOnly", rows));
component.setProducerOnly(getSafeValue("producerOnly", rows));
component.setJavaType(getSafeValue("javaType", rows));
component.setGroupId(getSafeValue("groupId", rows));
component.setArtifactId(getSafeValue("artifactId", rows));
component.setVersion(getSafeValue("version", rows));
rows = JSonSchemaHelper.parseJsonSchema("componentProperties", json, true);
for (Map<String, String> row : rows) {
ComponentOptionModel option = new ComponentOptionModel();
option.setName(getSafeValue("name", row));
option.setDisplayName(getSafeValue("displayName", row));
option.setKind(getSafeValue("kind", row));
option.setType(getSafeValue("type", row));
option.setJavaType(getSafeValue("javaType", row));
option.setDeprecated(getSafeValue("deprecated", row));
option.setDeprecationNote(getSafeValue("deprecationNote", row));
option.setDescription(getSafeValue("description", row));
option.setDefaultValue(getSafeValue("defaultValue", row));
option.setEnums(getSafeValue("enum", row));
component.addComponentOption(option);
}
rows = JSonSchemaHelper.parseJsonSchema("properties", json, true);
for (Map<String, String> row : rows) {
EndpointOptionModel option = new EndpointOptionModel();
option.setName(getSafeValue("name", row));
option.setDisplayName(getSafeValue("displayName", row));
option.setKind(getSafeValue("kind", row));
option.setGroup(getSafeValue("group", row));
option.setLabel(getSafeValue("label", row));
option.setRequired(getSafeValue("required", row));
option.setType(getSafeValue("type", row));
option.setJavaType(getSafeValue("javaType", row));
option.setEnums(getSafeValue("enum", row));
option.setPrefix(getSafeValue("prefix", row));
option.setMultiValue(getSafeValue("multiValue", row));
option.setDeprecated(getSafeValue("deprecated", row));
option.setDeprecationNote(getSafeValue("deprecationNote", row));
option.setDefaultValue(getSafeValue("defaultValue", row));
option.setDescription(getSafeValue("description", row));
option.setEnumValues(getSafeValue("enum", row));
component.addEndpointOption(option);
}
return component;
}
private void findComponentNames(File dir, Set<String> componentNames) {
File f = new File(dir, "classes/META-INF/services/org/apache/camel/component");
if (f.exists() && f.isDirectory()) {
File[] files = f.listFiles();
if (files != null) {
for (File file : files) {
// skip directories as there may be a sub .resolver
// directory
if (file.isDirectory()) {
continue;
}
String name = file.getName();
if (name.charAt(0) != '.') {
componentNames.add(name);
}
}
}
}
}
private void writeSourceIfChanged(JavaClass source, String fileName, boolean innerClassesLast) throws MojoFailureException {
writeSourceIfChanged(source.printClass(innerClassesLast), fileName);
}
private void writeSourceIfChanged(String source, String fileName) throws MojoFailureException {
File target = new File(outputDir, fileName);
try {
String header;
try (InputStream is = getClass().getClassLoader().getResourceAsStream("license-header-java.txt")) {
header = loadText(is);
}
String code = header + source;
getLog().debug("Source code generated:\n" + code);
updateResource(null, target.toPath(), code);
} catch (Exception e) {
throw new MojoFailureException("IOError with file " + target, e);
}
}
}