This is an automated email from the ASF dual-hosted git repository.

davsclaus pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/camel.git

commit e86896fa284842d5e66120d1390ab1888aa408e2
Author: Claus Ibsen <claus.ib...@gmail.com>
AuthorDate: Thu May 30 14:28:17 2019 +0200

    CAMEL-13557: Add property binding support to make it convenient to 
configure components and whatnot.
---
 ...st.java => PropertyBindingSupportListTest.java} | 67 +++++++++++-----------
 .../support/PropertyBindingSupportMapTest.java     |  2 +-
 .../apache/camel/support/IntrospectionSupport.java | 40 ++++++++++---
 .../camel/support/PropertyBindingSupport.java      | 29 ++++++++--
 4 files changed, 91 insertions(+), 47 deletions(-)

diff --git 
a/core/camel-core/src/test/java/org/apache/camel/support/PropertyBindingSupportMapTest.java
 
b/core/camel-core/src/test/java/org/apache/camel/support/PropertyBindingSupportListTest.java
similarity index 70%
copy from 
core/camel-core/src/test/java/org/apache/camel/support/PropertyBindingSupportMapTest.java
copy to 
core/camel-core/src/test/java/org/apache/camel/support/PropertyBindingSupportListTest.java
index f46e976..5579c64 100644
--- 
a/core/camel-core/src/test/java/org/apache/camel/support/PropertyBindingSupportMapTest.java
+++ 
b/core/camel-core/src/test/java/org/apache/camel/support/PropertyBindingSupportListTest.java
@@ -17,6 +17,7 @@
 package org.apache.camel.support;
 
 import java.util.LinkedHashMap;
+import java.util.List;
 import java.util.Map;
 import java.util.Properties;
 
@@ -28,7 +29,7 @@ import org.junit.Test;
 /**
  * Unit test for PropertyBindingSupport
  */
-public class PropertyBindingSupportMapTest extends ContextTestSupport {
+public class PropertyBindingSupportListTest extends ContextTestSupport {
 
     @Override
     protected CamelContext createCamelContext() throws Exception {
@@ -52,7 +53,7 @@ public class PropertyBindingSupportMapTest extends 
ContextTestSupport {
     }
 
     @Test
-    public void testPropertiesMap() throws Exception {
+    public void testPropertiesList() throws Exception {
         Foo foo = new Foo();
 
         Map<String, Object> prop = new LinkedHashMap<>();
@@ -60,8 +61,8 @@ public class PropertyBindingSupportMapTest extends 
ContextTestSupport {
         prop.put("bar.age", "33");
         prop.put("bar.{{committer}}", "true");
         prop.put("bar.gold-customer", "true");
-        prop.put("bar.works[acme]", "#bean:company1");
-        prop.put("bar.works[burger]", "#bean:company2");
+        prop.put("bar.works[0]", "#bean:company1");
+        prop.put("bar.works[1]", "#bean:company2");
 
         PropertyBindingSupport.bindProperties(context, foo, prop);
 
@@ -70,14 +71,14 @@ public class PropertyBindingSupportMapTest extends 
ContextTestSupport {
         assertTrue(foo.getBar().isRider());
         assertTrue(foo.getBar().isGoldCustomer());
         assertEquals(2, foo.getBar().getWorks().size());
-        assertEquals(123, foo.getBar().getWorks().get("acme").getId());
-        assertEquals("Acme", foo.getBar().getWorks().get("acme").getName());
-        assertEquals(456, foo.getBar().getWorks().get("burger").getId());
-        assertEquals("Acme 2", 
foo.getBar().getWorks().get("burger").getName());
+        assertEquals(123, foo.getBar().getWorks().get(0).getId());
+        assertEquals("Acme", foo.getBar().getWorks().get(0).getName());
+        assertEquals(456, foo.getBar().getWorks().get(1).getId());
+        assertEquals("Acme 2", foo.getBar().getWorks().get(1).getName());
     }
 
     @Test
-    public void testPropertiesMapNested() throws Exception {
+    public void testPropertiesListNested() throws Exception {
         Foo foo = new Foo();
 
         Map<String, Object> prop = new LinkedHashMap<>();
@@ -85,10 +86,10 @@ public class PropertyBindingSupportMapTest extends 
ContextTestSupport {
         prop.put("bar.age", "33");
         prop.put("bar.{{committer}}", "true");
         prop.put("bar.gold-customer", "true");
-        prop.put("bar.works[acme]", "#bean:company1");
-        prop.put("bar.works[acme].id", "666");
-        prop.put("bar.works[burger]", "#bean:company2");
-        prop.put("bar.works[burger].name", "I changed this");
+        prop.put("bar.works[0]", "#bean:company1");
+        prop.put("bar.works[0].id", "666");
+        prop.put("bar.works[1]", "#bean:company2");
+        prop.put("bar.works[1].name", "I changed this");
 
         PropertyBindingSupport.bindProperties(context, foo, prop);
 
@@ -97,47 +98,47 @@ public class PropertyBindingSupportMapTest extends 
ContextTestSupport {
         assertTrue(foo.getBar().isRider());
         assertTrue(foo.getBar().isGoldCustomer());
         assertEquals(2, foo.getBar().getWorks().size());
-        assertEquals(666, foo.getBar().getWorks().get("acme").getId());
-        assertEquals("Acme", foo.getBar().getWorks().get("acme").getName());
-        assertEquals(456, foo.getBar().getWorks().get("burger").getId());
-        assertEquals("I changed this", 
foo.getBar().getWorks().get("burger").getName());
+        assertEquals(666, foo.getBar().getWorks().get(0).getId());
+        assertEquals("Acme", foo.getBar().getWorks().get(0).getName());
+        assertEquals(456, foo.getBar().getWorks().get(1).getId());
+        assertEquals("I changed this", 
foo.getBar().getWorks().get(1).getName());
     }
 
     @Test
-    public void testPropertiesMapFirst() throws Exception {
+    public void testPropertiesListFirst() throws Exception {
         Bar bar = new Bar();
 
         Map<String, Object> prop = new LinkedHashMap<>();
-        prop.put("works[acme]", "#bean:company1");
-        prop.put("works[acme].id", "666");
-        prop.put("works[burger]", "#bean:company2");
-        prop.put("works[burger].name", "I changed this");
+        prop.put("works[0]", "#bean:company1");
+        prop.put("works[0].id", "666");
+        prop.put("works[1]", "#bean:company2");
+        prop.put("works[1].name", "I changed this");
 
         PropertyBindingSupport.bindProperties(context, bar, prop);
 
         assertEquals(2, bar.getWorks().size());
-        assertEquals(666, bar.getWorks().get("acme").getId());
-        assertEquals("Acme", bar.getWorks().get("acme").getName());
-        assertEquals(456, bar.getWorks().get("burger").getId());
-        assertEquals("I changed this", bar.getWorks().get("burger").getName());
+        assertEquals(666, bar.getWorks().get(0).getId());
+        assertEquals("Acme", bar.getWorks().get(0).getName());
+        assertEquals(456, bar.getWorks().get(1).getId());
+        assertEquals("I changed this", bar.getWorks().get(1).getName());
     }
 
     @Test
-    public void testPropertiesNotMap() throws Exception {
+    public void testPropertiesNotList() throws Exception {
         Foo foo = new Foo();
 
         Map<String, Object> prop = new LinkedHashMap<>();
         prop.put("name", "James");
         prop.put("bar.age", "33");
-        prop.put("bar.gold-customer[foo]", "true");
+        prop.put("bar.gold-customer[]", "true");
 
         try {
             PropertyBindingSupport.bindProperties(context, foo, prop);
             fail("Should have thrown exception");
         } catch (PropertyBindingException e) {
-            assertEquals("bar.gold-customer[foo]", e.getPropertyName());
+            assertEquals("bar.gold-customer[]", e.getPropertyName());
             IllegalArgumentException iae = 
assertIsInstanceOf(IllegalArgumentException.class, e.getCause());
-            assertTrue(iae.getMessage().startsWith("Cannot set property: 
gold-customer[foo] as a Map because target bean is not a Map"));
+            assertTrue(iae.getMessage().startsWith("Cannot set property: 
gold-customer[] as either a Map/List because target bean is not a Map or List 
type"));
         }
     }
 
@@ -165,7 +166,7 @@ public class PropertyBindingSupportMapTest extends 
ContextTestSupport {
     public static class Bar {
         private int age;
         private boolean rider;
-        private Map<String, Company> works; // should auto-create this via the 
setter
+        private List<Company> works; // should auto-create this via the setter
         private boolean goldCustomer;
 
         public int getAge() {
@@ -184,11 +185,11 @@ public class PropertyBindingSupportMapTest extends 
ContextTestSupport {
             this.rider = rider;
         }
 
-        public Map<String, Company> getWorks() {
+        public List<Company> getWorks() {
             return works;
         }
 
-        public void setWorks(Map<String, Company> works) {
+        public void setWorks(List<Company> works) {
             this.works = works;
         }
 
diff --git 
a/core/camel-core/src/test/java/org/apache/camel/support/PropertyBindingSupportMapTest.java
 
b/core/camel-core/src/test/java/org/apache/camel/support/PropertyBindingSupportMapTest.java
index f46e976..0acaa24 100644
--- 
a/core/camel-core/src/test/java/org/apache/camel/support/PropertyBindingSupportMapTest.java
+++ 
b/core/camel-core/src/test/java/org/apache/camel/support/PropertyBindingSupportMapTest.java
@@ -137,7 +137,7 @@ public class PropertyBindingSupportMapTest extends 
ContextTestSupport {
         } catch (PropertyBindingException e) {
             assertEquals("bar.gold-customer[foo]", e.getPropertyName());
             IllegalArgumentException iae = 
assertIsInstanceOf(IllegalArgumentException.class, e.getCause());
-            assertTrue(iae.getMessage().startsWith("Cannot set property: 
gold-customer[foo] as a Map because target bean is not a Map"));
+            assertTrue(iae.getMessage().startsWith("Cannot set property: 
gold-customer[foo] as either a Map/List because target bean is not a Map or 
List type"));
         }
     }
 
diff --git 
a/core/camel-support/src/main/java/org/apache/camel/support/IntrospectionSupport.java
 
b/core/camel-support/src/main/java/org/apache/camel/support/IntrospectionSupport.java
index 700260a..05893a7 100644
--- 
a/core/camel-support/src/main/java/org/apache/camel/support/IntrospectionSupport.java
+++ 
b/core/camel-support/src/main/java/org/apache/camel/support/IntrospectionSupport.java
@@ -44,6 +44,8 @@ import org.apache.camel.util.StringHelper;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import static org.apache.camel.util.ObjectHelper.isNotEmpty;
+
 /**
  * Helper for introspections of beans.
  * <p/>
@@ -538,16 +540,28 @@ public final class IntrospectionSupport {
     public static boolean setProperty(CamelContext context, TypeConverter 
typeConverter, Object target, String name, Object value, String refName,
                                       boolean allowBuilderPattern) throws 
Exception {
 
-        // does the property name include a mapped key, then we need to set 
the property as a map
+        // does the property name include a lookup key, then we need to set 
the property as a map or list
         if (name.contains("[") && name.endsWith("]")) {
             int pos = name.indexOf('[');
-            String mapKey = name.substring(pos + 1, name.length() - 1);
+            String lookupKey = name.substring(pos + 1, name.length() - 1);
             String key = name.substring(0, pos);
 
             Object obj = IntrospectionSupport.getOrElseProperty(target, key, 
null);
             if (obj == null) {
-                // it was supposed to be a map, but its null, so lets create a 
new map and set it automatically
-                obj = new LinkedHashMap<>();
+                // it was supposed to be a list or map, but its null, so lets 
create a new list or map and set it automatically
+                Method getter = 
IntrospectionSupport.getPropertyGetter(target.getClass(), key);
+                if (getter != null) {
+                    // what type does it have
+                    Class<?> returnType = getter.getReturnType();
+                    if (Map.class.isAssignableFrom(returnType)) {
+                        obj = new LinkedHashMap<>();
+                    } else if (Collection.class.isAssignableFrom(returnType)) {
+                        obj = new ArrayList<>();
+                    }
+                } else {
+                    // fallback as map type
+                    obj = new LinkedHashMap<>();
+                }
                 boolean hit = IntrospectionSupport.setProperty(context, 
target, key, obj);
                 if (!hit) {
                     throw new IllegalArgumentException("Cannot set property: " 
+ name + " as a Map because target bean has no setter method for the Map");
@@ -558,11 +572,23 @@ public final class IntrospectionSupport {
                 if (context != null && refName != null && value == null) {
                     value = CamelContextHelper.lookup(context, refName);
                 }
-                map.put(mapKey, value);
+                map.put(lookupKey, value);
+                return true;
+            } else if (obj instanceof List) {
+                List list = (List) obj;
+                if (context != null && refName != null && value == null) {
+                    value = CamelContextHelper.lookup(context, refName);
+                }
+                if (isNotEmpty(lookupKey)) {
+                    int idx = Integer.valueOf(lookupKey);
+                    list.add(idx, value);
+                } else {
+                    list.add(value);
+                }
                 return true;
             } else {
-                // not a map
-                throw new IllegalArgumentException("Cannot set property: " + 
name + " as a Map because target bean is not a Map: " + target);
+                // not a map or list
+                throw new IllegalArgumentException("Cannot set property: " + 
name + " as either a Map/List because target bean is not a Map or List type: " 
+ target);
             }
         }
 
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 760029c..9741b80 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
@@ -20,6 +20,7 @@ import java.lang.reflect.Method;
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.LinkedHashMap;
+import java.util.List;
 import java.util.Map;
 import java.util.Set;
 import java.util.regex.Matcher;
@@ -29,6 +30,8 @@ import org.apache.camel.CamelContext;
 import org.apache.camel.PropertyBindingException;
 
 import static org.apache.camel.support.IntrospectionSupport.findSetterMethods;
+import static org.apache.camel.util.ObjectHelper.isNotEmpty;
+import static org.apache.camel.util.StringHelper.notEmpty;
 
 /**
  * A convenient support class for binding String valued properties to an 
instance which
@@ -36,7 +39,9 @@ import static 
org.apache.camel.support.IntrospectionSupport.findSetterMethods;
  * <ul>
  *     <li>property placeholders - Keys and values using Camels property 
placeholder will be resolved</li>
  *     <li>nested - Properties can be nested using the dot syntax (OGNL and 
builder pattern using with as prefix), eg foo.bar=123</li>
- *     <li>keys with map</li> - Properties can lookup in Map's using map 
syntax, eg foo[bar] where foo is the name of the property that is a Map 
instance, and bar is the name of the key.</li>
+ *     <li>map</li> - Properties can lookup in Map's using map syntax, eg 
foo[bar] where foo is the name of the property that is a Map instance, and bar 
is the name of the key.</li>
+ *     <li>list</li> - Properties can refer or add to in List's using list 
syntax, eg foo[0] where foo is the name of the property that is a
+ *                     List instance, and 0 is the index. To refer to the last 
element, then use last as key.</li>
  *     <li>reference by bean id - Values can refer to other beans in the 
registry by prefixing with #nean: eg #bean:myBean</li>
  *     <li>reference by type - Values can refer to singleton beans by their 
type in the registry by prefixing with #type: syntax, eg 
#type:com.foo.MyClassType</li>
  *     <li>autowire by type - Values can refer to singleton beans by auto 
wiring by setting the value to #autowired</li>
@@ -444,22 +449,34 @@ public final class PropertyBindingSupport {
 
     private static Object getOrElseProperty(Object target, String property, 
Object defaultValue) {
         String key = property;
-        String mapKey = null;
+        String lookupKey = null;
 
         // support maps in keys
         if (property.contains("[") && property.endsWith("]")) {
             int pos = property.indexOf('[');
-            mapKey = property.substring(pos + 1, property.length() - 1);
+            lookupKey = property.substring(pos + 1, property.length() - 1);
             key = property.substring(0, pos);
         }
 
         Object answer = IntrospectionSupport.getOrElseProperty(target, key, 
defaultValue);
-        if (answer instanceof Map && mapKey != null) {
+        if (answer instanceof Map && lookupKey != null) {
             Map map = (Map) answer;
-            answer = map.getOrDefault(mapKey, defaultValue);
+            answer = map.getOrDefault(lookupKey, defaultValue);
+        } else if (answer instanceof List) {
+            List list = (List) answer;
+            if (isNotEmpty(lookupKey)) {
+                int idx = Integer.valueOf(lookupKey);
+                answer = list.get(idx);
+            } else {
+                if (list.isEmpty()) {
+                    answer = null;
+                } else {
+                    answer = list.get(list.size() - 1);
+                }
+            }
         }
 
-        return answer;
+        return answer != null ? answer : defaultValue;
     }
 
     private static Method findBestSetterMethod(Class clazz, String name, 
boolean fluentBuilder) {

Reply via email to