]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.databoard/src/org/simantics/databoard/util/DataValueUtil.java
Test equality of ObjectVariants using Objects.equals
[simantics/platform.git] / bundles / org.simantics.databoard / src / org / simantics / databoard / util / DataValueUtil.java
1 /*******************************************************************************
2  *  Copyright (c) 2010 Association for Decentralized Information Management in
3  *  Industry THTH ry.
4  *  All rights reserved. This program and the accompanying materials
5  *  are made available under the terms of the Eclipse Public License v1.0
6  *  which accompanies this distribution, and is available at
7  *  http://www.eclipse.org/legal/epl-v10.html
8  *
9  *  Contributors:
10  *      VTT Technical Research Centre of Finland - initial API and implementation
11  *******************************************************************************/
12 package org.simantics.databoard.util;
13
14 import java.util.Comparator;
15 import java.util.HashSet;
16 import java.util.Iterator;
17 import java.util.Map.Entry;
18 import java.util.Set;
19 import java.util.TreeMap;
20
21 import org.simantics.databoard.Bindings;
22 import org.simantics.databoard.binding.ArrayBinding;
23 import org.simantics.databoard.binding.Binding;
24 import org.simantics.databoard.binding.BooleanBinding;
25 import org.simantics.databoard.binding.ByteBinding;
26 import org.simantics.databoard.binding.DoubleBinding;
27 import org.simantics.databoard.binding.FloatBinding;
28 import org.simantics.databoard.binding.IntegerBinding;
29 import org.simantics.databoard.binding.LongBinding;
30 import org.simantics.databoard.binding.MapBinding;
31 import org.simantics.databoard.binding.OptionalBinding;
32 import org.simantics.databoard.binding.RecordBinding;
33 import org.simantics.databoard.binding.StringBinding;
34 import org.simantics.databoard.binding.UnionBinding;
35 import org.simantics.databoard.binding.VariantBinding;
36 import org.simantics.databoard.binding.error.BindingException;
37 import org.simantics.databoard.binding.error.RuntimeBindingException;
38 import org.simantics.databoard.type.ArrayType;
39 import org.simantics.databoard.type.BooleanType;
40 import org.simantics.databoard.type.ByteType;
41 import org.simantics.databoard.type.Datatype;
42 import org.simantics.databoard.type.DoubleType;
43 import org.simantics.databoard.type.FloatType;
44 import org.simantics.databoard.type.IntegerType;
45 import org.simantics.databoard.type.LongType;
46 import org.simantics.databoard.type.MapType;
47 import org.simantics.databoard.type.OptionalType;
48 import org.simantics.databoard.type.RecordType;
49 import org.simantics.databoard.type.StringType;
50 import org.simantics.databoard.type.UnionType;
51 import org.simantics.databoard.type.VariantType;
52
53 /**
54  * DataValue handling utilities
55  *
56  * @author Toni Kalajainen <toni.kalajainen@vtt.fi>
57  */
58 public class DataValueUtil {
59
60     /**
61      * Compares its two data values for order.  Returns a negative integer,
62      * zero, or a positive integer as the first argument is less than, equal
63      * to, or greater than the second.<p>
64      * 
65      * The implementor must also ensure that the relation is transitive:
66      * <code>((compare(x, y)&gt;0) &amp;&amp; (compare(y, z)&gt;0))</code> implies
67      * <code>compare(x, z)&gt;0</code>.<p>
68      *
69      * Finally, the implementor must ensure that <code>compare(x, y)==0</code>
70      * implies that <code>sgn(compare(x, z))==sgn(compare(y, z))</code> for all
71      * <code>z</code>.<p>
72      * 
73      * DataTypes of b1 and b2 are not equal then data types are compared.
74      *
75      * The comparison function is defined at 
76      * http://dev.simantics.org/index.php/Org.simantics.datatype_Manual#CompareTo_and_Equals
77      *
78      * @param b1 Binding of o1 
79      * @param o1 the first object to be compared.
80      * @param b2 Binding of o2
81      * @param o2 the second object to be compared.
82      * @return a negative integer, zero, or a positive integer as the
83      *         first argument is less than, equal to, or greater than the
84      *         second.
85      * @throws BindingException if object cannot be handled by a binding
86      */
87     public static int compare(Binding b1, Object o1, Binding b2, Object o2) 
88     throws BindingException 
89     {
90         // Not implemented yet
91         if (b1 == b2) {
92                 try {
93                         return b1.compare(o1, o2);
94                 } catch (RuntimeBindingException e) {
95                         throw e.getCause();
96                 }
97         }       
98                 
99         Datatype t1 = b1.type();
100         Datatype t2 = b2.type();
101         int typeComparison = Bindings.getBindingUnchecked(Datatype.class).compare(t1, t2);
102         if (typeComparison != 0) return typeComparison;
103         
104                 return deepCompare(t1, b1, o1, b2, o2, null);
105     }
106     
107     public static boolean equals(Binding b1, Object o1, Binding b2, Object o2)
108     throws BindingException 
109     {
110         // Not implemented yet
111         if (b1 == b2) {
112                 try {
113                         return b1.equals(o1, o2);
114                 } catch (RuntimeBindingException e) {
115                         throw e.getCause();
116                 }
117         }       
118                 
119         Datatype t1 = b1.type();
120         Datatype t2 = b2.type();
121         if (!t1.equals(t2)) return false;
122                 
123                 int compare = deepCompare(t1, b1, o1, b2, o2, null);
124                 return compare == 0;            
125     }
126     
127     static int deepCompare(Datatype t, Binding b1, Object o1, Binding b2, Object o2, Set<IdentityPair<Object, Object>> compareHistory) 
128     throws BindingException {
129         /*// Not implemented yet
130         if (b1 == b2) {
131                 try {
132                         return b1.compare(o1, o2);
133                 } catch (RuntimeBindingException e) {
134                         throw e.getCause();
135                 }
136         }
137         */
138         assert(b1.type().equals(t));
139         assert(b2.type().equals(t));
140         
141         if (t instanceof ByteType) {
142                 byte v1 = ((ByteBinding) b1).getValue_(o1);
143                 byte v2 = ((ByteBinding) b2).getValue_(o2);
144                 return v1 - v2;         
145         }
146
147         if (t instanceof IntegerType) {
148                 int v1 = ((IntegerBinding) b1).getValue_(o1);
149                 int v2 = ((IntegerBinding) b2).getValue_(o2);
150                 return (v1<v2 ? -1 : (v1==v2 ? 0 : 1));                 
151         }
152
153         if (t instanceof LongType) {
154                 long v1 = ((LongBinding) b1).getValue_(o1);
155                 long v2 = ((LongBinding) b2).getValue_(o2);
156                 return (v1<v2 ? -1 : (v1==v2 ? 0 : 1));
157         }
158
159         if (t instanceof FloatType) {
160                 float v1 = ((FloatBinding) b1).getValue_(o1);
161                 float v2 = ((FloatBinding) b2).getValue_(o2);
162                 return Float.compare(v1, v2);           
163         }
164
165         if (t instanceof DoubleType) {
166                 Double v1 = ((DoubleBinding) b1).getValue_(o1);
167                 Double v2 = ((DoubleBinding) b2).getValue_(o2);
168                 return Double.compare(v1, v2);                          
169         }
170         
171         if (t instanceof BooleanType) {
172                 boolean v1 = ((BooleanBinding) b1).getValue_(o1);
173                 boolean v2 = ((BooleanBinding) b2).getValue_(o2);
174                 return (v2 == v1 ? 0 : (v1 ? 1 : -1));          
175         }
176         
177         if (t instanceof ArrayType) {
178                 ArrayBinding a1 = (ArrayBinding) b1;
179                 ArrayBinding a2 = (ArrayBinding) b2;
180                 // Compare Lengths
181                 int l1 = a1.size(o1);
182                 int l2 = a2.size(o2);
183                 int dif = l1 - l2;
184                 if (dif!=0) return dif;
185                 // Compare elements
186                 Binding c1 = a1.getComponentBinding();
187                 Binding c2 = a2.getComponentBinding();                  
188                 Datatype ct = c1.type();
189                 for (int i=0; i<l1; i++) {
190                         Object e1 = a1.get(o1, i);
191                         Object e2 = a2.get(o2, i);
192                         dif = deepCompare(ct, c1, e1, c2, e2, compareHistory);
193                         if (dif!=0) return dif;
194                 }
195                 return 0;
196         }
197         
198         if (t instanceof MapType) {
199                 MapBinding m1 = (MapBinding) b1;
200                 MapBinding m2 = (MapBinding) b2;
201                 // Compare sizes
202                 int l1 = m1.size(o1);
203                 int l2 = m2.size(o2);
204                 int dif = l1 - l2;
205                 if (dif!=0) return dif;
206                 // Compare elements
207                 Binding k1 = m1.getKeyBinding();
208                 Binding k2 = m2.getKeyBinding();
209                 Binding v1 = m1.getValueBinding();
210                 Binding v2 = m2.getValueBinding();              
211                 TreeMap<Object, Object> e1 = new TreeMap<Object, Object>( k1 );
212                 TreeMap<Object, Object> e2 = new TreeMap<Object, Object>( k2 );
213                 m1.getAll(o1, e1);
214                 m2.getAll(o2, e2);
215                 
216                 Iterator<Entry<Object, Object>> i1 = e1.entrySet().iterator(); 
217                 Iterator<Entry<Object, Object>> i2 = e2.entrySet().iterator(); 
218                 while (i1.hasNext()) {
219                         Entry<Object, Object> h1 = i1.next();
220                         Entry<Object, Object> h2 = i2.next();
221                         dif = deepCompare(k1.type(), k1, h1.getKey(), k2, h2.getKey(), compareHistory);
222                         if (dif!=0) return dif;
223                         dif = deepCompare(v1.type(), v1, h1.getValue(), v2, h2.getValue(), compareHistory);
224                         if (dif!=0) return dif;
225                         i1.remove();
226                         i2.remove();
227                 }
228                 return 0;
229         }
230         
231         if (t instanceof OptionalType) {
232                 OptionalBinding ob1 = (OptionalBinding) b1;
233                 OptionalBinding ob2 = (OptionalBinding) b2;
234                 Boolean h1 = ob1.hasValue(o1);
235                 Boolean h2 = ob2.hasValue(o2);
236                 if (!h1 && !h2) return 0;
237                 int dif = h1.compareTo(h2);
238                 if (dif!=0) return dif;
239                 
240                 Binding c1 = ob1.getComponentBinding();
241                 Binding c2 = ob2.getComponentBinding();
242                 Object v1 = ob1.getValue(o1);
243                 Object v2 = ob2.getValue(o2);
244                 Datatype ct = c1.type();
245                 return deepCompare(ct, c1, v1, c2, v2, compareHistory);
246         }
247         
248         if (t instanceof StringType) {
249                 StringBinding s1 = (StringBinding) b1;
250                 StringBinding s2 = (StringBinding) b2;
251                 String v1 = s1.getValue(o1);
252                 String v2 = s2.getValue(o2);
253                 return v1.compareTo(v2);
254         }
255         
256         if (t instanceof UnionType) {
257                 UnionBinding u1 = (UnionBinding) b1;
258                 UnionBinding u2 = (UnionBinding) b2;
259                 Integer t1 = u1.getTag(o1);
260                 Integer t2 = u2.getTag(o2);
261                 int dif = t1.compareTo(t2);
262                 if (dif!=0) return dif;
263                 Object v1 = u1.getValue(o1);
264                 Object v2 = u2.getValue(o2);
265                 Binding c1 = u1.getComponentBindings()[t1];
266                 Binding c2 = u2.getComponentBindings()[t2];
267                 Datatype ct = c1.type();
268                 return deepCompare(ct, c1, v1, c2, v2, compareHistory);
269         }
270         
271         if (t instanceof VariantType) {
272                 VariantBinding v1 = (VariantBinding) b1;
273                 VariantBinding v2 = (VariantBinding) b2;
274                 // Compare Type
275                 Datatype t1 = v1.getContentType(o1);
276                 Datatype t2 = v2.getContentType(o2);
277                 Binding dataTypeBinding = Bindings.getBindingUnchecked(Datatype.class);
278                 int dif = dataTypeBinding.compare(t1, t2);
279                 if (dif!=0) return dif;
280                 // Compare Value
281                 Binding bi1 = v1.getContentBinding(o1);
282                 Binding bi2 = v2.getContentBinding(o2);
283                 Object va1 = v1.getContent(o1, bi1);
284                 Object va2 = v2.getContent(o2, bi2);
285                 return compare(bi1, va1, bi2, va2);
286         }
287         
288         if (t instanceof RecordType) {                                  
289                 RecordType rt    = (RecordType) t; 
290                 RecordBinding r1 = (RecordBinding) b1;
291                 RecordBinding r2 = (RecordBinding) b2;
292                 
293                 // Compare History is used to prevent infinite loops
294                 if (rt.isReferable()) {
295                 if (compareHistory==null) {
296                         compareHistory = new HashSet<IdentityPair<Object, Object>>(); 
297                 }       
298                 IdentityPair<Object, Object> p = new IdentityPair<Object, Object>(o1, o2);
299                 if (compareHistory.contains(p)) return 0;               
300                         compareHistory.add(p);
301                 }
302                 
303                 int len = rt.getComponentCount();
304                 for (int i=0; i<len; i++) {
305                         Binding c1 = r1.getComponentBindings()[i];
306                         Binding c2 = r2.getComponentBindings()[i];
307                         Object v1 = r1.getComponent(o1, i);
308                         Object v2 = r2.getComponent(o2, i);
309                         Datatype ct = c1.type();
310                         int dif = deepCompare(ct, c1, v1, c2, v2, compareHistory);
311                         if (dif!=0) return dif;
312                 }
313                 return 0;
314         }
315         
316         throw new IllegalArgumentException("Unsupported data type "+t);         
317     }
318     
319     public static Comparator<Object> createComparator(final Binding b1, final Binding b2)
320     {
321         if (b1==b2) return b1;
322         return new Comparator<Object>() {
323                         @Override
324                         public int compare(Object o1, Object o2)                        
325                         {                               
326                                 try {
327                                         return DataValueUtil.compare(b1, o1, b2, o2);
328                                 } catch (BindingException e) {
329                                         throw new RuntimeBindingException(e);
330                                 }
331                         }
332                 };
333     }
334         
335 }
336