cleaned up my stupid mistake in source tree. some are repushed by me. sorry
for noise.

On Mon, May 18, 2020 at 11:33 PM <ond...@apache.org> wrote:

> This is an automated email from the ASF dual-hosted git repository.
>
> onders pushed a commit to branch master
> in repository https://gitbox.apache.org/repos/asf/camel.git
>
> commit 22d7ec90b39cdbda1ad0478b06520a5d12720473
> Author: Claus Ibsen <claus.ib...@gmail.com>
> AuthorDate: Mon May 18 23:25:46 2020 +0300
>
>     CAMEL-15079: PropertyBindingSupport - Add support for parameters to
> factory method
> ---
>  .../org/apache/camel/support/AnimalFactory.java    | 29 +++++++
>  .../camel/support/PropertyBindingSupportTest.java  | 36 ++++++++
>  .../camel/support/PropertyBindingSupport.java      | 99
> +++++++++++++++++++++-
>  3 files changed, 163 insertions(+), 1 deletion(-)
>
> diff --git
> a/core/camel-core/src/test/java/org/apache/camel/support/AnimalFactory.java
> b/core/camel-core/src/test/java/org/apache/camel/support/AnimalFactory.java
> new file mode 100644
> index 0000000..ad5d1cc
> --- /dev/null
> +++
> b/core/camel-core/src/test/java/org/apache/camel/support/AnimalFactory.java
> @@ -0,0 +1,29 @@
> +/*
> + * 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.support;
> +
> +public class AnimalFactory {
> +
> +    public static Animal createAnimal(String name, boolean dangerous) {
> +        return new Animal(name, dangerous);
> +    }
> +
> +    public static Animal createAnimal(String name) {
> +        return new Animal(name, true);
> +    }
> +
> +}
> diff --git
> a/core/camel-core/src/test/java/org/apache/camel/support/PropertyBindingSupportTest.java
> b/core/camel-core/src/test/java/org/apache/camel/support/PropertyBindingSupportTest.java
> index b0fa8e6..8b96ed1 100644
> ---
> a/core/camel-core/src/test/java/org/apache/camel/support/PropertyBindingSupportTest.java
> +++
> b/core/camel-core/src/test/java/org/apache/camel/support/PropertyBindingSupportTest.java
> @@ -384,6 +384,42 @@ public class PropertyBindingSupportTest extends
> ContextTestSupport {
>      }
>
>      @Test
> +    public void testNestedClassFactoryParameterOneParameter() throws
> Exception {
> +        Foo foo = new Foo();
> +
> +        PropertyBindingSupport.build().bind(context, foo, "name",
> "James");
> +        PropertyBindingSupport.build().bind(context, foo, "animal",
> "#class:org.apache.camel.support.AnimalFactory#createAnimal('Tiger')");
> +
> +        assertEquals("James", foo.getName());
> +        assertEquals("Tiger", foo.getAnimal().getName());
> +        assertEquals(true, foo.getAnimal().isDangerous());
> +    }
> +
> +    @Test
> +    public void testNestedClassFactoryParameterTwoParameter() throws
> Exception {
> +        Foo foo = new Foo();
> +
> +        PropertyBindingSupport.build().bind(context, foo, "name",
> "James");
> +        PropertyBindingSupport.build().bind(context, foo, "animal",
> "#class:org.apache.camel.support.AnimalFactory#createAnimal('Donald Duck',
> false)");
> +
> +        assertEquals("James", foo.getName());
> +        assertEquals("Donald Duck", foo.getAnimal().getName());
> +        assertEquals(false, foo.getAnimal().isDangerous());
> +    }
> +
> +    @Test
> +    public void testNestedClassFactoryParameterPlaceholder() throws
> Exception {
> +        Foo foo = new Foo();
> +
> +        PropertyBindingSupport.build().bind(context, foo, "name",
> "James");
> +        PropertyBindingSupport.build().bind(context, foo, "animal",
> "#class:org.apache.camel.support.AnimalFactory#createAnimal('{{companyName}}',
> false)");
> +
> +        assertEquals("James", foo.getName());
> +        assertEquals("Acme", foo.getAnimal().getName());
> +        assertEquals(false, foo.getAnimal().isDangerous());
> +    }
> +
> +    @Test
>      public void testPropertiesOptionalKey() throws Exception {
>          Foo foo = new Foo();
>
> diff --git
> a/core/camel-support/src/main/java/org/apache/camel/support/PropertyBindingSupport.java
> b/core/camel-support/src/main/java/org/apache/camel/support/PropertyBindingSupport.java
> index df84e27..6ab4322 100644
> ---
> a/core/camel-support/src/main/java/org/apache/camel/support/PropertyBindingSupport.java
> +++
> b/core/camel-support/src/main/java/org/apache/camel/support/PropertyBindingSupport.java
> @@ -18,6 +18,7 @@ package org.apache.camel.support;
>
>  import java.lang.reflect.Constructor;
>  import java.lang.reflect.Method;
> +import java.lang.reflect.Modifier;
>  import java.util.ArrayList;
>  import java.util.Comparator;
>  import java.util.HashMap;
> @@ -56,6 +57,8 @@ import static
> org.apache.camel.util.ObjectHelper.isNotEmpty;
>   *     <li>reference new class - Values can refer to creating new beans
> by their class name by prefixing with #class, eg #class:com.foo.MyClassType.
>   *                               The class is created using a default
> no-arg constructor, however if you need to create the instance via a
> factory method
>   *                               then you specify the method as shown:
> #class:com.foo.MyClassType#myFactoryMethod.
> + *                               And if the factory method requires
> parameters they can be specified as follows:
> + *
>  #class:com.foo.MyClassType#myFactoryMethod('Hello World', 5, true).
>   *                               Or if you need to create the instance
> via constructor parameters then you can specify the parameters as shown:
>   *                               #class:com.foo.MyClass('Hello World', 5,
> true)</li>.
>   *     <li>ignore case - Whether to ignore case for property keys<li>
> @@ -929,6 +932,92 @@ public final class PropertyBindingSupport {
>          return candidates.size() == 1 ? candidates.get(0) :
> fallbackCandidate;
>      }
>
> +    private static Object newInstanceFactoryParameters(CamelContext
> camelContext, Class<?> type, String factoryMethod, String parameters)
> throws Exception {
> +        String[] params = StringQuoteHelper.splitSafeQuote(parameters,
> ',');
> +        Method found = findMatchingFactoryMethod(type.getMethods(),
> factoryMethod, params);
> +        if (found != null) {
> +            Object[] arr = new Object[found.getParameterCount()];
> +            for (int i = 0; i < found.getParameterCount(); i++) {
> +                Class<?> paramType = found.getParameterTypes()[i];
> +                Object param = params[i];
> +                Object val =
> camelContext.getTypeConverter().convertTo(paramType, param);
> +                // unquote text
> +                if (val instanceof String) {
> +                    val =
> StringHelper.removeLeadingAndEndingQuotes((String) val);
> +                }
> +                arr[i] = val;
> +            }
> +
> +            return found.invoke(null, arr);
> +        }
> +        return null;
> +    }
> +
> +    /**
> +     * Finds the best matching factory methods for the given parameters.
> +     * <p/>
> +     * This implementation is similar to the logic in camel-bean.
> +     *
> +     * @param methods       the methods
> +     * @param factoryMethod the name of the factory method
> +     * @param params        the parameters
> +     * @return the constructor, or null if no matching constructor can be
> found
> +     */
> +    private static Method findMatchingFactoryMethod(Method[] methods,
> String factoryMethod, String[] params) {
> +        List<Method> candidates = new ArrayList<>();
> +        Method fallbackCandidate = null;
> +
> +        for (Method method : methods) {
> +            // must match factory method name
> +            if (!factoryMethod.equals(method.getName())) {
> +                continue;
> +            }
> +            // must be a public static method that returns something
> +            if (!Modifier.isStatic(method.getModifiers()) ||
> +                !Modifier.isPublic(method.getModifiers()) ||
> +                method.getReturnType() == Void.TYPE) {
> +                continue;
> +            }
> +            // must match number of parameters
> +            if (method.getParameterCount() != params.length) {
> +                continue;
> +            }
> +
> +            boolean matches = true;
> +            for (int i = 0; i < method.getParameterCount(); i++) {
> +                String parameter = params[i];
> +                if (parameter != null) {
> +                    // must trim
> +                    parameter = parameter.trim();
> +                }
> +
> +                Class<?> parameterType = getValidParameterType(parameter);
> +                Class<?> expectedType = method.getParameterTypes()[i];
> +
> +                if (parameterType != null && expectedType != null) {
> +                    // skip java.lang.Object type, when we have multiple
> possible methods we want to avoid it if possible
> +                    if (Object.class.equals(expectedType)) {
> +                        fallbackCandidate = method;
> +                        matches = false;
> +                        break;
> +                    }
> +
> +                    boolean matchingTypes =
> isParameterMatchingType(parameterType, expectedType);
> +                    if (!matchingTypes) {
> +                        matches = false;
> +                        break;
> +                    }
> +                }
> +            }
> +
> +            if (matches) {
> +                candidates.add(method);
> +            }
> +        }
> +
> +        return candidates.size() == 1 ? candidates.get(0) :
> fallbackCandidate;
> +    }
> +
>      /**
>       * Determines and maps the given value is valid according to the
> supported
>       * values by the bean component.
> @@ -1030,7 +1119,15 @@ public final class PropertyBindingSupport {
>              }
>              Class<?> type =
> camelContext.getClassResolver().resolveMandatoryClass(className);
>              if (factoryMethod != null) {
> -                value = camelContext.getInjector().newInstance(type,
> factoryMethod);
> +                if (parameters != null) {
> +                    // special to support factory method parameters
> +                    value = newInstanceFactoryParameters(camelContext,
> type, factoryMethod, parameters);
> +                } else {
> +                    value = camelContext.getInjector().newInstance(type,
> factoryMethod);
> +                }
> +                if (value == null) {
> +                    throw new IllegalStateException("Cannot create bean
> instance using factory method: " + className + "#" + factoryMethod);
> +                }
>              } else if (parameters != null) {
>                  // special to support constructor parameters
>                  value = newInstanceConstructorParameters(camelContext,
> type, parameters);
>
>

Reply via email to