/*******************************************************************************
* 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() );
}
}