/** * 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.IOException; import java.nio.file.Paths; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.Set; import java.util.TreeSet; import java.util.stream.Collectors; import org.apache.camel.maven.packaging.model.ComponentModel; import org.apache.camel.maven.packaging.model.ComponentOptionModel; import org.apache.camel.maven.packaging.model.DataFormatModel; import org.apache.camel.maven.packaging.model.DataFormatOptionModel; import org.apache.camel.maven.packaging.model.EipModel; import org.apache.camel.maven.packaging.model.EipOptionModel; import org.apache.camel.maven.packaging.model.EndpointOptionModel; import org.apache.camel.maven.packaging.model.LanguageModel; import org.apache.camel.maven.packaging.model.LanguageOptionModel; import org.apache.maven.model.Resource; import org.apache.maven.plugin.AbstractMojo; import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.plugin.MojoFailureException; import org.apache.maven.project.MavenProject; import org.mvel2.templates.TemplateRuntime; import org.sonatype.plexus.build.incremental.BuildContext; import static org.apache.camel.maven.packaging.JSonSchemaHelper.*; import static org.apache.camel.maven.packaging.PackageHelper.loadText; import static org.apache.camel.maven.packaging.PackageHelper.writeText; import static org.apache.camel.maven.packaging.StringHelper.isEmpty; /** * Generate or updates the component/dataformat/language/eip readme.md and .adoc files in the project root directory. * * @goal update-readme */ public class UpdateReadmeMojo extends AbstractMojo { /** * The maven project. * * @parameter property="project" * @required * @readonly */ protected MavenProject project; /** * The project build directory * * @parameter default-value="${project.build.directory}" */ protected File buildDir; /** * The documentation directory * * @parameter default-value="${basedir}/src/main/docs" */ protected File docDir; /** * The documentation directory * * @parameter default-value="${basedir}/src/main/docs/eips" */ protected File eipDocDir; /** * Whether to fail the build fast if any Warnings was detected. * * @parameter */ protected Boolean failFast; /** * build context to check changed files and mark them for refresh (used for * m2e compatibility) * * @component * @readonly */ private BuildContext buildContext; @Override public void execute() throws MojoExecutionException, MojoFailureException { executeComponent(); executeDataFormat(); executeLanguage(); executeEips(); } private void executeComponent() throws MojoExecutionException, MojoFailureException { // find the component names List<String> componentNames = findComponentNames(); final Set<File> jsonFiles = new TreeSet<File>(); PackageHelper.findJsonFiles(buildDir, jsonFiles, new PackageHelper.CamelComponentsModelFilter()); // only if there is components we should update the documentation files if (!componentNames.isEmpty()) { getLog().debug("Found " + componentNames.size() + " components"); for (String componentName : componentNames) { String json = loadComponentJson(jsonFiles, componentName); if (json != null) { // special for some components componentName = asComponentName(componentName); File file = new File(docDir, componentName + "-component.adoc"); ComponentModel model = generateComponentModel(componentName, json); String title = asComponentTitle(model.getScheme(), model.getTitle()); model.setTitle(title); // we only want the first scheme as the alternatives do not have their own readme file if (!isEmpty(model.getAlternativeSchemes())) { String first = model.getAlternativeSchemes().split(",")[0]; if (!model.getScheme().equals(first)) { continue; } } String docTitle = model.getTitle() + " Component"; boolean deprecated = "true".equals(model.getDeprecated()); if (deprecated) { docTitle += " (deprecated)"; } boolean exists = file.exists(); boolean updated; updated = updateTitles(file, docTitle); updated |= updateAvailableFrom(file, model.getFirstVersion()); // resolvePropertyPlaceholders is an option which only make sense to use if the component has other options boolean hasOptions = model.getComponentOptions().stream().anyMatch(o -> !o.getName().equals("resolvePropertyPlaceholders")); if (!hasOptions) { model.getComponentOptions().clear(); } String options = templateComponentOptions(model); updated |= updateComponentOptions(file, options); options = templateEndpointOptions(model); updated |= updateEndpointOptions(file, options); if (updated) { getLog().info("Updated doc file: " + file); } else if (exists) { getLog().debug("No changes to doc file: " + file); } else { getLog().warn("No component doc file: " + file); if (isFailFast()) { throw new MojoExecutionException("Failed build due failFast=true"); } } } } } } private void executeDataFormat() throws MojoExecutionException, MojoFailureException { // find the dataformat names List<String> dataFormatNames = findDataFormatNames(); final Set<File> jsonFiles = new TreeSet<File>(); PackageHelper.findJsonFiles(buildDir, jsonFiles, new PackageHelper.CamelComponentsModelFilter()); // only if there is dataformat we should update the documentation files if (!dataFormatNames.isEmpty()) { getLog().debug("Found " + dataFormatNames.size() + " dataformats"); for (String dataFormatName : dataFormatNames) { String json = loadDataFormatJson(jsonFiles, dataFormatName); if (json != null) { // special for some data formats dataFormatName = asDataFormatName(dataFormatName); File file = new File(docDir, dataFormatName + "-dataformat.adoc"); DataFormatModel model = generateDataFormatModel(dataFormatName, json); String title = asDataFormatTitle(model.getName(), model.getTitle()); model.setTitle(title); String docTitle = model.getTitle() + " DataFormat"; boolean deprecated = "true".equals(model.getDeprecated()); if (deprecated) { docTitle += " (deprecated)"; } boolean exists = file.exists(); boolean updated; updated = updateTitles(file, docTitle); updated |= updateAvailableFrom(file, model.getFirstVersion()); String options = templateDataFormatOptions(model); updated |= updateDataFormatOptions(file, options); if (updated) { getLog().info("Updated doc file: " + file); } else if (exists) { getLog().debug("No changes to doc file: " + file); } else { getLog().warn("No dataformat doc file: " + file); if (isFailFast()) { throw new MojoExecutionException("Failed build due failFast=true"); } } } } } } private static String asComponentName(String name) { // special for some components which share the same readme file if (name.equals("imap") || name.equals("imaps") || name.equals("pop3") || name.equals("pop3s") || name.equals("smtp") || name.equals("smtps")) { return "mail"; } else { return name; } } private void executeLanguage() throws MojoExecutionException, MojoFailureException { // find the language names List<String> languageNames = findLanguageNames(); final Set<File> jsonFiles = new TreeSet<File>(); PackageHelper.findJsonFiles(buildDir, jsonFiles, new PackageHelper.CamelComponentsModelFilter()); // only if there is language we should update the documentation files if (!languageNames.isEmpty()) { getLog().debug("Found " + languageNames.size() + " languages"); for (String languageName : languageNames) { String json = loadLanguageJson(jsonFiles, languageName); if (json != null) { File file = new File(docDir, languageName + "-language.adoc"); LanguageModel model = generateLanguageModel(languageName, json); String docTitle = model.getTitle() + " Language"; boolean deprecated = "true".equals(model.getDeprecated()); if (deprecated) { docTitle += " (deprecated)"; } boolean exists = file.exists(); boolean updated; updated = updateTitles(file, docTitle); updated |= updateAvailableFrom(file, model.getFirstVersion()); String options = templateLanguageOptions(model); updated |= updateLanguageOptions(file, options); if (updated) { getLog().info("Updated doc file: " + file); } else if (exists) { getLog().debug("No changes to doc file: " + file); } else { getLog().warn("No language doc file: " + file); if (isFailFast()) { throw new MojoExecutionException("Failed build due failFast=true"); } } } } } } private void executeEips() throws MojoExecutionException, MojoFailureException { // only run if in camel-core String currentDir = Paths.get(".").normalize().toAbsolutePath().toString(); if (!currentDir.endsWith("camel-core")) { return; } final Set<File> jsonFiles = new TreeSet<File>(); // find all json files in camel-core File coreDir = new File("."); if (coreDir.isDirectory()) { File target = new File(coreDir, "target/classes/org/apache/camel/model"); PackageHelper.findJsonFiles(target, jsonFiles, new PackageHelper.CamelComponentsModelFilter()); } // only if there is dataformat we should update the documentation files if (!jsonFiles.isEmpty()) { getLog().debug("Found " + jsonFiles.size() + " eips"); for (File jsonFile : jsonFiles) { String json = loadEipJson(jsonFile); if (json != null) { EipModel model = generateEipModel(json); String title = asEipTitle(model.getName(), model.getTitle()); model.setTitle(title); String eipName = model.getName(); // we only want actual EIPs from the models if (!model.getLabel().startsWith("eip")) { continue; } File file = new File(eipDocDir, eipName + "-eip.adoc"); String docTitle = model.getTitle() + " EIP"; boolean deprecated = model.isDeprecated(); if (deprecated) { docTitle += " (deprecated)"; } boolean exists = file.exists(); boolean updated; updated = updateTitles(file, docTitle); String options = templateEipOptions(model); updated |= updateEipOptions(file, options); if (updated) { getLog().info("Updated doc file: " + file); } else if (exists) { getLog().debug("No changes to doc file: " + file); } else { getLog().warn("No eip doc file: " + file); if (isFailFast()) { throw new MojoExecutionException("Failed build due failFast=true"); } } } } } } private static String asComponentTitle(String name, String title) { // special for some components which share the same readme file if (name.equals("imap") || name.equals("imaps") || name.equals("pop3") || name.equals("pop3s") || name.equals("smtp") || name.equals("smtps")) { return "Mail"; } else { return title; } } private static String asDataFormatName(String name) { // special for some dataformats which share the same readme file if (name.startsWith("bindy")) { return "bindy"; } else { return name; } } private static String asEipName(String name) { return name; } private static String asDataFormatTitle(String name, String title) { // special for some dataformats which share the same readme file if (name.startsWith("bindy")) { return "Bindy"; } else { return title; } } private static String asEipTitle(String name, String title) { return title; } private boolean updateTitles(File file, String title) throws MojoExecutionException { if (!file.exists()) { return false; } boolean updated = false; try { List<String> newLines = new ArrayList<>(); String text = loadText(new FileInputStream(file)); String[] lines = text.split("\n"); for (int i = 0; i < lines.length; i++) { String line = lines[i]; if (i == 0) { // first line is the title to make the text less noisy we use level 2 String newLine = "## " + title; newLines.add(newLine); updated = !line.equals(newLine); continue; } // use single line headers with # as level instead of the cumbersome adoc weird style if (line.startsWith("^^^") || line.startsWith("~~~") || line.startsWith("+++")) { String level = line.startsWith("+++") ? "####" : "###"; // transform legacy heading into new style int idx = newLines.size() - 1; String prev = newLines.get(idx); newLines.set(idx, level + " " + prev); // okay if 2nd-prev line is a [[title]] we need to remove that too // so we have nice clean sub titles idx = newLines.size() - 2; if (idx >= 0) { prev = newLines.get(idx); if (prev.startsWith("[[")) { // remove newLines.remove(idx); } } updated = true; } else { // okay normal text so just add it newLines.add(line); } } if (updated) { // build the new updated text String newText = newLines.stream().collect(Collectors.joining("\n")); writeText(file, newText); } } catch (Exception e) { throw new MojoExecutionException("Error reading file " + file + " Reason: " + e, e); } return updated; } private boolean updateAvailableFrom(File file, String firstVersion) throws MojoExecutionException { if (firstVersion == null || !file.exists()) { return false; } // cut last digit so its not 2.18.0 but 2.18 String[] parts = firstVersion.split("\\."); if (parts.length == 3 && parts[2].equals("0")) { firstVersion = parts[0] + "." + parts[1]; } boolean updated = false; try { String text = loadText(new FileInputStream(file)); String[] lines = text.split("\n"); List<String> newLines = new ArrayList<>(); // copy over to all new lines newLines.addAll(Arrays.asList(lines)); // check the first four lines boolean title = lines[0].startsWith("##"); boolean empty = lines[1].trim().isEmpty(); boolean availableFrom = lines[2].trim().contains("Available as of") || lines[2].trim().contains("Available in"); boolean empty2 = lines[3].trim().isEmpty(); if (title && empty && availableFrom) { String newLine = "*Available as of Camel version " + firstVersion + "*"; if (!newLine.equals(lines[2])) { newLines.set(2, newLine); updated = true; } if (!empty2) { newLines.add(3, ""); updated = true; } } else if (!availableFrom) { String newLine = "*Available as of Camel version " + firstVersion + "*"; newLines.add(2, newLine); newLines.add(3, ""); updated = true; } if (updated) { // build the new updated text String newText = newLines.stream().collect(Collectors.joining("\n")); writeText(file, newText); } } catch (Exception e) { throw new MojoExecutionException("Error reading file " + file + " Reason: " + e, e); } return updated; } private boolean updateComponentOptions(File file, String changed) throws MojoExecutionException { if (!file.exists()) { return false; } try { String text = loadText(new FileInputStream(file)); String existing = StringHelper.between(text, "// component options: START", "// component options: END"); if (existing != null) { // remove leading line breaks etc existing = existing.trim(); changed = changed.trim(); if (existing.equals(changed)) { return false; } else { String before = StringHelper.before(text, "// component options: START"); String after = StringHelper.after(text, "// component options: END"); text = before + "// component options: START\n" + changed + "\n// component options: END" + after; writeText(file, text); return true; } } else { getLog().warn("Cannot find markers in file " + file); getLog().warn("Add the following markers"); getLog().warn("\t// component options: START"); getLog().warn("\t// component options: END"); if (isFailFast()) { throw new MojoExecutionException("Failed build due failFast=true"); } return false; } } catch (Exception e) { throw new MojoExecutionException("Error reading file " + file + " Reason: " + e, e); } } private boolean updateEndpointOptions(File file, String changed) throws MojoExecutionException { if (!file.exists()) { return false; } try { String text = loadText(new FileInputStream(file)); String existing = StringHelper.between(text, "// endpoint options: START", "// endpoint options: END"); if (existing != null) { // remove leading line breaks etc existing = existing.trim(); changed = changed.trim(); if (existing.equals(changed)) { return false; } else { String before = StringHelper.before(text, "// endpoint options: START"); String after = StringHelper.after(text, "// endpoint options: END"); text = before + "// endpoint options: START\n" + changed + "\n// endpoint options: END" + after; writeText(file, text); return true; } } else { getLog().warn("Cannot find markers in file " + file); getLog().warn("Add the following markers"); getLog().warn("\t// endpoint options: START"); getLog().warn("\t// endpoint options: END"); if (isFailFast()) { throw new MojoExecutionException("Failed build due failFast=true"); } return false; } } catch (Exception e) { throw new MojoExecutionException("Error reading file " + file + " Reason: " + e, e); } } private boolean updateDataFormatOptions(File file, String changed) throws MojoExecutionException { if (!file.exists()) { return false; } try { String text = loadText(new FileInputStream(file)); String existing = StringHelper.between(text, "// dataformat options: START", "// dataformat options: END"); if (existing != null) { // remove leading line breaks etc existing = existing.trim(); changed = changed.trim(); if (existing.equals(changed)) { return false; } else { String before = StringHelper.before(text, "// dataformat options: START"); String after = StringHelper.after(text, "// dataformat options: END"); text = before + "// dataformat options: START\n" + changed + "\n// dataformat options: END" + after; writeText(file, text); return true; } } else { getLog().warn("Cannot find markers in file " + file); getLog().warn("Add the following markers"); getLog().warn("\t// dataformat options: START"); getLog().warn("\t// dataformat options: END"); if (isFailFast()) { throw new MojoExecutionException("Failed build due failFast=true"); } return false; } } catch (Exception e) { throw new MojoExecutionException("Error reading file " + file + " Reason: " + e, e); } } private boolean updateLanguageOptions(File file, String changed) throws MojoExecutionException { if (!file.exists()) { return false; } try { String text = loadText(new FileInputStream(file)); String existing = StringHelper.between(text, "// language options: START", "// language options: END"); if (existing != null) { // remove leading line breaks etc existing = existing.trim(); changed = changed.trim(); if (existing.equals(changed)) { return false; } else { String before = StringHelper.before(text, "// language options: START"); String after = StringHelper.after(text, "// language options: END"); text = before + "// language options: START\n" + changed + "\n// language options: END" + after; writeText(file, text); return true; } } else { getLog().warn("Cannot find markers in file " + file); getLog().warn("Add the following markers"); getLog().warn("\t// language options: START"); getLog().warn("\t// language options: END"); if (isFailFast()) { throw new MojoExecutionException("Failed build due failFast=true"); } return false; } } catch (Exception e) { throw new MojoExecutionException("Error reading file " + file + " Reason: " + e, e); } } private boolean updateEipOptions(File file, String changed) throws MojoExecutionException { if (!file.exists()) { return false; } try { String text = loadText(new FileInputStream(file)); String existing = StringHelper.between(text, "// eip options: START", "// eip options: END"); if (existing != null) { // remove leading line breaks etc existing = existing.trim(); changed = changed.trim(); if (existing.equals(changed)) { return false; } else { String before = StringHelper.before(text, "// eip options: START"); String after = StringHelper.after(text, "// eip options: END"); text = before + "// eip options: START\n" + changed + "\n// eip options: END" + after; writeText(file, text); return true; } } else { getLog().warn("Cannot find markers in file " + file); getLog().warn("Add the following markers"); getLog().warn("\t// eip options: START"); getLog().warn("\t// eip options: END"); if (isFailFast()) { throw new MojoExecutionException("Failed build due failFast=true"); } return false; } } catch (Exception e) { throw new MojoExecutionException("Error reading file " + file + " Reason: " + e, e); } } private String loadComponentJson(Set<File> jsonFiles, String componentName) { try { for (File file : jsonFiles) { if (file.getName().equals(componentName + ".json")) { String json = loadText(new FileInputStream(file)); boolean isComponent = json.contains("\"kind\": \"component\""); if (isComponent) { return json; } } } } catch (IOException e) { // ignore } return null; } private String loadDataFormatJson(Set<File> jsonFiles, String dataFormatName) { try { for (File file : jsonFiles) { if (file.getName().equals(dataFormatName + ".json")) { String json = loadText(new FileInputStream(file)); boolean isDataFormat = json.contains("\"kind\": \"dataformat\""); if (isDataFormat) { return json; } } } } catch (IOException e) { // ignore } return null; } private String loadLanguageJson(Set<File> jsonFiles, String languageName) { try { for (File file : jsonFiles) { if (file.getName().equals(languageName + ".json")) { String json = loadText(new FileInputStream(file)); boolean isLanguage = json.contains("\"kind\": \"language\""); if (isLanguage) { return json; } } } } catch (IOException e) { // ignore } return null; } private String loadEipJson(File file) { try { String json = loadText(new FileInputStream(file)); boolean isEip = json.contains("\"kind\": \"model\""); if (isEip) { return json; } } catch (IOException e) { // ignore } return null; } private ComponentModel generateComponentModel(String componentName, String json) { List<Map<String, String>> rows = 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.setAlternativeSchemes(getSafeValue("alternativeSchemes", rows)); component.setTitle(getSafeValue("title", rows)); component.setDescription(getSafeValue("description", rows)); component.setFirstVersion(getSafeValue("firstVersion", rows)); component.setLabel(getSafeValue("label", rows)); component.setDeprecated(getSafeValue("deprecated", 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)); String oldGroup = null; rows = 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.setGroup(getSafeValue("group", row)); option.setRequired(getSafeValue("required", row)); option.setType(getSafeValue("type", row)); option.setJavaType(getSafeValue("javaType", row)); option.setEnums(getSafeValue("enum", row)); option.setDeprecated(getSafeValue("deprecated", row)); option.setSecret(getSafeValue("secret", row)); option.setDefaultValue(getSafeValue("defaultValue", row)); option.setDescription(getSafeValue("description", row)); // lets put required in the description if ("true".equals(option.getRequired())) { String desc = "*Required* " + option.getDescription(); option.setDescription(desc); } component.addComponentOption(option); // group separate between different options if (oldGroup == null || !oldGroup.equals(option.getGroup())) { option.setNewGroup(true); } oldGroup = option.getGroup(); } oldGroup = null; rows = 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.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.setSecret(getSafeValue("secret", row)); option.setDefaultValue(getSafeValue("defaultValue", row)); option.setDescription(getSafeValue("description", row)); // lets put required in the description if ("true".equals(option.getRequired())) { String desc = "*Required* " + option.getDescription(); option.setDescription(desc); } // separate the options in path vs parameter so we can generate two different tables if ("path".equals(option.getKind())) { component.addEndpointPathOption(option); } else { component.addEndpointOption(option); } // group separate between different options if (oldGroup == null || !oldGroup.equals(option.getGroup())) { option.setNewGroup(true); } oldGroup = option.getGroup(); } return component; } private DataFormatModel generateDataFormatModel(String dataFormatName, String json) { List<Map<String, String>> rows = parseJsonSchema("dataformat", json, false); DataFormatModel dataFormat = new DataFormatModel(); dataFormat.setTitle(getSafeValue("title", rows)); dataFormat.setModelName(getSafeValue("modelName", rows)); dataFormat.setName(getSafeValue("name", rows)); dataFormat.setDescription(getSafeValue("description", rows)); dataFormat.setFirstVersion(getSafeValue("firstVersion", rows)); dataFormat.setLabel(getSafeValue("label", rows)); dataFormat.setDeprecated(getSafeValue("deprecated", rows)); dataFormat.setJavaType(getSafeValue("javaType", rows)); dataFormat.setGroupId(getSafeValue("groupId", rows)); dataFormat.setArtifactId(getSafeValue("artifactId", rows)); dataFormat.setVersion(getSafeValue("version", rows)); rows = parseJsonSchema("properties", json, true); for (Map<String, String> row : rows) { DataFormatOptionModel option = new DataFormatOptionModel(); 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.setEnumValues(getSafeValue("enum", row)); option.setDefaultValue(getSafeValue("defaultValue", row)); option.setDescription(getSafeValue("description", row)); // special for bindy as we reuse one readme file if (dataFormatName.startsWith("bindy") && option.getName().equals("type")) { option.setDefaultValue(""); String doc = option.getDescription() + " The default value is either Csv or KeyValue depending on chosen dataformat."; option.setDescription(doc); } // skip option named id if ("id".equals(option.getName())) { getLog().debug("Skipping option: " + option.getName()); } else { dataFormat.addDataFormatOption(option); } } return dataFormat; } private LanguageModel generateLanguageModel(String languageName, String json) { List<Map<String, String>> rows = parseJsonSchema("language", json, false); LanguageModel language = new LanguageModel(); language.setTitle(getSafeValue("title", rows)); language.setModelName(getSafeValue("modelName", rows)); language.setName(getSafeValue("name", rows)); language.setDescription(getSafeValue("description", rows)); language.setFirstVersion(getSafeValue("firstVersion", rows)); language.setLabel(getSafeValue("label", rows)); language.setDeprecated(getSafeValue("deprecated", rows)); language.setJavaType(getSafeValue("javaType", rows)); language.setGroupId(getSafeValue("groupId", rows)); language.setArtifactId(getSafeValue("artifactId", rows)); language.setVersion(getSafeValue("version", rows)); rows = parseJsonSchema("properties", json, true); for (Map<String, String> row : rows) { LanguageOptionModel option = new LanguageOptionModel(); 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.setEnumValues(getSafeValue("enum", row)); option.setDefaultValue(getSafeValue("defaultValue", row)); option.setDescription(getSafeValue("description", row)); // skip option named id/expression if ("id".equals(option.getName()) || "expression".equals(option.getName())) { getLog().debug("Skipping option: " + option.getName()); } else { language.addLanguageOption(option); } } return language; } private EipModel generateEipModel(String json) { List<Map<String, String>> rows = parseJsonSchema("model", json, false); EipModel eip = new EipModel(); eip.setName(getSafeValue("name", rows)); eip.setTitle(getSafeValue("title", rows)); eip.setDescription(getSafeValue("description", rows)); eip.setJavaType(getSafeValue("javaType", rows)); eip.setLabel(getSafeValue("label", rows)); eip.setDeprecated("true".equals(getSafeValue("deprecated", rows))); eip.setInput("true".equals(getSafeValue("input", rows))); eip.setOutput("true".equals(getSafeValue("output", rows))); rows = parseJsonSchema("properties", json, true); for (Map<String, String> row : rows) { EipOptionModel option = new EipOptionModel(); option.setName(getSafeValue("name", row)); option.setDisplayName(getSafeValue("displayName", row)); option.setType(getSafeValue("type", row)); option.setJavaType(getSafeValue("javaType", row)); option.setRequired(getSafeValue("required", row)); option.setDeprecated("true".equals(getSafeValue("deprecated", row))); option.setDescription(getSafeValue("description", row)); option.setInput("true".equals(getSafeValue("input", row))); option.setOutput("true".equals(getSafeValue("output", row))); // lets put required in the description if ("true".equals(option.getRequired())) { String desc = "*Required* " + option.getDescription(); option.setDescription(desc); } // skip option named id/description/expression/outputs if ("id".equals(option.getName()) || "description".equals(option.getName()) || "expression".equals(option.getName()) || "outputs".equals(option.getName())) { getLog().debug("Skipping option: " + option.getName()); } else { eip.addEipOptionModel(option); } } return eip; } private String templateComponentHeader(ComponentModel model) throws MojoExecutionException { try { String template = loadText(UpdateReadmeMojo.class.getClassLoader().getResourceAsStream("component-header.mvel")); String out = (String) TemplateRuntime.eval(template, model); return out; } catch (Exception e) { throw new MojoExecutionException("Error processing mvel template. Reason: " + e, e); } } private String templateComponentOptions(ComponentModel model) throws MojoExecutionException { try { String template = loadText(UpdateReadmeMojo.class.getClassLoader().getResourceAsStream("component-options.mvel")); String out = (String) TemplateRuntime.eval(template, model); return out; } catch (Exception e) { throw new MojoExecutionException("Error processing mvel template. Reason: " + e, e); } } private String templateEndpointOptions(ComponentModel model) throws MojoExecutionException { try { String template = loadText(UpdateReadmeMojo.class.getClassLoader().getResourceAsStream("endpoint-options.mvel")); String out = (String) TemplateRuntime.eval(template, model); return out; } catch (Exception e) { throw new MojoExecutionException("Error processing mvel template. Reason: " + e, e); } } private String templateDataFormatOptions(DataFormatModel model) throws MojoExecutionException { try { String template = loadText(UpdateReadmeMojo.class.getClassLoader().getResourceAsStream("dataformat-options.mvel")); String out = (String) TemplateRuntime.eval(template, model); return out; } catch (Exception e) { throw new MojoExecutionException("Error processing mvel template. Reason: " + e, e); } } private String templateLanguageOptions(LanguageModel model) throws MojoExecutionException { try { String template = loadText(UpdateReadmeMojo.class.getClassLoader().getResourceAsStream("language-options.mvel")); String out = (String) TemplateRuntime.eval(template, model); return out; } catch (Exception e) { throw new MojoExecutionException("Error processing mvel template. Reason: " + e, e); } } private String templateEipOptions(EipModel model) throws MojoExecutionException { try { String template = loadText(UpdateReadmeMojo.class.getClassLoader().getResourceAsStream("eip-options.mvel")); String out = (String) TemplateRuntime.eval(template, model); return out; } catch (Exception e) { throw new MojoExecutionException("Error processing mvel template. Reason: " + e, e); } } private List<String> findComponentNames() { List<String> componentNames = new ArrayList<String>(); for (Resource r : project.getBuild().getResources()) { File f = new File(r.getDirectory()); if (!f.exists()) { f = new File(project.getBasedir(), r.getDirectory()); } f = new File(f, "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); } } } } } return componentNames; } private List<String> findDataFormatNames() { List<String> dataFormatNames = new ArrayList<String>(); for (Resource r : project.getBuild().getResources()) { File f = new File(r.getDirectory()); if (!f.exists()) { f = new File(project.getBasedir(), r.getDirectory()); } f = new File(f, "META-INF/services/org/apache/camel/dataformat"); 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) != '.') { dataFormatNames.add(name); } } } } } return dataFormatNames; } private List<String> findLanguageNames() { List<String> languageNames = new ArrayList<String>(); for (Resource r : project.getBuild().getResources()) { File f = new File(r.getDirectory()); if (!f.exists()) { f = new File(project.getBasedir(), r.getDirectory()); } f = new File(f, "META-INF/services/org/apache/camel/language"); 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) != '.') { languageNames.add(name); } } } } } return languageNames; } private boolean isFailFast() { return failFast != null && failFast; } }