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.HashSet;
15 import java.util.IdentityHashMap;
18 import org.simantics.databoard.Bindings;
19 import org.simantics.databoard.accessor.reference.ChildReference;
20 import org.simantics.databoard.accessor.reference.IndexReference;
21 import org.simantics.databoard.accessor.reference.LabelReference;
22 import org.simantics.databoard.accessor.reference.NameReference;
23 import org.simantics.databoard.adapter.AdaptException;
24 import org.simantics.databoard.binding.error.BindingException;
25 import org.simantics.databoard.binding.error.RuntimeBindingException;
26 import org.simantics.databoard.binding.impl.BindingPrintContext;
27 import org.simantics.databoard.type.Component;
28 import org.simantics.databoard.type.RecordType;
29 import org.simantics.databoard.util.IdentityHashSet;
30 import org.simantics.databoard.util.IdentityPair;
34 * This is a binding of a Record Type and a Java Object.
37 * @author Hannu Niemisto
38 * @author Toni Kalajainen <toni.kalajainen@vtt.fi>
40 public abstract class RecordBinding extends Binding {
42 public Binding[] componentBindings;
45 * Get binding by field name
47 * @return binding or <code>null</code>
49 public Binding getComponentBinding(String fieldName) {
50 int fieldIndex = type().getComponentIndex2(fieldName);
51 if (fieldIndex<0) return null;
52 return getComponentBinding(fieldIndex);
55 public Object getComponentObject(Object obj, String fieldName) throws BindingException {
56 int fieldIndex = type().getComponentIndex2(fieldName);
57 if (fieldIndex<0) return null;
58 return getComponent(obj, fieldIndex);
61 public int getComponentIndex(String fieldName) {
62 return type().getComponentIndex2(fieldName);
65 public Binding getComponentBinding(int fieldIndex) {
66 return componentBindings[fieldIndex];
69 public Binding[] getComponentBindings() {
70 return componentBindings;
73 protected void setComponentBindings(Binding[] componentBindings) {
74 this.componentBindings = componentBindings;
78 public RecordType type() {
79 return (RecordType) type;
82 public int getComponentCount() {
83 return type().getComponentCount();
86 public abstract Object getComponent(Object obj, int index)
87 throws BindingException;
90 * Create a record using values.
92 * Note! values may be consumed (used in the result)
96 * @throws BindingException
98 public abstract Object create(Object ... values)
99 throws BindingException;
102 * Creates partial and most likely invalid instance.
103 * This is used in two-phase construction of recursive instances.
107 public abstract Object createPartial() throws BindingException;
109 public abstract void setComponents(Object obj, Object ... value) throws BindingException;
110 public abstract void setComponent(Object obj, int index, Object value) throws BindingException;
112 public void setComponent(Object obj, String fieldName, Object value) throws BindingException
114 int fieldIndex = type().getComponentIndex2(fieldName);
115 if ( fieldIndex<0 ) throw new BindingException("Field "+fieldName+" does not exist.");
116 setComponent(obj, fieldIndex, value);
120 public void readFrom(Binding srcBinding, Object src, Object dst)
121 throws BindingException {
122 RecordBinding sb = (RecordBinding) srcBinding;
123 int len = getComponentCount();
124 if (sb.getComponentCount()!=len) throw new BindingException("field count mismatch");
126 for (int i=0; i<len; i++) {
127 Binding dcb = getComponentBinding(i);
128 Binding scb = sb.getComponentBinding(i);
129 Object sc = sb.getComponent(src, i);
130 if (dcb.isImmutable()) {
131 Object cv = Bindings.clone(sc, scb, dcb);
132 setComponent(dst, i, cv);
134 Object v = getComponent(dst, i);
135 v = dcb.readFromTry(scb, sc, v);
136 setComponent(dst, i, v);
139 } catch (AdaptException e) {
140 throw new BindingException(e);
145 public void accept(Visitor1 v, Object obj) {
150 public <T> T accept(Visitor<T> v) {
151 return v.visit(this);
155 * Assert obj is valid Record Type
157 * This asserts all fields are valid.
159 * @throws BindingException
162 public void assertInstaceIsValid(Object obj, Set<Object> validInstances) throws BindingException {
163 if (!isInstance(obj))
164 throw new BindingException("The object ("+obj+") is not correct instance.");
165 RecordType type = type();
166 int length = type.getComponentCount();
168 if (type.isReferable()) {
169 if (validInstances==null) {
170 validInstances = new IdentityHashSet<Object>();
172 if (validInstances.contains(obj)) return;
173 validInstances.add(obj);
176 for (int i=0; i<length; i++)
178 Binding componentBinding = getComponentBindings()[i];
179 Object componentValue = getComponent(obj, i);
180 componentBinding.assertInstaceIsValid(componentValue, validInstances);
185 public int deepHashValue(Object value, IdentityHashMap<Object, Object> hashedObjects) throws BindingException {
186 if (type().isReferable()) {
187 if (hashedObjects==null) {
188 hashedObjects = new IdentityHashMap<Object, Object>(1);
190 if (hashedObjects.containsKey(value)) {
193 hashedObjects.put(value, null);
198 int len = componentBindings.length;
199 for (int i=0; i<len; i++) {
200 Object element = getComponent(value, i);
201 result += 31*result + componentBindings[i].deepHashValue(element, hashedObjects);
207 public int deepCompare(Object o1, Object o2,
208 Set<IdentityPair<Object, Object>> compareHistory)
209 throws BindingException {
210 RecordType rt = (RecordType) type();
212 // Compare History is used to prevent infinite loops
213 if (rt.isReferable()) {
214 if (compareHistory==null) {
215 compareHistory = new HashSet<IdentityPair<Object, Object>>();
217 IdentityPair<Object, Object> p = new IdentityPair<Object, Object>(o1, o2);
218 if (compareHistory.contains(p)) return 0;
219 compareHistory.add(p);
222 int len = rt.getComponentCount();
223 for (int i=0; i<len; i++) {
224 Binding c = getComponentBindings()[i];
225 Object v1 = getComponent(o1, i);
226 Object v2 = getComponent(o2, i);
227 int dif = c.deepCompare(v1, v2, compareHistory);
234 public Object createUnchecked(Object ... values) throws RuntimeBindingException
237 return create(values);
238 } catch (BindingException e) {
239 throw new RuntimeBindingException(e);
243 public void setComponentsUnchecked(Object obj, Object ... value) throws RuntimeBindingException {
245 setComponents(obj, value);
246 } catch (BindingException e) {
247 throw new RuntimeBindingException(e);
251 public void setComponentUnchecked(Object obj, int index, Object value) throws RuntimeBindingException {
253 setComponent(obj, index, value);
254 } catch (BindingException e) {
255 throw new RuntimeBindingException(e);
259 protected void toStringAux(Object value, BindingPrintContext ctx) throws BindingException {
261 Component[] components = type().getComponents();
262 boolean first = true;
263 for(int i=0;i<components.length;++i) {
268 if (!ctx.singleLine) ctx.b.append('\n');
270 Binding binding = getComponentBinding(i);
271 Object cval = getComponent(value, i);
272 if(binding instanceof OptionalBinding &&
273 !((OptionalBinding)binding).hasValue(cval))
275 Component component = components[i];
276 ctx.b.append(component.name);
278 binding.toString(cval, ctx);
284 protected void toString(Object value, BindingPrintContext ctx) throws BindingException {
285 if(type().isReferable()) {
286 if(ctx.refs.contains(value)) {
287 int val = ctx.refs.get(value);
289 val = ctx.nameCount.value++;
290 ctx.refs.put(value, val);
292 ctx.b.append((char)('a' + val));
295 ctx.refs.put(value, -1);
296 toStringAux(value, ctx);
297 int val = ctx.refs.remove(value);
300 ctx.b.append((char)('a' + val));
305 toStringAux(value, ctx);
310 public Binding getComponentBinding(ChildReference path) throws IllegalArgumentException {
311 if (path==null) return this;
312 if (path instanceof IndexReference) {
313 IndexReference ir = (IndexReference) path;
314 return componentBindings[ir.index].getComponentBinding(path.childReference);
316 if (path instanceof NameReference) {
317 NameReference nr = (NameReference) path;
318 return getComponentBinding( nr.name ).getComponentBinding(path.childReference);
320 if (path instanceof LabelReference) {
321 LabelReference lr = (LabelReference) path;
323 Integer i = new Integer(lr.label);
324 return getComponentBinding( i ).getComponentBinding(path.childReference);
325 } catch (NumberFormatException nfe) {
326 return getComponentBinding( lr.label ).getComponentBinding(path.childReference);
329 throw new IllegalArgumentException();
333 public boolean isImmutable() {
334 return getComponentCount() == 0;
337 public void setBoolean(Object r, int index, boolean z) throws BindingException
339 setComponent(r, index, ((BooleanBinding)componentBindings[index]).create(z));
342 public boolean getBoolean(Object r, int index) throws BindingException
344 return ((BooleanBinding)componentBindings[index]).getValue_( getComponent(r, index) );
347 public void setByte(Object r, int index, byte x) throws BindingException
349 setComponent(r, index, ((ByteBinding)componentBindings[index]).create(x));
352 public byte getByte(Object r, int index) throws BindingException
354 return ((ByteBinding)componentBindings[index]).getValue_( getComponent(r, index) );
357 public void setInt(Object r, int index, int x) throws BindingException
359 setComponent(r, index, ((IntegerBinding)componentBindings[index]).create(x));
362 public int getInt(Object r, int index) throws BindingException
364 return ((IntegerBinding)componentBindings[index]).getValue_( getComponent(r, index) );
367 public void setLong(Object r, int index, long x) throws BindingException
369 setComponent(r, index, ((LongBinding)componentBindings[index]).create(x));
372 public long getLong(Object r, int index) throws BindingException
374 return ((LongBinding)componentBindings[index]).getValue_( getComponent(r, index) );
377 public void setFloat(Object r, int index, float x) throws BindingException
379 setComponent(r, index, ((FloatBinding)componentBindings[index]).create(x));
382 public float getFloat(Object r, int index) throws BindingException
384 return ((FloatBinding)componentBindings[index]).getValue_( getComponent(r, index) );
387 public void setDouble(Object r, int index, double x) throws BindingException
389 setComponent(r, index, ((DoubleBinding)componentBindings[index]).create(x));
392 public double getDouble(Object r, int index) throws BindingException
394 return ((DoubleBinding)componentBindings[index]).getValue_( getComponent(r, index) );
398 protected boolean deepEquals(Object obj,
399 Set<IdentityPair<Binding, Binding>> compareHistory) {
400 RecordBinding o = (RecordBinding)obj;
401 if (!super.deepEquals( obj, compareHistory ))
404 if (componentBindings.length != o.componentBindings.length) return false;
406 for (int i = 0; i < componentBindings.length; i++)
407 if (!componentBindings[i].equals(o.componentBindings[i], compareHistory))
414 public int deepHashCode(IdentityHashMap<Object, Object> hashedObjects) {
415 int code = super.deepHashCode(hashedObjects);
416 for (int i = 0; i < componentBindings.length; i++)
417 code = 17 * code + componentBindings[i].hashCode(hashedObjects);