]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.databoard/src/org/simantics/databoard/binding/RecordBinding.java
Use type reflection tools from databoard in objmap2.
[simantics/platform.git] / bundles / org.simantics.databoard / src / org / simantics / databoard / binding / RecordBinding.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.binding;
13
14 import java.util.HashSet;
15 import java.util.IdentityHashMap;
16 import java.util.Set;
17
18 import org.simantics.databoard.Bindings;
19 import org.simantics.databoard.accessor.reference.ChildReference;
20 import org.simantics.databoard.accessor.reference.IndexReference;
21 import org.simantics.databoard.accessor.reference.LabelReference;
22 import org.simantics.databoard.accessor.reference.NameReference;
23 import org.simantics.databoard.adapter.AdaptException;
24 import org.simantics.databoard.binding.error.BindingException;
25 import org.simantics.databoard.binding.error.RuntimeBindingException;
26 import org.simantics.databoard.binding.impl.BindingPrintContext;
27 import org.simantics.databoard.type.Component;
28 import org.simantics.databoard.type.RecordType;
29 import org.simantics.databoard.util.IdentityHashSet;
30 import org.simantics.databoard.util.IdentityPair;
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     
44     /**
45      * Get binding by field name
46      * @param fieldName
47      * @return binding or <code>null</code>
48      */
49     public Binding getComponentBinding(String fieldName) {
50         int fieldIndex = type().getComponentIndex2(fieldName);
51         if (fieldIndex<0) return null;
52         return getComponentBinding(fieldIndex);
53     }
54     
55     public Object getComponentObject(Object obj, String fieldName) throws BindingException {
56         int fieldIndex = type().getComponentIndex2(fieldName);
57         if (fieldIndex<0) return null;
58         return getComponent(obj, fieldIndex);
59     }
60         
61     public int getComponentIndex(String fieldName) {
62         return type().getComponentIndex2(fieldName);
63     }
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
112         public void setComponent(Object obj, String fieldName, Object value) throws BindingException
113         {
114                 int fieldIndex = type().getComponentIndex2(fieldName);
115                 if ( fieldIndex<0 ) throw new BindingException("Field "+fieldName+" does not exist.");
116                 setComponent(obj, fieldIndex, value);
117         }
118         
119         @Override
120         public void readFrom(Binding srcBinding, Object src, Object dst)
121                         throws BindingException {
122                 RecordBinding sb = (RecordBinding) srcBinding;
123                 int len = getComponentCount();
124                 if (sb.getComponentCount()!=len) throw new BindingException("field count mismatch");
125                 try {
126                         for (int i=0; i<len; i++) {
127                                 Binding dcb = getComponentBinding(i);
128                                 Binding scb = sb.getComponentBinding(i);
129                                 Object sc = sb.getComponent(src, i);
130                                 if (dcb.isImmutable()) {
131                                         Object cv = Bindings.clone(sc, scb, dcb);
132                                         setComponent(dst, i, cv);
133                                 } else {
134                                         Object v = getComponent(dst, i);
135                                         v = dcb.readFromTry(scb, sc, v);
136                                         setComponent(dst, i, v);
137                                 }
138                         }               
139                 } catch (AdaptException e) {
140                         throw new BindingException(e);
141                 }
142         }
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)
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
259         protected void toStringAux(Object value, BindingPrintContext ctx) throws BindingException {
260                 ctx.b.append('{');
261                 Component[] components = type().getComponents();
262                 boolean first = true;
263                 for(int i=0;i<components.length;++i) {
264                         if(first)
265                                 first = false;
266                         else {
267                                 ctx.b.append(", ");
268                                 if (!ctx.singleLine) ctx.b.append('\n');
269                         }
270                         Binding binding = getComponentBinding(i);
271                         Object cval = getComponent(value, i);
272                         if(binding instanceof OptionalBinding && 
273                                         !((OptionalBinding)binding).hasValue(cval))
274                                 continue;
275                         Component component = components[i];                    
276                         ctx.b.append(component.name);
277                         ctx.b.append(" = ");
278                         binding.toString(cval, ctx);
279                 }
280                 ctx.b.append('}');
281         }
282         
283     @Override
284         protected void toString(Object value, BindingPrintContext ctx) throws BindingException {
285         if(type().isReferable()) {
286                 if(ctx.refs.contains(value)) {
287                         int val = ctx.refs.get(value);
288                         if(val < 0) {
289                                 val = ctx.nameCount.value++;
290                                 ctx.refs.put(value, val);
291                         }
292                         ctx.b.append((char)('a' + val));
293                 }
294                 else {
295                         ctx.refs.put(value, -1);
296                         toStringAux(value, ctx);
297                         int val = ctx.refs.remove(value);
298                         if(val >= 0) {
299                                 ctx.b.append("/");
300                                 ctx.b.append((char)('a' + val));
301                         }
302                 }
303         }
304         else {
305                 toStringAux(value, ctx);
306         }
307         }
308
309         @Override
310         public Binding getComponentBinding(ChildReference path) throws IllegalArgumentException {
311                 if (path==null) return this;
312                 if (path instanceof IndexReference) {
313                         IndexReference ir = (IndexReference) path;
314                         return componentBindings[ir.index].getComponentBinding(path.childReference);
315                 }
316                 if (path instanceof NameReference) {
317                         NameReference nr = (NameReference) path;
318                         return getComponentBinding( nr.name ).getComponentBinding(path.childReference);
319                 }
320                 if (path instanceof LabelReference) {
321                         LabelReference lr = (LabelReference) path;                      
322                         try {
323                                 Integer i = new Integer(lr.label);
324                                 return getComponentBinding( i ).getComponentBinding(path.childReference);
325                         } catch (NumberFormatException nfe) {
326                                 return getComponentBinding( lr.label ).getComponentBinding(path.childReference);
327                         }
328                 }
329                 throw new IllegalArgumentException();
330         }
331         
332         @Override
333         public boolean isImmutable() {
334                 return getComponentCount() == 0;
335         }
336         
337         public void setBoolean(Object r, int index, boolean z) throws BindingException
338         {
339                 setComponent(r, index, ((BooleanBinding)componentBindings[index]).create(z));
340         }
341         
342         public boolean getBoolean(Object r, int index) throws BindingException
343         {
344                 return ((BooleanBinding)componentBindings[index]).getValue_( getComponent(r, index) );
345         }
346         
347         public void setByte(Object r, int index, byte x) throws BindingException
348         {
349                 setComponent(r, index, ((ByteBinding)componentBindings[index]).create(x));
350         }
351         
352         public byte getByte(Object r, int index) throws BindingException
353         {
354                 return ((ByteBinding)componentBindings[index]).getValue_( getComponent(r, index) );
355         }
356
357         public void setInt(Object r, int index, int x) throws BindingException
358         {
359                 setComponent(r, index, ((IntegerBinding)componentBindings[index]).create(x));
360         }
361         
362         public int getInt(Object r, int index) throws BindingException
363         {
364                 return ((IntegerBinding)componentBindings[index]).getValue_( getComponent(r, index) );
365         }
366         
367         public void setLong(Object r, int index, long x) throws BindingException
368         {
369                 setComponent(r, index, ((LongBinding)componentBindings[index]).create(x));
370         }
371         
372         public long getLong(Object r, int index) throws BindingException
373         {
374                 return ((LongBinding)componentBindings[index]).getValue_( getComponent(r, index) );
375         }
376         
377         public void setFloat(Object r, int index, float x) throws BindingException
378         {
379                 setComponent(r, index, ((FloatBinding)componentBindings[index]).create(x));
380         }
381         
382         public float getFloat(Object r, int index) throws BindingException
383         {
384                 return ((FloatBinding)componentBindings[index]).getValue_( getComponent(r, index) );
385         }
386         
387         public void setDouble(Object r, int index, double x) throws BindingException
388         {
389                 setComponent(r, index, ((DoubleBinding)componentBindings[index]).create(x));
390         }
391         
392         public double getDouble(Object r, int index) throws BindingException
393         {
394                 return ((DoubleBinding)componentBindings[index]).getValue_( getComponent(r, index) );
395         }
396
397         @Override
398         protected boolean deepEquals(Object obj,
399                         Set<IdentityPair<Binding, Binding>> compareHistory) {
400                 RecordBinding o = (RecordBinding)obj;
401                 if (!super.deepEquals( obj, compareHistory ))
402                     return false;
403                 
404                 if (componentBindings.length != o.componentBindings.length) return false;
405                 
406                 for (int i = 0; i < componentBindings.length; i++)
407                         if (!componentBindings[i].equals(o.componentBindings[i], compareHistory))
408                                 return false;
409                 
410                 return true;
411         }
412
413         @Override
414         public int deepHashCode(IdentityHashMap<Object, Object> hashedObjects) {
415                 int code = super.deepHashCode(hashedObjects);
416                 for (int i = 0; i < componentBindings.length; i++)
417                         code = 17 * code + componentBindings[i].hashCode(hashedObjects);
418                 return code;
419         }
420 }