1 /*******************************************************************************
2 * Copyright (c) 2010 Association for Decentralized Information Management in
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
10 * VTT Technical Research Centre of Finland - initial API and implementation
11 *******************************************************************************/
12 package org.simantics.databoard.binding;
14 import java.util.IdentityHashMap;
15 import java.util.Iterator;
16 import java.util.List;
18 import java.util.Map.Entry;
20 import java.util.TreeMap;
21 import java.util.TreeSet;
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;
38 * This is a binding of Map Type and a Java Object
41 * @author Toni Kalajainen <toni.kalajainen@vtt.fi>
43 public abstract class MapBinding extends Binding {
45 protected Binding keyBinding;
46 protected Binding valueBinding;
49 * Create new map binding. Creates new data type.
54 public MapBinding(Binding keyBinding, Binding valueBinding)
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;
64 * Create new map binding for a type.
70 public MapBinding(MapType mapType, Binding keyBinding, Binding valueBinding)
73 // if (keyBinding==null || valueBinding==null) throw new IllegalArgumentException("null arg");
74 this.keyBinding = keyBinding;
75 this.valueBinding = valueBinding;
80 public MapType type() {
81 return (MapType) type;
84 public Binding getKeyBinding() {
88 public Binding getValueBinding() {
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());
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());
104 public abstract Object create() throws BindingException;
107 * Create a new map with initial values.
108 * The values of the initialMap are accessible with the respective key and value binding.
112 * @throws BindingException
114 public abstract Object create(Map<?, ?> initialMap) throws BindingException;
117 * Create a new map with initial values.
118 * The values of the initialMap are accessible with the respective key and value binding.
123 * @throws BindingException
125 public abstract Object create(List<Object> keys, List<Object> values) throws BindingException;
128 * Create a new map with initial values.
129 * The values of the initialMap are accessible with the key and value binding.
134 * @throws BindingException
136 public abstract Object create(Object[] keys, Object[] values) throws BindingException;
138 public abstract int size(Object map) throws BindingException;
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.
148 * @throws BindingException
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;
159 * Get keys and values, in order
164 * @throws BindingException
166 public abstract void getAll(Object mapFrom, Object[] keys, Object[] values) throws BindingException;
173 * @throws BindingException
175 public abstract Object[] getKeys(Object map) throws BindingException;
177 public abstract void getKeys(Object map, Set<Object> keys) throws BindingException;
180 * Count the number of entries between two keyes
184 * @param fromInclusive
186 * @param endInclusive
187 * @throws BindingException
189 public abstract int count(Object src, Object from, boolean fromInclusive, Object end, boolean endInclusive) throws BindingException;
192 * Read a range of entries
196 * @param fromInclusive
198 * @param endInclusive
199 * @param dstKeyArrayBinding
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
207 public abstract int getEntries(
209 Object from, boolean fromInclusive, Object end, boolean endInclusive,
210 ArrayBinding dstKeyArrayBinding, Object dstKeyArray,
211 ArrayBinding dstValueArrayBinding, Object dstValueArray,
212 int resultLimit) throws BindingException;
215 * Get values in order
219 * @throws BindingException
221 public abstract Object[] getValues(Object map) throws BindingException;
222 public abstract void clear(Object map) throws BindingException;
224 // Views considered - more knowledge required
227 * Assert the instance is valid and follows restrictions set in data type.
229 * 1. correct instance
230 * 2. assertion of each key and value
232 * @param map the instance
233 * @param validInstances a collection of validated instances or <code>null</code>
234 * @throws BindingException on invalid instance
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);
247 public void accept(Visitor1 v, Object obj) {
252 public <T> T accept(Visitor<T> v) {
253 return v.visit(this);
257 public void readFrom(Binding srcBinding, Object src, Object dst)
258 throws BindingException {
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;
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);
276 Object dv = va.adapt(sv);
279 if (containsKey(dst, dk)) {
280 Object dv = get(dst, dk);
281 dv = dvb.readFromTry(svb, sv, dv);
284 Object dv = dvb.createDefault();
285 dv = dvb.readFromTry(svb, sv, dv);
291 // Remove unused keys
292 for (Object k : oldKeys) remove(dst, k);
294 } catch (AdapterConstructionException e) {
295 throw new BindingException(e);
296 } catch (AdaptException e) {
297 throw new BindingException(e);
303 public int deepHashValue(Object map, IdentityHashMap<Object, Object> hashedObjects) throws BindingException {
305 Object keys[] = getKeys(map);
306 Object values[] = getValues(map);
308 for (int i=0; i<len; i++) {
309 Object key = keys[i];
310 Object value = values[i];
312 int keyHash = keyBinding.deepHashValue(key, hashedObjects);
313 int valueHash = valueBinding.deepHashValue(value, hashedObjects);
315 result += (keyHash ^ valueHash);
321 public int deepCompare(Object o1, Object o2,
322 Set<IdentityPair<Object, Object>> compareHistory)
323 throws BindingException {
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 );
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);
346 dif = v.deepCompare(h1.getValue(), h2.getValue(), compareHistory);
356 public Object createUnchecked(Object[] keys, Object[] values)
357 throws RuntimeBindingException {
359 return create(keys, values);
360 } catch (BindingException e) {
361 throw new RuntimeBindingException(e);
365 public Object createUnchecked(List<Object> keys, List<Object> values) throws RuntimeBindingException
368 return create(keys, values);
369 } catch (BindingException e) {
370 throw new RuntimeBindingException(e);
374 public Object createUnchecked(Map<Object, Object> initialMap)
375 throws RuntimeBindingException {
377 return create(initialMap);
378 } catch (BindingException e) {
379 throw new RuntimeBindingException(e);
383 public Object createUnchecked() throws RuntimeBindingException {
386 } catch (BindingException e) {
387 throw new RuntimeBindingException(e);
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);
400 protected void toString(Object value, BindingPrintContext ctx) throws BindingException {
401 Binding keyBinding = getKeyBinding();
402 Binding valueBinding = getValueBinding();
404 boolean first = true;
405 for(Object key : getKeys(value)) {
410 if ( !ctx.singleLine ) ctx.b.append('\n');
412 keyBinding.toString(key, ctx);
414 valueBinding.toString(get(value, key), ctx);
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);
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);
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);
437 throw new IllegalArgumentException();
442 public int getComponentCount() {
447 public Binding getComponentBinding(int index) {
448 if (index==0) return keyBinding;
449 if (index==1) return valueBinding;
450 throw new IllegalArgumentException();
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);
461 public int deepHashCode(IdentityHashMap<Object, Object> hashedObjects) {
462 return super.deepHashCode(hashedObjects) + 13 * keyBinding.hashCode(hashedObjects) + 17 * valueBinding.hashCode(hashedObjects);