]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.databoard/src/org/simantics/databoard/binding/RecordBinding.java
Migrated source code from Simantics SVN
[simantics/platform.git] / bundles / org.simantics.databoard / src / org / simantics / databoard / binding / RecordBinding.java
1 /*******************************************************************************\r
2  *  Copyright (c) 2010 Association for Decentralized Information Management in\r
3  *  Industry THTH ry.\r
4  *  All rights reserved. This program and the accompanying materials\r
5  *  are made available under the terms of the Eclipse Public License v1.0\r
6  *  which accompanies this distribution, and is available at\r
7  *  http://www.eclipse.org/legal/epl-v10.html\r
8  *\r
9  *  Contributors:\r
10  *      VTT Technical Research Centre of Finland - initial API and implementation\r
11  *******************************************************************************/\r
12 package org.simantics.databoard.binding;
13
14 import java.util.HashSet;\r
15 import java.util.IdentityHashMap;\r
16 import java.util.Set;\r
17 \r
18 import org.simantics.databoard.Bindings;\r
19 import org.simantics.databoard.accessor.reference.ChildReference;\r
20 import org.simantics.databoard.accessor.reference.IndexReference;\r
21 import org.simantics.databoard.accessor.reference.LabelReference;\r
22 import org.simantics.databoard.accessor.reference.NameReference;\r
23 import org.simantics.databoard.adapter.AdaptException;\r
24 import org.simantics.databoard.binding.error.BindingException;\r
25 import org.simantics.databoard.binding.error.RuntimeBindingException;\r
26 import org.simantics.databoard.binding.impl.BindingPrintContext;\r
27 import org.simantics.databoard.type.Component;\r
28 import org.simantics.databoard.type.RecordType;\r
29 import org.simantics.databoard.util.IdentityHashSet;\r
30 import org.simantics.databoard.util.IdentityPair;\r
31
32
33 /**
34  * This is a binding of a Record Type and a Java Object.
35  * 
36  * @see RecordType
37  * @author Hannu Niemisto
38  * @author Toni Kalajainen <toni.kalajainen@vtt.fi>
39  */
40 public abstract class RecordBinding extends Binding {
41     
42     public Binding[] componentBindings;
43     \r
44     /**\r
45      * Get binding by field name\r
46      * @param fieldName\r
47      * @return binding or <code>null</code>\r
48      */\r
49     public Binding getComponentBinding(String fieldName) {\r
50         int fieldIndex = type().getComponentIndex2(fieldName);\r
51         if (fieldIndex<0) return null;\r
52         return getComponentBinding(fieldIndex);\r
53     }\r
54     \r
55     public Object getComponentObject(Object obj, String fieldName) throws BindingException {\r
56         int fieldIndex = type().getComponentIndex2(fieldName);\r
57         if (fieldIndex<0) return null;\r
58         return getComponent(obj, fieldIndex);\r
59     }\r
60         \r
61     public int getComponentIndex(String fieldName) {\r
62         return type().getComponentIndex2(fieldName);\r
63     }\r
64     
65     public Binding getComponentBinding(int fieldIndex) {
66         return componentBindings[fieldIndex];
67     }
68     
69         public Binding[] getComponentBindings() {
70             return componentBindings;
71         }
72         
73         protected void setComponentBindings(Binding[] componentBindings) {
74                 this.componentBindings = componentBindings;
75         }
76         
77         @Override
78         public RecordType type() {
79                 return (RecordType) type;
80         }
81         
82         public int getComponentCount() {
83                 return type().getComponentCount();
84         }
85         
86         public abstract Object getComponent(Object obj, int index)
87         throws BindingException;
88         
89         /**
90          * Create a record using values.
91          * 
92          * Note! values may be consumed (used in the result)
93          * 
94          * @param values
95          * @return new record
96          * @throws BindingException
97          */
98         public abstract Object create(Object ... values)
99         throws BindingException;
100         
101         /**
102          * Creates partial and most likely invalid instance.
103          * This is used in two-phase construction of recursive instances.
104          *  
105          * @return instance.
106          */
107         public abstract Object createPartial() throws BindingException;
108         
109         public abstract void setComponents(Object obj, Object ... value) throws BindingException;
110         public abstract void setComponent(Object obj, int index, Object value) throws BindingException;
111 \r
112         public void setComponent(Object obj, String fieldName, Object value) throws BindingException\r
113         {\r
114                 int fieldIndex = type().getComponentIndex2(fieldName);\r
115                 if ( fieldIndex<0 ) throw new BindingException("Field "+fieldName+" does not exist.");\r
116                 setComponent(obj, fieldIndex, value);\r
117         }\r
118         \r
119         @Override\r
120         public void readFrom(Binding srcBinding, Object src, Object dst)\r
121                         throws BindingException {\r
122                 RecordBinding sb = (RecordBinding) srcBinding;\r
123                 int len = getComponentCount();\r
124                 if (sb.getComponentCount()!=len) throw new BindingException("field count mismatch");\r
125                 try {\r
126                         for (int i=0; i<len; i++) {\r
127                                 Binding dcb = getComponentBinding(i);\r
128                                 Binding scb = sb.getComponentBinding(i);\r
129                                 Object sc = sb.getComponent(src, i);\r
130                                 if (dcb.isImmutable()) {\r
131                                         Object cv = Bindings.clone(sc, scb, dcb);\r
132                                         setComponent(dst, i, cv);\r
133                                 } else {\r
134                                         Object v = getComponent(dst, i);\r
135                                         v = dcb.readFromTry(scb, sc, v);\r
136                                         setComponent(dst, i, v);\r
137                                 }\r
138                         }               \r
139                 } catch (AdaptException e) {\r
140                         throw new BindingException(e);\r
141                 }\r
142         }\r
143         
144     @Override
145     public void accept(Visitor1 v, Object obj) {
146         v.visit(this, obj);        
147     }
148     
149     @Override
150     public <T> T accept(Visitor<T> v) {
151         return v.visit(this);
152     }
153     
154     /**
155      * Assert obj is valid Record Type
156      * 
157      * This asserts all fields are valid. 
158      * 
159      * @throws BindingException
160      */
161     @Override
162     public void assertInstaceIsValid(Object obj, Set<Object> validInstances) throws BindingException {
163         if (!isInstance(obj)) 
164                 throw new BindingException("The object ("+obj+") is not correct instance.");
165         RecordType type = type();       
166         int length = type.getComponentCount();
167         
168         if (type.isReferable()) {
169                 if (validInstances==null) {
170                         validInstances = new IdentityHashSet<Object>();
171                 }
172                 if (validInstances.contains(obj)) return;
173                 validInstances.add(obj);
174         }
175         
176         for (int i=0; i<length; i++)
177         {
178                 Binding componentBinding = getComponentBindings()[i]; 
179                 Object componentValue = getComponent(obj, i);
180                 componentBinding.assertInstaceIsValid(componentValue, validInstances);
181         }
182     }
183     
184     @Override
185     public int deepHashValue(Object value, IdentityHashMap<Object, Object> hashedObjects) throws BindingException {
186         if (type().isReferable()) {
187                 if (hashedObjects==null) {
188                         hashedObjects = new IdentityHashMap<Object, Object>(1);
189                 }
190                 if (hashedObjects.containsKey(value)) {
191                         return 0;       
192                 } else {
193                         hashedObjects.put(value, null);
194                 }
195         }
196         
197         int result = 3;
198         int len = componentBindings.length;
199         for (int i=0; i<len; i++) {
200                 Object element = getComponent(value, i);
201                 result += 31*result + componentBindings[i].deepHashValue(element, hashedObjects);
202         }
203         return result;
204     }
205     
206     @Override
207     public int deepCompare(Object o1, Object o2,
208                 Set<IdentityPair<Object, Object>> compareHistory)
209                 throws BindingException {
210                 RecordType rt    = (RecordType) type(); 
211                 
212                 // Compare History is used to prevent infinite loops
213                 if (rt.isReferable()) {
214                 if (compareHistory==null) {
215                         compareHistory = new HashSet<IdentityPair<Object, Object>>(); 
216                 }       
217                 IdentityPair<Object, Object> p = new IdentityPair<Object, Object>(o1, o2);
218                 if (compareHistory.contains(p)) return 0;               
219                 compareHistory.add(p);
220                 }
221                 
222                 int len = rt.getComponentCount();
223                 for (int i=0; i<len; i++) {
224                         Binding c = getComponentBindings()[i];
225                         Object v1 = getComponent(o1, i);
226                         Object v2 = getComponent(o2, i);
227                         int dif = c.deepCompare(v1, v2, compareHistory);
228                         if (dif!=0)\r
229                                 return dif;
230                 }
231                 return 0;
232     }
233         
234         public Object createUnchecked(Object ... values) throws RuntimeBindingException
235         {
236                 try {
237                         return create(values);
238                 } catch (BindingException e) {
239                         throw new RuntimeBindingException(e);
240                 }
241         }
242         
243         public void setComponentsUnchecked(Object obj, Object ... value) throws RuntimeBindingException {
244                 try {
245                         setComponents(obj, value);
246                 } catch (BindingException e) {
247                         throw new RuntimeBindingException(e);
248                 }
249         }
250         
251         public void setComponentUnchecked(Object obj, int index, Object value) throws RuntimeBindingException {
252                 try {
253                         setComponent(obj, index, value);
254                 } catch (BindingException e) {
255                         throw new RuntimeBindingException(e);
256                 }
257         }
258 \r
259         protected void toStringAux(Object value, BindingPrintContext ctx) throws BindingException {\r
260                 ctx.b.append('{');\r
261                 Component[] components = type().getComponents();\r
262                 boolean first = true;\r
263                 for(int i=0;i<components.length;++i) {\r
264                         if(first)\r
265                                 first = false;\r
266                         else {\r
267                                 ctx.b.append(", ");\r
268                                 if (!ctx.singleLine) ctx.b.append('\n');\r
269                         }\r
270                         Binding binding = getComponentBinding(i);\r
271                         Object cval = getComponent(value, i);\r
272                         if(binding instanceof OptionalBinding && \r
273                                         !((OptionalBinding)binding).hasValue(cval))\r
274                                 continue;\r
275                         Component component = components[i];                    \r
276                         ctx.b.append(component.name);\r
277                         ctx.b.append(" = ");\r
278                         binding.toString(cval, ctx);\r
279                 }\r
280                 ctx.b.append('}');\r
281         }\r
282         \r
283     @Override\r
284         protected void toString(Object value, BindingPrintContext ctx) throws BindingException {\r
285         if(type().isReferable()) {\r
286                 if(ctx.refs.contains(value)) {\r
287                         int val = ctx.refs.get(value);\r
288                         if(val < 0) {\r
289                                 val = ctx.nameCount.value++;\r
290                                 ctx.refs.put(value, val);\r
291                         }\r
292                         ctx.b.append((char)('a' + val));\r
293                 }\r
294                 else {\r
295                         ctx.refs.put(value, -1);\r
296                         toStringAux(value, ctx);\r
297                         int val = ctx.refs.remove(value);\r
298                         if(val >= 0) {\r
299                                 ctx.b.append("/");\r
300                                 ctx.b.append((char)('a' + val));\r
301                         }\r
302                 }\r
303         }\r
304         else {\r
305                 toStringAux(value, ctx);\r
306         }\r
307         }
308 \r
309         @Override\r
310         public Binding getComponentBinding(ChildReference path) throws IllegalArgumentException {\r
311                 if (path==null) return this;\r
312                 if (path instanceof IndexReference) {\r
313                         IndexReference ir = (IndexReference) path;\r
314                         return componentBindings[ir.index].getComponentBinding(path.childReference);\r
315                 }\r
316                 if (path instanceof NameReference) {\r
317                         NameReference nr = (NameReference) path;\r
318                         return getComponentBinding( nr.name ).getComponentBinding(path.childReference);\r
319                 }\r
320                 if (path instanceof LabelReference) {\r
321                         LabelReference lr = (LabelReference) path;                      \r
322                         try {\r
323                                 Integer i = new Integer(lr.label);\r
324                                 return getComponentBinding( i ).getComponentBinding(path.childReference);\r
325                         } catch (NumberFormatException nfe) {\r
326                                 return getComponentBinding( lr.label ).getComponentBinding(path.childReference);\r
327                         }\r
328                 }\r
329                 throw new IllegalArgumentException();\r
330         }\r
331         \r
332         @Override\r
333         public boolean isImmutable() {\r
334                 return getComponentCount() == 0;\r
335         }\r
336         \r
337         public void setBoolean(Object r, int index, boolean z) throws BindingException\r
338         {\r
339                 setComponent(r, index, ((BooleanBinding)componentBindings[index]).create(z));\r
340         }\r
341         \r
342         public boolean getBoolean(Object r, int index) throws BindingException\r
343         {\r
344                 return ((BooleanBinding)componentBindings[index]).getValue_( getComponent(r, index) );\r
345         }\r
346         \r
347         public void setByte(Object r, int index, byte x) throws BindingException\r
348         {\r
349                 setComponent(r, index, ((ByteBinding)componentBindings[index]).create(x));\r
350         }\r
351         \r
352         public byte getByte(Object r, int index) throws BindingException\r
353         {\r
354                 return ((ByteBinding)componentBindings[index]).getValue_( getComponent(r, index) );\r
355         }\r
356 \r
357         public void setInt(Object r, int index, int x) throws BindingException\r
358         {\r
359                 setComponent(r, index, ((IntegerBinding)componentBindings[index]).create(x));\r
360         }\r
361         \r
362         public int getInt(Object r, int index) throws BindingException\r
363         {\r
364                 return ((IntegerBinding)componentBindings[index]).getValue_( getComponent(r, index) );\r
365         }\r
366         \r
367         public void setLong(Object r, int index, long x) throws BindingException\r
368         {\r
369                 setComponent(r, index, ((LongBinding)componentBindings[index]).create(x));\r
370         }\r
371         \r
372         public long getLong(Object r, int index) throws BindingException\r
373         {\r
374                 return ((LongBinding)componentBindings[index]).getValue_( getComponent(r, index) );\r
375         }\r
376         \r
377         public void setFloat(Object r, int index, float x) throws BindingException\r
378         {\r
379                 setComponent(r, index, ((FloatBinding)componentBindings[index]).create(x));\r
380         }\r
381         \r
382         public float getFloat(Object r, int index) throws BindingException\r
383         {\r
384                 return ((FloatBinding)componentBindings[index]).getValue_( getComponent(r, index) );\r
385         }\r
386         \r
387         public void setDouble(Object r, int index, double x) throws BindingException\r
388         {\r
389                 setComponent(r, index, ((DoubleBinding)componentBindings[index]).create(x));\r
390         }\r
391         \r
392         public double getDouble(Object r, int index) throws BindingException\r
393         {\r
394                 return ((DoubleBinding)componentBindings[index]).getValue_( getComponent(r, index) );\r
395         }\r
396 \r
397         @Override\r
398         protected boolean deepEquals(Object obj,\r
399                         Set<IdentityPair<Binding, Binding>> compareHistory) {\r
400                 RecordBinding o = (RecordBinding)obj;\r
401                 if (!super.deepEquals( obj, compareHistory ))\r
402                     return false;\r
403                 \r
404                 if (componentBindings.length != o.componentBindings.length) return false;\r
405                 \r
406                 for (int i = 0; i < componentBindings.length; i++)\r
407                         if (!componentBindings[i].equals(o.componentBindings[i], compareHistory))\r
408                                 return false;\r
409                 \r
410                 return true;\r
411         }\r
412 \r
413         @Override\r
414         public int deepHashCode(IdentityHashMap<Object, Object> hashedObjects) {\r
415                 int code = super.deepHashCode(hashedObjects);\r
416                 for (int i = 0; i < componentBindings.length; i++)\r
417                         code = 17 * code + componentBindings[i].hashCode(hashedObjects);\r
418                 return code;\r
419         }\r
420 }