/******************************************************************************* * Copyright (c) 2010 Association for Decentralized Information Management in * Industry THTH ry. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * VTT Technical Research Centre of Finland - initial API and implementation *******************************************************************************/ package org.simantics.databoard.util; import java.util.Comparator; import java.util.HashSet; import java.util.Iterator; import java.util.Map.Entry; import java.util.Set; import java.util.TreeMap; import org.simantics.databoard.Bindings; import org.simantics.databoard.binding.ArrayBinding; import org.simantics.databoard.binding.Binding; import org.simantics.databoard.binding.BooleanBinding; import org.simantics.databoard.binding.ByteBinding; import org.simantics.databoard.binding.DoubleBinding; import org.simantics.databoard.binding.FloatBinding; import org.simantics.databoard.binding.IntegerBinding; import org.simantics.databoard.binding.LongBinding; import org.simantics.databoard.binding.MapBinding; import org.simantics.databoard.binding.OptionalBinding; import org.simantics.databoard.binding.RecordBinding; import org.simantics.databoard.binding.StringBinding; import org.simantics.databoard.binding.UnionBinding; import org.simantics.databoard.binding.VariantBinding; import org.simantics.databoard.binding.error.BindingException; import org.simantics.databoard.binding.error.RuntimeBindingException; import org.simantics.databoard.type.ArrayType; import org.simantics.databoard.type.BooleanType; import org.simantics.databoard.type.ByteType; import org.simantics.databoard.type.Datatype; import org.simantics.databoard.type.DoubleType; import org.simantics.databoard.type.FloatType; import org.simantics.databoard.type.IntegerType; import org.simantics.databoard.type.LongType; import org.simantics.databoard.type.MapType; import org.simantics.databoard.type.OptionalType; import org.simantics.databoard.type.RecordType; import org.simantics.databoard.type.StringType; import org.simantics.databoard.type.UnionType; import org.simantics.databoard.type.VariantType; /** * DataValue handling utilities * * @author Toni Kalajainen */ 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.

* * The implementor must also ensure that the relation is transitive: * ((compare(x, y)>0) && (compare(y, z)>0)) implies * compare(x, z)>0.

* * Finally, the implementor must ensure that compare(x, y)==0 * implies that sgn(compare(x, z))==sgn(compare(y, z)) for all * z.

* * 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> 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 e1 = new TreeMap( k1 ); TreeMap e2 = new TreeMap( k2 ); m1.getAll(o1, e1); m2.getAll(o2, e2); Iterator> i1 = e1.entrySet().iterator(); Iterator> i2 = e2.entrySet().iterator(); while (i1.hasNext()) { Entry h1 = i1.next(); Entry 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 p = new IdentityPair(o1, o2); if (compareHistory.contains(p)) return 0; compareHistory.add(p); } int len = rt.getComponentCount(); for (int i=0; i createComparator(final Binding b1, final Binding b2) { if (b1==b2) return b1; return new Comparator() { @Override public int compare(Object o1, Object o2) { try { return DataValueUtil.compare(b1, o1, b2, o2); } catch (BindingException e) { throw new RuntimeBindingException(e); } } }; } }