--- /dev/null
+/*******************************************************************************\r
+ * Copyright (c) 2010 Association for Decentralized Information Management in\r
+ * Industry THTH ry.\r
+ * All rights reserved. This program and the accompanying materials\r
+ * are made available under the terms of the Eclipse Public License v1.0\r
+ * which accompanies this distribution, and is available at\r
+ * http://www.eclipse.org/legal/epl-v10.html\r
+ *\r
+ * Contributors:\r
+ * VTT Technical Research Centre of Finland - initial API and implementation\r
+ *******************************************************************************/\r
+package org.simantics.databoard.util;
+
+import java.util.Comparator;\r
+import java.util.HashSet;\r
+import java.util.Iterator;\r
+import java.util.Map.Entry;\r
+import java.util.Set;\r
+import java.util.TreeMap;\r
+\r
+import org.simantics.databoard.Bindings;\r
+import org.simantics.databoard.binding.ArrayBinding;\r
+import org.simantics.databoard.binding.Binding;\r
+import org.simantics.databoard.binding.BooleanBinding;\r
+import org.simantics.databoard.binding.ByteBinding;\r
+import org.simantics.databoard.binding.DoubleBinding;\r
+import org.simantics.databoard.binding.FloatBinding;\r
+import org.simantics.databoard.binding.IntegerBinding;\r
+import org.simantics.databoard.binding.LongBinding;\r
+import org.simantics.databoard.binding.MapBinding;\r
+import org.simantics.databoard.binding.OptionalBinding;\r
+import org.simantics.databoard.binding.RecordBinding;\r
+import org.simantics.databoard.binding.StringBinding;\r
+import org.simantics.databoard.binding.UnionBinding;\r
+import org.simantics.databoard.binding.VariantBinding;\r
+import org.simantics.databoard.binding.error.BindingException;\r
+import org.simantics.databoard.binding.error.RuntimeBindingException;\r
+import org.simantics.databoard.type.ArrayType;\r
+import org.simantics.databoard.type.BooleanType;\r
+import org.simantics.databoard.type.ByteType;\r
+import org.simantics.databoard.type.Datatype;\r
+import org.simantics.databoard.type.DoubleType;\r
+import org.simantics.databoard.type.FloatType;\r
+import org.simantics.databoard.type.IntegerType;\r
+import org.simantics.databoard.type.LongType;\r
+import org.simantics.databoard.type.MapType;\r
+import org.simantics.databoard.type.OptionalType;\r
+import org.simantics.databoard.type.RecordType;\r
+import org.simantics.databoard.type.StringType;\r
+import org.simantics.databoard.type.UnionType;\r
+import org.simantics.databoard.type.VariantType;\r
+
+/**
+ * DataValue handling utilities
+ *
+ * @author Toni Kalajainen <toni.kalajainen@vtt.fi>
+ */
+public class DataValueUtil {
+
+ /**
+ * Compares its two data values for order. Returns a negative integer,
+ * zero, or a positive integer as the first argument is less than, equal
+ * to, or greater than the second.<p>
+ *
+ * The implementor must also ensure that the relation is transitive:
+ * <code>((compare(x, y)>0) && (compare(y, z)>0))</code> implies
+ * <code>compare(x, z)>0</code>.<p>
+ *
+ * Finally, the implementor must ensure that <code>compare(x, y)==0</code>
+ * implies that <code>sgn(compare(x, z))==sgn(compare(y, z))</code> for all
+ * <code>z</code>.<p>
+ *
+ * DataTypes of b1 and b2 are not equal then data types are compared.
+ *
+ * The comparison function is defined at
+ * http://dev.simantics.org/index.php/Org.simantics.datatype_Manual#CompareTo_and_Equals
+ *
+ * @param b1 Binding of o1
+ * @param o1 the first object to be compared.
+ * @param b2 Binding of o2
+ * @param o2 the second object to be compared.
+ * @return a negative integer, zero, or a positive integer as the
+ * first argument is less than, equal to, or greater than the
+ * second.
+ * @throws BindingException if object cannot be handled by a binding
+ */
+ public static int compare(Binding b1, Object o1, Binding b2, Object o2)
+ throws BindingException
+ {
+ // Not implemented yet
+ if (b1 == b2) {
+ try {
+ return b1.compare(o1, o2);
+ } catch (RuntimeBindingException e) {
+ throw e.getCause();
+ }
+ }
+
+ Datatype t1 = b1.type();
+ Datatype t2 = b2.type();
+ int typeComparison = Bindings.getBindingUnchecked(Datatype.class).compare(t1, t2);
+ if (typeComparison != 0) return typeComparison;
+
+ return deepCompare(t1, b1, o1, b2, o2, null);
+ }
+
+ public static boolean equals(Binding b1, Object o1, Binding b2, Object o2)
+ throws BindingException
+ {
+ // Not implemented yet
+ if (b1 == b2) {
+ try {
+ return b1.compare(o1, o2) == 0;
+ } catch (RuntimeBindingException e) {
+ throw e.getCause();
+ }
+ }
+
+ Datatype t1 = b1.type();
+ Datatype t2 = b2.type();
+ if (!t1.equals(t2)) return false;
+
+ int compare = deepCompare(t1, b1, o1, b2, o2, null);
+ return compare == 0;
+ }
+
+ static int deepCompare(Datatype t, Binding b1, Object o1, Binding b2, Object o2, Set<IdentityPair<Object, Object>> compareHistory)
+ throws BindingException {
+ /*// Not implemented yet
+ if (b1 == b2) {
+ try {
+ return b1.compare(o1, o2);
+ } catch (RuntimeBindingException e) {
+ throw e.getCause();
+ }
+ }
+ */
+ assert(b1.type().equals(t));
+ assert(b2.type().equals(t));
+
+ if (t instanceof ByteType) {
+ byte v1 = ((ByteBinding) b1).getValue_(o1);
+ byte v2 = ((ByteBinding) b2).getValue_(o2);
+ return v1 - v2;
+ }
+
+ if (t instanceof IntegerType) {
+ int v1 = ((IntegerBinding) b1).getValue_(o1);
+ int v2 = ((IntegerBinding) b2).getValue_(o2);
+ return (v1<v2 ? -1 : (v1==v2 ? 0 : 1));
+ }
+
+ if (t instanceof LongType) {
+ long v1 = ((LongBinding) b1).getValue_(o1);
+ long v2 = ((LongBinding) b2).getValue_(o2);
+ return (v1<v2 ? -1 : (v1==v2 ? 0 : 1));
+ }
+
+ if (t instanceof FloatType) {
+ float v1 = ((FloatBinding) b1).getValue_(o1);
+ float v2 = ((FloatBinding) b2).getValue_(o2);
+ return Float.compare(v1, v2);
+ }
+
+ if (t instanceof DoubleType) {
+ Double v1 = ((DoubleBinding) b1).getValue_(o1);
+ Double v2 = ((DoubleBinding) b2).getValue_(o2);
+ return Double.compare(v1, v2);
+ }
+
+ if (t instanceof BooleanType) {
+ boolean v1 = ((BooleanBinding) b1).getValue_(o1);
+ boolean v2 = ((BooleanBinding) b2).getValue_(o2);
+ return (v2 == v1 ? 0 : (v1 ? 1 : -1));
+ }
+
+ if (t instanceof ArrayType) {
+ ArrayBinding a1 = (ArrayBinding) b1;
+ ArrayBinding a2 = (ArrayBinding) b2;
+ // Compare Lengths
+ int l1 = a1.size(o1);
+ int l2 = a2.size(o2);
+ int dif = l1 - l2;
+ if (dif!=0) return dif;
+ // Compare elements
+ Binding c1 = a1.getComponentBinding();
+ Binding c2 = a2.getComponentBinding();
+ Datatype ct = c1.type();
+ for (int i=0; i<l1; i++) {
+ Object e1 = a1.get(o1, i);
+ Object e2 = a2.get(o2, i);
+ dif = deepCompare(ct, c1, e1, c2, e2, compareHistory);
+ if (dif!=0) return dif;
+ }
+ return 0;
+ }
+
+ if (t instanceof MapType) {
+ MapBinding m1 = (MapBinding) b1;
+ MapBinding m2 = (MapBinding) b2;
+ // Compare sizes
+ int l1 = m1.size(o1);
+ int l2 = m2.size(o2);
+ int dif = l1 - l2;
+ if (dif!=0) return dif;
+ // Compare elements
+ Binding k1 = m1.getKeyBinding();
+ Binding k2 = m2.getKeyBinding();
+ Binding v1 = m1.getValueBinding();
+ Binding v2 = m2.getValueBinding();
+ TreeMap<Object, Object> e1 = new TreeMap<Object, Object>( k1 );
+ TreeMap<Object, Object> e2 = new TreeMap<Object, Object>( k2 );
+ m1.getAll(o1, e1);
+ m2.getAll(o2, e2);
+
+ Iterator<Entry<Object, Object>> i1 = e1.entrySet().iterator();
+ Iterator<Entry<Object, Object>> i2 = e2.entrySet().iterator();
+ while (i1.hasNext()) {
+ Entry<Object, Object> h1 = i1.next();
+ Entry<Object, Object> h2 = i2.next();
+ dif = deepCompare(k1.type(), k1, h1.getKey(), k2, h2.getKey(), compareHistory);
+ if (dif!=0) return dif;
+ dif = deepCompare(v1.type(), v1, h1.getValue(), v2, h2.getValue(), compareHistory);
+ if (dif!=0) return dif;
+ i1.remove();
+ i2.remove();
+ }
+ return 0;
+ }
+
+ if (t instanceof OptionalType) {
+ OptionalBinding ob1 = (OptionalBinding) b1;
+ OptionalBinding ob2 = (OptionalBinding) b2;
+ Boolean h1 = ob1.hasValue(o1);
+ Boolean h2 = ob2.hasValue(o2);
+ if (!h1 && !h2) return 0;
+ int dif = h1.compareTo(h2);
+ if (dif!=0) return dif;
+
+ Binding c1 = ob1.getComponentBinding();
+ Binding c2 = ob2.getComponentBinding();
+ Object v1 = ob1.getValue(o1);
+ Object v2 = ob2.getValue(o2);
+ Datatype ct = c1.type();
+ return deepCompare(ct, c1, v1, c2, v2, compareHistory);
+ }
+
+ if (t instanceof StringType) {
+ StringBinding s1 = (StringBinding) b1;
+ StringBinding s2 = (StringBinding) b2;
+ String v1 = s1.getValue(o1);
+ String v2 = s2.getValue(o2);
+ return v1.compareTo(v2);
+ }
+
+ if (t instanceof UnionType) {
+ UnionBinding u1 = (UnionBinding) b1;
+ UnionBinding u2 = (UnionBinding) b2;
+ Integer t1 = u1.getTag(o1);
+ Integer t2 = u2.getTag(o2);
+ int dif = t1.compareTo(t2);
+ if (dif!=0) return dif;
+ Object v1 = u1.getValue(o1);
+ Object v2 = u2.getValue(o2);
+ Binding c1 = u1.getComponentBindings()[t1];
+ Binding c2 = u2.getComponentBindings()[t2];
+ Datatype ct = c1.type();
+ return deepCompare(ct, c1, v1, c2, v2, compareHistory);
+ }
+
+ if (t instanceof VariantType) {
+ VariantBinding v1 = (VariantBinding) b1;
+ VariantBinding v2 = (VariantBinding) b2;
+ // Compare Type
+ Datatype t1 = v1.getContentType(o1);
+ Datatype t2 = v2.getContentType(o2);
+ Binding dataTypeBinding = Bindings.getBindingUnchecked(Datatype.class);
+ int dif = dataTypeBinding.compare(t1, t2);
+ if (dif!=0) return dif;
+ // Compare Value
+ Binding bi1 = v1.getContentBinding(o1);
+ Binding bi2 = v2.getContentBinding(o2);
+ Object va1 = v1.getContent(o1, bi1);
+ Object va2 = v2.getContent(o2, bi2);
+ return compare(bi1, va1, bi2, va2);
+ }
+
+ if (t instanceof RecordType) {
+ RecordType rt = (RecordType) t;
+ RecordBinding r1 = (RecordBinding) b1;
+ RecordBinding r2 = (RecordBinding) b2;
+
+ // Compare History is used to prevent infinite loops
+ if (rt.isReferable()) {
+ if (compareHistory==null) {
+ compareHistory = new HashSet<IdentityPair<Object, Object>>();
+ }
+ IdentityPair<Object, Object> p = new IdentityPair<Object, Object>(o1, o2);
+ if (compareHistory.contains(p)) return 0;
+ compareHistory.add(p);
+ }
+
+ int len = rt.getComponentCount();
+ for (int i=0; i<len; i++) {
+ Binding c1 = r1.getComponentBindings()[i];
+ Binding c2 = r2.getComponentBindings()[i];
+ Object v1 = r1.getComponent(o1, i);
+ Object v2 = r2.getComponent(o2, i);
+ Datatype ct = c1.type();
+ int dif = deepCompare(ct, c1, v1, c2, v2, compareHistory);
+ if (dif!=0) return dif;
+ }
+ return 0;
+ }
+
+ throw new IllegalArgumentException("Unsupported data type "+t);
+ }
+
+ public static Comparator<Object> createComparator(final Binding b1, final Binding b2)
+ {
+ if (b1==b2) return b1;
+ return new Comparator<Object>() {
+ @Override
+ public int compare(Object o1, Object o2)
+ {
+ try {
+ return DataValueUtil.compare(b1, o1, b2, o2);
+ } catch (BindingException e) {
+ throw new RuntimeBindingException(e);
+ }
+ }
+ };
+ }
+
+}
+