1 /*******************************************************************************
\r
2 * Copyright (c) 2010 Association for Decentralized Information Management in
\r
4 * All rights reserved. This program and the accompanying materials
\r
5 * are made available under the terms of the Eclipse Public License v1.0
\r
6 * which accompanies this distribution, and is available at
\r
7 * http://www.eclipse.org/legal/epl-v10.html
\r
10 * VTT Technical Research Centre of Finland - initial API and implementation
\r
11 *******************************************************************************/
\r
12 package org.simantics.databoard.binding;
14 import java.util.HashSet;
\r
15 import java.util.IdentityHashMap;
\r
16 import java.util.Set;
\r
18 import org.simantics.databoard.Bindings;
\r
19 import org.simantics.databoard.accessor.reference.ChildReference;
\r
20 import org.simantics.databoard.accessor.reference.IndexReference;
\r
21 import org.simantics.databoard.accessor.reference.LabelReference;
\r
22 import org.simantics.databoard.accessor.reference.NameReference;
\r
23 import org.simantics.databoard.adapter.AdaptException;
\r
24 import org.simantics.databoard.binding.error.BindingException;
\r
25 import org.simantics.databoard.binding.error.RuntimeBindingException;
\r
26 import org.simantics.databoard.binding.impl.BindingPrintContext;
\r
27 import org.simantics.databoard.type.Component;
\r
28 import org.simantics.databoard.type.RecordType;
\r
29 import org.simantics.databoard.util.IdentityHashSet;
\r
30 import org.simantics.databoard.util.IdentityPair;
\r
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
\r
47 * @return binding or <code>null</code>
\r
49 public Binding getComponentBinding(String fieldName) {
\r
50 int fieldIndex = type().getComponentIndex2(fieldName);
\r
51 if (fieldIndex<0) return null;
\r
52 return getComponentBinding(fieldIndex);
\r
55 public Object getComponentObject(Object obj, String fieldName) throws BindingException {
\r
56 int fieldIndex = type().getComponentIndex2(fieldName);
\r
57 if (fieldIndex<0) return null;
\r
58 return getComponent(obj, fieldIndex);
\r
61 public int getComponentIndex(String fieldName) {
\r
62 return type().getComponentIndex2(fieldName);
\r
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
\r
114 int fieldIndex = type().getComponentIndex2(fieldName);
\r
115 if ( fieldIndex<0 ) throw new BindingException("Field "+fieldName+" does not exist.");
\r
116 setComponent(obj, fieldIndex, value);
\r
120 public void readFrom(Binding srcBinding, Object src, Object dst)
\r
121 throws BindingException {
\r
122 RecordBinding sb = (RecordBinding) srcBinding;
\r
123 int len = getComponentCount();
\r
124 if (sb.getComponentCount()!=len) throw new BindingException("field count mismatch");
\r
126 for (int i=0; i<len; i++) {
\r
127 Binding dcb = getComponentBinding(i);
\r
128 Binding scb = sb.getComponentBinding(i);
\r
129 Object sc = sb.getComponent(src, i);
\r
130 if (dcb.isImmutable()) {
\r
131 Object cv = Bindings.clone(sc, scb, dcb);
\r
132 setComponent(dst, i, cv);
\r
134 Object v = getComponent(dst, i);
\r
135 v = dcb.readFromTry(scb, sc, v);
\r
136 setComponent(dst, i, v);
\r
139 } catch (AdaptException e) {
\r
140 throw new BindingException(e);
\r
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 {
\r
261 Component[] components = type().getComponents();
\r
262 boolean first = true;
\r
263 for(int i=0;i<components.length;++i) {
\r
267 ctx.b.append(", ");
\r
268 if (!ctx.singleLine) ctx.b.append('\n');
\r
270 Binding binding = getComponentBinding(i);
\r
271 Object cval = getComponent(value, i);
\r
272 if(binding instanceof OptionalBinding &&
\r
273 !((OptionalBinding)binding).hasValue(cval))
\r
275 Component component = components[i];
\r
276 ctx.b.append(component.name);
\r
277 ctx.b.append(" = ");
\r
278 binding.toString(cval, ctx);
\r
284 protected void toString(Object value, BindingPrintContext ctx) throws BindingException {
\r
285 if(type().isReferable()) {
\r
286 if(ctx.refs.contains(value)) {
\r
287 int val = ctx.refs.get(value);
\r
289 val = ctx.nameCount.value++;
\r
290 ctx.refs.put(value, val);
\r
292 ctx.b.append((char)('a' + val));
\r
295 ctx.refs.put(value, -1);
\r
296 toStringAux(value, ctx);
\r
297 int val = ctx.refs.remove(value);
\r
300 ctx.b.append((char)('a' + val));
\r
305 toStringAux(value, ctx);
\r
310 public Binding getComponentBinding(ChildReference path) throws IllegalArgumentException {
\r
311 if (path==null) return this;
\r
312 if (path instanceof IndexReference) {
\r
313 IndexReference ir = (IndexReference) path;
\r
314 return componentBindings[ir.index].getComponentBinding(path.childReference);
\r
316 if (path instanceof NameReference) {
\r
317 NameReference nr = (NameReference) path;
\r
318 return getComponentBinding( nr.name ).getComponentBinding(path.childReference);
\r
320 if (path instanceof LabelReference) {
\r
321 LabelReference lr = (LabelReference) path;
\r
323 Integer i = new Integer(lr.label);
\r
324 return getComponentBinding( i ).getComponentBinding(path.childReference);
\r
325 } catch (NumberFormatException nfe) {
\r
326 return getComponentBinding( lr.label ).getComponentBinding(path.childReference);
\r
329 throw new IllegalArgumentException();
\r
333 public boolean isImmutable() {
\r
334 return getComponentCount() == 0;
\r
337 public void setBoolean(Object r, int index, boolean z) throws BindingException
\r
339 setComponent(r, index, ((BooleanBinding)componentBindings[index]).create(z));
\r
342 public boolean getBoolean(Object r, int index) throws BindingException
\r
344 return ((BooleanBinding)componentBindings[index]).getValue_( getComponent(r, index) );
\r
347 public void setByte(Object r, int index, byte x) throws BindingException
\r
349 setComponent(r, index, ((ByteBinding)componentBindings[index]).create(x));
\r
352 public byte getByte(Object r, int index) throws BindingException
\r
354 return ((ByteBinding)componentBindings[index]).getValue_( getComponent(r, index) );
\r
357 public void setInt(Object r, int index, int x) throws BindingException
\r
359 setComponent(r, index, ((IntegerBinding)componentBindings[index]).create(x));
\r
362 public int getInt(Object r, int index) throws BindingException
\r
364 return ((IntegerBinding)componentBindings[index]).getValue_( getComponent(r, index) );
\r
367 public void setLong(Object r, int index, long x) throws BindingException
\r
369 setComponent(r, index, ((LongBinding)componentBindings[index]).create(x));
\r
372 public long getLong(Object r, int index) throws BindingException
\r
374 return ((LongBinding)componentBindings[index]).getValue_( getComponent(r, index) );
\r
377 public void setFloat(Object r, int index, float x) throws BindingException
\r
379 setComponent(r, index, ((FloatBinding)componentBindings[index]).create(x));
\r
382 public float getFloat(Object r, int index) throws BindingException
\r
384 return ((FloatBinding)componentBindings[index]).getValue_( getComponent(r, index) );
\r
387 public void setDouble(Object r, int index, double x) throws BindingException
\r
389 setComponent(r, index, ((DoubleBinding)componentBindings[index]).create(x));
\r
392 public double getDouble(Object r, int index) throws BindingException
\r
394 return ((DoubleBinding)componentBindings[index]).getValue_( getComponent(r, index) );
\r
398 protected boolean deepEquals(Object obj,
\r
399 Set<IdentityPair<Binding, Binding>> compareHistory) {
\r
400 RecordBinding o = (RecordBinding)obj;
\r
401 if (!super.deepEquals( obj, compareHistory ))
\r
404 if (componentBindings.length != o.componentBindings.length) return false;
\r
406 for (int i = 0; i < componentBindings.length; i++)
\r
407 if (!componentBindings[i].equals(o.componentBindings[i], compareHistory))
\r
414 public int deepHashCode(IdentityHashMap<Object, Object> hashedObjects) {
\r
415 int code = super.deepHashCode(hashedObjects);
\r
416 for (int i = 0; i < componentBindings.length; i++)
\r
417 code = 17 * code + componentBindings[i].hashCode(hashedObjects);
\r