Repository: camel Updated Branches: refs/heads/master 47e392f78 -> 857311be5
CAMEL-10107: [api-component-framework] Make ApiMethodImpl's arguments type safe Project: http://git-wip-us.apache.org/repos/asf/camel/repo Commit: http://git-wip-us.apache.org/repos/asf/camel/commit/857311be Tree: http://git-wip-us.apache.org/repos/asf/camel/tree/857311be Diff: http://git-wip-us.apache.org/repos/asf/camel/diff/857311be Branch: refs/heads/master Commit: 857311be5b136e2a7271f0e680799eaf1424bdc3 Parents: aac2662 Author: lburgazzoli <[email protected]> Authored: Thu Jun 30 17:23:05 2016 +0200 Committer: lburgazzoli <[email protected]> Committed: Thu Jul 21 11:47:40 2016 +0200 ---------------------------------------------------------------------- .../camel/util/component/ApiMethodArg.java | 60 ++++++++++++++++++++ .../camel/util/component/ApiMethodHelper.java | 4 +- .../camel/util/component/ApiMethodImpl.java | 24 +++----- .../camel/util/component/ApiMethodParser.java | 53 +++-------------- .../component/ArgumentSubstitutionParser.java | 10 ++-- .../util/component/ApiMethodHelperTest.java | 19 ++++--- .../maven/AbstractApiMethodGeneratorMojo.java | 9 +-- .../camel/maven/AbstractGeneratorMojo.java | 1 + .../src/main/resources/api-method-enum.vm | 12 +++- 9 files changed, 110 insertions(+), 82 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/camel/blob/857311be/camel-core/src/main/java/org/apache/camel/util/component/ApiMethodArg.java ---------------------------------------------------------------------- diff --git a/camel-core/src/main/java/org/apache/camel/util/component/ApiMethodArg.java b/camel-core/src/main/java/org/apache/camel/util/component/ApiMethodArg.java new file mode 100644 index 0000000..34bac83 --- /dev/null +++ b/camel-core/src/main/java/org/apache/camel/util/component/ApiMethodArg.java @@ -0,0 +1,60 @@ +/** + * 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.util.component; + +public class ApiMethodArg { + private final String name; + private final Class<?> type; + private final String typeArgs; + + public ApiMethodArg(String name, Class<?> type, String typeArgs) { + this.name = name; + this.type = type; + this.typeArgs = typeArgs; + } + + public String getName() { + return this.name; + } + + public Class<?> getType() { + return this.type; + } + + public String getTypeArgs() { + return this.typeArgs; + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + builder.append(type.getCanonicalName()); + if (typeArgs != null) { + builder.append("<").append(typeArgs).append(">"); + } + builder.append(" ").append(name); + return builder.toString(); + } + + public static ApiMethodArg arg(String name, Class<?> type) { + return new ApiMethodArg(name, type, null); + } + + public static ApiMethodArg arg(String name, Class<?> type, String typeArgs) { + return new ApiMethodArg(name, type, typeArgs); + } +} http://git-wip-us.apache.org/repos/asf/camel/blob/857311be/camel-core/src/main/java/org/apache/camel/util/component/ApiMethodHelper.java ---------------------------------------------------------------------- diff --git a/camel-core/src/main/java/org/apache/camel/util/component/ApiMethodHelper.java b/camel-core/src/main/java/org/apache/camel/util/component/ApiMethodHelper.java index 8d09517..ff979b0 100644 --- a/camel-core/src/main/java/org/apache/camel/util/component/ApiMethodHelper.java +++ b/camel-core/src/main/java/org/apache/camel/util/component/ApiMethodHelper.java @@ -327,7 +327,7 @@ public final class ApiMethodHelper<T extends Enum<T> & ApiMethod> { // method takes a subset, unused args extraArgs.add(method); } - } else if (result.isEmpty() && extraArgs != null && extraArgs.isEmpty()) { + } else if (result.isEmpty() && extraArgs == null) { // avoid looking for nullable args by checking for empty result and extraArgs if (withNullableArgsList != null && withNullableArgsList.containsAll(methodArgs)) { if (nullArgs == null) { @@ -341,7 +341,7 @@ public final class ApiMethodHelper<T extends Enum<T> & ApiMethod> { } List<ApiMethod> methodList = result.isEmpty() - ? (extraArgs != null && extraArgs.isEmpty()) + ? extraArgs == null ? nullArgs : extraArgs : result; http://git-wip-us.apache.org/repos/asf/camel/blob/857311be/camel-core/src/main/java/org/apache/camel/util/component/ApiMethodImpl.java ---------------------------------------------------------------------- diff --git a/camel-core/src/main/java/org/apache/camel/util/component/ApiMethodImpl.java b/camel-core/src/main/java/org/apache/camel/util/component/ApiMethodImpl.java index 324ac80..73aa438 100644 --- a/camel-core/src/main/java/org/apache/camel/util/component/ApiMethodImpl.java +++ b/camel-core/src/main/java/org/apache/camel/util/component/ApiMethodImpl.java @@ -30,11 +30,11 @@ import java.util.List; * <pre> * {@code * public enum HelloWorldMethod implements ApiMethod { - * SAYHI(String.class, "sayHi", "name", String.class); + * SAYHI(String.class, "sayHi", ApiMethodArg.from(String.class, "name"); * * private ApiMethodImpl apiMethod; * - * private HelloWorldMethods(Class<?> resultType, String name, Object... args) throws IllegalArgumentException { + * private HelloWorldMethods(Class<?> resultType, String name, ApiMethodArg... args) throws IllegalArgumentException { * this.apiMethod = new ApiMethod(HelloWorld.class, resultType, name, args); * } * @@ -57,21 +57,15 @@ public final class ApiMethodImpl implements ApiMethod { private final List<Class<?>> argTypes; private final Method method; - public ApiMethodImpl(Class<?> proxyType, Class<?> resultType, String name, Object... args) throws IllegalArgumentException { + public ApiMethodImpl(Class<?> proxyType, Class<?> resultType, String name, ApiMethodArg... args) throws IllegalArgumentException { this.name = name; this.resultType = resultType; - if (args.length % 2 != 0) { - throw new IllegalArgumentException("Invalid parameter list, " - + "must be of the form 'Class arg1, String arg1Name, Class arg2, String arg2Name..."); - } - - int nArgs = args.length / 2; - final List<String> tmpArgNames = new ArrayList<>(nArgs); - final List<Class<?>> tmpArgTypes = new ArrayList<>(nArgs); - for (int i = 0; i < nArgs; i++) { - tmpArgTypes.add((Class<?>) args[i * 2]); - tmpArgNames.add((String) args[i * 2 + 1]); + final List<String> tmpArgNames = new ArrayList<>(args.length); + final List<Class<?>> tmpArgTypes = new ArrayList<>(args.length); + for (ApiMethodArg arg : args) { + tmpArgTypes.add(arg.getType()); + tmpArgNames.add(arg.getName()); } this.argNames = Collections.unmodifiableList(tmpArgNames); @@ -79,7 +73,7 @@ public final class ApiMethodImpl implements ApiMethod { // find method in Proxy type try { - this.method = proxyType.getMethod(name, argTypes.toArray(new Class[nArgs])); + this.method = proxyType.getMethod(name, argTypes.toArray(new Class[args.length])); } catch (NoSuchMethodException e) { throw new IllegalArgumentException( String.format("Missing method %s %s", name, argTypes.toString().replace('[', '(').replace(']', ')')), http://git-wip-us.apache.org/repos/asf/camel/blob/857311be/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 a7a1a70..488dd9e 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 @@ -123,7 +123,7 @@ public abstract class ApiMethodParser<T> { final String name = methodMatcher.group(3); final String argSignature = methodMatcher.group(4); - final List<Argument> arguments = new ArrayList<Argument>(); + final List<ApiMethodArg> arguments = new ArrayList<ApiMethodArg>(); final List<Class<?>> argTypes = new ArrayList<Class<?>>(); final Matcher argsMatcher = ARGS_PATTERN.matcher(argSignature); @@ -135,7 +135,7 @@ public abstract class ApiMethodParser<T> { final String typeArgsGroup = argsMatcher.group(2); final String typeArgs = typeArgsGroup != null ? typeArgsGroup.substring(1, typeArgsGroup.length() - 1).replaceAll(" ", "") : null; - arguments.add(new Argument(argsMatcher.group(3), type, typeArgs)); + arguments.add(new ApiMethodArg(argsMatcher.group(3), type, typeArgs)); } Method method; @@ -153,7 +153,7 @@ public abstract class ApiMethodParser<T> { // check that argument names have the same type across methods Map<String, Class<?>> allArguments = new HashMap<String, Class<?>>(); for (ApiMethodModel model : result) { - for (Argument argument : model.getArguments()) { + for (ApiMethodArg argument : model.getArguments()) { String name = argument.getName(); Class<?> argClass = allArguments.get(name); Class<?> type = argument.getType(); @@ -185,7 +185,7 @@ public abstract class ApiMethodParser<T> { } else { // same number of args, compare arg names, kinda arbitrary to use alphabetized order for (int i = 0; i < nArgs1; i++) { - final int argCompare = model1.arguments.get(i).name.compareTo(model2.arguments.get(i).name); + final int argCompare = model1.arguments.get(i).getName().compareTo(model2.arguments.get(i).getName()); if (argCompare != 0) { return argCompare; } @@ -283,19 +283,19 @@ public abstract class ApiMethodParser<T> { public static final class ApiMethodModel { private final String name; private final Class<?> resultType; - private final List<Argument> arguments; + private final List<ApiMethodArg> arguments; private final Method method; private String uniqueName; - protected ApiMethodModel(String name, Class<?> resultType, List<Argument> arguments, Method method) { + protected ApiMethodModel(String name, Class<?> resultType, List<ApiMethodArg> arguments, Method method) { this.name = name; this.resultType = resultType; this.arguments = arguments; this.method = method; } - protected ApiMethodModel(String uniqueName, String name, Class<?> resultType, List<Argument> arguments, Method method) { + protected ApiMethodModel(String uniqueName, String name, Class<?> resultType, List<ApiMethodArg> arguments, Method method) { this.name = name; this.uniqueName = uniqueName; this.resultType = resultType; @@ -319,7 +319,7 @@ public abstract class ApiMethodParser<T> { return method; } - public List<Argument> getArguments() { + public List<ApiMethodArg> getArguments() { return arguments; } @@ -328,7 +328,7 @@ public abstract class ApiMethodParser<T> { StringBuilder builder = new StringBuilder(); builder.append(resultType.getName()).append(" "); builder.append(name).append("("); - for (Argument argument : arguments) { + for (ApiMethodArg argument : arguments) { builder.append(argument.getType().getCanonicalName()).append(" "); builder.append(argument.getName()).append(", "); } @@ -339,39 +339,4 @@ public abstract class ApiMethodParser<T> { return builder.toString(); } } - - public static final class Argument { - private final String name; - private final Class<?> type; - private final String typeArgs; - - public Argument(String name, Class<?> type, String typeArgs) { - this.name = name; - this.type = type; - this.typeArgs = typeArgs; - } - - public String getName() { - return name; - } - - public Class<?> getType() { - return type; - } - - public String getTypeArgs() { - return typeArgs; - } - - @Override - public String toString() { - StringBuilder builder = new StringBuilder(); - 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/857311be/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 06ff551..00d9aa8 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 @@ -84,9 +84,9 @@ public class ArgumentSubstitutionParser<T> extends ApiMethodParser<T> { if (methodEntry.getKey().matcher(model.getName()).matches()) { // look for arg name matches - final List<Argument> updatedArguments = new ArrayList<Argument>(); + final List<ApiMethodArg> updatedArguments = new ArrayList<ApiMethodArg>(); final Map<Pattern, List<NameReplacement>> argMap = methodEntry.getValue(); - for (Argument argument : model.getArguments()) { + for (ApiMethodArg argument : model.getArguments()) { final Class<?> argType = argument.getType(); final String typeArgs = argument.getTypeArgs(); @@ -103,7 +103,7 @@ public class ArgumentSubstitutionParser<T> extends ApiMethodParser<T> { // no type pattern final String newName = getJavaArgName(matcher.replaceAll(adapter.replacement)); - argument = new Argument(newName, argType, typeArgs); + argument = new ApiMethodArg(newName, argType, typeArgs); } else { @@ -112,11 +112,11 @@ public class ArgumentSubstitutionParser<T> extends ApiMethodParser<T> { if (!adapter.replaceWithType) { // replace argument name final String newName = getJavaArgName(matcher.replaceAll(adapter.replacement)); - argument = new Argument(newName, argType, typeArgs); + argument = new ApiMethodArg(newName, argType, typeArgs); } else { // replace name with argument type name final String newName = getJavaArgName(typeMatcher.replaceAll(adapter.replacement)); - argument = new Argument(newName, argType, typeArgs); + argument = new ApiMethodArg(newName, argType, typeArgs); } } } http://git-wip-us.apache.org/repos/asf/camel/blob/857311be/camel-core/src/test/java/org/apache/camel/util/component/ApiMethodHelperTest.java ---------------------------------------------------------------------- diff --git a/camel-core/src/test/java/org/apache/camel/util/component/ApiMethodHelperTest.java b/camel-core/src/test/java/org/apache/camel/util/component/ApiMethodHelperTest.java index 8913e33..aa9f4d8 100644 --- a/camel-core/src/test/java/org/apache/camel/util/component/ApiMethodHelperTest.java +++ b/camel-core/src/test/java/org/apache/camel/util/component/ApiMethodHelperTest.java @@ -26,6 +26,7 @@ import java.util.Map; import org.junit.Test; +import static org.apache.camel.util.component.ApiMethodArg.arg; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; @@ -183,18 +184,18 @@ public class ApiMethodHelperTest { enum TestMethod implements ApiMethod { SAYHI(String.class, "sayHi"), - SAYHI_1(String.class, "sayHi", String.class, "name"), - GREETME(String.class, "greetMe", String.class, "name"), - GREETUS(String.class, "greetUs", String.class, "name1", String.class, "name2"), - GREETALL(String.class, "greetAll", new String[0].getClass(), "names"), - GREETALL_1(String.class, "greetAll", List.class, "nameList"), - GREETALL_2(Map.class, "greetAll", Map.class, "nameMap"), - GREETTIMES(new String[0].getClass(), "greetTimes", String.class, "name", int.class, "times"), - GREETINNERCHILD(new String[0].getClass(), "greetInnerChild", TestProxy.InnerChild.class, "child"); + SAYHI_1(String.class, "sayHi", arg("name", String.class)), + GREETME(String.class, "greetMe", arg("name", String.class)), + GREETUS(String.class, "greetUs", arg("name1", String.class), arg("name2", String.class)), + GREETALL(String.class, "greetAll", arg("names", new String[0].getClass())), + GREETALL_1(String.class, "greetAll", arg("nameList", List.class)), + GREETALL_2(Map.class, "greetAll", arg("nameMap", Map.class)), + GREETTIMES(new String[0].getClass(), "greetTimes", arg("name", String.class), arg("times", int.class)), + GREETINNERCHILD(new String[0].getClass(), "greetInnerChild", arg("child", TestProxy.InnerChild.class)); private final ApiMethod apiMethod; - TestMethod(Class<?> resultType, String name, Object... args) { + TestMethod(Class<?> resultType, String name, ApiMethodArg... args) { this.apiMethod = new ApiMethodImpl(TestProxy.class, resultType, name, args); } http://git-wip-us.apache.org/repos/asf/camel/blob/857311be/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 5ac7cee..dc8d1d9 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 @@ -24,6 +24,7 @@ import java.util.TreeMap; import java.util.regex.Matcher; import java.util.regex.Pattern; +import org.apache.camel.util.component.ApiMethodArg; import org.apache.camel.util.component.ApiMethodParser; import org.apache.camel.util.component.ArgumentSubstitutionParser; import org.apache.commons.lang.ClassUtils; @@ -151,9 +152,9 @@ public abstract class AbstractApiMethodGeneratorMojo extends AbstractApiMethodBa context.put("componentPackage", componentPackage); // generate parameter names and types for configuration, sorted by parameter name - Map<String, ApiMethodParser.Argument> parameters = new TreeMap<String, ApiMethodParser.Argument>(); + Map<String, ApiMethodArg> parameters = new TreeMap<String, ApiMethodArg>(); for (ApiMethodParser.ApiMethodModel model : models) { - for (ApiMethodParser.Argument argument : model.getArguments()) { + for (ApiMethodArg argument : model.getArguments()) { final String name = argument.getName(); final Class<?> type = argument.getType(); @@ -185,7 +186,7 @@ public abstract class AbstractApiMethodGeneratorMojo extends AbstractApiMethodBa throw new MojoExecutionException(String.format("Error loading extra option [%s %s] : %s", argWithTypes, name, e.getMessage()), e); } - parameters.put(name, new ApiMethodParser.Argument(name, argType, typeArgs)); + parameters.put(name, new ApiMethodArg(name, argType, typeArgs)); } } @@ -298,7 +299,7 @@ public abstract class AbstractApiMethodGeneratorMojo extends AbstractApiMethodBa return builder.toString(); } - public String getCanonicalName(ApiMethodParser.Argument argument) throws MojoExecutionException { + public String getCanonicalName(ApiMethodArg argument) throws MojoExecutionException { // replace primitives with wrapper classes final Class<?> type = argument.getType(); http://git-wip-us.apache.org/repos/asf/camel/blob/857311be/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 e21f573..2fff5c0 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 @@ -138,6 +138,7 @@ public abstract class AbstractGeneratorMojo extends AbstractMojo { context.put("generatedDate", new Date().toString()); // add output package context.put("packageName", outPackage); + context.put("newLine", "\n"); // load velocity template final Template template = getEngine().getTemplate(templateName, "UTF-8"); http://git-wip-us.apache.org/repos/asf/camel/blob/857311be/tooling/maven/camel-api-component-maven-plugin/src/main/resources/api-method-enum.vm ---------------------------------------------------------------------- diff --git a/tooling/maven/camel-api-component-maven-plugin/src/main/resources/api-method-enum.vm b/tooling/maven/camel-api-component-maven-plugin/src/main/resources/api-method-enum.vm index 5b9470d..db31132 100644 --- a/tooling/maven/camel-api-component-maven-plugin/src/main/resources/api-method-enum.vm +++ b/tooling/maven/camel-api-component-maven-plugin/src/main/resources/api-method-enum.vm @@ -27,21 +27,27 @@ import java.util.List; import $proxyType.CanonicalName; import org.apache.camel.util.component.ApiMethod; +import org.apache.camel.util.component.ApiMethodArg; import org.apache.camel.util.component.ApiMethodImpl; +import static org.apache.camel.util.component.ApiMethodArg.arg; + /** * Camel {@link ApiMethod} Enumeration for $proxyType.Name */ public enum $enumName implements ApiMethod { #foreach ( $model in $models ) - ${model.UniqueName}($helper.getType($model.ResultType), "$model.Name"#foreach ( $arg in $model.Arguments ), $helper.getType($arg.Type), "$arg.Name"#end)#if ( $foreach.hasNext ),#else;#end - + ${model.UniqueName}( + $helper.getType($model.ResultType), + "$model.Name"#foreach ( $arg in $model.Arguments ), + arg("$arg.Name", $helper.getType($arg.Type))#end)#if ( $foreach.hasNext ),$newLine#else;#end #end + $newLine private final ApiMethod apiMethod; - private ${enumName}(Class<?> resultType, String name, Object... args) { + private ${enumName}(Class<?> resultType, String name, ApiMethodArg... args) { this.apiMethod = new ApiMethodImpl(${proxyType.SimpleName}.class, resultType, name, args); }
