fixed bug of deep-equality; added custom ser/de for json and json ser/de is working now
Project: http://git-wip-us.apache.org/repos/asf/ode/repo Commit: http://git-wip-us.apache.org/repos/asf/ode/commit/c6c985e1 Tree: http://git-wip-us.apache.org/repos/asf/ode/tree/c6c985e1 Diff: http://git-wip-us.apache.org/repos/asf/ode/diff/c6c985e1 Branch: refs/heads/ODE-912 Commit: c6c985e19502915ddb0b5d958d30de6d69431f3b Parents: 016c344 Author: fangzhen <[email protected]> Authored: Sat Aug 2 19:15:16 2014 +0800 Committer: fangzhen <[email protected]> Committed: Sat Aug 2 19:16:16 2014 +0800 ---------------------------------------------------------------------- .../ode/bpel/compiler_2_0/GoodCompileTest.java | 15 +- .../ode/bpel/compiler_2_0/MigrationTest.java | 12 +- .../bpel/compiler_2_0/SerializationTest.java | 14 +- .../bpel/obj/migrate/DeepEqualityHelper.java | 395 +++++++++++++++++++ .../bpel/obj/migrate/DomElementComparator.java | 88 +++++ .../bpel/obj/migrate/EqualityComparator.java | 1 + .../ode/bpel/obj/migrate/EqualityVisitor.java | 357 ----------------- .../obj/migrate/ExtensibeImplEqualityComp.java | 26 +- .../ode/bpel/obj/serde/JsonOmDeserializer.java | 48 ++- .../ode/bpel/obj/serde/JsonOmSerializer.java | 40 +- .../ode/bpel/obj/serde/OmSerdeFactory.java | 1 + .../ode/bpel/obj/migrate/DeepEqualityTest.java | 35 +- 12 files changed, 614 insertions(+), 418 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/ode/blob/c6c985e1/bpel-compiler/src/test/java/org/apache/ode/bpel/compiler_2_0/GoodCompileTest.java ---------------------------------------------------------------------- diff --git a/bpel-compiler/src/test/java/org/apache/ode/bpel/compiler_2_0/GoodCompileTest.java b/bpel-compiler/src/test/java/org/apache/ode/bpel/compiler_2_0/GoodCompileTest.java index 98a1088..2602642 100644 --- a/bpel-compiler/src/test/java/org/apache/ode/bpel/compiler_2_0/GoodCompileTest.java +++ b/bpel-compiler/src/test/java/org/apache/ode/bpel/compiler_2_0/GoodCompileTest.java @@ -28,12 +28,10 @@ import java.net.URL; import org.apache.ode.bpel.compiler.api.CompileListener; import org.apache.ode.bpel.obj.OProcess; -import org.apache.ode.bpel.obj.migrate.EqualityVisitor; +import org.apache.ode.bpel.obj.migrate.DeepEqualityHelper; +import org.apache.ode.bpel.obj.migrate.DomElementComparator; import org.apache.ode.bpel.obj.migrate.ExtensibeImplEqualityComp; -import org.apache.ode.bpel.obj.migrate.ObjectTraverser; import org.apache.ode.bpel.obj.serde.DeSerializer; -import org.apache.ode.bpel.obj.serde.OmDeserializer; -import org.apache.ode.bpel.obj.serde.OmSerdeFactory; import org.junit.Assert; import org.junit.Test; @@ -56,11 +54,10 @@ public class GoodCompileTest extends AbstractCompileTestCase implements DeSerializer deserializer = new DeSerializer(new FileInputStream(cbpPath)); OProcess desered = deserializer.deserialize(); - ObjectTraverser traverse = new ObjectTraverser(); - EqualityVisitor visitor = new EqualityVisitor(desered); - visitor.addCustomComparator(new ExtensibeImplEqualityComp(visitor)); - traverse.accept(visitor); - boolean res = (Boolean) traverse.traverseObject(origi); + DeepEqualityHelper de = new DeepEqualityHelper(); + de.addCustomComparator(new ExtensibeImplEqualityComp()); + de.addCustomComparator(new DomElementComparator()); + boolean res = de.deepEquals(origi, desered); assertEquals(Boolean.TRUE, res); } catch (Exception ex) { ex.printStackTrace(); http://git-wip-us.apache.org/repos/asf/ode/blob/c6c985e1/bpel-compiler/src/test/java/org/apache/ode/bpel/compiler_2_0/MigrationTest.java ---------------------------------------------------------------------- diff --git a/bpel-compiler/src/test/java/org/apache/ode/bpel/compiler_2_0/MigrationTest.java b/bpel-compiler/src/test/java/org/apache/ode/bpel/compiler_2_0/MigrationTest.java index 7fc9cf1..ee27926 100644 --- a/bpel-compiler/src/test/java/org/apache/ode/bpel/compiler_2_0/MigrationTest.java +++ b/bpel-compiler/src/test/java/org/apache/ode/bpel/compiler_2_0/MigrationTest.java @@ -11,7 +11,8 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.ode.bpel.o.Serializer; import org.apache.ode.bpel.obj.OProcess; -import org.apache.ode.bpel.obj.migrate.EqualityVisitor; +import org.apache.ode.bpel.obj.migrate.DeepEqualityHelper; +import org.apache.ode.bpel.obj.migrate.DomElementComparator; import org.apache.ode.bpel.obj.migrate.ExtensibeImplEqualityComp; import org.apache.ode.bpel.obj.migrate.ObjectTraverser; import org.apache.ode.bpel.obj.migrate.OmOld2new; @@ -44,11 +45,10 @@ public class MigrationTest extends GoodCompileTest{ OProcess migrated = (OProcess) mtraverse.traverseObject(old); __log.debug("migrated new OProcess " + migrated.getFieldContainer()); - ObjectTraverser traverse = new ObjectTraverser(); - EqualityVisitor visitor = new EqualityVisitor(nu); - visitor.addCustomComparator(new ExtensibeImplEqualityComp(visitor)); - traverse.accept(visitor); - boolean res = (Boolean)traverse.traverseObject(migrated); + DeepEqualityHelper de = new DeepEqualityHelper(); + de.addCustomComparator(new ExtensibeImplEqualityComp()); + de.addCustomComparator(new DomElementComparator()); + boolean res = de.deepEquals(nu, migrated); assertEquals(Boolean.TRUE, res); } catch (Exception ex) { ex.printStackTrace(); http://git-wip-us.apache.org/repos/asf/ode/blob/c6c985e1/bpel-compiler/src/test/java/org/apache/ode/bpel/compiler_2_0/SerializationTest.java ---------------------------------------------------------------------- diff --git a/bpel-compiler/src/test/java/org/apache/ode/bpel/compiler_2_0/SerializationTest.java b/bpel-compiler/src/test/java/org/apache/ode/bpel/compiler_2_0/SerializationTest.java index eb8b082..22df441 100644 --- a/bpel-compiler/src/test/java/org/apache/ode/bpel/compiler_2_0/SerializationTest.java +++ b/bpel-compiler/src/test/java/org/apache/ode/bpel/compiler_2_0/SerializationTest.java @@ -9,10 +9,9 @@ import java.net.URI; import java.net.URL; import org.apache.ode.bpel.obj.OProcess; -import org.apache.ode.bpel.obj.OProcessWrapper; -import org.apache.ode.bpel.obj.migrate.EqualityVisitor; +import org.apache.ode.bpel.obj.migrate.DeepEqualityHelper; +import org.apache.ode.bpel.obj.migrate.DomElementComparator; import org.apache.ode.bpel.obj.migrate.ExtensibeImplEqualityComp; -import org.apache.ode.bpel.obj.migrate.ObjectTraverser; import org.apache.ode.bpel.obj.serde.DeSerializer; import org.apache.ode.bpel.obj.serde.OmSerdeFactory; import org.junit.Assert; @@ -36,11 +35,10 @@ public class SerializationTest extends GoodCompileTest{ DeSerializer deserializer = new DeSerializer(new FileInputStream(cbpPath)); OProcess desered = deserializer.deserialize(); - ObjectTraverser traverse = new ObjectTraverser(); - EqualityVisitor visitor = new EqualityVisitor(desered); - visitor.addCustomComparator(new ExtensibeImplEqualityComp(visitor)); - traverse.accept(visitor); - boolean res = (Boolean) traverse.traverseObject(origi); + DeepEqualityHelper de = new DeepEqualityHelper(); + de.addCustomComparator(new ExtensibeImplEqualityComp()); + de.addCustomComparator(new DomElementComparator()); + boolean res = de.deepEquals(origi, desered); assertEquals(Boolean.TRUE, res); } catch (Exception ex) { ex.printStackTrace(); http://git-wip-us.apache.org/repos/asf/ode/blob/c6c985e1/bpel-nobj/src/main/java/org/apache/ode/bpel/obj/migrate/DeepEqualityHelper.java ---------------------------------------------------------------------- diff --git a/bpel-nobj/src/main/java/org/apache/ode/bpel/obj/migrate/DeepEqualityHelper.java b/bpel-nobj/src/main/java/org/apache/ode/bpel/obj/migrate/DeepEqualityHelper.java new file mode 100644 index 0000000..5982665 --- /dev/null +++ b/bpel-nobj/src/main/java/org/apache/ode/bpel/obj/migrate/DeepEqualityHelper.java @@ -0,0 +1,395 @@ +package org.apache.ode.bpel.obj.migrate; + +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.Stack; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * compare two object for equality. default strategy: + * for collections and maps, compare their contents + * for POJO, + * if any custom equality comparator can handle it, then use it; + * if it has an equals() method defined, use it. + * or compare their non-transient accessible fields by reflection. + * @author fangzhen + * + */ +public class DeepEqualityHelper{ + + private static final Log __log = LogFactory.getLog(ObjectTraverser.class); + public boolean logFalseThrough = false; + private Stack<String> st = new Stack<String>(); + + private List<EqualityComparator> comparators = new LinkedList<EqualityComparator>(); + private Stack<Long> ongoing = new Stack<Long>(); + + private Map<Long, Boolean> cache = new HashMap<Long, Boolean>(); + + public boolean deepEquals(Object obj1, Object obj2){ + // __log.debug("comparing Objects: " + obj1 + " and " + obj2); //will cause too much log + Boolean c = cachedRes(obj1, obj2); + if (c != null) { + return c; + } + Long h12 = hash(obj1, obj2); + if (ongoing.contains(h12)) { + return true; + } + ongoing.push(h12); + + boolean n; + if (isMap(obj1)){ + n = visitMap(obj1, obj2); + }else if (isSet(obj1)){ + n = visitSet(obj1, obj2); + }else if (isCollection(obj1)){ + n = visitCollection(obj1, obj2); + }else if (isArray(obj1)){ + n = visitArray(obj1, obj2); + }else{ + n = visitPojo(obj1, obj2); + } + cacheRes(obj1, obj2, n); + ongoing.pop(); + return n; + } + + private void cacheRes(Object obj1, Object obj2, Boolean n) { + cache.put(hash(obj1, obj2), n); + } + + private Boolean cachedRes(Object obj1, Object obj2) { + return cache.get(hash(obj1, obj2)); + } + + public Boolean visitMap(Object obj, Object other) { + if (obj == other) return true; + if (other == null) { + if (!logFalseThrough){ + __log.debug("Unequal in Map: Object2 is null. " + st); + } + return false; + } + Map m1 = (Map)obj; + Map m2 = null; + try{ + m2 = (Map)other; + }catch (ClassCastException e){ + if (!logFalseThrough){ + __log.debug("Unequal in Map: Object2 is not a map, it's a" + other.getClass() + "\n" + st); + } + return false; + } + if (m1.size() != m2.size()) { + if (!logFalseThrough){ + __log.debug("Unequal in Map: size mismatch. " + st + + "\n size: " + m1.size() + " and " + m2.size()); + } + return false; + } + Set ks1 = m1.keySet(); + Set ks2 = m2.keySet(); + for (Object k1 : ks1){ + st.push(k1.toString()); + Object k2 = contains(ks2, k1); + if (k2 == null){ + if (!logFalseThrough){ + __log.debug("Unequal in Map: cant find key. " + st + "\n missing key: " + k1); + } + st.pop(); + return false; + } + Object o1 = m1.get(k1); + Object o2 = m2.get(k2); + if (o1 == null){ + if (!(o2 == null)){ + if (!logFalseThrough){ + __log.debug("Unequal in Map: mismatch, one is null" + st + + "\n When dealing with " + o1 + " and " + o2); + } + st.pop(); + return false; + } + } + + st.pop(); + st.push(k1.toString() + ":" + o1.getClass().getSimpleName()); + + Boolean e = deepEquals(o1, o2); + if (!e) { + st.pop(); + return false; + } + st.pop(); + } + return true; + } + + @SuppressWarnings("rawtypes") + public Boolean visitSet(Object obj, Object other){ + if (obj == other) return true; + if (other == null) { + if (!logFalseThrough){ + __log.debug("Unequal in Set, Object2 is null. " + st); + } + return false; + } + + Collection c1 = (Collection)obj; + Collection c2 = null; + try { + c2 = (Collection)other; + }catch(ClassCastException e){ + if (!logFalseThrough){ + __log.debug("Unequal in Set: Object2 is not a Set, it's a" + other.getClass() + "\n" + st); + } + return false; + } + if (c1.size() != c2.size()) { + if (!logFalseThrough){ + __log.debug("Unequal in Set: size mismatch. " + st + + "\n. sizes are " + c1.size() + " and " + c2.size()); + } + return false; + } + Iterator i1 = c1.iterator(); + while (i1.hasNext()){ + Object o1 = i1.next(); + st.push(":" + o1.getClass().getSimpleName()); + if (contains(c2, o1) == null) { + if (!logFalseThrough){ + __log.debug("Unequal in Set: Object mismatch. " + st + + "\n" + "cann't find " + o1); + } + st.pop(); + return false; + } + st.pop(); + } + return true; + } + + private Object contains(Collection c, Object t1) { + Iterator itor = c.iterator(); + Object t2; + logFalseThrough = true; + while (itor.hasNext()){ + t2 = itor.next(); + if (deepEquals(t1, t2)) { + logFalseThrough = false; + return t2; + } + } + logFalseThrough = false; + return null; + } + + @SuppressWarnings("rawtypes") + public Boolean visitCollection(Object obj, Object other) { + if (obj == other) return true; + if (other == null) { + if (!logFalseThrough){ + __log.debug("Unequal in Collection, Object2 is null. " + st); + } + return false; + } + + Collection c = (Collection)obj; + Collection c2 = null; + try { + c2 = (Collection)other; + }catch(ClassCastException e){ + if (!logFalseThrough){ + __log.debug("Unequal in Collection: Object2 is not a Collection, it's a" + other.getClass() + "\n" + st); + } + return false; + } + if (c.size() != c2.size()) { + if (!logFalseThrough){ + __log.debug("Unequal in Collection: size mismatch. " + st + + "\n. sizes are " + c.size() + " and " + c2.size()); + } + return false; + } + + Iterator i1 = c.iterator(); + Iterator i2 = c2.iterator(); + while (i1.hasNext()){ + Object o1 = i1.next(); + Object o2 = i2.next(); + st.push(":" + o1.getClass().getSimpleName()); + Boolean e = deepEquals(o1, o2); + if (!e) { + st.pop(); + return false; + } + st.pop(); + } + return true; + } + + public Boolean visitArray(Object obj, Object other) { + throw new UnsupportedOperationException(); + } + + public Boolean visitPojo(Object obj, Object other) { + EqualityComparator customComp = getCustomComparator(obj); + if (customComp != null){ + return customComp.objectsEqual(obj, other); + } + if (obj == other) return true; + if (other == null) { + if (!logFalseThrough){ + __log.debug("Unequal in POJO: Object2 is null." + st); + } + return false; + } + if (obj.getClass() != other.getClass()) { + if(!logFalseThrough){ + __log.debug("Unequal in POJO: type mistach. " + st + + "\nmismatched types are: " + obj.getClass().getSimpleName() + + " and " + other.getClass().getSimpleName()); + } + return false; + } + try{ + obj.getClass().getDeclaredMethod("equals", Object.class); + boolean e = obj.equals(other); + if (!e){ + if (!logFalseThrough){ + __log.debug("Unequal in POJO: not equal by equals() method" + st); + } + } + return e; + }catch (NoSuchMethodException e){ + return equalityByReflection(obj, other); + } + } + + private EqualityComparator getCustomComparator(Object obj) { + for (EqualityComparator c : comparators){ + if (c.canHanle(obj)){ + return c; + } + } + return null; + } + public Boolean equalityByReflection(Object obj, Object other) { + List<Field> fields = getAllFields(obj.getClass()); + List<Field> fields2 = getAllFields(other.getClass()); + if (!fields.equals(fields2)){ + if (!logFalseThrough){ + __log.debug("Unequal: getFields() of two Object do not match " + st); + } + return false; + } + + for (Field f : fields){ + f.setAccessible(true); + if (((Modifier.TRANSIENT | Modifier.STATIC) & f.getModifiers()) != 0){ + continue; //skip transient fields + } + try { + Object v2 = f.get(other); + Object v1 = f.get(obj); + if (v1 == null && v2 == null){ + continue; + } + st.push(f.getName()+ ":" + f.getType().getSimpleName()); + if (v1 == null || v2 == null){ + if (!logFalseThrough){ + __log.debug("Unequal: one field is null" + st + ".\n When dealing with " + + v1 + " and " + v2); + } + st.pop(); + return false; + } + Boolean res = deepEquals(v1, v2); + if (!res){ + st.pop(); + return false; + } + st.pop(); + } catch (Exception e) { + //should not get here + e.printStackTrace(); + } + } + return true; + } + private List<Field> getAllFields(Class cls) { + List<Field> fields = getFieldsRec(cls.getSuperclass(), new ArrayList<Field>()); + fields.addAll(Arrays.asList(cls.getDeclaredFields())); + return fields; + } + /** + * get fields that are accessible to its sub-classes. + * @param cls + * @param fields + * @return + */ + private List<Field> getFieldsRec(Class cls, ArrayList<Field> fields) { + if (cls != null){ + Field[] fs = cls.getDeclaredFields(); + for (Field f : fs){ + if ((f.getModifiers() & Modifier.PRIVATE) == 0){ + fields.add(f); + } + } + getFieldsRec(cls.getSuperclass(), fields); + } + return fields; + } + + /** + * determine if obj is collections that order doesn't matter. + * @param obj + * @return + */ + protected boolean isSet(Object obj) { + return obj instanceof Set; + } + + protected boolean isArray(Object old) { + return old.getClass().isArray(); + } + + /** + * determine if obj is collections that order does matter. + * @param obj + * @return + */ + protected boolean isCollection(Object old) { + return (old instanceof Collection) && !isSet(old); + } + + protected boolean isMap(Object old) { + return old instanceof Map; + } + + private Long hash(Object obj1, Object obj2) { + int h1 = System.identityHashCode(obj1); + int h2 = System.identityHashCode(obj2); + return ((long)h1) << 32 | h2; + } + + public void addCustomComparator(EqualityComparator oe){ + comparators.add(0, oe); + oe.setDeepEquality(this); + } + public Stack<String> getSt() { + return st; + } +} http://git-wip-us.apache.org/repos/asf/ode/blob/c6c985e1/bpel-nobj/src/main/java/org/apache/ode/bpel/obj/migrate/DomElementComparator.java ---------------------------------------------------------------------- diff --git a/bpel-nobj/src/main/java/org/apache/ode/bpel/obj/migrate/DomElementComparator.java b/bpel-nobj/src/main/java/org/apache/ode/bpel/obj/migrate/DomElementComparator.java new file mode 100644 index 0000000..05ebb1d --- /dev/null +++ b/bpel-nobj/src/main/java/org/apache/ode/bpel/obj/migrate/DomElementComparator.java @@ -0,0 +1,88 @@ +package org.apache.ode.bpel.obj.migrate; + +import java.io.StringWriter; + +import javax.xml.transform.OutputKeys; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerException; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.dom.DOMSource; +import javax.xml.transform.stream.StreamResult; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.ls.DOMImplementationLS; +import org.w3c.dom.ls.LSSerializer; + +public class DomElementComparator implements EqualityComparator{ + private static final Log __log = LogFactory.getLog(DomElementComparator.class); + private DeepEqualityHelper deepEquality; + @Override + public Boolean objectsEqual(Object obj1, Object obj2) { + if (obj1 == obj2) return true; + if (!(obj2 instanceof Element)){ + if (!deepEquality.logFalseThrough){ + __log.debug("Unequal in Dom Element: Type mismatch. " + deepEquality.getSt() + + "Object2 has type " + obj2.getClass()); + } + return false; + } + try{ + String str1 = Element2String((Element)obj1); + String str2 = Element2String((Element)obj2); + boolean e = str1.equals(str2); + if (!e){ + if (!deepEquality.logFalseThrough){ + __log.debug("Unequal in Dom Element: " + deepEquality.getSt() + + "\n" + str1 + "\nand\n " + str2); + } + } + return e; + }catch(Exception e){ + if (!deepEquality.logFalseThrough){ + __log.debug("Unequal in Dom Element: Exception when comparing. " + + deepEquality.getSt() + e); + } + return false; + } + } + + public String Element2String(Element node){ + Document document = node.getOwnerDocument(); + DOMImplementationLS domImplLS = (DOMImplementationLS) document + .getImplementation(); + LSSerializer serializer = domImplLS.createLSSerializer(); + String str = serializer.writeToString(node); + serializer .getDomConfig().setParameter("xml-declaration", false); + return str; + } + /** + * Another option + */ + public String Element2String2(Element obj1) throws TransformerException{ + TransformerFactory transFactory = TransformerFactory.newInstance(); + Transformer transformer = transFactory.newTransformer(); + StringWriter buffer = new StringWriter(); + transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes"); + transformer.transform(new DOMSource(obj1), + new StreamResult(buffer)); + String str = buffer.toString(); + return str; + } + @Override + public Boolean canHanle(Object obj) { + return obj instanceof Element; + } + + public DeepEqualityHelper getDeepEquality() { + return deepEquality; + } + + @Override + public void setDeepEquality(DeepEqualityHelper deepEquality) { + this.deepEquality = deepEquality; + } + +} http://git-wip-us.apache.org/repos/asf/ode/blob/c6c985e1/bpel-nobj/src/main/java/org/apache/ode/bpel/obj/migrate/EqualityComparator.java ---------------------------------------------------------------------- diff --git a/bpel-nobj/src/main/java/org/apache/ode/bpel/obj/migrate/EqualityComparator.java b/bpel-nobj/src/main/java/org/apache/ode/bpel/obj/migrate/EqualityComparator.java index c9fb45d..6783969 100644 --- a/bpel-nobj/src/main/java/org/apache/ode/bpel/obj/migrate/EqualityComparator.java +++ b/bpel-nobj/src/main/java/org/apache/ode/bpel/obj/migrate/EqualityComparator.java @@ -4,4 +4,5 @@ public interface EqualityComparator { Boolean objectsEqual(Object obj1, Object obj2); Boolean canHanle(Object obj); + void setDeepEquality(DeepEqualityHelper deepEquality); } http://git-wip-us.apache.org/repos/asf/ode/blob/c6c985e1/bpel-nobj/src/main/java/org/apache/ode/bpel/obj/migrate/EqualityVisitor.java ---------------------------------------------------------------------- diff --git a/bpel-nobj/src/main/java/org/apache/ode/bpel/obj/migrate/EqualityVisitor.java b/bpel-nobj/src/main/java/org/apache/ode/bpel/obj/migrate/EqualityVisitor.java deleted file mode 100644 index b600940..0000000 --- a/bpel-nobj/src/main/java/org/apache/ode/bpel/obj/migrate/EqualityVisitor.java +++ /dev/null @@ -1,357 +0,0 @@ -package org.apache.ode.bpel.obj.migrate; - -import java.lang.reflect.Field; -import java.lang.reflect.Modifier; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Iterator; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.Stack; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -/** - * compare two object for equality. default strategy: - * for collections and maps, compare their contents - * for POJO, - * if any custom equality comparator can handle it, then use it; - * if it has an equals() method defined, use it. - * or compare their non-transient accessible fields by reflection. - * @author fangzhen - * - */ -public class EqualityVisitor extends AbstractObjectVisitor{ - private static final Log __log = LogFactory.getLog(ObjectTraverser.class); - private List<EqualityComparator> comparators = new LinkedList<EqualityComparator>(); - /**stack that holds current visit path */ - private Stack<String> st = new Stack<String>(); - /**object that compared to */ - protected Object other; - public boolean logFalseThrough = false; - - public EqualityVisitor(Object other){ - this.other = other; - st.push(":" + other.getClass().getSimpleName()); - } - - @Override - public Boolean visit(Object obj){ - if (!logFalseThrough){ - __log.debug("comparing Object " + obj.getClass() + "@" + System.identityHashCode(obj) + " " + obj + - " and " + other.getClass() + "@" + System.identityHashCode(other) + " " + other); - } - Boolean res = (Boolean)super.visit(obj); - return res; - } - @SuppressWarnings("rawtypes") - @Override - public Boolean visitMap(Object obj) { - if (obj == other) return true; - if (other == null) { - if (!logFalseThrough){ - __log.debug("Unequal in Map: Object2 is null. " + st); - } - return false; - } - Map m1 = (Map)obj; - Map m2 = null; - try{ - m2 = (Map)other; - }catch (ClassCastException e){ - if (!logFalseThrough){ - __log.debug("Unequal in Map: Object2 is not a map, it's a" + other.getClass() + "\n" + st); - } - return false; - } - if (m1.size() != m2.size()) { - if (!logFalseThrough){ - __log.debug("Unequal in Map: size mismatch. " + st + - "\n size: " + m1.size() + " and " + m2.size()); - } - return false; - } - Set ks1 = m1.keySet(); - Set ks2 = m2.keySet(); - for (Object k1 : ks1){ - st.push(k1.toString()); - Object k2 = contains(ks2, k1); - if (k2 == null){ - if (!logFalseThrough){ - __log.debug("Unequal in Map: cant find key. " + st + "\n missing key: " + k1); - } - return false; - } - Object o1 = m1.get(k1); - Object o2 = m2.get(k2); - if (o1 == null){ - if (!(o2 == null)){ - if (!logFalseThrough){ - __log.debug("Unequal in Map: mismatch, one is null" + st + - "\n When dealing with " + o1 + " and " + o2); - } - return false; - } - } - - st.pop(); - st.push(k1.toString() + ":" + o1.getClass().getSimpleName()); - - Object pre = other; - other = o2; - Boolean e = (Boolean)traverse.traverseObject(o1); - if (!e) { - return false; - } - other = pre; - st.pop(); - } - return true; - } - - @Override - @SuppressWarnings("rawtypes") - public Boolean visitSet(Object obj){ - if (obj == other) return true; - if (other == null) { - if (!logFalseThrough){ - __log.debug("Unequal in Set, Object2 is null. " + st); - } - return false; - } - - Collection c1 = (Collection)obj; - Collection c2 = null; - try { - c2 = (Collection)other; - }catch(ClassCastException e){ - if (!logFalseThrough){ - __log.debug("Unequal in Set: Object2 is not a Set, it's a" + other.getClass() + "\n" + st); - } - return false; - } - if (c1.size() != c2.size()) { - if (!logFalseThrough){ - __log.debug("Unequal in Set: size mismatch. " + st + - "\n. sizes are " + c1.size() + " and " + c2.size()); - } - return false; - } - Iterator i1 = c1.iterator(); - while (i1.hasNext()){ - Object o1 = i1.next(); - st.push(":" + o1.getClass().getSimpleName()); - if (contains(c2, o1) == null) { - if (!logFalseThrough){ - __log.debug("Unequal in Set: Object mismatch. " + st + - "\n" + "cann't find" + o1); - } - return false; - } - st.pop(); - } - return true; - } - - private Object contains(Collection c, Object t1) { - Iterator itor = c.iterator(); - Object t2; - Object pre = other; - logFalseThrough = true; - while (itor.hasNext()){ - t2 = itor.next(); - other = t2; - if ((Boolean)traverse.traverseObject(t1, false)) { - logFalseThrough = false; - other = pre; - return t2; - } - } - traverse.getHtab().assign(t1); - other = pre; - logFalseThrough = false; - return null; - } - - @SuppressWarnings("rawtypes") - @Override - public Boolean visitCollection(Object obj) { - if (obj == other) return true; - if (other == null) { - if (!logFalseThrough){ - __log.debug("Unequal in Collection, Object2 is null. " + st); - } - return false; - } - - Collection c = (Collection)obj; - Collection c2 = null; - try { - c2 = (Collection)other; - }catch(ClassCastException e){ - if (!logFalseThrough){ - __log.debug("Unequal in Collection: Object2 is not a Collection, it's a" + other.getClass() + "\n" + st); - } - return false; - } - if (c.size() != c2.size()) { - if (!logFalseThrough){ - __log.debug("Unequal in Collection: size mismatch. " + st + - "\n. sizes are " + c.size() + " and " + c2.size()); - } - return false; - } - - Iterator i1 = c.iterator(); - Iterator i2 = c2.iterator(); - while (i1.hasNext()){ - Object o1 = i1.next(); - Object o2 = i2.next(); - st.push(":" + o1.getClass().getSimpleName()); - Object pre = other; - other = o2; - Boolean e = (Boolean)traverse.traverseObject(o1); - if (!e) { - return false; - } - other = pre; - st.pop(); - } - return true; - } - - @Override - public Boolean visitArray(Object obj) { - throw new UnsupportedOperationException(); - } - - @Override - public Boolean visitPojo(Object obj) { - EqualityComparator customComp = getCustomComparator(obj); - if (customComp != null){ - return customComp.objectsEqual(obj, other); - } - if (obj == other) return true; - if (other == null) { - if (!logFalseThrough){ - __log.debug("Unequal in POJO: Object2 is null." + st); - } - return false; - } - if (obj.getClass() != other.getClass()) { - if(!logFalseThrough){ - __log.debug("Unequal in POJO: type mistach. " + st + - "\nmismatched types are: " + obj.getClass().getSimpleName() + - " and " + other.getClass().getSimpleName()); - } - return false; - } - try{ - obj.getClass().getDeclaredMethod("equals", Object.class); - boolean e = obj.equals(other); - if (!e){ - if (!logFalseThrough){ - __log.debug("Unequal in POJO: not equal by equals() method" + st); - } - } - return e; - }catch (NoSuchMethodException e){ - return equalityByReflection(obj); - } - } - - private EqualityComparator getCustomComparator(Object obj) { - for (EqualityComparator c : comparators){ - if (c.canHanle(obj)){ - return c; - } - } - return null; - } - public Boolean equalityByReflection(Object obj) { - //TODO if it's sufficient to just compare public fields? - List<Field> fields = getAllFields(obj.getClass()); - List<Field> fields2 = getAllFields(other.getClass()); - if (!fields.equals(fields2)){ - if (!logFalseThrough){ - __log.debug("Unequal: getFields() of two Object do not match " + st); - } - return false; - } - - for (Field f : fields){ - f.setAccessible(true); - if (((Modifier.TRANSIENT | Modifier.STATIC) & f.getModifiers()) != 0){ - continue; //skip transient fields - } - try { - st.push(f.getName()+ ":" + f.getType().getSimpleName()); - Object v1 = f.get(obj); - Object v2 = f.get(other); - if (v1 == null && v2 == null){ - continue; - } - if (v1 == null || v2 == null){ - if (!logFalseThrough){ - __log.debug("Unequal: one field is null" + st + ".\n When dealing with " - + v1 + " and " + v2); - } - return false; - } - Object pre = other; - other = v2; - Boolean res = (Boolean)traverse.traverseObject(v1); - if (!res){ - return false; - } - other = pre; - st.pop(); - } catch (Exception e) { - //should not get here - e.printStackTrace(); - } - } - return true; - } - private List<Field> getAllFields(Class cls) { - List<Field> fields = getFieldsRec(cls.getSuperclass(), new ArrayList<Field>()); - fields.addAll(Arrays.asList(cls.getDeclaredFields())); - return fields; - } - /** - * get fields that are accessible to its sub-classes. - * @param cls - * @param fields - * @return - */ - private List<Field> getFieldsRec(Class cls, ArrayList<Field> fields) { - if (cls != null){ - Field[] fs = cls.getDeclaredFields(); - for (Field f : fs){ - if ((f.getModifiers() & Modifier.PRIVATE) == 0){ - fields.add(f); - } - } - getFieldsRec(cls.getSuperclass(), fields); - } - return fields; - } - - public void setOther(Object other){ - this.other = other; - } - public void addCustomComparator(EqualityComparator oe){ - comparators.add(0, oe); - } - @Override - public Boolean visited(Object obj){ - return true; - } - public Stack<String> getSt() { - return st; - } -} http://git-wip-us.apache.org/repos/asf/ode/blob/c6c985e1/bpel-nobj/src/main/java/org/apache/ode/bpel/obj/migrate/ExtensibeImplEqualityComp.java ---------------------------------------------------------------------- diff --git a/bpel-nobj/src/main/java/org/apache/ode/bpel/obj/migrate/ExtensibeImplEqualityComp.java b/bpel-nobj/src/main/java/org/apache/ode/bpel/obj/migrate/ExtensibeImplEqualityComp.java index 72d4985..56cebc2 100644 --- a/bpel-nobj/src/main/java/org/apache/ode/bpel/obj/migrate/ExtensibeImplEqualityComp.java +++ b/bpel-nobj/src/main/java/org/apache/ode/bpel/obj/migrate/ExtensibeImplEqualityComp.java @@ -16,28 +16,24 @@ import org.apache.ode.bpel.obj.OProcess; public class ExtensibeImplEqualityComp implements EqualityComparator{ private static final Log __log = LogFactory.getLog(ExtensibeImplEqualityComp.class); - private EqualityVisitor visitor; - + private DeepEqualityHelper deepEquality; public ExtensibeImplEqualityComp() { } - public ExtensibeImplEqualityComp(EqualityVisitor visitor){ - this.visitor = visitor; - } @Override public Boolean objectsEqual(Object obj1, Object obj2) { if (obj2 == null) { - if (!visitor.logFalseThrough){ + if (!deepEquality.logFalseThrough){ __log.debug("Unequal in ExtensibleImpl: Object2 is null. " + - visitor.getSt()); + deepEquality.getSt()); } return false; } ExtensibleImpl esi = (ExtensibleImpl)obj1; ExtensibleImpl esio = null; if (obj1.getClass() != obj2.getClass()){ - if (!visitor.logFalseThrough){ - __log.debug("Unequal in ExtensibleImpl: Type mismatch. " + visitor.getSt() + + if (!deepEquality.logFalseThrough){ + __log.debug("Unequal in ExtensibleImpl: Type mismatch. " + deepEquality.getSt() + "\nmismatched type: " + obj1.getClass().getSimpleName() + " and " + obj2.getClass().getSimpleName()); } @@ -52,8 +48,8 @@ public class ExtensibeImplEqualityComp implements EqualityComparator{ if (obj1 instanceof DebugInfo){ boolean r = obj1.equals(obj2); if (!r){ - if(!visitor.logFalseThrough){ - __log.debug("Unequal in ExtensibleImpl: DebugInfo unequal." + visitor.getSt()); + if(!deepEquality.logFalseThrough){ + __log.debug("Unequal in ExtensibleImpl: DebugInfo unequal." + deepEquality.getSt()); } } return r; @@ -66,8 +62,7 @@ public class ExtensibeImplEqualityComp implements EqualityComparator{ dehydrateOProcess(m1); dehydrateOProcess(m2); } - visitor.setOther(m2); - return (Boolean) visitor.getTraverse().traverseObject(m1); + return (Boolean) deepEquality.deepEquals(m1, m2); } private void dehydrateOProcess(Map m) { m.remove("compileDate"); @@ -90,4 +85,9 @@ public class ExtensibeImplEqualityComp implements EqualityComparator{ public Boolean canHanle(Object obj) { return obj instanceof ExtensibleImpl; } + + @Override + public void setDeepEquality(DeepEqualityHelper deepEquality) { + this.deepEquality = deepEquality; + } } http://git-wip-us.apache.org/repos/asf/ode/blob/c6c985e1/bpel-nobj/src/main/java/org/apache/ode/bpel/obj/serde/JsonOmDeserializer.java ---------------------------------------------------------------------- diff --git a/bpel-nobj/src/main/java/org/apache/ode/bpel/obj/serde/JsonOmDeserializer.java b/bpel-nobj/src/main/java/org/apache/ode/bpel/obj/serde/JsonOmDeserializer.java index a170ca5..0c1b09a 100644 --- a/bpel-nobj/src/main/java/org/apache/ode/bpel/obj/serde/JsonOmDeserializer.java +++ b/bpel-nobj/src/main/java/org/apache/ode/bpel/obj/serde/JsonOmDeserializer.java @@ -2,15 +2,20 @@ package org.apache.ode.bpel.obj.serde; import java.io.IOException; import java.io.InputStream; +import java.lang.reflect.Field; import java.util.HashMap; +import java.util.List; import java.util.Map; +import java.util.Vector; +import javax.wsdl.Message; import javax.wsdl.OperationType; +import javax.wsdl.Part; +import javax.xml.namespace.QName; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.ode.bpel.obj.OProcess; -import org.apache.ode.bpel.obj.OProcessWrapper; import org.apache.ode.bpel.obj.serde.jacksonhack.TypeBeanSerializerFactory; import org.apache.ode.utils.NSContext; import org.w3c.dom.Element; @@ -29,6 +34,7 @@ import com.fasterxml.jackson.databind.deser.std.StdScalarDeserializer; import com.fasterxml.jackson.databind.jsontype.TypeDeserializer; import com.fasterxml.jackson.databind.module.SimpleModule; import com.fasterxml.jackson.module.jaxb.deser.DomElementJsonDeserializer; +import com.ibm.wsdl.MessageImpl; public class JsonOmDeserializer implements OmDeserializer { protected static final Log __log = LogFactory .getLog(JsonOmDeserializer.class); @@ -46,6 +52,7 @@ public class JsonOmDeserializer implements OmDeserializer { new OperationTypeDeserializer()); addCustomDeserializer(Element.class, new DomElementDeserializerHack()); addCustomDeserializer(NSContext.class, new NSContextDeserializer(NSContext.class)); + addCustomDeserializer(MessageImpl.class, new MessageDeserializer(MessageImpl.class)); } public JsonOmDeserializer(InputStream is) { @@ -189,5 +196,44 @@ public class JsonOmDeserializer implements OmDeserializer { } } + + public static class MessageDeserializer extends StdScalarDeserializer<MessageImpl>{ + + protected MessageDeserializer(Class<?> vc) { + super(vc); + } + + @Override + public MessageImpl deserialize(JsonParser jp, DeserializationContext ctxt) + throws IOException, JsonProcessingException { + MessageImpl value = new MessageImpl(); + value.setDocumentationElement(jp.readValueAs(Element.class)); + value.getExtensibilityElements().addAll(jp.readValueAs(Vector.class)); + value.getExtensionAttributes().putAll(jp.readValueAs(HashMap.class)); + + value.getParts().putAll(jp.readValueAs(HashMap.class)); + Field f1; + try { + f1 = MessageImpl.class.getDeclaredField("nativeAttributeNames"); + f1.setAccessible(true); + f1.set(value, jp.readValueAs(List.class)); + } catch (Exception e) { + __log.debug("Exception when serialize MessageImpl:" + e); + } + value.setUndefined(jp.readValueAs(Boolean.class)); + value.setQName(jp.readValueAs(QName.class)); + + Vector<String> parts = jp.readValueAs(Vector.class); + try{ + Field f = MessageImpl.class.getDeclaredField("additionOrderOfParts"); + f.setAccessible(true); + f.set(value, parts); + }catch(Exception e){ + __log.debug("Exception when serialize MessageImpl:" + e); + } + return value; + } + + } } http://git-wip-us.apache.org/repos/asf/ode/blob/c6c985e1/bpel-nobj/src/main/java/org/apache/ode/bpel/obj/serde/JsonOmSerializer.java ---------------------------------------------------------------------- diff --git a/bpel-nobj/src/main/java/org/apache/ode/bpel/obj/serde/JsonOmSerializer.java b/bpel-nobj/src/main/java/org/apache/ode/bpel/obj/serde/JsonOmSerializer.java index 87d4355..cd01a16 100644 --- a/bpel-nobj/src/main/java/org/apache/ode/bpel/obj/serde/JsonOmSerializer.java +++ b/bpel-nobj/src/main/java/org/apache/ode/bpel/obj/serde/JsonOmSerializer.java @@ -2,16 +2,20 @@ package org.apache.ode.bpel.obj.serde; import java.io.IOException; import java.io.OutputStream; +import java.lang.reflect.Field; import java.lang.reflect.Type; +import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Map; +import java.util.Vector; import javax.wsdl.OperationType; +import javax.wsdl.Part; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.ode.bpel.obj.OProcess; -import org.apache.ode.bpel.obj.OProcessWrapper; import org.apache.ode.bpel.obj.serde.jacksonhack.TypeBeanSerializerFactory; import org.apache.ode.utils.NSContext; import org.w3c.dom.Element; @@ -19,7 +23,6 @@ import org.w3c.dom.Element; import com.fasterxml.jackson.core.JsonFactory; import com.fasterxml.jackson.core.JsonGenerationException; import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.databind.JsonMappingException; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.JsonSerializer; import com.fasterxml.jackson.databind.ObjectMapper; @@ -29,6 +32,7 @@ import com.fasterxml.jackson.databind.jsontype.TypeSerializer; import com.fasterxml.jackson.databind.module.SimpleModule; import com.fasterxml.jackson.databind.ser.std.StdScalarSerializer; import com.fasterxml.jackson.module.jaxb.ser.DomElementJsonSerializer; +import com.ibm.wsdl.MessageImpl; public class JsonOmSerializer implements OmSerializer { @@ -47,6 +51,7 @@ public class JsonOmSerializer implements OmSerializer { addCustomSerializer(OperationType.class, new OperationTypeSerializer()); addCustomSerializer(Element.class, new DomElementSerializerHack()); addCustomSerializer(NSContext.class, new NSContextSerializer(NSContext.class)); + addCustomSerializer(MessageImpl.class, new MessageSerializer(MessageImpl.class)); } public JsonOmSerializer(OutputStream os, OProcess process){ @@ -168,4 +173,35 @@ public class JsonOmSerializer implements OmSerializer { } } + + public static class MessageSerializer extends StdScalarSerializer<MessageImpl>{ + + protected MessageSerializer(Class<MessageImpl> class1) { + super(class1); + } + + @Override + public void serialize(MessageImpl value, JsonGenerator jgen, + SerializerProvider provider) throws IOException, + JsonGenerationException { + jgen.writeObject(value.getDocumentationElement()); + jgen.writeObject(value.getExtensibilityElements()); + jgen.writeObject(value.getExtensionAttributes()); + + jgen.writeObject(value.getParts()); + jgen.writeObject(value.getNativeAttributeNames()); + jgen.writeObject(value.isUndefined()); + jgen.writeObject(value.getQName()); + try { + Field f = value.getClass().getDeclaredField("additionOrderOfParts"); + f.setAccessible(true); + Vector<Part> parts = (Vector)f.get(value); + jgen.writeObject(parts); + } catch (Exception e) { + //nothing to do. + __log.debug("Exception when serialize MessageImpl:" + e); + } + } + + } } http://git-wip-us.apache.org/repos/asf/ode/blob/c6c985e1/bpel-nobj/src/main/java/org/apache/ode/bpel/obj/serde/OmSerdeFactory.java ---------------------------------------------------------------------- diff --git a/bpel-nobj/src/main/java/org/apache/ode/bpel/obj/serde/OmSerdeFactory.java b/bpel-nobj/src/main/java/org/apache/ode/bpel/obj/serde/OmSerdeFactory.java index 5c006fd..3cdd955 100644 --- a/bpel-nobj/src/main/java/org/apache/ode/bpel/obj/serde/OmSerdeFactory.java +++ b/bpel-nobj/src/main/java/org/apache/ode/bpel/obj/serde/OmSerdeFactory.java @@ -37,6 +37,7 @@ public class OmSerdeFactory { break; case FORMAT_SERIALIZED_SMILE: deser = new SmileOmDeserializer(is); + break; case FORMAT_SERIALIZED_JAVA: deser = new JavaSerOmDeserializer(is); break; http://git-wip-us.apache.org/repos/asf/ode/blob/c6c985e1/bpel-nobj/src/test/java/org/apache/ode/bpel/obj/migrate/DeepEqualityTest.java ---------------------------------------------------------------------- diff --git a/bpel-nobj/src/test/java/org/apache/ode/bpel/obj/migrate/DeepEqualityTest.java b/bpel-nobj/src/test/java/org/apache/ode/bpel/obj/migrate/DeepEqualityTest.java index 2b8bad6..4d40ad8 100644 --- a/bpel-nobj/src/test/java/org/apache/ode/bpel/obj/migrate/DeepEqualityTest.java +++ b/bpel-nobj/src/test/java/org/apache/ode/bpel/obj/migrate/DeepEqualityTest.java @@ -24,16 +24,13 @@ public class DeepEqualityTest { List<String> ls2 = new ArrayList<String>(); ls2.add("Hello"); ls2.add("world"); - ObjectTraverser traverse = new ObjectTraverser(); - EqualityVisitor visitor = new EqualityVisitor(ls2); - traverse.accept(visitor); - assertEquals(Boolean.TRUE, traverse.traverseObject(ls1)); + DeepEqualityHelper de = new DeepEqualityHelper(); + assertEquals(Boolean.TRUE, de.deepEquals(ls1, ls2)); ls1.add(0, "!"); ls2.add("!"); - visitor.setOther(ls2); - assertEquals(Boolean.TRUE, traverse.traverseObject(ls1)); + assertEquals(Boolean.TRUE, de.deepEquals(ls1, ls2)); } - + @Test public void simpleSetTest(){ Set<String> s1 = new LinkedHashSet(); @@ -42,11 +39,9 @@ public class DeepEqualityTest { Set<String> s2 = new LinkedHashSet(); s2.add("world"); s2.add("hello"); - - ObjectTraverser traverse = new ObjectTraverser(); - EqualityVisitor visitor = new EqualityVisitor(s2); - traverse.accept(visitor); - assertEquals(Boolean.TRUE, traverse.traverseObject(s1)); + + DeepEqualityHelper de = new DeepEqualityHelper(); + assertEquals(Boolean.TRUE, de.deepEquals(s1, s2)); } @@ -60,10 +55,8 @@ public class DeepEqualityTest { m2.put("item1", "string1"); m2.put("item2", new ArrayList()); - ObjectTraverser traverse = new ObjectTraverser(); - EqualityVisitor visitor = new EqualityVisitor(m2); - traverse.accept(visitor); - assertEquals(Boolean.TRUE, traverse.traverseObject(m1)); + DeepEqualityHelper de = new DeepEqualityHelper(); + assertEquals(Boolean.TRUE, de.deepEquals(m1, m2)); } @SuppressWarnings({ "rawtypes", "unchecked" }) @@ -81,12 +74,10 @@ public class DeepEqualityTest { URI u2 = new URI("urn://a/uri"); m2.put(u2, n2); DebugInfo d2 = new DebugInfo("/a/path", 0, 1, m2); - m2.put("cylic", d2); + m2.put("cyclic", d2); - ObjectTraverser traverse = new ObjectTraverser(); - EqualityVisitor visitor = new EqualityVisitor(d2); - visitor.addCustomComparator(new ExtensibeImplEqualityComp(visitor)); - traverse.accept(visitor); - assertEquals(Boolean.TRUE, traverse.traverseObject(d1)); + DeepEqualityHelper de = new DeepEqualityHelper(); + de.addCustomComparator(new ExtensibeImplEqualityComp()); + assertEquals(Boolean.TRUE, de.deepEquals(m1, m2)); } }
