X-Git-Url: https://gerrit.simantics.org/r/gitweb?a=blobdiff_plain;ds=sidebyside;f=bundles%2Forg.simantics.databoard%2Fsrc%2Forg%2Fsimantics%2Fdataboard%2Fbinding%2FMapBinding.java;fp=bundles%2Forg.simantics.databoard%2Fsrc%2Forg%2Fsimantics%2Fdataboard%2Fbinding%2FMapBinding.java;h=94f238ad91d9bbd788aa424896c4182776d380ab;hb=969bd23cab98a79ca9101af33334000879fb60c5;hp=0000000000000000000000000000000000000000;hpb=866dba5cd5a3929bbeae85991796acb212338a08;p=simantics%2Fplatform.git 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 index 000000000..94f238ad9 --- /dev/null +++ b/bundles/org.simantics.databoard/src/org/simantics/databoard/binding/MapBinding.java @@ -0,0 +1,464 @@ +/******************************************************************************* + * Copyright (c) 2010 Association for Decentralized Information Management in + * Industry THTH ry. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * VTT Technical Research Centre of Finland - initial API and implementation + *******************************************************************************/ +package org.simantics.databoard.binding; + +import java.util.IdentityHashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import java.util.TreeMap; +import java.util.TreeSet; + +import org.simantics.databoard.Bindings; +import org.simantics.databoard.accessor.reference.ChildReference; +import org.simantics.databoard.accessor.reference.IndexReference; +import org.simantics.databoard.accessor.reference.LabelReference; +import org.simantics.databoard.accessor.reference.NameReference; +import org.simantics.databoard.adapter.AdaptException; +import org.simantics.databoard.adapter.Adapter; +import org.simantics.databoard.adapter.AdapterConstructionException; +import org.simantics.databoard.binding.error.BindingException; +import org.simantics.databoard.binding.error.RuntimeBindingException; +import org.simantics.databoard.binding.impl.BindingPrintContext; +import org.simantics.databoard.type.MapType; +import org.simantics.databoard.util.IdentityPair; + +/** + * This is a binding of Map Type and a Java Object + * + * @see MapType + * @author Toni Kalajainen + */ +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 keys, List 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 void put(Object map, K key, V value) throws BindingException; + public abstract Object remove(Object map, Object key) throws BindingException; + public abstract void putAll(Object mapTo, Map mapFrom) throws BindingException; + public abstract void getAll(Object mapFrom, Map 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 keys) throws BindingException; + + /** + * Count the number of entries between two keyes + * + * @param src + * @param from + * @param fromInclusive + * @param end + * @param endInclusive + * @throws BindingException + */ + public abstract int count(Object src, Object from, boolean fromInclusive, Object end, boolean endInclusive) throws BindingException; + + /** + * Read a range of entries + * + * @param src + * @param from + * @param fromInclusive + * @param end + * @param endInclusive + * @param dstKeyArrayBinding + * @param dstKeyArray + * @param dstValueArrayBinding + * @param dstValueArray + * @param resultLimit maximum number of entries to read, -1 for no limit + * @return the number of entries read + * @throws BindingException + */ + public abstract int getEntries( + Object src, + Object from, boolean fromInclusive, Object end, boolean endInclusive, + ArrayBinding dstKeyArrayBinding, Object dstKeyArray, + ArrayBinding dstValueArrayBinding, Object dstValueArray, + int resultLimit) throws BindingException; + + /** + * 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 null + * @throws BindingException on invalid instance + */ + @Override + public void assertInstaceIsValid(Object map, Set 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 accept(Visitor v) { + return v.visit(this); + } + + @Override + public void readFrom(Binding srcBinding, Object src, Object dst) + throws BindingException { + try { + MapBinding sb = (MapBinding) srcBinding; + Binding dkb = getKeyBinding(); + Binding dvb = getValueBinding(); + Set oldKeys = new TreeSet(dkb); + getKeys(dst, oldKeys); + Binding skb = sb.getKeyBinding(); + Binding svb = sb.getValueBinding(); + boolean cbImmutable = dvb.isImmutable(); + Adapter ka = Bindings.adapterFactory.getAdapter(skb, dkb, false, false); + Adapter va = cbImmutable ? Bindings.adapterFactory.getAdapter(svb, dvb, false, true) : null; + + // Copy keys from other map + for (Object sk : sb.getKeys(src)) { + Object dk = ka.adapt(sk); + Object sv = sb.get(src, sk); + if (cbImmutable) { + Object dv = va.adapt(sv); + put(dst, dk, dv); + } else + if (containsKey(dst, dk)) { + Object dv = get(dst, dk); + dv = dvb.readFromTry(svb, sv, dv); + put(dst, dk, dv); + } else { + Object dv = dvb.createDefault(); + dv = dvb.readFromTry(svb, sv, dv); + put(dst, dk, dv); + } + oldKeys.remove(dk); + } + + // Remove unused keys + for (Object k : oldKeys) remove(dst, k); + + } catch (AdapterConstructionException e) { + throw new BindingException(e); + } catch (AdaptException e) { + throw new BindingException(e); + } + } + + + @Override + public int deepHashValue(Object map, IdentityHashMap hashedObjects) throws BindingException { + int result = 0; + Object keys[] = getKeys(map); + Object values[] = getValues(map); + int len = size(map); + for (int i=0; i> 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 e1 = new TreeMap( k ); + TreeMap e2 = new TreeMap( k ); + getAll(o1, e1); + getAll(o2, e2); + + Iterator> i1 = e1.entrySet().iterator(); + Iterator> i2 = e2.entrySet().iterator(); + while (i1.hasNext()) { + Entry h1 = i1.next(); + Entry 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 keys, List values) throws RuntimeBindingException + { + try { + return create(keys, values); + } catch (BindingException e) { + throw new RuntimeBindingException(e); + } + } + + public Object createUnchecked(Map 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); + + + @Override + protected void toString(Object value, BindingPrintContext ctx) throws BindingException { + Binding keyBinding = getKeyBinding(); + Binding valueBinding = getValueBinding(); + ctx.b.append("{ "); + boolean first = true; + for(Object key : getKeys(value)) { + if(first) + first = false; + else { + ctx.b.append(", "); + if ( !ctx.singleLine ) ctx.b.append('\n'); + } + keyBinding.toString(key, ctx); + ctx.b.append(" = "); + valueBinding.toString(get(value, key), ctx); + } + ctx.b.append(" }"); + } + + @Override + public Binding getComponentBinding(ChildReference path) { + if (path==null) return this; + if (path instanceof IndexReference) { + IndexReference ir = (IndexReference) path; + if (ir.index==0) return keyBinding.getComponentBinding(path.childReference); + if (ir.index==1) return valueBinding.getComponentBinding(path.childReference); + } + if (path instanceof LabelReference) { + LabelReference lr = (LabelReference) path; + if (lr.label.equals("0") || lr.label.equals("key")) return keyBinding.getComponentBinding(path.childReference); + if (lr.label.equals("1") || lr.label.equals("value")) return valueBinding.getComponentBinding(path.childReference); + } + if (path instanceof NameReference) { + NameReference nr = (NameReference) path; + if (nr.name.equals("key")) return keyBinding.getComponentBinding(path.childReference); + if (nr.name.equals("value")) return valueBinding.getComponentBinding(path.childReference); + } + throw new IllegalArgumentException(); + } + + + @Override + public int getComponentCount() { + return 2; + } + + @Override + public Binding getComponentBinding(int index) { + if (index==0) return keyBinding; + if (index==1) return valueBinding; + throw new IllegalArgumentException(); + } + + @Override + protected boolean deepEquals(Object obj, + Set> compareHistory) { + MapBinding o = (MapBinding)obj; + return super.deepEquals( obj, compareHistory ) && keyBinding.equals(o.keyBinding, compareHistory) && valueBinding.equals(o.valueBinding, compareHistory); + } + + @Override + public int deepHashCode(IdentityHashMap hashedObjects) { + return super.deepHashCode(hashedObjects) + 13 * keyBinding.hashCode(hashedObjects) + 17 * valueBinding.hashCode(hashedObjects); + } +}