]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.databoard/src/org/simantics/databoard/binding/MapBinding.java
Use type reflection tools from databoard in objmap2.
[simantics/platform.git] / bundles / org.simantics.databoard / src / org / simantics / databoard / binding / MapBinding.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.IdentityHashMap;
15 import java.util.Iterator;
16 import java.util.List;
17 import java.util.Map;
18 import java.util.Map.Entry;
19 import java.util.Set;
20 import java.util.TreeMap;
21 import java.util.TreeSet;
22
23 import org.simantics.databoard.Bindings;
24 import org.simantics.databoard.accessor.reference.ChildReference;
25 import org.simantics.databoard.accessor.reference.IndexReference;
26 import org.simantics.databoard.accessor.reference.LabelReference;
27 import org.simantics.databoard.accessor.reference.NameReference;
28 import org.simantics.databoard.adapter.AdaptException;
29 import org.simantics.databoard.adapter.Adapter;
30 import org.simantics.databoard.adapter.AdapterConstructionException;
31 import org.simantics.databoard.binding.error.BindingException;
32 import org.simantics.databoard.binding.error.RuntimeBindingException;
33 import org.simantics.databoard.binding.impl.BindingPrintContext;
34 import org.simantics.databoard.type.MapType;
35 import org.simantics.databoard.util.IdentityPair;
36
37 /**
38  * This is a binding of Map Type and a Java Object
39  *
40  * @see MapType
41  * @author Toni Kalajainen <toni.kalajainen@vtt.fi>
42  */
43 public abstract class MapBinding extends Binding {
44                 
45         protected Binding keyBinding;
46         protected Binding valueBinding; 
47         
48         /**
49          * Create new map binding. Creates new data type. 
50          * 
51          * @param keyBinding
52          * @param valueBinding
53          */
54         public MapBinding(Binding keyBinding, Binding valueBinding) 
55         {
56                 super();
57 //              if (keyBinding==null || valueBinding==null) throw new IllegalArgumentException("null arg");
58         this.type = new MapType(keyBinding.type(), valueBinding.type());
59         this.keyBinding = keyBinding;
60         this.valueBinding = valueBinding;
61         }
62
63         /**
64          * Create new map binding for a type.
65          * 
66          * @param mapType
67          * @param keyBinding
68          * @param valueBinding
69          */
70         public MapBinding(MapType mapType, Binding keyBinding, Binding valueBinding) 
71         {
72                 super();
73 //              if (keyBinding==null || valueBinding==null) throw new IllegalArgumentException("null arg");
74                 this.keyBinding = keyBinding;
75                 this.valueBinding = valueBinding;
76         this.type = mapType;
77         }
78         
79         @Override
80         public MapType type() {
81                 return (MapType) type;
82         }
83         
84         public Binding getKeyBinding() {
85                 return keyBinding;
86         }
87         
88         public Binding getValueBinding() {
89                 return valueBinding;
90         }
91         
92         public void setKeyBinding(Binding keyBinding) {
93                 this.keyBinding = keyBinding;
94                 if (!type().keyType.equals(keyBinding.type()))
95                         throw new IllegalArgumentException("Binding for "+type().keyType+" expected, got "+keyBinding.type());
96         }
97
98         public void setValueBinding(Binding valueBinding) {
99                 this.valueBinding = valueBinding;
100                 if (!type().valueType.equals(valueBinding.type()))
101                         throw new IllegalArgumentException("Binding for "+type().valueType+" expected, got "+valueBinding.type());              
102         }
103
104         public abstract Object create() throws BindingException;
105         
106         /**
107          * Create a new map with initial values.
108          * The values of the initialMap are accessible with the respective key and value binding.
109          *  
110          * @param initialMap 
111          * @return map object
112          * @throws BindingException
113          */
114         public abstract Object create(Map<?, ?> initialMap) throws BindingException;
115
116         /**
117          * Create a new map with initial values.
118          * The values of the initialMap are accessible with the respective key and value binding.
119          *  
120          * @param keys
121          * @param values
122          * @return map object
123          * @throws BindingException
124          */
125         public abstract Object create(List<Object> keys, List<Object> values) throws BindingException;
126         
127         /**
128          * Create a new map with initial values.
129          * The values of the initialMap are accessible with the key and value binding.
130          * 
131          * @param keys
132          * @param values
133          * @return map object
134          * @throws BindingException
135          */
136         public abstract Object create(Object[] keys, Object[] values) throws BindingException;  
137         
138         public abstract int size(Object map) throws BindingException;
139         
140         /**
141          * Return the value to which the specified key is mapped. 
142          * If the key is not mapped, BindingException is thrown.
143          * The key and the value objects are accessible with the respective bindings. 
144          * 
145          * @param map
146          * @param key
147          * @return value
148          * @throws BindingException
149          */
150         public abstract Object get(Object map, Object key) throws BindingException;
151         public abstract boolean containsKey(Object map, Object key) throws BindingException;
152         public abstract boolean containsValue(Object map, Object value) throws BindingException;
153         public abstract <K, V> void put(Object map, K key, V value) throws BindingException;
154         public abstract Object remove(Object map, Object key) throws BindingException;
155         public abstract <K, V> void putAll(Object mapTo, Map<K, V> mapFrom) throws BindingException;
156         public abstract <K, V> void getAll(Object mapFrom, Map<K, V> to) throws BindingException;
157         
158         /**
159          * Get keys and values, in order
160          * 
161          * @param mapFrom
162          * @param keys
163          * @param values
164          * @throws BindingException
165          */
166         public abstract void getAll(Object mapFrom, Object[] keys, Object[] values) throws BindingException;
167         
168         /**
169          * Get keys in order
170          * 
171          * @param map
172          * @return keys
173          * @throws BindingException
174          */
175         public abstract Object[] getKeys(Object map) throws BindingException;
176         
177         public abstract void getKeys(Object map, Set<Object> keys) throws BindingException;
178         
179         /**
180          * Count the number of entries between two keyes
181          * 
182          * @param src
183          * @param from
184      * @param fromInclusive
185          * @param end 
186      * @param endInclusive
187          * @throws BindingException
188          */
189         public abstract int count(Object src, Object from, boolean fromInclusive, Object end, boolean endInclusive) throws BindingException;
190         
191         /**
192          * Read a range of entries
193          * 
194          * @param src
195          * @param from
196      * @param fromInclusive
197          * @param end 
198      * @param endInclusive
199          * @param dstKeyArrayBinding
200          * @param dstKeyArray
201      * @param dstValueArrayBinding
202          * @param dstValueArray
203          * @param resultLimit maximum number of entries to read, -1 for no limit
204          * @return the number of entries read 
205          * @throws BindingException
206          */
207         public abstract int getEntries(
208                         Object src, 
209                         Object from, boolean fromInclusive, Object end, boolean endInclusive, 
210                         ArrayBinding dstKeyArrayBinding, Object dstKeyArray, 
211                         ArrayBinding dstValueArrayBinding, Object dstValueArray, 
212                         int resultLimit) throws BindingException;
213                 
214         /**
215          * Get values in order
216          * 
217          * @param map
218          * @return values
219          * @throws BindingException
220          */
221         public abstract Object[] getValues(Object map) throws BindingException;
222         public abstract void clear(Object map) throws BindingException;
223
224         // Views considered - more knowledge required
225
226     /**
227      * Assert the instance is valid and follows restrictions set in data type.
228      * Assertions:   
229      *   1. correct instance
230      *   2. assertion of each key and value
231      * 
232      * @param map the instance
233      * @param validInstances a collection of validated instances or <code>null</code>
234      * @throws BindingException on invalid instance
235      */
236         @Override
237         public void assertInstaceIsValid(Object map, Set<Object> validInstances) throws BindingException {
238                 if (!isInstance(map)) throw new BindingException("Not a map");
239                 for (Object key : getKeys(map)) {
240                         keyBinding.assertInstaceIsValid(key, validInstances);
241                         Object value = get(map, key);
242                         valueBinding.assertInstaceIsValid(value, validInstances);
243                 }
244         }
245
246         @Override
247         public void accept(Visitor1 v, Object obj) {
248             v.visit(this, obj);        
249         }
250
251         @Override
252         public <T> T accept(Visitor<T> v) {
253             return v.visit(this);
254         }
255         
256         @Override
257         public void readFrom(Binding srcBinding, Object src, Object dst)
258                         throws BindingException {
259                 try {
260                         MapBinding sb = (MapBinding) srcBinding;
261                         Binding dkb = getKeyBinding();
262                         Binding dvb = getValueBinding();
263                         Set<Object> oldKeys = new TreeSet<Object>(dkb);
264                         getKeys(dst, oldKeys);
265                         Binding skb = sb.getKeyBinding();
266                         Binding svb = sb.getValueBinding();
267                         boolean cbImmutable = dvb.isImmutable();
268                         Adapter ka = Bindings.adapterFactory.getAdapter(skb, dkb, false, false);
269                         Adapter va = cbImmutable ? Bindings.adapterFactory.getAdapter(svb, dvb, false, true) : null;
270                 
271                         // Copy keys from other map
272                         for (Object sk : sb.getKeys(src)) {
273                                 Object dk = ka.adapt(sk);
274                                 Object sv = sb.get(src, sk);
275                                 if (cbImmutable) {
276                                         Object dv = va.adapt(sv);
277                                         put(dst, dk, dv);
278                                 } else
279                                 if (containsKey(dst, dk)) {
280                                         Object dv = get(dst, dk);
281                                         dv = dvb.readFromTry(svb, sv, dv);
282                                         put(dst, dk, dv);
283                                 } else {
284                                         Object dv = dvb.createDefault();
285                                         dv = dvb.readFromTry(svb, sv, dv);
286                                         put(dst, dk, dv);
287                                 }
288                                 oldKeys.remove(dk);
289                         }
290                         
291                         // Remove unused keys
292                         for (Object k : oldKeys) remove(dst, k);
293
294                 } catch (AdapterConstructionException e) {
295                         throw new BindingException(e);
296                 } catch (AdaptException e) {
297                         throw new BindingException(e);
298                 }
299         }
300         
301         
302         @Override
303     public int deepHashValue(Object map, IdentityHashMap<Object, Object> hashedObjects) throws BindingException {
304                 int result = 0;
305                 Object keys[] = getKeys(map);
306                 Object values[] = getValues(map);
307                 int len = size(map);
308                 for (int i=0; i<len; i++) {
309                         Object key = keys[i];
310                         Object value = values[i];
311                         
312                         int keyHash = keyBinding.deepHashValue(key, hashedObjects);
313                         int valueHash = valueBinding.deepHashValue(value, hashedObjects);
314                         
315                         result += (keyHash ^ valueHash);
316                 }
317                 return result;
318         }
319         
320     @Override
321     public int deepCompare(Object o1, Object o2,
322                 Set<IdentityPair<Object, Object>> compareHistory)
323                 throws BindingException {
324                 // Compare sizes
325                 int l1 = size(o1);
326                 int l2 = size(o2);
327                 int dif = l1 - l2;
328                 if (dif!=0) 
329                         return dif;
330                 // Compare elements
331                 Binding k = getKeyBinding();
332                 Binding v = getValueBinding();
333                 TreeMap<Object, Object> e1 = new TreeMap<Object, Object>( k );
334                 TreeMap<Object, Object> e2 = new TreeMap<Object, Object>( k );
335                 getAll(o1, e1);
336                 getAll(o2, e2);
337                 
338                 Iterator<Entry<Object, Object>> i1 = e1.entrySet().iterator(); 
339                 Iterator<Entry<Object, Object>> i2 = e2.entrySet().iterator(); 
340                 while (i1.hasNext()) {
341                         Entry<Object, Object> h1 = i1.next();
342                         Entry<Object, Object> h2 = i2.next();
343                         dif = k.deepCompare(h1.getKey(), h2.getKey(), compareHistory);
344                         if (dif!=0) 
345                                 return dif;
346                         dif = v.deepCompare(h1.getValue(), h2.getValue(), compareHistory);
347                         if (dif!=0) 
348                                 return dif;
349                         i1.remove();
350                         i2.remove();
351                 }
352                 return 0;
353         
354     }
355
356         public Object createUnchecked(Object[] keys, Object[] values)
357                         throws RuntimeBindingException {
358                 try {
359                         return create(keys, values);
360                 } catch (BindingException e) {
361                         throw new RuntimeBindingException(e);
362                 }
363         }
364         
365         public Object createUnchecked(List<Object> keys, List<Object> values) throws RuntimeBindingException
366         {
367                 try {
368                         return create(keys, values);
369                 } catch (BindingException e) {
370                         throw new RuntimeBindingException(e);
371                 }
372         }
373
374         public Object createUnchecked(Map<Object, Object> initialMap)
375                         throws RuntimeBindingException {
376                 try {
377                         return create(initialMap);
378                 } catch (BindingException e) {
379                         throw new RuntimeBindingException(e);
380                 }
381         }
382         
383         public Object createUnchecked() throws RuntimeBindingException {
384                 try {
385                         return create();
386                 } catch (BindingException e) {
387                         throw new RuntimeBindingException(e);
388                 }
389         }
390
391         public abstract Object getFirstKey(Object map);
392         public abstract Object getLastKey(Object map);
393         public abstract Object getLowerKey(Object map, Object key);
394         public abstract Object getFloorKey(Object map, Object key);
395         public abstract Object getCeilingKey(Object map, Object key);
396         public abstract Object getHigherKey(Object map, Object key);
397
398         
399         @Override
400         protected void toString(Object value, BindingPrintContext ctx) throws BindingException {
401                 Binding keyBinding = getKeyBinding();
402                 Binding valueBinding = getValueBinding();
403                 ctx.b.append("{ ");
404                 boolean first = true;
405                 for(Object key : getKeys(value)) {
406                         if(first)
407                                 first = false;
408                         else {
409                                 ctx.b.append(", ");
410                                 if ( !ctx.singleLine ) ctx.b.append('\n');
411                         }
412                         keyBinding.toString(key, ctx);
413                         ctx.b.append(" = ");
414                         valueBinding.toString(get(value, key), ctx);
415                 }
416                 ctx.b.append(" }");
417         }
418
419         @Override
420         public Binding getComponentBinding(ChildReference path) {
421         if (path==null) return this;
422         if (path instanceof IndexReference) {
423                 IndexReference ir = (IndexReference) path;
424                 if (ir.index==0) return keyBinding.getComponentBinding(path.childReference);
425                 if (ir.index==1) return valueBinding.getComponentBinding(path.childReference);
426         }
427         if (path instanceof LabelReference) {
428                 LabelReference lr = (LabelReference) path;
429                 if (lr.label.equals("0") || lr.label.equals("key")) return keyBinding.getComponentBinding(path.childReference);
430                 if (lr.label.equals("1") || lr.label.equals("value")) return valueBinding.getComponentBinding(path.childReference);
431         }
432         if (path instanceof NameReference) {
433                 NameReference nr = (NameReference) path;
434                 if (nr.name.equals("key")) return keyBinding.getComponentBinding(path.childReference);
435                 if (nr.name.equals("value")) return valueBinding.getComponentBinding(path.childReference);
436         }
437         throw new IllegalArgumentException();
438         }       
439
440         
441     @Override
442     public int getComponentCount() {
443         return 2;
444     }
445     
446     @Override
447     public Binding getComponentBinding(int index) {
448         if (index==0) return keyBinding;
449         if (index==1) return valueBinding;
450         throw new IllegalArgumentException();
451     }   
452
453     @Override
454     protected boolean deepEquals(Object obj,
455                 Set<IdentityPair<Binding, Binding>> compareHistory) {
456         MapBinding o = (MapBinding)obj;
457         return super.deepEquals( obj, compareHistory ) && keyBinding.equals(o.keyBinding, compareHistory) && valueBinding.equals(o.valueBinding, compareHistory);
458     }
459     
460     @Override
461     public int deepHashCode(IdentityHashMap<Object, Object> hashedObjects) {
462         return super.deepHashCode(hashedObjects) + 13 * keyBinding.hashCode(hashedObjects) + 17 * valueBinding.hashCode(hashedObjects);
463     }
464 }