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;
17 import org.simantics.databoard.Bindings;
18 import org.simantics.databoard.accessor.reference.ChildReference;
19 import org.simantics.databoard.accessor.reference.IndexReference;
20 import org.simantics.databoard.accessor.reference.LabelReference;
21 import org.simantics.databoard.accessor.reference.NameReference;
22 import org.simantics.databoard.adapter.AdaptException;
23 import org.simantics.databoard.binding.error.BindingException;
24 import org.simantics.databoard.binding.error.RuntimeBindingException;
25 import org.simantics.databoard.binding.impl.BindingPrintContext;
26 import org.simantics.databoard.type.UnionType;
27 import org.simantics.databoard.util.IdentityPair;
31 * This is a binding of Union Type and a Java Object.
34 * @author Toni Kalajainen <toni.kalajainen@vtt.fi>
36 public abstract class UnionBinding extends Binding {
38 protected Binding[] componentBindings;
40 public UnionBinding() {}
42 public UnionBinding(Binding...componentBindings) {
43 this.componentBindings = componentBindings;
47 public UnionType type() {
48 return (UnionType) type;
51 public int getComponentCount() {
52 return type().getComponentCount();
55 public Binding getComponentBinding(int tagIndex) {
56 return componentBindings[tagIndex];
59 public Binding getComponentBinding(String tagName) {
60 return componentBindings[ type().getComponentIndex2(tagName) ];
63 public Binding[] getComponentBindings() {
64 return componentBindings;
68 * Get tag number of an instance.
71 * @return the tag number
72 * @throws BindingException is thrown if the instance is not a tag of this union
74 public abstract int getTag(Object obj) throws BindingException;
76 public abstract Object getValue(Object obj) throws BindingException;
78 public abstract Object create(int tag, Object value) throws BindingException;
81 * Create a new union object with tag of default value.
84 * @return new union object
85 * @throws BindingException
87 public Object createDefault(int tag) throws BindingException {
88 Binding cb = getComponentBinding(tag);
89 Object to = cb.createDefault();
90 return create(tag, to);
93 public Object create(String tag, Object value) throws BindingException {
94 Integer tagIndex = type().getComponentIndex(tag);
96 throw new BindingException("Union type does not have a tag " + tag + ".");
97 return create(tagIndex, value);
100 public Object createUnchecked(int tag, Object value) throws RuntimeBindingException
103 return create(tag, value);
104 } catch (BindingException e) {
105 throw new RuntimeBindingException(e);
110 public void readFrom(Binding srcBinding, Object src, Object dst)
111 throws BindingException {
112 UnionBinding sb = (UnionBinding) srcBinding;
113 int newTag = sb.getTag(src);
114 int oldTag = getTag(dst);
117 Binding nvb = sb.getComponentBinding(newTag);
119 Object nv = sb.getValue(src);
122 if (newTag==oldTag) {
123 // Same tag - old value is used if possible
126 Binding ovb = getComponentBinding(oldTag);
128 Object ov = getValue(dst);
129 ov = ovb.readFromTry(nvb, nv, ov);
130 setValue(dst, oldTag, ov);
133 // Different tag - old value is not used
134 boolean clone = !nvb.isImmutable();
135 Binding dcb = getComponentBinding(newTag);
136 boolean adapt = nvb!=dcb;
138 if ( !adapt && !clone) {
139 setValue(dst, newTag, nv);
142 // Clone or adapt value if necessary.
143 Object dv = Bindings.clone(nv, nvb, dcb);
144 setValue(dst, newTag, dv);
145 } catch(AdaptException e) {
146 throw new BindingException(e);
152 if (dcb.isImmutable() || st!=dt) {
154 Object dv = Bindings.clone(sv, scb, dcb);
155 setValue(dst, st, dv);
156 } catch(AdaptException e) {
157 throw new BindingException(e);
160 Object dv = getValue(dst);
161 dv = dcb.readFromTry(scb, sv, dv);
162 setValue(dst, st, dv);
168 * Set value to an union.
169 * Throws BindingException if value cannot be written.
174 * @throws BindingException
176 public abstract void setValue(Object union, int tag, Object value) throws BindingException;
179 * Set to tag with default value.
183 * @throws BindingException
185 public void setTag(Object union, int tag) throws BindingException {
186 Binding componentBinding = getComponentBinding(tag);
187 Object instance = componentBinding.createDefault();
188 setValue(union, tag, instance);
192 public void accept(Visitor1 v, Object obj) {
197 public <T> T accept(Visitor<T> v) {
198 return v.visit(this);
202 * Asserts the obj is valid to its UnionType.
204 * Asserts the obj using the component type
206 * @throws BindingException if obj is not valid according to the UnionType
209 public void assertInstaceIsValid(Object obj, Set<Object> validInstances) throws BindingException {
210 UnionType type = type();
211 int length = type.getComponentCount();
212 if (length==0) return;
213 int tag = getTag(obj);
214 if (tag<0 || tag>=length)
215 throw new BindingException("Instance tag ("+tag+") is out of range [0.."+(length-1)+"], faulty UnionBinding");
217 Object componentValue = getValue(obj);
218 Binding componentBinding = getComponentBindings()[tag];
219 componentBinding.assertInstaceIsValid(componentValue, validInstances);
223 public int deepHashValue(Object value, IdentityHashMap<Object, Object> hashedObjects) throws BindingException {
224 int tag = getTag(value);
225 Object element = getValue(value);
226 return tag + componentBindings[tag].deepHashValue(element, hashedObjects);
230 public int deepCompare(Object o1, Object o2,
231 Set<IdentityPair<Object, Object>> compareHistory)
232 throws BindingException {
233 Integer t1 = getTag(o1);
234 Integer t2 = getTag(o2);
235 int dif = t1.compareTo(t2);
236 if (dif!=0) return dif;
237 Object v1 = getValue(o1);
238 Object v2 = getValue(o2);
239 Binding c = getComponentBindings()[t1];
240 return c.deepCompare(v1, v2, compareHistory);
243 public void setComponentBindings(Binding[] componentBindings) {
244 this.componentBindings = componentBindings;
248 protected void toString(Object value, BindingPrintContext ctx) throws BindingException {
249 int tag = getTag(value);
250 ctx.b.append(type().getComponent(tag).name);
252 getComponentBinding(tag).toString(getValue(value), ctx);
256 public Binding getComponentBinding(ChildReference path) {
257 if (path==null) return this;
258 if (path instanceof IndexReference) {
259 IndexReference ir = (IndexReference) path;
260 return componentBindings[ir.index].getComponentBinding(path.childReference);
262 if (path instanceof NameReference) {
263 NameReference nr = (NameReference) path;
264 return getComponentBinding( nr.name ).getComponentBinding(path.childReference);
266 if (path instanceof LabelReference) {
267 LabelReference lr = (LabelReference) path;
269 Integer i = new Integer(lr.label);
270 return getComponentBinding( i ).getComponentBinding(path.childReference);
271 } catch (NumberFormatException nfe) {
272 return getComponentBinding( lr.label ).getComponentBinding(path.childReference);
275 throw new IllegalArgumentException();
279 * Returns true if the tag of this union type can be modified
281 * @return true if mutable
283 public boolean isTagMutable() {
288 protected boolean deepEquals(Object obj,
289 Set<IdentityPair<Binding, Binding>> compareHistory) {
290 if (!super.deepEquals( obj, compareHistory ))
293 UnionBinding o = (UnionBinding)obj;
294 if (componentBindings.length != o.componentBindings.length) return false;
296 for (int i = 0; i < componentBindings.length; i++)
297 if (!componentBindings[i].equals(o.componentBindings[i], compareHistory))
304 public int deepHashCode(IdentityHashMap<Object, Object> hashedObjects) {
305 int code = super.deepHashCode( hashedObjects );
306 for (int i = 0; i < componentBindings.length; i++)
307 code = 17 * code + componentBindings[i].hashCode(hashedObjects);