/******************************************************************************* * 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.mutable; import java.io.IOException; import org.simantics.databoard.Accessors; import org.simantics.databoard.Bindings; import org.simantics.databoard.accessor.VariantAccessor; import org.simantics.databoard.accessor.error.AccessorConstructionException; import org.simantics.databoard.accessor.java.JavaObject; import org.simantics.databoard.accessor.reference.ChildReference; import org.simantics.databoard.adapter.AdaptException; import org.simantics.databoard.binding.Binding; import org.simantics.databoard.binding.error.BindingException; import org.simantics.databoard.binding.error.RuntimeBindingException; import org.simantics.databoard.binding.reflection.VoidBinding; import org.simantics.databoard.parser.DataValuePrinter; import org.simantics.databoard.parser.PrintFormat; import org.simantics.databoard.parser.repository.DataValueRepository; import org.simantics.databoard.parser.unparsing.DataTypePrinter; import org.simantics.databoard.type.Datatype; /** * Variant is a container to a data value of any type. * This very class is immutable, the value and type cannot be changed, but the * sub-class {@link MutableVariant} is not.

* * Variant is hash-equals-comparable, even variants of bindings (and types). * The hash function and comparison rules are defined in the manual.

* * @see ImmutableVariantBinding is binding for Variant-class * @author Toni Kalajainen */ public class Variant implements Comparable, Cloneable { public static Variant ofInstance(Object instance) { Binding binding = Bindings.getBindingUnchecked( instance.getClass() ); return new Variant(binding, instance); } Binding binding; Object value; /** * Constract a variant with a default value of empty record {} */ public Variant() { binding = VoidBinding.VOID_BINDING; value = null; } public Variant(Variant v) { //assert(isValid(binding, value)); this.binding = v.getBinding(); this.value = v.getValue(); } public Variant(Binding binding, Object value) { //assert(isValid(binding, value)); this.binding = binding; this.value = value; } public Object getValue() { return value; } /** * Get and if necessary and possible, type-adapt the value. * * @param binding * @return the value in given binding * @throws AdaptException */ public Object getValue(Binding binding) throws AdaptException { return Bindings.adapt(value, this.binding, binding); } public VariantAccessor getAccessor() { try { return (VariantAccessor) Accessors.getAccessor( Bindings.VARIANT, this); } catch (AccessorConstructionException e) { // Unexpected throw new RuntimeException(e); } } public Datatype type() { return binding.type(); } public Binding getBinding() { return binding; } @Override public int hashCode() { try { return binding.hashValue(value); } catch (BindingException e) { throw new RuntimeBindingException(e); } } @Override public boolean equals(Object obj) { if(this == obj) return true; if(obj == null) return false; if(!(obj instanceof Variant)) return false; try { Variant o = (Variant) obj; if (o.binding==binding && o.value == value) return true; return Bindings.equals(binding, value, o.binding, o.value); } catch (BindingException e) { throw new RuntimeBindingException(e); } } public boolean valueEquals(Binding binding, Object obj) { if (this.binding == binding) return binding.equals(obj, this.value); Object adapted; try { adapted = Bindings.adapt(obj, binding, this.binding); return this.binding.equals(adapted, this.value); } catch (AdaptException e) { return false; } } @Override public int compareTo(Variant o) { try { if (o.binding==binding && o.value == value) return 0; return Bindings.compare(binding, value, o.binding, o.value); } catch (BindingException e) { throw new RuntimeBindingException(e); } } @Override public String toString() { try { StringBuilder sb = new StringBuilder(); DataValueRepository repo = new DataValueRepository(); DataValuePrinter printer = new DataValuePrinter(sb, repo); printer.setFormat(PrintFormat.SINGLE_LINE); printer.print(binding, value); sb.append(" : "); DataTypePrinter printer2 = new DataTypePrinter(sb); printer2.print(binding.type()); return sb.toString(); } catch (IOException e) { throw new RuntimeException(e); } catch (BindingException e) { throw new RuntimeBindingException(e); } } public Variant clone() { if (binding.isImmutable()) return new Variant(binding, value); Object newValue = Bindings.cloneUnchecked(value, binding, binding); return new Variant(binding, newValue); } boolean isValid(Binding binding, Object obj) throws RuntimeBindingException { try { binding.assertInstaceIsValid(obj); return true; } catch (BindingException e) { throw new RuntimeBindingException(e); } } /** * Open a variant to a component object. * * @param ref child reference, if null then this object is returned * @return variant to this or new variant that refers to actual child object * @throws AccessorConstructionException */ public Variant getComponent(ChildReference ref) throws AccessorConstructionException { if ( ref == null ) return this; JavaObject jo = (JavaObject) Accessors.getAccessor(this, ref); return new Variant( jo.getBinding(), jo.getObject() ); } }