Repository: camel Updated Branches: refs/heads/master 394dbb462 -> ad26a8ab4
Added support in API components for parameterized endpoint configuration fields, e.g. parameters are now mapped to List<String>, instead of List, and documentation also shows the generic type parameters Project: http://git-wip-us.apache.org/repos/asf/camel/repo Commit: http://git-wip-us.apache.org/repos/asf/camel/commit/ad26a8ab Tree: http://git-wip-us.apache.org/repos/asf/camel/tree/ad26a8ab Diff: http://git-wip-us.apache.org/repos/asf/camel/diff/ad26a8ab Branch: refs/heads/master Commit: ad26a8ab4fef1b44fcf1d8ca2c81c9972272287a Parents: 394dbb4 Author: Dhiraj Bokde <dhira...@yahoo.com> Authored: Thu Jun 19 13:34:00 2014 -0700 Committer: Dhiraj Bokde <dhira...@yahoo.com> Committed: Thu Jun 19 13:34:32 2014 -0700 ---------------------------------------------------------------------- .../camel/util/component/ApiMethodParser.java | 47 +++++++++++-- .../component/ArgumentSubstitutionParser.java | 11 +++- .../camel-api-component-maven-plugin/pom.xml | 7 ++ .../maven/AbstractApiMethodGeneratorMojo.java | 69 ++++++++++++++++++-- .../camel/maven/AbstractGeneratorMojo.java | 20 +++--- .../camel/maven/DocumentGeneratorMojo.java | 33 ++++++++++ .../maven/JavadocApiMethodGeneratorMojo.java | 43 +++++++----- .../src/main/resources/api-document.vm | 2 +- 8 files changed, 188 insertions(+), 44 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/camel/blob/ad26a8ab/camel-core/src/main/java/org/apache/camel/util/component/ApiMethodParser.java ---------------------------------------------------------------------- diff --git a/camel-core/src/main/java/org/apache/camel/util/component/ApiMethodParser.java b/camel-core/src/main/java/org/apache/camel/util/component/ApiMethodParser.java index 03c9124..c5331be 100644 --- a/camel-core/src/main/java/org/apache/camel/util/component/ApiMethodParser.java +++ b/camel-core/src/main/java/org/apache/camel/util/component/ApiMethodParser.java @@ -35,8 +35,10 @@ import org.slf4j.LoggerFactory; */ public abstract class ApiMethodParser<T> { + private static final String METHOD_PREFIX = "^(\\s*(public|final|synchronized|native)\\s+)*(\\s*<[^\\>]>)?\\s*(\\S+)\\s+([^\\(]+\\s*)\\("; private static final Pattern METHOD_PATTERN = Pattern.compile("\\s*(\\S+)\\s+(\\S+)\\s*\\(\\s*([\\S\\s,]*)\\)\\s*;?\\s*"); private static final Pattern ARGS_PATTERN = Pattern.compile("\\s*(\\S+)\\s+([^\\s,]+)\\s*,?"); + private static final Pattern GENERIC_ARG_PATTERN = Pattern.compile("(\\S+)<([^>]+)>"); private static final String JAVA_LANG = "java.lang."; private static final Map<String, Class> PRIMITIVE_TYPES; @@ -53,6 +55,7 @@ public abstract class ApiMethodParser<T> { PRIMITIVE_TYPES.put("short", Short.TYPE); } + private final Logger log = LoggerFactory.getLogger(getClass()); private final Class<T> proxyType; @@ -92,8 +95,14 @@ public abstract class ApiMethodParser<T> { // parse sorted signatures and generate descriptions List<ApiMethodModel> result = new ArrayList<ApiMethodModel>(); for (String signature: signatures) { - // remove all type parameters and modifiers - signature = signature.replaceAll("<[^>]*>|\\s*(public|final|synchronized|native)\\s*", ""); + + // remove all modifiers and type parameters for method + signature = signature.replaceAll(METHOD_PREFIX, "$4 $5("); + // remove all final modifiers for arguments + signature = signature.replaceAll("(\\(|,\\s*)final\\s+", "$1"); + // remove all redundant spaces in generic parameters + signature = signature.replaceAll("\\s*<\\s*", "<").replaceAll("\\s*>", ">"); + log.debug("Processing " + signature); final Matcher methodMatcher = METHOD_PATTERN.matcher(signature); @@ -101,7 +110,12 @@ public abstract class ApiMethodParser<T> { throw new IllegalArgumentException("Invalid method signature " + signature); } - final Class<?> resultType = forName(methodMatcher.group(1)); + // drop any generic type parameters in result, if any + final String resultTypeWithArgs = methodMatcher.group(1); + final Matcher resultMatcher = GENERIC_ARG_PATTERN.matcher(resultTypeWithArgs); + final Class<?> resultType = (resultMatcher.matches()) ? + forName(resultMatcher.group(1)) : forName(resultTypeWithArgs); + final String name = methodMatcher.group(2); final String argSignature = methodMatcher.group(3); @@ -110,8 +124,17 @@ public abstract class ApiMethodParser<T> { List<Class<?>> argTypes = new ArrayList<Class<?>>(); final Matcher argsMatcher = ARGS_PATTERN.matcher(argSignature); while (argsMatcher.find()) { - final Class<?> type = forName(argsMatcher.group(1)); - arguments.add(new Argument(argsMatcher.group(2), type)); + final String argTypeWithParams = argsMatcher.group(1); + final Matcher genericMatcher = GENERIC_ARG_PATTERN.matcher(argTypeWithParams); + Class<?> type; + String typeArgs = null; + if (genericMatcher.matches()) { + type = forName(genericMatcher.group(1)); + typeArgs = genericMatcher.group(2); + } else { + type = forName(argTypeWithParams); + } + arguments.add(new Argument(argsMatcher.group(2), type, typeArgs)); argTypes.add(type); } @@ -298,10 +321,12 @@ public abstract class ApiMethodParser<T> { public static final class Argument { private final String name; private final Class<?> type; + private final String typeArgs; - protected Argument(String name, Class<?> type) { + protected Argument(String name, Class<?> type, String typeArgs) { this.name = name; this.type = type; + this.typeArgs = typeArgs; } public String getName() { @@ -312,10 +337,18 @@ public abstract class ApiMethodParser<T> { return type; } + public String getTypeArgs() { + return typeArgs; + } + @Override public String toString() { StringBuilder builder = new StringBuilder(); - builder.append(type.getCanonicalName()).append(" ").append(name); + builder.append(type.getCanonicalName()); + if (typeArgs != null) { + builder.append("<").append(typeArgs).append(">"); + } + builder.append(" ").append(name); return builder.toString(); } } http://git-wip-us.apache.org/repos/asf/camel/blob/ad26a8ab/camel-core/src/main/java/org/apache/camel/util/component/ArgumentSubstitutionParser.java ---------------------------------------------------------------------- diff --git a/camel-core/src/main/java/org/apache/camel/util/component/ArgumentSubstitutionParser.java b/camel-core/src/main/java/org/apache/camel/util/component/ArgumentSubstitutionParser.java index d9686e9..06ff551 100644 --- a/camel-core/src/main/java/org/apache/camel/util/component/ArgumentSubstitutionParser.java +++ b/camel-core/src/main/java/org/apache/camel/util/component/ArgumentSubstitutionParser.java @@ -87,7 +87,9 @@ public class ArgumentSubstitutionParser<T> extends ApiMethodParser<T> { final List<Argument> updatedArguments = new ArrayList<Argument>(); final Map<Pattern, List<NameReplacement>> argMap = methodEntry.getValue(); for (Argument argument : model.getArguments()) { + final Class<?> argType = argument.getType(); + final String typeArgs = argument.getTypeArgs(); final String argTypeName = argType.getCanonicalName(); for (Map.Entry<Pattern, List<NameReplacement>> argEntry : argMap.entrySet()) { @@ -98,20 +100,23 @@ public class ArgumentSubstitutionParser<T> extends ApiMethodParser<T> { final List<NameReplacement> adapters = argEntry.getValue(); for (NameReplacement adapter : adapters) { if (adapter.typePattern == null) { + // no type pattern final String newName = getJavaArgName(matcher.replaceAll(adapter.replacement)); - argument = new Argument(newName, argType); + argument = new Argument(newName, argType, typeArgs); + } else { + final Matcher typeMatcher = adapter.typePattern.matcher(argTypeName); if (typeMatcher.find()) { if (!adapter.replaceWithType) { // replace argument name final String newName = getJavaArgName(matcher.replaceAll(adapter.replacement)); - argument = new Argument(newName, argType); + argument = new Argument(newName, argType, typeArgs); } else { // replace name with argument type name final String newName = getJavaArgName(typeMatcher.replaceAll(adapter.replacement)); - argument = new Argument(newName, argType); + argument = new Argument(newName, argType, typeArgs); } } } http://git-wip-us.apache.org/repos/asf/camel/blob/ad26a8ab/tooling/maven/camel-api-component-maven-plugin/pom.xml ---------------------------------------------------------------------- diff --git a/tooling/maven/camel-api-component-maven-plugin/pom.xml b/tooling/maven/camel-api-component-maven-plugin/pom.xml index dcffb8e..a7e5549 100644 --- a/tooling/maven/camel-api-component-maven-plugin/pom.xml +++ b/tooling/maven/camel-api-component-maven-plugin/pom.xml @@ -101,6 +101,13 @@ <artifactId>junit</artifactId> <scope>test</scope> </dependency> + <!-- Camel annotations in provided scope to avoid compile errors in IDEs --> + <dependency> + <groupId>org.apache.camel</groupId> + <artifactId>spi-annotations</artifactId> + <version>${project.version}</version> + <scope>provided</scope> + </dependency> <!-- VelocityEngine javadoc for testing --> <dependency> <groupId>org.apache.velocity</groupId> http://git-wip-us.apache.org/repos/asf/camel/blob/ad26a8ab/tooling/maven/camel-api-component-maven-plugin/src/main/java/org/apache/camel/maven/AbstractApiMethodGeneratorMojo.java ---------------------------------------------------------------------- diff --git a/tooling/maven/camel-api-component-maven-plugin/src/main/java/org/apache/camel/maven/AbstractApiMethodGeneratorMojo.java b/tooling/maven/camel-api-component-maven-plugin/src/main/java/org/apache/camel/maven/AbstractApiMethodGeneratorMojo.java index 7a38ce0..82132ed 100644 --- a/tooling/maven/camel-api-component-maven-plugin/src/main/java/org/apache/camel/maven/AbstractApiMethodGeneratorMojo.java +++ b/tooling/maven/camel-api-component-maven-plugin/src/main/java/org/apache/camel/maven/AbstractApiMethodGeneratorMojo.java @@ -74,6 +74,7 @@ public abstract class AbstractApiMethodGeneratorMojo extends AbstractSourceGener parser.setClassLoader(getProjectClassLoader()); // parse signatures + @SuppressWarnings("unchecked") final List<ApiMethodParser.ApiMethodModel> models = parser.parse(); // generate enumeration from model @@ -90,6 +91,7 @@ public abstract class AbstractApiMethodGeneratorMojo extends AbstractSourceGener } } + @SuppressWarnings("unchecked") protected ApiMethodParser createAdapterParser(Class proxyType) { return new ArgumentSubstitutionParser(proxyType, getArgumentSubstitutions()); } @@ -152,7 +154,7 @@ public abstract class AbstractApiMethodGeneratorMojo extends AbstractSourceGener context.put("componentPackage", componentPackage); // generate parameter names and types for configuration, sorted by parameter name - Map<String, Class<?>> parameters = new TreeMap<String, Class<?>>(); + Map<String,ApiMethodParser.Argument> parameters = new TreeMap<String, ApiMethodParser.Argument>(); for (ApiMethodParser.ApiMethodModel model : models) { for (ApiMethodParser.Argument argument : model.getArguments()) { final String name = argument.getName(); @@ -161,11 +163,7 @@ public abstract class AbstractApiMethodGeneratorMojo extends AbstractSourceGener if (!parameters.containsKey(name) && (propertyNamePattern == null || !propertyNamePattern.matcher(name).matches()) && (propertyTypePattern == null || !propertyTypePattern.matcher(typeName).matches())) { - if (type.isPrimitive()) { - // replace primitives with wrapper classes - type = ClassUtils.primitiveToWrapper(type); - } - parameters.put(name, type); + parameters.put(name, argument); } } } @@ -275,4 +273,63 @@ public abstract class AbstractApiMethodGeneratorMojo extends AbstractSourceGener return builder.toString(); } + public String getCanonicalName(ApiMethodParser.Argument argument) throws MojoExecutionException { + + // replace primitives with wrapper classes + final Class<?> type = argument.getType(); + if (type.isPrimitive()) { + return getCanonicalName(ClassUtils.primitiveToWrapper(type)); + } + + // get default name prefix + String canonicalName = getCanonicalName(type); + + final String typeArgs = argument.getTypeArgs(); + if (typeArgs != null) { + + // add generic type arguments + StringBuilder parameterizedType = new StringBuilder(canonicalName); + parameterizedType.append('<'); + + String[] argTypes = typeArgs.split(","); + boolean ignore = false; + for (String argType : argTypes) { + + // try loading as is first + try { + parameterizedType.append(getCanonicalName(getProjectClassLoader().loadClass(argType))); + } catch (ClassNotFoundException e) { + + // try loading with default java.lang package prefix + try { + if (log.isDebugEnabled()) { + log.debug("Could not load " + argType + ", trying to load java.lang." + argType); + } + parameterizedType.append( + getCanonicalName(getProjectClassLoader().loadClass("java.lang." + argType))); + } catch (ClassNotFoundException e1) { + log.warn("Ignoring type parameters " + typeArgs + "> for argument " + argument.getName() + + ", unable to load parameteric type argument " + argType, e1); + ignore = true; + } + } + + if (ignore) { + // give up + break; + } else { + parameterizedType.append(","); + } + } + + if (!ignore) { + // replace the last ',' with '>' + parameterizedType.deleteCharAt(parameterizedType.length() - 1); + parameterizedType.append('>'); + canonicalName = parameterizedType.toString(); + } + } + + return canonicalName; + } } http://git-wip-us.apache.org/repos/asf/camel/blob/ad26a8ab/tooling/maven/camel-api-component-maven-plugin/src/main/java/org/apache/camel/maven/AbstractGeneratorMojo.java ---------------------------------------------------------------------- diff --git a/tooling/maven/camel-api-component-maven-plugin/src/main/java/org/apache/camel/maven/AbstractGeneratorMojo.java b/tooling/maven/camel-api-component-maven-plugin/src/main/java/org/apache/camel/maven/AbstractGeneratorMojo.java index b37d272..26ebdaf 100644 --- a/tooling/maven/camel-api-component-maven-plugin/src/main/java/org/apache/camel/maven/AbstractGeneratorMojo.java +++ b/tooling/maven/camel-api-component-maven-plugin/src/main/java/org/apache/camel/maven/AbstractGeneratorMojo.java @@ -71,16 +71,6 @@ public abstract class AbstractGeneratorMojo extends AbstractMojo { private VelocityEngine engine; private ClassLoader projectClassLoader; - public static String getCanonicalName(Class<?> type) { - // remove java.lang prefix for default Java package - String canonicalName = type.getCanonicalName(); - final int pkgEnd = canonicalName.lastIndexOf('.'); - if (pkgEnd > 0 && canonicalName.substring(0, pkgEnd).equals("java.lang")) { - canonicalName = canonicalName.substring(pkgEnd + 1); - } - return canonicalName; - } - public VelocityEngine getEngine() { if (engine == null) { // initialize velocity to load resources from class loader and use Log4J @@ -151,4 +141,14 @@ public abstract class AbstractGeneratorMojo extends AbstractMojo { } } } + + public static String getCanonicalName(Class<?> type) { + // remove java.lang prefix for default Java package + String canonicalName = type.getCanonicalName(); + final int pkgEnd = canonicalName.lastIndexOf('.'); + if (pkgEnd > 0 && canonicalName.substring(0, pkgEnd).equals("java.lang")) { + canonicalName = canonicalName.substring(pkgEnd + 1); + } + return canonicalName; + } } http://git-wip-us.apache.org/repos/asf/camel/blob/ad26a8ab/tooling/maven/camel-api-component-maven-plugin/src/main/java/org/apache/camel/maven/DocumentGeneratorMojo.java ---------------------------------------------------------------------- diff --git a/tooling/maven/camel-api-component-maven-plugin/src/main/java/org/apache/camel/maven/DocumentGeneratorMojo.java b/tooling/maven/camel-api-component-maven-plugin/src/main/java/org/apache/camel/maven/DocumentGeneratorMojo.java index 3db7343..17a87cb 100644 --- a/tooling/maven/camel-api-component-maven-plugin/src/main/java/org/apache/camel/maven/DocumentGeneratorMojo.java +++ b/tooling/maven/camel-api-component-maven-plugin/src/main/java/org/apache/camel/maven/DocumentGeneratorMojo.java @@ -20,6 +20,8 @@ import java.io.File; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; import java.util.ArrayList; import java.util.HashSet; import java.util.List; @@ -407,6 +409,37 @@ public class DocumentGeneratorMojo extends AbstractGeneratorMojo implements Mave } } + public static String getCanonicalName(Field field) { + final Type fieldType = field.getGenericType(); + if (fieldType instanceof ParameterizedType) { + return getCanonicalName((ParameterizedType) fieldType); + } else { + return getCanonicalName(field.getType()); + } + } + + private static String getCanonicalName(ParameterizedType fieldType) { + final Type[] typeArguments = fieldType.getActualTypeArguments(); + + if (typeArguments.length > 0) { + final StringBuilder result = new StringBuilder(getCanonicalName((Class<?>) fieldType.getRawType())); + result.append("<"); + for (Type typeArg : typeArguments) { + if (typeArg instanceof ParameterizedType) { + result.append(getCanonicalName((ParameterizedType) typeArg)); + } else { + result.append(getCanonicalName((Class<?>) typeArg)); + } + result.append(','); + } + result.deleteCharAt(result.length() - 1); + result.append(">"); + return result.toString(); + } + + return getCanonicalName((Class<?>) fieldType.getRawType()); + } + public static class EndpointInfo { private String endpoint; private String aliases; http://git-wip-us.apache.org/repos/asf/camel/blob/ad26a8ab/tooling/maven/camel-api-component-maven-plugin/src/main/java/org/apache/camel/maven/JavadocApiMethodGeneratorMojo.java ---------------------------------------------------------------------- diff --git a/tooling/maven/camel-api-component-maven-plugin/src/main/java/org/apache/camel/maven/JavadocApiMethodGeneratorMojo.java b/tooling/maven/camel-api-component-maven-plugin/src/main/java/org/apache/camel/maven/JavadocApiMethodGeneratorMojo.java index 902682c..8b02c3e 100644 --- a/tooling/maven/camel-api-component-maven-plugin/src/main/java/org/apache/camel/maven/JavadocApiMethodGeneratorMojo.java +++ b/tooling/maven/camel-api-component-maven-plugin/src/main/java/org/apache/camel/maven/JavadocApiMethodGeneratorMojo.java @@ -19,10 +19,8 @@ package org.apache.camel.maven; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; -import java.io.UnsupportedEncodingException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; -import java.net.URLDecoder; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -36,6 +34,7 @@ import javax.swing.text.html.parser.Parser; import javax.swing.text.html.parser.TagElement; import org.apache.camel.util.component.ApiMethodParser; +import org.apache.commons.lang.StringEscapeUtils; import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.plugins.annotations.LifecyclePhase; import org.apache.maven.plugins.annotations.Mojo; @@ -72,14 +71,8 @@ public class JavadocApiMethodGeneratorMojo extends AbstractApiMethodGeneratorMoj Map<String, String> result = new HashMap<String, String>(); final Pattern packagePatterns = Pattern.compile(excludePackages); - Pattern classPatterns = null; - if (excludeClasses != null) { - classPatterns = Pattern.compile(excludeClasses); - } - Pattern methodPatterns = null; - if (excludeMethods != null) { - methodPatterns = Pattern.compile(excludeMethods); - } + final Pattern classPatterns = (excludeClasses != null) ? Pattern.compile(excludeClasses) : null; + final Pattern methodPatterns = (excludeMethods != null) ? Pattern.compile(excludeMethods) : null; // for proxy class and super classes not matching excluded packages or classes for (Class aClass = getProxyType(); @@ -104,6 +97,12 @@ public class JavadocApiMethodGeneratorMojo extends AbstractApiMethodGeneratorMoj final JavadocParser htmlParser = new JavadocParser(dtd, javaDocPath); htmlParser.parse(new InputStreamReader(inputStream, "UTF-8")); + // look for parse errors + final String parseError = htmlParser.getErrorMessage(); + if (parseError != null) { + throw new MojoExecutionException(parseError); + } + // get public method signature final Map<String, String> methodMap = htmlParser.getMethodText(); for (String method : htmlParser.getMethods()) { @@ -185,6 +184,7 @@ public class JavadocApiMethodGeneratorMojo extends AbstractApiMethodGeneratorMoj private List<String> methods = new ArrayList<String>(); private Map<String, String> methodText = new HashMap<String, String>(); + private String errorMessage; public JavadocParser(DTD dtd, String docPath) { super(dtd); @@ -211,11 +211,7 @@ public class JavadocApiMethodGeneratorMojo extends AbstractApiMethodGeneratorMoj if (href != null) { String hrefAttr = (String) href; if (hrefAttr.contains(hrefPattern)) { - try { - methodWithTypes = URLDecoder.decode(hrefAttr.substring(hrefAttr.indexOf('#') + 1), "UTF-8"); - } catch (UnsupportedEncodingException e) { - throw new IllegalStateException(e); - } + methodWithTypes = StringEscapeUtils.unescapeHtml(hrefAttr.substring(hrefAttr.indexOf('#') + 1)); } } } @@ -249,11 +245,13 @@ public class JavadocApiMethodGeneratorMojo extends AbstractApiMethodGeneratorMoj return "()"; } final String[] types = typeString.split(","); - String argText = methodTextBuilder.toString().replaceAll(" ", " ").replaceAll(" ", " "); + // use HTTP decode + String argText = StringEscapeUtils.unescapeHtml(methodTextBuilder.toString()); final String[] args = argText.substring(argText.indexOf('(') + 1, argText.indexOf(')')).split(","); StringBuilder builder = new StringBuilder("("); for (int i = 0; i < types.length; i++) { - final String[] arg = args[i].trim().split(" "); + // split on space or non-breaking space + final String[] arg = args[i].trim().split(" |\u00A0"); builder.append(types[i]).append(" ").append(arg[1].trim()).append(","); } builder.deleteCharAt(builder.length() - 1); @@ -268,6 +266,17 @@ public class JavadocApiMethodGeneratorMojo extends AbstractApiMethodGeneratorMoj } } + @Override + protected void handleError(int ln, String msg) { + if (msg.startsWith("exception ")) { + this.errorMessage = "Exception parsing Javadoc line " + ln + ": " + msg; + } + } + + private String getErrorMessage() { + return errorMessage; + } + private List<String> getMethods() { return methods; } http://git-wip-us.apache.org/repos/asf/camel/blob/ad26a8ab/tooling/maven/camel-api-component-maven-plugin/src/main/resources/api-document.vm ---------------------------------------------------------------------- diff --git a/tooling/maven/camel-api-component-maven-plugin/src/main/resources/api-document.vm b/tooling/maven/camel-api-component-maven-plugin/src/main/resources/api-document.vm index 66d7f18..b87e19c 100644 --- a/tooling/maven/camel-api-component-maven-plugin/src/main/resources/api-document.vm +++ b/tooling/maven/camel-api-component-maven-plugin/src/main/resources/api-document.vm @@ -179,7 +179,7 @@ #foreach( $field in $endpointConfig.DeclaredFields ) <tr> <td>$field.Name</td> - <td>$helper.getCanonicalName($field.Type)</td> + <td>$helper.getCanonicalName($field)</td> </tr> #end </table>