/******************************************************************************* * 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); } }