]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - bundles/org.simantics.databoard/src/org/simantics/databoard/binding/MapBinding.java
Migrated source code from Simantics SVN
[simantics/platform.git] / bundles / org.simantics.databoard / src / org / simantics / databoard / binding / MapBinding.java
diff --git a/bundles/org.simantics.databoard/src/org/simantics/databoard/binding/MapBinding.java b/bundles/org.simantics.databoard/src/org/simantics/databoard/binding/MapBinding.java
new file mode 100644 (file)
index 0000000..94f238a
--- /dev/null
@@ -0,0 +1,464 @@
+/*******************************************************************************\r
+ *  Copyright (c) 2010 Association for Decentralized Information Management in\r
+ *  Industry THTH ry.\r
+ *  All rights reserved. This program and the accompanying materials\r
+ *  are made available under the terms of the Eclipse Public License v1.0\r
+ *  which accompanies this distribution, and is available at\r
+ *  http://www.eclipse.org/legal/epl-v10.html\r
+ *\r
+ *  Contributors:\r
+ *      VTT Technical Research Centre of Finland - initial API and implementation\r
+ *******************************************************************************/\r
+package org.simantics.databoard.binding;
+
+import java.util.IdentityHashMap;\r
+import java.util.Iterator;\r
+import java.util.List;\r
+import java.util.Map;\r
+import java.util.Map.Entry;\r
+import java.util.Set;\r
+import java.util.TreeMap;\r
+import java.util.TreeSet;\r
+\r
+import org.simantics.databoard.Bindings;\r
+import org.simantics.databoard.accessor.reference.ChildReference;\r
+import org.simantics.databoard.accessor.reference.IndexReference;\r
+import org.simantics.databoard.accessor.reference.LabelReference;\r
+import org.simantics.databoard.accessor.reference.NameReference;\r
+import org.simantics.databoard.adapter.AdaptException;\r
+import org.simantics.databoard.adapter.Adapter;\r
+import org.simantics.databoard.adapter.AdapterConstructionException;\r
+import org.simantics.databoard.binding.error.BindingException;\r
+import org.simantics.databoard.binding.error.RuntimeBindingException;\r
+import org.simantics.databoard.binding.impl.BindingPrintContext;\r
+import org.simantics.databoard.type.MapType;\r
+import org.simantics.databoard.util.IdentityPair;\r
+
+/**
+ * This is a binding of Map Type and a Java Object
+ *
+ * @see MapType
+ * @author Toni Kalajainen <toni.kalajainen@vtt.fi>
+ */
+public abstract class MapBinding extends Binding {
+               
+       protected Binding keyBinding;
+       protected Binding valueBinding; 
+       
+       /**
+        * Create new map binding. Creates new data type. 
+        * 
+        * @param keyBinding
+        * @param valueBinding
+        */
+       public MapBinding(Binding keyBinding, Binding valueBinding) 
+       {
+               super();
+//             if (keyBinding==null || valueBinding==null) throw new IllegalArgumentException("null arg");
+        this.type = new MapType(keyBinding.type(), valueBinding.type());
+        this.keyBinding = keyBinding;
+        this.valueBinding = valueBinding;
+       }
+
+       /**
+        * Create new map binding for a type.
+        * 
+        * @param mapType
+        * @param keyBinding
+        * @param valueBinding
+        */
+       public MapBinding(MapType mapType, Binding keyBinding, Binding valueBinding) 
+       {
+               super();
+//             if (keyBinding==null || valueBinding==null) throw new IllegalArgumentException("null arg");
+               this.keyBinding = keyBinding;
+               this.valueBinding = valueBinding;
+        this.type = mapType;
+       }
+       
+       @Override
+       public MapType type() {
+               return (MapType) type;
+       }
+       
+       public Binding getKeyBinding() {
+               return keyBinding;
+       }
+       
+       public Binding getValueBinding() {
+               return valueBinding;
+       }
+       
+       public void setKeyBinding(Binding keyBinding) {
+               this.keyBinding = keyBinding;
+               if (!type().keyType.equals(keyBinding.type()))
+                       throw new IllegalArgumentException("Binding for "+type().keyType+" expected, got "+keyBinding.type());
+       }
+
+       public void setValueBinding(Binding valueBinding) {
+               this.valueBinding = valueBinding;
+               if (!type().valueType.equals(valueBinding.type()))
+                       throw new IllegalArgumentException("Binding for "+type().valueType+" expected, got "+valueBinding.type());              
+       }
+
+       public abstract Object create() throws BindingException;
+       
+       /**
+        * Create a new map with initial values.
+        * The values of the initialMap are accessible with the respective key and value binding.
+        *  
+        * @param initialMap 
+        * @return map object
+        * @throws BindingException
+        */
+       public abstract Object create(Map<?, ?> initialMap) throws BindingException;
+
+       /**
+        * Create a new map with initial values.
+        * The values of the initialMap are accessible with the respective key and value binding.
+        *  
+        * @param keys
+        * @param values
+        * @return map object
+        * @throws BindingException
+        */
+       public abstract Object create(List<Object> keys, List<Object> values) throws BindingException;
+       
+       /**
+        * Create a new map with initial values.
+        * The values of the initialMap are accessible with the key and value binding.
+        * 
+        * @param keys
+        * @param values
+        * @return map object
+        * @throws BindingException
+        */
+       public abstract Object create(Object[] keys, Object[] values) throws BindingException;  
+       
+       public abstract int size(Object map) throws BindingException;
+       
+       /**
+        * Return the value to which the specified key is mapped. 
+        * If the key is not mapped, BindingException is thrown.
+        * The key and the value objects are accessible with the respective bindings. 
+        * 
+        * @param map
+        * @param key
+        * @return value
+        * @throws BindingException
+        */
+       public abstract Object get(Object map, Object key) throws BindingException;
+       public abstract boolean containsKey(Object map, Object key) throws BindingException;
+       public abstract boolean containsValue(Object map, Object value) throws BindingException;
+       public abstract <K, V> void put(Object map, K key, V value) throws BindingException;
+       public abstract Object remove(Object map, Object key) throws BindingException;
+       public abstract <K, V> void putAll(Object mapTo, Map<K, V> mapFrom) throws BindingException;
+       public abstract <K, V> void getAll(Object mapFrom, Map<K, V> to) throws BindingException;
+       
+       /**
+        * Get keys and values, in order
+        * 
+        * @param mapFrom
+        * @param keys
+        * @param values
+        * @throws BindingException
+        */
+       public abstract void getAll(Object mapFrom, Object[] keys, Object[] values) throws BindingException;
+       
+       /**
+        * Get keys in order
+        * 
+        * @param map
+        * @return keys
+        * @throws BindingException
+        */
+       public abstract Object[] getKeys(Object map) throws BindingException;
+       
+       public abstract void getKeys(Object map, Set<Object> keys) throws BindingException;
+       \r
+       /**\r
+        * Count the number of entries between two keyes\r
+        * \r
+        * @param src\r
+        * @param from\r
+     * @param fromInclusive\r
+        * @param end \r
+     * @param endInclusive\r
+        * @throws BindingException\r
+        */\r
+       public abstract int count(Object src, Object from, boolean fromInclusive, Object end, boolean endInclusive) throws BindingException;\r
+       \r
+       /**\r
+        * Read a range of entries\r
+        * \r
+        * @param src\r
+        * @param from\r
+     * @param fromInclusive\r
+        * @param end \r
+     * @param endInclusive\r
+        * @param dstKeyArrayBinding\r
+        * @param dstKeyArray\r
+     * @param dstValueArrayBinding\r
+        * @param dstValueArray\r
+        * @param resultLimit maximum number of entries to read, -1 for no limit\r
+        * @return the number of entries read \r
+        * @throws BindingException\r
+        */\r
+       public abstract int getEntries(\r
+                       Object src, \r
+                       Object from, boolean fromInclusive, Object end, boolean endInclusive, \r
+                       ArrayBinding dstKeyArrayBinding, Object dstKeyArray, \r
+                       ArrayBinding dstValueArrayBinding, Object dstValueArray, \r
+                       int resultLimit) throws BindingException;\r
+               
+       /**
+        * Get values in order
+        * 
+        * @param map
+        * @return values
+        * @throws BindingException
+        */
+       public abstract Object[] getValues(Object map) throws BindingException;
+       public abstract void clear(Object map) throws BindingException;
+
+       // Views considered - more knowledge required
+
+    /**
+     * Assert the instance is valid and follows restrictions set in data type.
+     * Assertions:   
+     *   1. correct instance
+     *   2. assertion of each key and value
+     * 
+     * @param map the instance
+     * @param validInstances a collection of validated instances or <code>null</code>
+     * @throws BindingException on invalid instance
+     */
+       @Override
+       public void assertInstaceIsValid(Object map, Set<Object> validInstances) throws BindingException {
+               if (!isInstance(map)) throw new BindingException("Not a map");
+               for (Object key : getKeys(map)) {
+                       keyBinding.assertInstaceIsValid(key, validInstances);
+                       Object value = get(map, key);
+                       valueBinding.assertInstaceIsValid(value, validInstances);
+               }
+       }
+
+       @Override
+       public void accept(Visitor1 v, Object obj) {
+           v.visit(this, obj);        
+       }
+
+       @Override
+       public <T> T accept(Visitor<T> v) {
+           return v.visit(this);
+       }
+       \r
+       @Override\r
+       public void readFrom(Binding srcBinding, Object src, Object dst)\r
+                       throws BindingException {\r
+               try {\r
+                       MapBinding sb = (MapBinding) srcBinding;\r
+                       Binding dkb = getKeyBinding();\r
+                       Binding dvb = getValueBinding();\r
+                       Set<Object> oldKeys = new TreeSet<Object>(dkb);\r
+                       getKeys(dst, oldKeys);\r
+                       Binding skb = sb.getKeyBinding();\r
+                       Binding svb = sb.getValueBinding();\r
+                       boolean cbImmutable = dvb.isImmutable();\r
+                       Adapter ka = Bindings.adapterFactory.getAdapter(skb, dkb, false, false);\r
+                       Adapter va = cbImmutable ? Bindings.adapterFactory.getAdapter(svb, dvb, false, true) : null;\r
+               \r
+                       // Copy keys from other map\r
+                       for (Object sk : sb.getKeys(src)) {\r
+                               Object dk = ka.adapt(sk);\r
+                               Object sv = sb.get(src, sk);\r
+                               if (cbImmutable) {\r
+                                       Object dv = va.adapt(sv);\r
+                                       put(dst, dk, dv);\r
+                               } else\r
+                               if (containsKey(dst, dk)) {\r
+                                       Object dv = get(dst, dk);\r
+                                       dv = dvb.readFromTry(svb, sv, dv);\r
+                                       put(dst, dk, dv);\r
+                               } else {\r
+                                       Object dv = dvb.createDefault();\r
+                                       dv = dvb.readFromTry(svb, sv, dv);\r
+                                       put(dst, dk, dv);\r
+                               }\r
+                               oldKeys.remove(dk);\r
+                       }\r
+                       \r
+                       // Remove unused keys\r
+                       for (Object k : oldKeys) remove(dst, k);\r
+\r
+               } catch (AdapterConstructionException e) {\r
+                       throw new BindingException(e);\r
+               } catch (AdaptException e) {\r
+                       throw new BindingException(e);\r
+               }\r
+       }\r
+       \r
+       
+       @Override
+    public int deepHashValue(Object map, IdentityHashMap<Object, Object> hashedObjects) throws BindingException {
+               int result = 0;
+               Object keys[] = getKeys(map);
+               Object values[] = getValues(map);
+               int len = size(map);
+               for (int i=0; i<len; i++) {
+                       Object key = keys[i];
+                       Object value = values[i];
+                       
+                       int keyHash = keyBinding.deepHashValue(key, hashedObjects);
+                       int valueHash = valueBinding.deepHashValue(value, hashedObjects);
+                       
+                       result += (keyHash ^ valueHash);
+               }
+               return result;
+       }
+       
+    @Override
+    public int deepCompare(Object o1, Object o2,
+               Set<IdentityPair<Object, Object>> compareHistory)
+               throws BindingException {
+               // Compare sizes
+               int l1 = size(o1);
+               int l2 = size(o2);
+               int dif = l1 - l2;
+               if (dif!=0) 
+                       return dif;
+               // Compare elements
+               Binding k = getKeyBinding();
+               Binding v = getValueBinding();
+               TreeMap<Object, Object> e1 = new TreeMap<Object, Object>( k );
+               TreeMap<Object, Object> e2 = new TreeMap<Object, Object>( k );
+               getAll(o1, e1);
+               getAll(o2, e2);
+               
+               Iterator<Entry<Object, Object>> i1 = e1.entrySet().iterator(); 
+               Iterator<Entry<Object, Object>> i2 = e2.entrySet().iterator(); 
+               while (i1.hasNext()) {
+                       Entry<Object, Object> h1 = i1.next();
+                       Entry<Object, Object> h2 = i2.next();
+                       dif = k.deepCompare(h1.getKey(), h2.getKey(), compareHistory);
+                       if (dif!=0) 
+                               return dif;
+                       dif = v.deepCompare(h1.getValue(), h2.getValue(), compareHistory);
+                       if (dif!=0) 
+                               return dif;
+                       i1.remove();
+                       i2.remove();
+               }
+               return 0;
+       
+    }
+
+       public Object createUnchecked(Object[] keys, Object[] values)
+                       throws RuntimeBindingException {
+               try {
+                       return create(keys, values);
+               } catch (BindingException e) {
+                       throw new RuntimeBindingException(e);
+               }
+       }
+       
+       public Object createUnchecked(List<Object> keys, List<Object> values) throws RuntimeBindingException
+       {
+               try {
+                       return create(keys, values);
+               } catch (BindingException e) {
+                       throw new RuntimeBindingException(e);
+               }
+       }
+
+       public Object createUnchecked(Map<Object, Object> initialMap)
+                       throws RuntimeBindingException {
+               try {
+                       return create(initialMap);
+               } catch (BindingException e) {
+                       throw new RuntimeBindingException(e);
+               }
+       }
+       
+       public Object createUnchecked() throws RuntimeBindingException {
+               try {
+                       return create();
+               } catch (BindingException e) {
+                       throw new RuntimeBindingException(e);
+               }
+       }
+
+       public abstract Object getFirstKey(Object map);
+       public abstract Object getLastKey(Object map);
+       public abstract Object getLowerKey(Object map, Object key);
+       public abstract Object getFloorKey(Object map, Object key);
+       public abstract Object getCeilingKey(Object map, Object key);
+       public abstract Object getHigherKey(Object map, Object key);
+
+       \r
+       @Override\r
+       protected void toString(Object value, BindingPrintContext ctx) throws BindingException {\r
+               Binding keyBinding = getKeyBinding();\r
+               Binding valueBinding = getValueBinding();\r
+               ctx.b.append("{ ");\r
+               boolean first = true;\r
+               for(Object key : getKeys(value)) {\r
+                       if(first)\r
+                               first = false;\r
+                       else {\r
+                               ctx.b.append(", ");\r
+                               if ( !ctx.singleLine ) ctx.b.append('\n');\r
+                       }\r
+                       keyBinding.toString(key, ctx);\r
+                       ctx.b.append(" = ");\r
+                       valueBinding.toString(get(value, key), ctx);\r
+               }\r
+               ctx.b.append(" }");\r
+       }\r
+\r
+       @Override\r
+       public Binding getComponentBinding(ChildReference path) {\r
+       if (path==null) return this;\r
+       if (path instanceof IndexReference) {\r
+               IndexReference ir = (IndexReference) path;\r
+               if (ir.index==0) return keyBinding.getComponentBinding(path.childReference);\r
+               if (ir.index==1) return valueBinding.getComponentBinding(path.childReference);\r
+       }\r
+       if (path instanceof LabelReference) {\r
+               LabelReference lr = (LabelReference) path;\r
+               if (lr.label.equals("0") || lr.label.equals("key")) return keyBinding.getComponentBinding(path.childReference);\r
+               if (lr.label.equals("1") || lr.label.equals("value")) return valueBinding.getComponentBinding(path.childReference);\r
+       }\r
+       if (path instanceof NameReference) {\r
+               NameReference nr = (NameReference) path;\r
+               if (nr.name.equals("key")) return keyBinding.getComponentBinding(path.childReference);\r
+               if (nr.name.equals("value")) return valueBinding.getComponentBinding(path.childReference);\r
+       }\r
+       throw new IllegalArgumentException();\r
+       }       \r
+\r
+       \r
+    @Override\r
+    public int getComponentCount() {\r
+       return 2;\r
+    }\r
+    \r
+    @Override\r
+    public Binding getComponentBinding(int index) {\r
+       if (index==0) return keyBinding;\r
+       if (index==1) return valueBinding;\r
+       throw new IllegalArgumentException();\r
+    }  \r
+\r
+    @Override\r
+    protected boolean deepEquals(Object obj,\r
+               Set<IdentityPair<Binding, Binding>> compareHistory) {\r
+       MapBinding o = (MapBinding)obj;\r
+       return super.deepEquals( obj, compareHistory ) && keyBinding.equals(o.keyBinding, compareHistory) && valueBinding.equals(o.valueBinding, compareHistory);\r
+    }\r
+    \r
+    @Override\r
+    public int deepHashCode(IdentityHashMap<Object, Object> hashedObjects) {\r
+       return super.deepHashCode(hashedObjects) + 13 * keyBinding.hashCode(hashedObjects) + 17 * valueBinding.hashCode(hashedObjects);\r
+    }
+}