From 1d9f0b24e5c531557ceab1da85c3a03a75e62218 Mon Sep 17 00:00:00 2001 From: Oleg Zhurakousky Date: Wed, 31 Aug 2011 17:54:47 -0400 Subject: [PATCH] ObjectToMapTransformer now handles BigDecimal refactored it to delegate to Jackson mapper (much simpler) deleted unused classes changed the name of the attribute to 'shouldFlattenKeys' added support and tests for Collection of Collections fixed method signature, polishing fixed javadoc polishing, fixed spelling error, updated javadocs --- .../transformer/CycleDetector.java | 160 ----------- .../transformer/ObjectToMapTransformer.java | 82 +++++- .../transformer/ObjectToSpelMapBuilder.java | 142 ---------- .../transformer/CycleDetectorTests.java | 266 ------------------ .../ObjectToMapTransformerTests.java | 157 +++++++++-- 5 files changed, 207 insertions(+), 600 deletions(-) delete mode 100644 spring-integration-core/src/main/java/org/springframework/integration/transformer/CycleDetector.java delete mode 100644 spring-integration-core/src/main/java/org/springframework/integration/transformer/ObjectToSpelMapBuilder.java delete mode 100644 spring-integration-core/src/test/java/org/springframework/integration/transformer/CycleDetectorTests.java diff --git a/spring-integration-core/src/main/java/org/springframework/integration/transformer/CycleDetector.java b/spring-integration-core/src/main/java/org/springframework/integration/transformer/CycleDetector.java deleted file mode 100644 index 346bced406..0000000000 --- a/spring-integration-core/src/main/java/org/springframework/integration/transformer/CycleDetector.java +++ /dev/null @@ -1,160 +0,0 @@ -/* - * Copyright 2002-2010 the original author or authors. - * - * Licensed 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.springframework.integration.transformer; - -import java.beans.PropertyDescriptor; -import java.util.Collection; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; - -import org.springframework.beans.BeanWrapperImpl; -import org.springframework.expression.EvaluationContext; -import org.springframework.expression.Expression; -import org.springframework.expression.ExpressionParser; -import org.springframework.expression.spel.standard.SpelExpressionParser; -import org.springframework.expression.spel.support.StandardEvaluationContext; -import org.springframework.util.CollectionUtils; - -/** - * @author Oleg Zhurakousky - * @since 2.0 - */ -class CycleDetector { - private final ExpressionParser parser = new SpelExpressionParser(); - private final String[] defaultIgnorePakages = new String[]{ - "com.apple", - "com.sun", - "java.awt", - "java", - "javax", - "org.jcp", - "org.omg", - "sun" - }; - private boolean ignoreClassAttribute = true; - private boolean ignoreDefaultPackages = true; - /** - * - * @param target - * @param ignorePakages - */ - public void detectCycle(Object target, String... ignorePakages){ - Map> objectReferenceMap = new HashMap>(); - this.doDetect(target, objectReferenceMap, ignorePakages); - } - /* - * - */ - private void doDetect(Object target, Map> objectReferenceMap, String[] ignorePakages){ - if (propertyInIgnoredPackage(target, ignorePakages)){ - return; - } - if (collectionMapOrArray(target)){ - Iterable iterable = null; - if (target instanceof Collection) { - iterable = (Iterable) target; - } else if (target instanceof Map){ - iterable = ((Map) target).values(); - } else if (target.getClass().isArray()){ - iterable = CollectionUtils.arrayToList(target); - } - for (Object value : iterable) { - this.doDetect(value, objectReferenceMap, ignorePakages); - } - } else { - if (!objectReferenceMap.containsKey(target)){ - objectReferenceMap.put(target, new HashSet()); - } - EvaluationContext context = new StandardEvaluationContext(target); - BeanWrapperImpl bw = new BeanWrapperImpl(target); - PropertyDescriptor[] descriptors = bw.getPropertyDescriptors(); - for (PropertyDescriptor propertyDescriptor : descriptors) { - String propertyName = propertyDescriptor.getName(); - if (propertyName.equals("class") && ignoreClassAttribute){ - continue; // no need to process - } - Expression expression = parser.parseExpression(propertyName); - Object propertyValue = null; - try { - propertyValue = expression.getValue(context); - } catch (Exception e) {/*nothing to do, might only happen when 'ignoreClassAttribute' is set to false ('true' by default)*/} - - if (propertyValue != null){ - if (!collectionMapOrArray(propertyValue)){ - Set references = objectReferenceMap.get(target); - if (!references.contains(propertyValue)){ - references.add(propertyValue); - } - if (objectReferenceMap.containsKey(propertyValue)){ - references = objectReferenceMap.get(propertyValue); - if (references.contains(target)){ - throw new MessageTransformationException("Cyclic reference detected between: " + - propertyValue.getClass().getSimpleName() + " - " + target.getClass().getSimpleName()); - } - } - } - this.doDetect(propertyValue, objectReferenceMap, ignorePakages); - } - } - } - } - /* - * - */ - private boolean collectionMapOrArray(Object elementValue){ - return (elementValue instanceof Map || - elementValue instanceof Collection || - elementValue.getClass().isArray()); - } - /* - * - */ - private boolean propertyInIgnoredPackage(Object elementValue, String[] ignorePakagess){ - if (this.collectionMapOrArray(elementValue)){ - return false; - } - for (String packagePattern : ignorePakagess) { - if (elementValue.getClass().getPackage().getName().startsWith(packagePattern)){ - return true; - } - } - if (ignoreDefaultPackages){ - for (String packagePattern : defaultIgnorePakages) { - if (elementValue.getClass().getPackage().getName().startsWith(packagePattern)){ - return true; - } - } - } - return false; - } - public boolean isIgnoreClassAttribute() { - return ignoreClassAttribute; - } - - public void setIgnoreClassAttribute(boolean ignoreClassAttribute) { - this.ignoreClassAttribute = ignoreClassAttribute; - } - - public boolean isIgnoreDefaultPackages() { - return ignoreDefaultPackages; - } - - public void setIgnoreDefaultPackages(boolean ignoreDefaultPackages) { - this.ignoreDefaultPackages = ignoreDefaultPackages; - } -} diff --git a/spring-integration-core/src/main/java/org/springframework/integration/transformer/ObjectToMapTransformer.java b/spring-integration-core/src/main/java/org/springframework/integration/transformer/ObjectToMapTransformer.java index f7d640c37c..e35a8659f3 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/transformer/ObjectToMapTransformer.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/transformer/ObjectToMapTransformer.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2010 the original author or authors. + * Copyright 2002-2011 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,20 +13,25 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.integration.transformer; +import java.util.Collection; +import java.util.HashMap; import java.util.Map; +import org.codehaus.jackson.map.ObjectMapper; +import org.springframework.util.CollectionUtils; +import org.springframework.util.StringUtils; + /** - * Will transform an object graph into a flat Map where keys are valid SpEL expressions - * and values are of java.lang.* type. This means that this transformer will recursively navigate - * through the Object graph until the value could be java.lang.* - * It supports Collections, Maps and Arrays which means it will flatten Object's attributes that are defined as such:
+ * Will transform an object graph into a Map. It supports a conventional Map (map of maps) where complex attributes are + * represented as Map values as well as a flat Map where keys document the path to the value. It supports Collections, + * Maps and Arrays which means that for flat maps it will flatten an Object's properties as such:
* * private Map> testMapInMapData;
* private List departments;
* private String[] akaNames;
- * private Map> mapWithListData;
* * The resulting Map structure will look similar to this:
* @@ -34,20 +39,69 @@ import java.util.Map; * departments[0]=HR
* person.lname=Case * + * By default it will transform to a flat Map. If you need to transform to a Map of Maps set the 'shouldFlattenKeys' + * property to 'false' via the {@link ObjectToMapTransformer#setShouldFlattenKeys(boolean)} method. + * * @author Oleg Zhurakousky * @since 2.0 */ public class ObjectToMapTransformer extends AbstractPayloadTransformer> { - /* - * (non-Javadoc) - * @see org.springframework.integration.transformer.AbstractPayloadTransformer#transformPayload(java.lang.Object) - */ + private volatile boolean shouldFlattenKeys = true; + + public void setShouldFlattenKeys(boolean shouldFlattenKeys) { + this.shouldFlattenKeys = shouldFlattenKeys; + } + + @SuppressWarnings("unchecked") protected Map transformPayload(Object payload) throws Exception { - CycleDetector cycleDetector = new CycleDetector(); - cycleDetector.detectCycle(payload); - ObjectToSpelMapBuilder builder = new ObjectToSpelMapBuilder(); - return builder.buildSpelMap(payload); + ObjectMapper mapper = new ObjectMapper(); + Map result = new ObjectMapper().readValue(mapper.writeValueAsString(payload), Map.class); + if (this.shouldFlattenKeys) { + result = this.flattenMap(result); + } + return result; + } + + private Map flattenMap(Map result){ + Map resultMap = new HashMap(); + this.doFlatten("", result, resultMap); + return resultMap; + } + + private void doFlatten(String propertyPrefix, Map inputMap, Map resultMap){ + if (StringUtils.hasText(propertyPrefix)) { + propertyPrefix = propertyPrefix + "."; + } + for (String key : inputMap.keySet()) { + Object value = inputMap.get(key); + this.doProcessElement(propertyPrefix + key, value, resultMap); + } + } + + private void doProcessCollection(String propertyPrefix, Collection list, Map resultMap) { + int counter = 0; + for (Object element : list) { + this.doProcessElement(propertyPrefix + "[" + counter + "]", element, resultMap); + counter ++; + } + } + + @SuppressWarnings("unchecked") + private void doProcessElement(String propertyPrefix, Object element, Map resultMap) { + if (element instanceof Map) { + this.doFlatten(propertyPrefix, (Map) element, resultMap); + } + else if (element instanceof Collection) { + this.doProcessCollection(propertyPrefix, (Collection) element, resultMap); + } + else if (element != null && element.getClass().isArray()) { + Collection collection = CollectionUtils.arrayToList(element); + this.doProcessCollection(propertyPrefix, collection, resultMap); + } + else { + resultMap.put(propertyPrefix, element); + } } } diff --git a/spring-integration-core/src/main/java/org/springframework/integration/transformer/ObjectToSpelMapBuilder.java b/spring-integration-core/src/main/java/org/springframework/integration/transformer/ObjectToSpelMapBuilder.java deleted file mode 100644 index 3800959ee6..0000000000 --- a/spring-integration-core/src/main/java/org/springframework/integration/transformer/ObjectToSpelMapBuilder.java +++ /dev/null @@ -1,142 +0,0 @@ -/* - * Copyright 2002-2010 the original author or authors. - * - * Licensed 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.springframework.integration.transformer; - -import java.beans.PropertyDescriptor; -import java.util.Collection; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Map; - -import org.springframework.beans.BeanWrapperImpl; -import org.springframework.expression.EvaluationContext; -import org.springframework.expression.Expression; -import org.springframework.expression.ExpressionParser; -import org.springframework.expression.spel.standard.SpelExpressionParser; -import org.springframework.expression.spel.support.StandardEvaluationContext; -import org.springframework.util.CollectionUtils; - -/** - * @author Oleg Zhurakousky - * @since 2.0 - */ -class ObjectToSpelMapBuilder { - - private final ExpressionParser parser = new SpelExpressionParser(); - - private boolean serializeTypeName; - - - public Map buildSpelMap(Object rootObject) { - Map propertiesMap = new HashMap(); - EvaluationContext context = new StandardEvaluationContext(rootObject); - this.buildProperties(context, "", propertiesMap, rootObject); - return propertiesMap; - } - - private void buildProperties(EvaluationContext context, String parentPropertyPath, Map propertiesMap, Object object) { - BeanWrapperImpl bw = new BeanWrapperImpl(object); - PropertyDescriptor[] descriptors = bw.getPropertyDescriptors(); - for (PropertyDescriptor propertyDescriptor : descriptors) { - Class propertyType = propertyDescriptor.getPropertyType(); - String propertyName = propertyDescriptor.getName(); - String propertyPath = parentPropertyPath + propertyName; - if (propertyType.isArray() || Collection.class.isAssignableFrom(propertyType)) { - this.processArray(context, propertyPath, propertyName, propertyType, propertiesMap); - } - else if (propertyType.isAnonymousClass()) { - throw new IllegalArgumentException("anonymous class property transformation is not supported"); - } - else if (propertyType.isAssignableFrom(Map.class)) { - Expression expression = parser.parseExpression(propertyPath); - Map map = (Map) expression.getValue(context); - if (map != null) { - this.processMap(context, propertiesMap, map, parentPropertyPath, propertyName); - } - } - else { - Expression expression = parser.parseExpression(propertyPath); - Object propertyValue = expression.getValue(context); - if (propertyValue != null) { - this.processElementValue(context, propertyValue, propertyName, propertyPath, propertiesMap); - } - } - } - } - - private void processMap(EvaluationContext context, Map mappedProperties, Map mapToTransform, String propertyPath, String propertyName) { - Iterator mapIter = mapToTransform.keySet().iterator(); - while (mapIter.hasNext()) { - Object keyElement = mapIter.next(); - Object elementValue = mapToTransform.get(keyElement); - String mapPropertyPath = propertyPath + propertyName + "['" + keyElement.toString() + "']"; - if (elementValue.getClass().isArray() || elementValue instanceof Collection) { - this.processArray(context, mapPropertyPath, "", elementValue.getClass(), mappedProperties); - } - else if (elementValue instanceof Map) { - this.processMap(context, mappedProperties, (Map) elementValue, mapPropertyPath, ""); - } - else { - this.processElementValue(context, elementValue, propertyName, mapPropertyPath, mappedProperties); - } - } - } - - private void processArray(EvaluationContext context, String propertyPath, String propertyName, Class elementType, Map propertiesMap) { - Expression arrayExp = parser.parseExpression(propertyPath); - Object array = arrayExp.getValue(context); - List arrayElements = null; - if (elementType.isArray()) { - arrayElements = CollectionUtils.arrayToList(array); - } - else { - arrayElements = (List) array; - } - - int i = 0; - for (Object arrayElement : arrayElements) { - String arrayPropertyPath = propertyPath + "[" + i++ + "]"; - if (arrayElement instanceof Map) { - // last argument is empty because it is not a named property, but an array element - this.processMap(context, propertiesMap, (Map) arrayElement, arrayPropertyPath, ""); - } - else if (arrayElement.getClass().isArray()){ - this.processArray(context, arrayPropertyPath, propertyName, elementType, propertiesMap); - } - else { - this.processElementValue(context, arrayElement, propertyName, arrayPropertyPath, propertiesMap); - } - } - } - - private void processElementValue(EvaluationContext context, Object elementValue, String propertyName, String propertyPath, Map propertiesMap) { - // JDK packages considered shared, thus serializable - if (elementValue.getClass().getPackage().getName().startsWith("java.lang")) { - if (propertyName.equals("class") && !this.serializeTypeName) { - return; - } - else { - propertiesMap.put(propertyPath, elementValue); - } - } - else { - this.buildProperties(context, propertyPath + ".", propertiesMap, elementValue); - } - } - -} diff --git a/spring-integration-core/src/test/java/org/springframework/integration/transformer/CycleDetectorTests.java b/spring-integration-core/src/test/java/org/springframework/integration/transformer/CycleDetectorTests.java deleted file mode 100644 index f36be852ca..0000000000 --- a/spring-integration-core/src/test/java/org/springframework/integration/transformer/CycleDetectorTests.java +++ /dev/null @@ -1,266 +0,0 @@ -/* - * Copyright 2002-2010 the original author or authors. - * - * Licensed 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.springframework.integration.transformer; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import org.junit.Test; - -/** - * @author Oleg Zhurakousky - * - */ -public class CycleDetectorTests { - @Test - public void testWithNoIgnoredPackages(){ - Parent parent = this.prepare(false); - CycleDetector builder = new CycleDetector(); - builder.setIgnoreDefaultPackages(false); - builder.detectCycle(parent); - // should not throw an exception - } - @Test - public void testWithNoIgnoredPackagesAndClassProperty(){ - Parent parent = this.prepare(false); - CycleDetector builder = new CycleDetector(); - builder.setIgnoreDefaultPackages(false); - builder.setIgnoreClassAttribute(false); - builder.detectCycle(parent); - // should not throw an exception - } - @Test - public void testWithAditionalIgnoredPackages(){ - Parent parent = this.prepare(true); - CycleDetector builder = new CycleDetector(); - builder.detectCycle(parent, "org.springframework.integration.transformer"); - // should not throw an exception, however if you remove additional package from - // the above method there is a cycle in the domain core - } - - @Test - public void testObjectReferenceMapWithoutCycle(){ - Parent parent = this.prepare(false); - CycleDetector builder = new CycleDetector(); - builder.detectCycle(parent); - // should not throw an exception - } - - @Test(expected=MessageTransformationException.class) - public void testObjectReferenceMapWithCycle(){ - Parent parent = this.prepare(true); - CycleDetector builder = new CycleDetector(); - builder.detectCycle(parent); - } - @Test - public void testWithNoCyclesInArray(){ - Parent parent = this.prepare(false); - Foo[] foos = new Foo[]{new Foo(), new Foo()}; - parent.getAddress().setFoos(foos); - CycleDetector builder = new CycleDetector(); - builder.detectCycle(parent); - } - @Test(expected=MessageTransformationException.class) - public void testWithCyclesInArray(){ - Parent parent = this.prepare(false); - Foo foo = new Foo(); - Bar bar = new Bar(); - foo.setBar(bar); - bar.setFoo(foo); - Foo[] foos = new Foo[]{foo, new Foo()}; - parent.getAddress().setFoos(foos); - CycleDetector builder = new CycleDetector(); - builder.detectCycle(parent); - } - @Test - public void testWithNoCyclesInMapWithList(){ - Parent parent = this.prepare(false); - Foo fooA = new Foo(); - Foo fooB = new Foo(); - Foo[] foos = new Foo[]{fooA, fooB}; - parent.getAddress().setFoos(foos); - List fooList = new ArrayList(); - fooList.add(fooA); - fooList.add(fooB); - Map> mapOfFoos = new HashMap>(); - mapOfFoos.put("listOfFoos", fooList); - parent.getChild().setMapOfFoos(mapOfFoos); - CycleDetector builder = new CycleDetector(); - builder.detectCycle(parent); - } - @Test(expected=MessageTransformationException.class) - public void testWithCyclesInMapWithList(){ - Parent parent = this.prepare(false); - Foo fooA = new Foo(); - Bar bar = new Bar(); - bar.setFoo(fooA); - fooA.setBar(bar); - Foo fooB = new Foo(); - Foo[] foos = new Foo[]{new Foo(), new Foo()}; - parent.getAddress().setFoos(foos); - List fooList = new ArrayList(); - fooList.add(fooA); - fooList.add(fooB); - Map> mapOfFoos = new HashMap>(); - mapOfFoos.put("listOfFoos", fooList); - parent.getChild().setMapOfFoos(mapOfFoos); - CycleDetector builder = new CycleDetector(); - builder.detectCycle(parent); - } - - //################# Test Classes ################### - public Parent prepare(boolean cycle){ - Parent parent = new Parent(); - Child child = new Child(); - Address address = new Address(); - address.setStreet("123 Main st"); - child.setAddress(address); - List nickNames = new ArrayList(); - nickNames.add("spanky"); - nickNames.add("goofy"); - child.setNickNames(nickNames); - child.setName("Seva"); - if (cycle){ - child.setParent(parent); - } - - parent.setAddress(address); - parent.setChild(child); - parent.setName("Oleg"); - return parent; - } - /* - * - */ - public static class Parent{ - private Address address; - private Child child; - private String name; - public String getName() { - return name; - } - public void setName(String name) { - this.name = name; - } - public Address getAddress() { - return address; - } - public void setAddress(Address address) { - this.address = address; - } - public Child getChild() { - return child; - } - public void setChild(Child child) { - this.child = child; - } - } - /* - * - */ - public static class Child{ - private String name; - private List nickNames; - private Map> mapOfFoos; - private Address address; - private Parent parent; - public Parent getParent() { - return parent; - } - - public void setParent(Parent parent) { - this.parent = parent; - } - - public List getNickNames() { - return nickNames; - } - - public void setNickNames(List nickNames) { - this.nickNames = nickNames; - } - - public Address getAddress() { - return address; - } - - public void setAddress(Address address) { - this.address = address; - } - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - public Map> getMapOfFoos() { - return mapOfFoos; - } - - public void setMapOfFoos(Map> mapOfFoos) { - this.mapOfFoos = mapOfFoos; - } - } - /* - * - */ - public static class Address{ - private String street; - private Foo[] foos; - - public Foo[] getFoos() { - return foos; - } - - public void setFoos(Foo[] foos) { - this.foos = foos; - } - - public String getStreet() { - return street; - } - - public void setStreet(String street) { - this.street = street; - } - } - - public static class Foo{ - private Bar bar; - - public Bar getBar() { - return bar; - } - - public void setBar(Bar bar) { - this.bar = bar; - } - } - public static class Bar{ - private Foo foo; - - public Foo getFoo() { - return foo; - } - - public void setFoo(Foo foo) { - this.foo = foo; - } - } -} diff --git a/spring-integration-core/src/test/java/org/springframework/integration/transformer/ObjectToMapTransformerTests.java b/spring-integration-core/src/test/java/org/springframework/integration/transformer/ObjectToMapTransformerTests.java index 725cf74520..21013a808d 100644 --- a/spring-integration-core/src/test/java/org/springframework/integration/transformer/ObjectToMapTransformerTests.java +++ b/spring-integration-core/src/test/java/org/springframework/integration/transformer/ObjectToMapTransformerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2010 the original author or authors. + * Copyright 2002-2011 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,17 +15,19 @@ */ package org.springframework.integration.transformer; -import static junit.framework.Assert.assertEquals; -import static junit.framework.Assert.assertNotNull; -import static junit.framework.Assert.assertTrue; - +import java.io.IOException; +import java.math.BigDecimal; import java.util.ArrayList; +import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; +import org.codehaus.jackson.JsonGenerationException; +import org.codehaus.jackson.JsonParseException; +import org.codehaus.jackson.map.JsonMappingException; import org.junit.Test; -import org.springframework.expression.EvaluationContext; +import org.springframework.context.expression.MapAccessor; import org.springframework.expression.Expression; import org.springframework.expression.ExpressionParser; import org.springframework.expression.spel.standard.SpelExpressionParser; @@ -33,6 +35,11 @@ import org.springframework.expression.spel.support.StandardEvaluationContext; import org.springframework.integration.Message; import org.springframework.integration.support.MessageBuilder; +import static junit.framework.Assert.assertEquals; +import static junit.framework.Assert.assertNotNull; + +import static org.junit.Assert.assertNull; + /** * * @author Oleg Zhurakousky @@ -41,27 +48,101 @@ import org.springframework.integration.support.MessageBuilder; public class ObjectToMapTransformerTests { @SuppressWarnings("unchecked") @Test - public void testObjectToSpelMapTransformer(){ + public void testObjectToSpelMapTransformer() throws JsonParseException, JsonMappingException, JsonGenerationException, IOException{ Employee employee = this.buildEmployee(); - EvaluationContext context = new StandardEvaluationContext(employee); + StandardEvaluationContext context = new StandardEvaluationContext(); + context.addPropertyAccessor(new MapAccessor()); ExpressionParser parser = new SpelExpressionParser(); ObjectToMapTransformer transformer = new ObjectToMapTransformer(); Message message = MessageBuilder.withPayload(employee).build(); + Message transformedMessage = transformer.transform(message); Map transformedMap = (Map) transformedMessage.getPayload(); - + System.out.println(transformedMap); assertNotNull(transformedMap); - for (String key : transformedMap.keySet()) { - Expression expression = parser.parseExpression(key); - //System.out.println("Testing: " + key); - Object valueFromTheMap = transformedMap.get(key); - Object valueFromExpression = expression.getValue(context); - String packageNameOfValueType = valueFromTheMap.getClass().getPackage().getName(); - assertTrue(packageNameOfValueType.startsWith("java.lang")); - assertEquals(valueFromTheMap, valueFromExpression); - } + + Object valueFromTheMap = null; + Object valueFromExpression = null; + Expression expression = null; + + expression = parser.parseExpression("departments[0]"); + valueFromTheMap = transformedMap.get("departments[0]"); + valueFromExpression = expression.getValue(context, employee, String.class); + assertEquals(valueFromTheMap, valueFromExpression); + + expression = parser.parseExpression("person.address.coordinates"); + valueFromTheMap = transformedMap.get("person.address.coordinates"); + valueFromExpression = expression.getValue(context, employee, Map.class); + assertEquals(valueFromTheMap, valueFromExpression); + + expression = parser.parseExpression("person.akaNames[0]"); + valueFromTheMap = transformedMap.get("person.akaNames[0]"); + valueFromExpression = expression.getValue(context, employee, String.class); + assertEquals(valueFromTheMap, valueFromExpression); + + expression = parser.parseExpression("testMapInMapData.internalMapA.bar"); + valueFromTheMap = transformedMap.get("testMapInMapData.internalMapA.bar"); + valueFromExpression = expression.getValue(context, employee, String.class); + assertEquals(valueFromTheMap, valueFromExpression); + + expression = parser.parseExpression("companyAddress.street"); + valueFromTheMap = transformedMap.get("companyAddress.street"); + valueFromExpression = expression.getValue(context, employee, String.class); + assertEquals(valueFromTheMap, valueFromExpression); + + expression = parser.parseExpression("person.lname"); + valueFromTheMap = transformedMap.get("person.lname"); + valueFromExpression = expression.getValue(context, employee, String.class); + assertEquals(valueFromTheMap, valueFromExpression); + + expression = parser.parseExpression("person.address.mapWithListData.mapWithListTestData[1]"); + valueFromTheMap = transformedMap.get("person.address.mapWithListData.mapWithListTestData[1]"); + valueFromExpression = expression.getValue(context, employee, String.class); + assertEquals(valueFromTheMap, valueFromExpression); + + expression = parser.parseExpression("companyAddress.city"); + valueFromTheMap = transformedMap.get("companyAddress.city"); + valueFromExpression = expression.getValue(context, employee, String.class); + assertEquals(valueFromTheMap, valueFromExpression); + + expression = parser.parseExpression("person.akaNames[2]"); + valueFromTheMap = transformedMap.get("person.akaNames[2]"); + valueFromExpression = expression.getValue(context, employee, String.class); + assertEquals(valueFromTheMap, valueFromExpression); + + expression = parser.parseExpression("person.child"); + valueFromTheMap = transformedMap.get("person.child"); + valueFromExpression = expression.getValue(context, employee, String.class); + assertNull(valueFromTheMap); + assertNull(valueFromExpression); + + expression = parser.parseExpression("testMapInMapData.internalMapA.foo"); + valueFromTheMap = transformedMap.get("testMapInMapData.internalMapA.foo"); + valueFromExpression = expression.getValue(context, employee, String.class); + assertEquals(valueFromTheMap, valueFromExpression); + + expression = parser.parseExpression("person.address.city"); + valueFromTheMap = transformedMap.get("person.address.city"); + valueFromExpression = expression.getValue(context, employee, String.class); + assertEquals(valueFromTheMap, valueFromExpression); + + expression = parser.parseExpression("companyAddress.coordinates.latitude[0]"); + valueFromTheMap = transformedMap.get("companyAddress.coordinates.latitude[0]"); + valueFromExpression = expression.getValue(context, employee, Integer.class); + assertEquals(valueFromTheMap, valueFromExpression); + + expression = parser.parseExpression("person.remarks[1].baz"); + valueFromTheMap = transformedMap.get("person.remarks[1].baz"); + valueFromExpression = expression.getValue(context, employee, String.class); + assertEquals(valueFromTheMap, valueFromExpression); + + expression = parser.parseExpression("listOfDates[0][1]"); + valueFromTheMap = new Date((Long) transformedMap.get("listOfDates[0][1]")); + valueFromExpression = expression.getValue(context, employee, Date.class); + assertEquals(valueFromTheMap, valueFromExpression); } + @Test(expected=MessageTransformationException.class) public void testObjectToSpelMapTransformerWithCycle(){ Employee employee = this.buildEmployee(); @@ -86,9 +167,22 @@ public class ObjectToMapTransformerTests { coordinates.put("longitude", new Long[]{(long)156}); companyAddress.setCoordinates(coordinates); + List datesA = new ArrayList(); + datesA.add(new Date(System.currentTimeMillis() + 10000)); + datesA.add(new Date(System.currentTimeMillis() + 20000)); + + List datesB = new ArrayList(); + datesB.add(new Date(System.currentTimeMillis() + 30000)); + datesB.add(new Date(System.currentTimeMillis() + 40000)); + + List> listOfDates = new ArrayList>(); + listOfDates.add(datesA); + listOfDates.add(datesB); + Employee employee = new Employee(); employee.setCompanyName("ABC Inc."); employee.setCompanyAddress(companyAddress); + employee.setListOfDates(listOfDates); ArrayList departments = new ArrayList(); departments.add("HR"); departments.add("IT"); @@ -98,6 +192,8 @@ public class ObjectToMapTransformerTests { person.setFname("Justin"); person.setLname("Case"); person.setAkaNames("Hard", "Use", "Beer"); + person.setBirthDate(new Date()); + person.setAge(new BigDecimal(10)); Address personAddress = new Address(); personAddress.setCity("Philly"); personAddress.setStreet("123 Main"); @@ -137,10 +233,17 @@ public class ObjectToMapTransformerTests { public static class Employee{ private List departments; + private List> listOfDates; private String companyName; private Person person; private Address companyAddress; private Map> testMapInMapData; + public List> getListOfDates() { + return listOfDates; + } + public void setListOfDates(List> listOfDates) { + this.listOfDates = listOfDates; + } public Map> getTestMapInMapData() { return testMapInMapData; } @@ -180,6 +283,24 @@ public class ObjectToMapTransformerTests { private String[] akaNames; private List> remarks; private Child child; + private BigDecimal age; + public BigDecimal getAge() { + return age; + } + + public void setAge(BigDecimal age) { + this.age = age; + } + + public Date getBirthDate() { + return birthDate; + } + + public void setBirthDate(Date birthDate) { + this.birthDate = birthDate; + } + + private Date birthDate; public Child getChild() { return child; }