camel-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From jans...@apache.org
Subject [1/2] camel git commit: FUSETOOLS-1347: Add built-in functions for transformation
Date Tue, 01 Sep 2015 12:21:54 GMT
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.verhaeg@gmail.com>
Authored: Tue Aug 25 22:48:57 2015 -0500
Committer: Jonathan Anstey <janstey@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());
+    }
+}
+


Mime
View raw message