/******************************************************************************* * 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.Set; 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.binding.error.BindingException; import org.simantics.databoard.binding.error.RuntimeBindingException; import org.simantics.databoard.binding.impl.BindingPrintContext; import org.simantics.databoard.type.UnionType; import org.simantics.databoard.util.IdentityPair; /** * This is a binding of Union Type and a Java Object. * * @see UnionType * @author Toni Kalajainen */ public abstract class UnionBinding extends Binding { protected Binding[] componentBindings; public UnionBinding() {} public UnionBinding(Binding...componentBindings) { this.componentBindings = componentBindings; } @Override public UnionType type() { return (UnionType) type; } public int getComponentCount() { return type().getComponentCount(); } public Binding getComponentBinding(int tagIndex) { return componentBindings[tagIndex]; } public Binding getComponentBinding(String tagName) { return componentBindings[ type().getComponentIndex2(tagName) ]; } public Binding[] getComponentBindings() { return componentBindings; } /** * Get tag number of an instance. * * @param obj * @return the tag number * @throws BindingException is thrown if the instance is not a tag of this union */ public abstract int getTag(Object obj) throws BindingException; public abstract Object getValue(Object obj) throws BindingException; public abstract Object create(int tag, Object value) throws BindingException; /** * Create a new union object with tag of default value. * * @param tag * @return new union object * @throws BindingException */ public Object createDefault(int tag) throws BindingException { Binding cb = getComponentBinding(tag); Object to = cb.createDefault(); return create(tag, to); } public Object create(String tag, Object value) throws BindingException { Integer tagIndex = type().getComponentIndex(tag); if(tagIndex == null) throw new BindingException("Union type does not have a tag " + tag + "."); return create(tagIndex, value); } public Object createUnchecked(int tag, Object value) throws RuntimeBindingException { try { return create(tag, value); } catch (BindingException e) { throw new RuntimeBindingException(e); } } @Override public void readFrom(Binding srcBinding, Object src, Object dst) throws BindingException { UnionBinding sb = (UnionBinding) srcBinding; int newTag = sb.getTag(src); int oldTag = getTag(dst); // New value binding Binding nvb = sb.getComponentBinding(newTag); // New value Object nv = sb.getValue(src); // Same tag if (newTag==oldTag) { // Same tag - old value is used if possible // Old value binding Binding ovb = getComponentBinding(oldTag); Object ov = getValue(dst); ov = ovb.readFromTry(nvb, nv, ov); setValue(dst, oldTag, ov); } else { // Different tag - old value is not used boolean clone = !nvb.isImmutable(); Binding dcb = getComponentBinding(newTag); boolean adapt = nvb!=dcb; if ( !adapt && !clone) { setValue(dst, newTag, nv); } else { try { // Clone or adapt value if necessary. Object dv = Bindings.clone(nv, nvb, dcb); setValue(dst, newTag, dv); } catch(AdaptException e) { throw new BindingException(e); } } } /* if (dcb.isImmutable() || st!=dt) { try { Object dv = Bindings.clone(sv, scb, dcb); setValue(dst, st, dv); } catch(AdaptException e) { throw new BindingException(e); } } else { Object dv = getValue(dst); dv = dcb.readFromTry(scb, sv, dv); setValue(dst, st, dv); } */ } /** * Set value to an union. * Throws BindingException if value cannot be written. * * @param union * @param tag * @param value * @throws BindingException */ public abstract void setValue(Object union, int tag, Object value) throws BindingException; /** * Set to tag with default value. * * @param union * @param tag * @throws BindingException */ public void setTag(Object union, int tag) throws BindingException { Binding componentBinding = getComponentBinding(tag); Object instance = componentBinding.createDefault(); setValue(union, tag, instance); } @Override public void accept(Visitor1 v, Object obj) { v.visit(this, obj); } @Override public T accept(Visitor v) { return v.visit(this); } /** * Asserts the obj is valid to its UnionType. * * Asserts the obj using the component type * * @throws BindingException if obj is not valid according to the UnionType */ @Override public void assertInstaceIsValid(Object obj, Set validInstances) throws BindingException { UnionType type = type(); int length = type.getComponentCount(); if (length==0) return; int tag = getTag(obj); if (tag<0 || tag>=length) throw new BindingException("Instance tag ("+tag+") is out of range [0.."+(length-1)+"], faulty UnionBinding"); Object componentValue = getValue(obj); Binding componentBinding = getComponentBindings()[tag]; componentBinding.assertInstaceIsValid(componentValue, validInstances); } @Override public int deepHashValue(Object value, IdentityHashMap hashedObjects) throws BindingException { int tag = getTag(value); Object element = getValue(value); return tag + componentBindings[tag].deepHashValue(element, hashedObjects); } @Override public int deepCompare(Object o1, Object o2, Set> compareHistory) throws BindingException { Integer t1 = getTag(o1); Integer t2 = getTag(o2); int dif = t1.compareTo(t2); if (dif!=0) return dif; Object v1 = getValue(o1); Object v2 = getValue(o2); Binding c = getComponentBindings()[t1]; return c.deepCompare(v1, v2, compareHistory); } public void setComponentBindings(Binding[] componentBindings) { this.componentBindings = componentBindings; } @Override protected void toString(Object value, BindingPrintContext ctx) throws BindingException { int tag = getTag(value); ctx.b.append(type().getComponent(tag).name); ctx.b.append(' '); getComponentBinding(tag).toString(getValue(value), ctx); } @Override public Binding getComponentBinding(ChildReference path) { if (path==null) return this; if (path instanceof IndexReference) { IndexReference ir = (IndexReference) path; return componentBindings[ir.index].getComponentBinding(path.childReference); } if (path instanceof NameReference) { NameReference nr = (NameReference) path; return getComponentBinding( nr.name ).getComponentBinding(path.childReference); } if (path instanceof LabelReference) { LabelReference lr = (LabelReference) path; try { Integer i = new Integer(lr.label); return getComponentBinding( i ).getComponentBinding(path.childReference); } catch (NumberFormatException nfe) { return getComponentBinding( lr.label ).getComponentBinding(path.childReference); } } throw new IllegalArgumentException(); } /** * Returns true if the tag of this union type can be modified * * @return true if mutable */ public boolean isTagMutable() { return true; } @Override protected boolean deepEquals(Object obj, Set> compareHistory) { if (!super.deepEquals( obj, compareHistory )) return false; UnionBinding o = (UnionBinding)obj; if (componentBindings.length != o.componentBindings.length) return false; for (int i = 0; i < componentBindings.length; i++) if (!componentBindings[i].equals(o.componentBindings[i], compareHistory)) return false; return true; } @Override public int deepHashCode(IdentityHashMap hashedObjects) { int code = super.deepHashCode( hashedObjects ); for (int i = 0; i < componentBindings.length; i++) code = 17 * code + componentBindings[i].hashCode(hashedObjects); return code; } }