Repository: camel
Updated Branches:
  refs/heads/master e1289b864 -> 803d14888


FUSETOOLS-1347: Add built-in functions for transformation


Project: http://git-wip-us.apache.org/repos/asf/camel/repo
Commit: http://git-wip-us.apache.org/repos/asf/camel/commit/c53a5c81
Tree: http://git-wip-us.apache.org/repos/asf/camel/tree/c53a5c81
Diff: http://git-wip-us.apache.org/repos/asf/camel/diff/c53a5c81

Branch: refs/heads/master
Commit: c53a5c814fc235cd1c47f6abd0e3bfc2b9356ac4
Parents: e1289b8
Author: John Verhaeg <john.verh...@gmail.com>
Authored: Tue Aug 25 22:48:57 2015 -0500
Committer: Jonathan Anstey <jans...@gmail.com>
Committed: Tue Sep 1 09:32:56 2015 -0230

----------------------------------------------------------------------
 .../camel/component/dozer/CustomMapper.java     | 204 +++++++++++++++----
 .../camel/component/dozer/DozerProducer.java    |   6 +
 .../dozer/CustomMapperParametersTest.java       |  62 ++++++
 3 files changed, 231 insertions(+), 41 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/camel/blob/c53a5c81/components/camel-dozer/src/main/java/org/apache/camel/component/dozer/CustomMapper.java
----------------------------------------------------------------------
diff --git 
a/components/camel-dozer/src/main/java/org/apache/camel/component/dozer/CustomMapper.java
 
b/components/camel-dozer/src/main/java/org/apache/camel/component/dozer/CustomMapper.java
index 331de0c..0807160 100644
--- 
a/components/camel-dozer/src/main/java/org/apache/camel/component/dozer/CustomMapper.java
+++ 
b/components/camel-dozer/src/main/java/org/apache/camel/component/dozer/CustomMapper.java
@@ -16,7 +16,12 @@
  */
 package org.apache.camel.component.dozer;
 
+import java.lang.reflect.Array;
 import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
 
 import org.apache.camel.spi.ClassResolver;
 
@@ -25,71 +30,188 @@ import org.apache.camel.spi.ClassResolver;
  * required to extend/implement Dozer-specific classes.
  */
 public class CustomMapper extends BaseConverter {
-    
+
     private ClassResolver resolver;
-    
+
     public CustomMapper(ClassResolver resolver) {
         this.resolver = resolver;
     }
-    
+
     @Override
-    public Object convert(Object existingDestinationFieldValue, 
-            Object sourceFieldValue, 
-            Class<?> destinationClass,
-            Class<?> sourceClass) {
+    public Object convert(Object existingDestinationFieldValue,
+                                         Object sourceFieldValue,
+                                         Class<?> destinationClass,
+                                         Class<?> sourceClass) {
         try {
             return mapCustom(sourceFieldValue);
         } finally {
             done();
         }
     }
-    
-    Method selectMethod(Class<?> customClass, Object fromType) {
-        Method method = null;
-        for (Method m : customClass.getDeclaredMethods()) {
-            if (m.getReturnType() != null 
-                    && m.getParameterTypes().length == 1
-                    && 
m.getParameterTypes()[0].isAssignableFrom(fromType.getClass())) {
-                method = m;
-                break;
-            }
-        }
-        return method;
+
+    private Object invokeFunction(Method method,
+                                                         Object customObj,
+                                                         Object source,
+                                                         String[][] 
parameters) throws Exception {
+               Class<?>[] prmTypes = method.getParameterTypes();
+               Object[] methodPrms = new Object[prmTypes.length];
+               methodPrms[0] = source;
+               for (int parameterNdx = 0, methodPrmNdx = 1; parameterNdx < 
parameters.length; parameterNdx++, methodPrmNdx++) {
+                       if (method.isVarArgs() && methodPrmNdx == 
prmTypes.length - 1) {
+                               Object array = 
Array.newInstance(prmTypes[methodPrmNdx].getComponentType(), parameters.length 
- parameterNdx);
+                               for (int arrayNdx = 0; parameterNdx < 
parameters.length; parameterNdx++, arrayNdx++) {
+                                       String[] parts = 
parameters[parameterNdx];
+                                       Array.set(array, arrayNdx, 
resolver.resolveClass(parts[0]).getConstructor(String.class).newInstance(parts[1]));
+                               }
+                               methodPrms[methodPrmNdx] = array;
+                       } else {
+                               String[] parts = parameters[parameterNdx];
+                               methodPrms[methodPrmNdx] = 
resolver.resolveClass(parts[0]).getConstructor(String.class).newInstance(parts[1]);;
+                       }
+               }
+               return method.invoke(customObj, methodPrms);
     }
 
     Object mapCustom(Object source) {
-        Object customMapObj;
-        Method mapMethod;
-        
-        // The converter parameter is stored in a thread local variable, so 
+        // The converter parameter is stored in a thread local variable, so
         // we need to parse the parameter on each invocation
-        String[] params = getParameter().split(",");
-        String className = params[0];
-        String operation = params.length > 1 ? params[1] : null;
-        
+        // ex: custom-converter-param="org.example.MyMapping,map"
+        // className = org.example.MyMapping
+        // operation = map
+        String[] prms = getParameter().split(",");
+        String className = prms[0];
+        String operation = prms.length > 1 ? prms[1] : null;
+
+        // now attempt to process any additional parameters passed along
+        // ex: 
custom-converter-param="org.example.MyMapping,substring,java.lang.Integer=3,java.lang.Integer=10"
+        // className = org.example.MyMapping
+        // operation = substring
+        // parameters = ["java.lang.Integer=3","java.lang.Integer=10"]
+        String[][] prmTypesAndValues;
+        if (prms.length > 2) {
+               // Break parameters down into types and values
+               prmTypesAndValues = new String[prms.length - 2][2];
+               for (int ndx = 0; ndx < prmTypesAndValues.length; ndx++) {
+                       String prm = prms[ndx + 2];
+                       String[] parts = prm.split("=");
+                       if (parts.length != 2) throw new 
RuntimeException("Value missing for parameter " + prm);
+                       prmTypesAndValues[ndx][0] = parts[0];
+                       prmTypesAndValues[ndx][1] = parts[1];
+               }
+        } else prmTypesAndValues = null;
+
+        Object customObj;
+        Method method = null;
         try {
             Class<?> customClass = resolver.resolveClass(className);
-            customMapObj = customClass.newInstance();
+            customObj = customClass.newInstance();
+
             // If a specific mapping operation has been supplied use that
-            if (operation != null) {
-                mapMethod = customClass.getMethod(operation, 
source.getClass());
+            if (operation != null && prmTypesAndValues != null) {
+               method = selectMethod(customClass, operation, source, 
prmTypesAndValues);
+            } else if (operation != null) {
+                method = customClass.getMethod(operation, source.getClass());
             } else {
-                mapMethod = selectMethod(customClass, source);
+                method = selectMethod(customClass, source);
             }
-        } catch (Exception cnfEx) {
-            throw new RuntimeException("Failed to load custom mapping", cnfEx);
+        } catch (Exception e) {
+            throw new RuntimeException("Failed to load custom function", e);
         }
-        
+
         // Verify that we found a matching method
-        if (mapMethod == null) {
-            throw new RuntimeException("No eligible custom mapping methods in 
" + className);
+        if (method == null) {
+            throw new RuntimeException("No eligible custom function methods in 
" + className);
         }
-        
+
         // Invoke the custom mapping method
         try {
-            return mapMethod.invoke(customMapObj, source);
-        } catch (Exception ex) {
-            throw new RuntimeException("Error while invoking custom mapping", 
ex);
+               if (prmTypesAndValues != null) {
+                       return invokeFunction(method, customObj, source, 
prmTypesAndValues);
+               } else {
+                       return method.invoke(customObj, source);
+               }
+        } catch (Exception e) {
+            throw new RuntimeException("Error while invoking custom function", 
e);
         }
     }
-}
+
+    private boolean parametersMatchParameterList(Class<?>[] prmTypes,
+                                                                               
         String[][] parameters) {
+       int ndx = 0;
+       while (ndx < prmTypes.length) {
+               Class<?> prmType = prmTypes[ndx];
+               if (ndx >= parameters.length) return ndx == prmTypes.length - 1 
&& prmType.isArray();
+               if (ndx == prmTypes.length - 1 && prmType.isArray()) { // 
Assume this only occurs for functions with var args
+                       Class<?> varArgClass = prmType.getComponentType();
+                       while (ndx < parameters.length) {
+                       Class<?> prmClass = 
resolver.resolveClass(parameters[ndx][0]);
+                       if (!varArgClass.isAssignableFrom(prmClass)) return 
false;
+                       ndx++;
+                       }
+               } else {
+                       Class<?> prmClass = 
resolver.resolveClass(parameters[ndx][0]);
+                       if (!prmTypes[ndx].isAssignableFrom(prmClass)) return 
false;
+               }
+               ndx++;
+       }
+       return true;
+    }
+
+    Method selectMethod(Class<?> customClass,
+                                       Object source) {
+        Method method = null;
+        for (Method m : customClass.getDeclaredMethods()) {
+            if (m.getReturnType() != null
+                    && m.getParameterTypes().length == 1
+                    && 
m.getParameterTypes()[0].isAssignableFrom(source.getClass())) {
+                method = m;
+                break;
+            }
+        }
+        return method;
+    }
+
+    // Assumes source is a separate parameter in method even if it has var 
args and that there are no
+    // ambiguous calls based upon number and types of parameters
+    private Method selectMethod(Class<?> customClass,
+                                                       String operation,
+                                                       Object source,
+                                                       String[][] parameters) {
+       // Create list of potential methods
+       List<Method> methods = new ArrayList<>();
+        for (Method method : customClass.getDeclaredMethods()) {
+               methods.add(method);
+        }
+
+        // Remove methods that are not applicable
+        for (Iterator<Method> iter = methods.iterator(); iter.hasNext();) {
+               Method method = iter.next();
+               Class<?>[] prmTypes = method.getParameterTypes();
+            if (!method.getName().equals(operation)
+                       || method.getReturnType() == null
+                       || !prmTypes[0].isAssignableFrom(source.getClass())) {
+               iter.remove();
+               continue;
+            }
+            prmTypes = Arrays.copyOfRange(prmTypes, 1, prmTypes.length); // 
Remove source from type list
+               if (!method.isVarArgs() && prmTypes.length != 
parameters.length) {
+               iter.remove();
+                       continue;
+               }
+               if (!parametersMatchParameterList(prmTypes, parameters)) {
+               iter.remove();
+                       continue;
+               }
+        }
+
+        // If more than one method is applicable, return the method whose prm 
list exactly matches the parameters
+        // if possible
+        if (methods.size() > 1) {
+               for (Method method : methods) {
+                       if (!method.isVarArgs()) return method;
+               }
+        }
+
+        return methods.size() > 0 ? methods.get(0) : null;
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/camel/blob/c53a5c81/components/camel-dozer/src/main/java/org/apache/camel/component/dozer/DozerProducer.java
----------------------------------------------------------------------
diff --git 
a/components/camel-dozer/src/main/java/org/apache/camel/component/dozer/DozerProducer.java
 
b/components/camel-dozer/src/main/java/org/apache/camel/component/dozer/DozerProducer.java
index b7b12c8..5ae109c 100644
--- 
a/components/camel-dozer/src/main/java/org/apache/camel/component/dozer/DozerProducer.java
+++ 
b/components/camel-dozer/src/main/java/org/apache/camel/component/dozer/DozerProducer.java
@@ -53,6 +53,9 @@ public class DozerProducer extends DefaultProducer {
         if (unmarshalId != null) {
             LOG.debug("Unmarshalling input data using data format '{}'.", 
unmarshalId);
             resolveUnmarshaller(exchange, unmarshalId).process(exchange);
+            if (exchange.getException() != null) {
+               throw exchange.getException();
+            }
         }
         
         // Load the target model class
@@ -95,6 +98,9 @@ public class DozerProducer extends DefaultProducer {
         if (marshalId != null) {
             LOG.debug("Marshalling output data using data format '{}'.", 
marshalId);
             resolveMarshaller(exchange, marshalId).process(exchange);
+            if (exchange.getException() != null) {
+               throw exchange.getException();
+            }
         }
     }
     

http://git-wip-us.apache.org/repos/asf/camel/blob/c53a5c81/components/camel-dozer/src/test/java/org/apache/camel/component/dozer/CustomMapperParametersTest.java
----------------------------------------------------------------------
diff --git 
a/components/camel-dozer/src/test/java/org/apache/camel/component/dozer/CustomMapperParametersTest.java
 
b/components/camel-dozer/src/test/java/org/apache/camel/component/dozer/CustomMapperParametersTest.java
new file mode 100644
index 0000000..da3daec
--- /dev/null
+++ 
b/components/camel-dozer/src/test/java/org/apache/camel/component/dozer/CustomMapperParametersTest.java
@@ -0,0 +1,62 @@
+/**
+ * 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.component.dozer;
+
+import org.apache.camel.impl.DefaultClassResolver;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+public class CustomMapperParametersTest {
+
+    private CustomMapper customMapper;
+
+    @Before
+    public void setup() {
+        customMapper = new CustomMapper(new DefaultClassResolver());
+    }
+
+    @Test
+    public void shouldExecuteCustomFunctionWithArguments() throws Exception {
+        customMapper.setParameter(MapperWithMultiParmMethod.class.getName() + 
",test,java.lang.Integer=12,java.lang.Integer=20");
+        Object result = customMapper.mapCustom("JeremiahWasABullfrog");
+        Assert.assertEquals("Bullfrog", result);
+    }
+
+    @Test
+    public void shouldExecuteCustomFunctionWithVariableArguments() throws 
Exception {
+        customMapper.setParameter(MapperWithMultiParmMethod.class.getName() + 
",add,java.lang.Integer=12,java.lang.Integer=20");
+        Object result = customMapper.mapCustom("JeremiahWasABullfrog");
+        Assert.assertEquals(32L, result);
+    }
+}
+
+class MapperWithMultiParmMethod {
+
+    public Object add(String source, Integer... operands) {
+       long sum = 0L;
+       for (Integer operand : operands) {
+               sum += operand;
+       }
+       return sum;
+    }
+
+    public Object test(String source, Integer beginindex, Integer endindex) {
+       return source.substring(beginindex.intValue(), endindex.intValue());
+    }
+}
+

Reply via email to