--- /dev/null
+/*******************************************************************************\r
+ * Copyright (c) 2010 Association for Decentralized Information Management in\r
+ * Industry THTH ry.\r
+ * All rights reserved. This program and the accompanying materials\r
+ * are made available under the terms of the Eclipse Public License v1.0\r
+ * which accompanies this distribution, and is available at\r
+ * http://www.eclipse.org/legal/epl-v10.html\r
+ *\r
+ * Contributors:\r
+ * VTT Technical Research Centre of Finland - initial API and implementation\r
+ *******************************************************************************/\r
+package org.simantics.databoard.adapter;
+
+import java.util.ArrayList;\r
+import java.util.Map;\r
+\r
+import org.apache.commons.collections.map.ReferenceMap;\r
+import org.simantics.databoard.Units;\r
+import org.simantics.databoard.binding.ArrayBinding;\r
+import org.simantics.databoard.binding.Binding;\r
+import org.simantics.databoard.binding.BooleanBinding;\r
+import org.simantics.databoard.binding.MapBinding;\r
+import org.simantics.databoard.binding.NumberBinding;\r
+import org.simantics.databoard.binding.OptionalBinding;\r
+import org.simantics.databoard.binding.RecordBinding;\r
+import org.simantics.databoard.binding.StringBinding;\r
+import org.simantics.databoard.binding.UnionBinding;\r
+import org.simantics.databoard.binding.VariantBinding;\r
+import org.simantics.databoard.binding.error.BindingException;\r
+import org.simantics.databoard.binding.error.RuntimeBindingException;\r
+import org.simantics.databoard.binding.impl.ArrayListBinding;\r
+import org.simantics.databoard.binding.impl.BooleanArrayBinding;\r
+import org.simantics.databoard.binding.impl.ByteArrayBinding;\r
+import org.simantics.databoard.binding.impl.DoubleArrayBinding;\r
+import org.simantics.databoard.binding.impl.FloatArrayBinding;\r
+import org.simantics.databoard.binding.impl.IntArrayBinding;\r
+import org.simantics.databoard.binding.impl.LongArrayBinding;\r
+import org.simantics.databoard.type.ArrayType;\r
+import org.simantics.databoard.type.NumberType;\r
+import org.simantics.databoard.type.RecordType;\r
+import org.simantics.databoard.type.UnionType;\r
+import org.simantics.databoard.units.IUnitConverter;\r
+import org.simantics.databoard.units.IdentityConverter;\r
+import org.simantics.databoard.units.internal.UnitParseException;\r
+import org.simantics.databoard.util.ObjectUtils;\r
+
+/**
+ * AdapterRepository is a factory and a collection of adapters.
+ *
+ * @author Toni Kalajainen <toni.kalajainen@vtt.fi>
+ */
+public class AdapterFactory {
+
+ @SuppressWarnings( "unchecked" )\r
+ Map<AdapterRequest, AbstractAdapter> cache = (Map<AdapterRequest, AbstractAdapter>) new ReferenceMap(ReferenceMap.SOFT, ReferenceMap.HARD);
+
+ public synchronized Adapter getAdapter(Binding domain, Binding range, boolean typeAdapter, boolean mustClone)
+ throws AdapterConstructionException
+ {
+ if ((!mustClone || domain.isImmutable()) && domain.equals(range)) return PassThruAdapter.PASSTHRU;\r
+ \r
+ if (domain.getClass() == range.getClass() &&\r
+ ( !mustClone || domain.isImmutable() ) &&\r
+ NumberBinding.class.isAssignableFrom( domain.getClass() ) ) {\r
+ \r
+ NumberBinding db = (NumberBinding) domain;\r
+ NumberBinding rb = (NumberBinding) range;\r
+ String u1 = db.type().getUnit();\r
+ String u2 = rb.type().getUnit();\r
+ if (u1==null || u2==null || u1.equals("") || u2.equals("") || u1.equals(u2)) return PassThruAdapter.PASSTHRU;\r
+ }\r
+
+ return getAdapterUnsynchronized(domain, range, typeAdapter, mustClone);
+ }\r
+
+ private AbstractAdapter getCached(AdapterRequest type)
+ {
+ return cache.get(type);
+ }
+
+ private void cache(AdapterRequest type, AbstractAdapter binding) {
+ cache.put(type, binding);
+ }
+
+ private void addToCache(AdapterRequest request, AbstractAdapter impl) {\r
+ impl.request = request;
+ cache(request, impl);
+
+ // This request applies to "must clone" request aswell, remember this implementation
+ if (!request.mustClone && impl.clones) {
+ request = new AdapterRequest(request.domain, request.range, true);
+ cache(request, impl);
+ }
+ }
+
+ /**
+ * Create adapter, does not cache the result.
+ *
+ * @param domain
+ * @param range
+ * @param typeAdapter if true, primitive conversion is allowed (e.g. int -> double)
+ * @return
+ */
+ private AbstractAdapter getAdapterUnsynchronized(Binding domain, Binding range, boolean typeAdapter, final boolean mustClone)
+ throws AdapterConstructionException
+ {
+ if ( !mustClone && domain.equals(range) ) return PassThruAdapter.PASSTHRU;
+
+ AdapterRequest req = new AdapterRequest(domain, range, mustClone);
+ AbstractAdapter cachedResult = getCached(req);
+ if (cachedResult!=null) return cachedResult;
+
+ try {
+
+ if (domain instanceof RecordBinding && range instanceof RecordBinding)
+ {
+ final RecordBinding domainRecord = (RecordBinding) domain;
+ final RecordBinding rangeRecord = (RecordBinding) range;
+ RecordType domainType = domainRecord.type();
+ RecordType rangeType = rangeRecord.type(); \r
+ \r
+ // Field-Map describes the index of the fields in domain for each field in range
+ boolean requiresTypeAdapting = domainType.getComponentCount() != rangeType.getComponentCount();
+ int fieldMap[] = new int[rangeType.getComponentCount()];
+ for (int rangeIndex=0; rangeIndex<fieldMap.length; rangeIndex++)
+ {
+ String fieldName = rangeType.getComponent(rangeIndex).name;
+ Integer domainIndex = domainType.getComponentIndex(fieldName);\r
+ if (domainIndex!=null) {\r
+ fieldMap[rangeIndex] = domainIndex;\r
+ requiresTypeAdapting |= rangeIndex != domainIndex;\r
+ } else {\r
+ fieldMap[rangeIndex] = -1;\r
+ requiresTypeAdapting = true;\r
+ }
+ }
+
+ if (requiresTypeAdapting && !typeAdapter) {
+ throw new AdapterConstructionException("Type Adapter required.");
+ }
+
+ final int len = rangeRecord.componentBindings.length;
+ final AbstractAdapter[] componentAdapters = new AbstractAdapter[len];
+ AbstractAdapter result = null;
+
+ if (!requiresTypeAdapting) {
+ // Normal Adapter
+ result = new AbstractAdapter() {
+ @Override
+ public Object adapt(Object src) throws AdaptException {
+ try {
+ Object values[] = new Object[len];
+ for (int i=0; i<len; i++)
+ {
+ Object srcValue = domainRecord.getComponent(src, i);
+ Object dstValue = componentAdapters[i].adapt(srcValue);
+ values[i] = dstValue;
+ }
+ return rangeRecord.create(values);
+ } catch (BindingException e) {
+ throw new AdaptException(e);
+ }
+ }
+ };
+ } else {
+ // Type Adapter - Type adapter maps fields of different order
+ final int _fieldMap[] = fieldMap;
+ result = new AbstractAdapter() {
+ @Override
+ public Object adapt(Object src) throws AdaptException {
+ try {
+ Object values[] = new Object[len];
+ for (int rangeIndex=0; rangeIndex<len; rangeIndex++)
+ {
+ int domainIndex = _fieldMap[rangeIndex];\r
+ if (domainIndex>=0) {\r
+ Object srcValue = domainRecord.getComponent(src, domainIndex);\r
+ Object dstValue = componentAdapters[rangeIndex].adapt(srcValue);\r
+ values[rangeIndex] = dstValue;\r
+ } else {\r
+ // Optional value\r
+ values[rangeIndex] = rangeRecord.componentBindings[rangeIndex].createDefault();\r
+ }
+ }
+ return rangeRecord.create(values);
+ } catch (BindingException e) {
+ throw new AdaptException(e);
+ }
+ }
+ };
+ result.typeAdapter = true;
+ }
+
+ addToCache(req, result);
+ result.clones = true;
+ for (int rangeIndex=0; rangeIndex<len; rangeIndex++)
+ {
+ int domainIndex = fieldMap[rangeIndex];\r
+ if (domainIndex>=0) {
+ componentAdapters[rangeIndex] = getAdapterUnsynchronized(domainRecord.componentBindings[domainIndex], rangeRecord.componentBindings[rangeIndex], typeAdapter, mustClone);
+ result.typeAdapter |= componentAdapters[rangeIndex].typeAdapter;
+ result.clones &= componentAdapters[rangeIndex].clones;\r
+ }
+ }
+ return result;
+ }
+
+ if (domain instanceof UnionBinding && range instanceof UnionBinding)
+ {
+ final UnionBinding domainBinding = (UnionBinding) domain;
+ final UnionBinding rangeBinding = (UnionBinding) range;
+ UnionType domainType = domainBinding.type();
+ UnionType rangeType = rangeBinding.type();
+
+ // Tag-Map describes the index of the tag-types in domain for each tag-type in range
+ boolean requiresTypeAdapting = domainType.getComponentCount() != rangeType.getComponentCount();
+ int tagMap[] = new int[domainType.getComponentCount()];
+ for (int domainIndex=0; domainIndex<tagMap.length; domainIndex++)
+ {
+ String fieldName = domainType.getComponent(domainIndex).name;
+ Integer rangeIndex = rangeType.getComponentIndex(fieldName);
+ if (rangeIndex==null) throw new AdapterConstructionException("The range UnionType does not have expected tag \""+fieldName+"\"");
+ tagMap[domainIndex] = rangeIndex;
+ requiresTypeAdapting |= rangeIndex != domainIndex;
+ }
+
+ if (requiresTypeAdapting && !typeAdapter) {
+ throw new AdapterConstructionException("Type Adapter required.");
+ }
+
+ final AbstractAdapter[] componentAdapters = new AbstractAdapter[domainType.getComponentCount()];
+
+ AbstractAdapter result = null;
+
+ if (!requiresTypeAdapting) {
+ // Normal adapter
+ result = new AbstractAdapter() {
+ @Override
+ public Object adapt(Object obj) throws AdaptException {
+ try {
+ int tag = domainBinding.getTag(obj);
+ Object srcValue = domainBinding.getValue(obj);
+ Object dstValue = componentAdapters[tag].adapt(srcValue);
+ return rangeBinding.create(tag, dstValue);
+ } catch (BindingException e) {
+ throw new AdaptException(e);
+ }
+ }
+ };
+ } else {
+ // Type adapter, type adapter rearranges tag indices
+ final int _tagMap[] = tagMap;
+ result = new AbstractAdapter() {
+ @Override
+ public Object adapt(Object obj) throws AdaptException {
+ try {
+ int domainTag = domainBinding.getTag(obj);
+ int rangeTag = _tagMap[domainTag];
+ // Domain Component Binding
+ Object srcValue = domainBinding.getValue(obj);
+ Object dstValue = componentAdapters[domainTag].adapt(srcValue);
+ return rangeBinding.create(rangeTag, dstValue);
+ } catch (BindingException e) {
+ throw new AdaptException(e);
+ }
+ }
+ };
+ }
+
+ addToCache(req, result);
+ result.clones = true;
+ for (int domainIndex=0; domainIndex<domainType.getComponentCount(); domainIndex++)
+ {
+ int rangeIndex = tagMap[domainIndex];
+ componentAdapters[domainIndex] = getAdapterUnsynchronized(domainBinding.getComponentBindings()[domainIndex], rangeBinding.getComponentBindings()[rangeIndex], typeAdapter, mustClone);
+ result.typeAdapter |= componentAdapters[domainIndex].typeAdapter;
+ result.clones &= componentAdapters[domainIndex].clones;
+ }
+ return result;
+ }
+
+ if (domain instanceof BooleanBinding && range instanceof BooleanBinding)
+ {
+ final BooleanBinding domainBoolean = (BooleanBinding) domain;
+ final BooleanBinding rangeBoolean = (BooleanBinding) range;
+ AbstractAdapter result = new AbstractAdapter() {
+ @Override
+ public Object adapt(Object obj) throws AdaptException {
+ try {
+ boolean value = domainBoolean.getValue_(obj);
+ return rangeBoolean.create(value);
+ } catch (BindingException e) {
+ throw new AdaptException(e);
+ }
+ }
+ };
+ result.clones = mustClone;
+ result.typeAdapter = true;\r
+ addToCache(req, result);
+ return result;
+ }
+\r
+ if (domain instanceof BooleanBinding && range instanceof NumberBinding)\r
+ {\r
+ try {\r
+ final BooleanBinding domainBoolean = (BooleanBinding) domain;\r
+ final NumberBinding rangeNumber = (NumberBinding) range;\r
+ final Object falseValue = rangeNumber.create( Integer.valueOf(0) );\r
+ final Object trueValue = rangeNumber.create( Integer.valueOf(1) );\r
+ AbstractAdapter result = new AbstractAdapter() {\r
+ @Override\r
+ public Object adapt(Object obj) throws AdaptException {\r
+ try {\r
+ boolean value = domainBoolean.getValue_(obj);\r
+ return value ? trueValue : falseValue;\r
+ } catch (BindingException e) {\r
+ throw new AdaptException(e);\r
+ } \r
+ }\r
+ };\r
+ result.clones = true;\r
+ result.typeAdapter = true;\r
+ addToCache(req, result);\r
+ return result;\r
+ } catch (BindingException e1) {\r
+ throw new AdapterConstructionException(e1);\r
+ }\r
+ } \r
+ \r
+ if (domain instanceof NumberBinding && range instanceof BooleanBinding)\r
+ {\r
+ try {\r
+ final NumberBinding domainNumber = (NumberBinding) domain;\r
+ final BooleanBinding rangeBoolean = (BooleanBinding) range;\r
+ final Object zeroValue = domainNumber.create( Integer.valueOf(0) );\r
+ AbstractAdapter result = new AbstractAdapter() {\r
+ @Override\r
+ public Object adapt(Object obj) throws AdaptException {\r
+ try {\r
+ Object value = domainNumber.getValue(obj);\r
+ boolean bool = !domainNumber.equals(value, zeroValue);\r
+ return rangeBoolean.create(bool);\r
+ } catch (BindingException e) {\r
+ throw new AdaptException(e);\r
+ } \r
+ }\r
+ };\r
+ result.clones = true;\r
+ addToCache(req, result);\r
+ return result;\r
+ } catch (BindingException e1) {\r
+ throw new AdapterConstructionException(e1);\r
+ }\r
+ }\r
+
+ if (domain instanceof StringBinding && range instanceof StringBinding)
+ {
+ final StringBinding domainString = (StringBinding) domain;
+ final StringBinding rangeString = (StringBinding) range;
+ AbstractAdapter result = new AbstractAdapter() {
+ @Override
+ public Object adapt(Object obj) throws AdaptException {
+ try {
+ String value = domainString.getValue(obj);
+ return rangeString.create(value);
+ } catch (BindingException e) {
+ throw new AdaptException(e);
+ }
+ }
+ };
+ result.clones = true;
+ addToCache(req, result);
+ return result;
+ } \r
+ \r
+ if(domain instanceof StringBinding && range instanceof NumberBinding)\r
+ {\r
+ final StringBinding domainString = (StringBinding) domain;\r
+ final NumberBinding rangeString = (NumberBinding) range;\r
+ AbstractAdapter result = new AbstractAdapter() {\r
+ @Override\r
+ public Object adapt(Object obj) throws AdaptException {\r
+ try {\r
+ String value = domainString.getValue(obj);\r
+ return rangeString.create(value);\r
+ } catch (BindingException e) {\r
+ throw new AdaptException(e);\r
+ } \r
+ }\r
+ };\r
+ result.clones = true;\r
+ addToCache(req, result);\r
+ return result;\r
+ }
+\r
+ if(domain instanceof StringBinding && range instanceof BooleanBinding)\r
+ {\r
+ final StringBinding domainString = (StringBinding) domain;\r
+ final BooleanBinding rangeString = (BooleanBinding) range;\r
+ AbstractAdapter result = new AbstractAdapter() {\r
+ @Override\r
+ public Object adapt(Object obj) throws AdaptException {\r
+ try {\r
+ String value = domainString.getValue(obj);\r
+ return rangeString.create(Boolean.parseBoolean(value));\r
+ } catch (BindingException e) {\r
+ throw new AdaptException(e);\r
+ } \r
+ }\r
+ };\r
+ result.clones = true;\r
+ addToCache(req, result);\r
+ return result;\r
+ }\r
+
+ // XXX We can optimizes here by using primitives
+ if (domain instanceof NumberBinding && range instanceof NumberBinding)
+ {
+ final NumberBinding domainNumber = (NumberBinding) domain;
+ final NumberBinding rangeNumber = (NumberBinding) range;
+
+ String domainUnit = ((NumberType) domainNumber.type()).getUnit();
+ String rangeUnit = ((NumberType) rangeNumber.type()).getUnit();\r
+ IUnitConverter unitConverter;\r
+ if(domainUnit == null || rangeUnit == null || domainUnit.equals(rangeUnit))\r
+ unitConverter = null;\r
+ else\r
+ unitConverter = Units.createConverter(domainUnit, rangeUnit); \r
+ /*if(domainUnit == null || domainUnit.equals("")) {\r
+ if(rangeUnit == null || rangeUnit.equals(""))\r
+ unitConverter = null;\r
+ else\r
+ unitConverter = null;\r
+// throw new AdapterConstructionException("Cannot convert from a unitless type to a type with unit.");\r
+ }\r
+ else {\r
+ if(rangeUnit == null || rangeUnit.equals(""))\r
+ unitConverter = null; \r
+// throw new AdapterConstructionException("Cannot convert from a type with unit to unitless type.");\r
+ else\r
+ unitConverter = Units.createConverter(domainUnit, rangeUnit); \r
+ } */
+ boolean doUnitConversion = unitConverter != null && unitConverter != IdentityConverter.INSTANCE;
+ boolean doPrimitiveConversion = !domainNumber.type().getClass().equals( rangeNumber.type().getClass() );
+ if (doPrimitiveConversion && !typeAdapter)
+ throw new AdapterConstructionException("Type mismatch, use Type Adapter instead.");
+
+ AbstractAdapter result;
+ if (!doUnitConversion) {
+ result = new AbstractAdapter() {
+ @Override
+ public Object adapt(Object obj) throws AdaptException {
+ Number value;
+ try {
+ value = domainNumber.getValue(obj);
+ return rangeNumber.create(value);
+ } catch (BindingException e) {
+ throw new AdaptException(e);
+ }
+ }
+ };
+ } else {
+ final IUnitConverter _unitConverter = unitConverter;
+ result = new AbstractAdapter() {
+ @Override
+ public Object adapt(Object obj) throws AdaptException {
+ try {
+ Number value = domainNumber.getValue(obj);
+ double convertedValue = _unitConverter.convert(value.doubleValue());
+ return rangeNumber.create(Double.valueOf(convertedValue));
+ } catch (BindingException e) {
+ throw new AdaptException(e);
+ }
+ }
+ };
+ }
+ result.typeAdapter = doPrimitiveConversion;
+ result.clones = true;
+ addToCache(req, result);
+ return result;
+ }
+
+ if (domain instanceof BooleanArrayBinding && range instanceof BooleanArrayBinding)
+ {
+ final BooleanArrayBinding domainArray = (BooleanArrayBinding) domain;
+ final BooleanArrayBinding rangeArray = (BooleanArrayBinding) range;
+ AbstractAdapter result = new AbstractAdapter() {
+ @Override
+ public Object adapt(Object obj) throws AdaptException {
+ try {
+ boolean[] data = domainArray.getArray(obj);\r
+ if (mustClone) data = data.clone();
+ return rangeArray.create(data);
+ } catch (BindingException e) {
+ throw new AdaptException(e);
+ }
+ }
+ };
+ result.clones = true;
+ addToCache(req, result);
+ return result;
+ }
+
+ if (domain instanceof ByteArrayBinding && range instanceof ByteArrayBinding)
+ {
+ final ByteArrayBinding domainArray = (ByteArrayBinding) domain;
+ final ByteArrayBinding rangeArray = (ByteArrayBinding) range;
+
+ String domainUnit = ((NumberType) ((ArrayType)domainArray.type()).componentType).getUnit();
+ String rangeUnit = ((NumberType) ((ArrayType)rangeArray.type()).componentType).getUnit();
+ IUnitConverter unitConverter = ObjectUtils.objectEquals(domainUnit, rangeUnit) ? null : Units.createConverter(domainUnit, rangeUnit);
+ boolean doUnitConversion = unitConverter != null && unitConverter != IdentityConverter.INSTANCE;
+
+ AbstractAdapter result;
+ if (doUnitConversion) {
+ final IUnitConverter _unitConverter = unitConverter;
+ result = new AbstractAdapter() {
+ @Override
+ public Object adapt(Object obj) throws AdaptException {
+ try {
+ byte[] data = domainArray.getArray(obj);
+ for (int i=0; i<data.length; i++) {
+ byte value = data[i];
+ double convertedValue = _unitConverter.convert((double)value);
+ data[i] = (byte) convertedValue;
+ }
+ return rangeArray.create(data);
+ } catch (BindingException e) {
+ throw new AdaptException(e);
+ }
+ }
+ };
+ } else {
+ result = new AbstractAdapter() {
+ @Override
+ public Object adapt(Object obj) throws AdaptException {
+ try {
+ byte[] data = domainArray.getArray(obj);\r
+ if (mustClone) data = data.clone();
+ return rangeArray.create(data);
+ } catch (BindingException e) {
+ throw new AdaptException(e);
+ }
+ }
+ };
+ }
+ result.clones = true;
+ addToCache(req, result);
+ return result;
+ }
+
+ if (domain instanceof IntArrayBinding && range instanceof IntArrayBinding)
+ {
+ final IntArrayBinding domainArray = (IntArrayBinding) domain;
+ final IntArrayBinding rangeArray = (IntArrayBinding) range;
+
+ String domainUnit = ((NumberType) ((ArrayType)domainArray.type()).componentType).getUnit();
+ String rangeUnit = ((NumberType) ((ArrayType)rangeArray.type()).componentType).getUnit();
+ IUnitConverter unitConverter = ObjectUtils.objectEquals(domainUnit, rangeUnit) ||\r
+ domainUnit == null || rangeUnit == null ? null : Units.createConverter(domainUnit, rangeUnit);
+ boolean doUnitConversion = unitConverter != null && unitConverter != IdentityConverter.INSTANCE;
+
+ AbstractAdapter result;
+ if (doUnitConversion) {
+ final IUnitConverter _unitConverter = unitConverter;
+ result = new AbstractAdapter() {
+ @Override
+ public Object adapt(Object obj) throws AdaptException {
+ try {
+ int[] data = domainArray.getArray(obj);
+ for (int i=0; i<data.length; i++) {
+ int value = data[i];
+ double convertedValue = _unitConverter.convert((double)value);
+ data[i] = (int) convertedValue;
+ }
+ return rangeArray.create(data);
+ } catch (BindingException e) {
+ throw new AdaptException(e);
+ }
+ }
+ };
+ } else {
+ result = new AbstractAdapter() {
+ @Override
+ public Object adapt(Object obj) throws AdaptException {
+ try {
+ int[] data = domainArray.getArray(obj);\r
+ if (mustClone) data = data.clone();
+ return rangeArray.create(data);
+ } catch (BindingException e) {
+ throw new AdaptException(e);
+ }
+ }
+ };
+ }
+
+ result.clones = true;
+ addToCache(req, result);
+ return result;
+ }
+
+ if (domain instanceof LongArrayBinding && range instanceof LongArrayBinding)
+ {
+ final LongArrayBinding domainArray = (LongArrayBinding) domain;
+ final LongArrayBinding rangeArray = (LongArrayBinding) range;
+
+ String domainUnit = ((NumberType) ((ArrayType)domainArray.type()).componentType).getUnit();
+ String rangeUnit = ((NumberType) ((ArrayType)rangeArray.type()).componentType).getUnit();
+ IUnitConverter unitConverter = ObjectUtils.objectEquals(domainUnit, rangeUnit) ||\r
+ domainUnit == null || rangeUnit == null ? null : Units.createConverter(domainUnit, rangeUnit);
+ boolean doUnitConversion = unitConverter != null && unitConverter != IdentityConverter.INSTANCE;
+
+ AbstractAdapter result;
+ if (doUnitConversion) {
+ final IUnitConverter _unitConverter = unitConverter;
+ result = new AbstractAdapter() {
+ @Override
+ public Object adapt(Object obj) throws AdaptException {
+ try {
+ long[] data = domainArray.getArray(obj);
+ for (int i=0; i<data.length; i++) {
+ long value = data[i];
+ double convertedValue = _unitConverter.convert((double)value);
+ data[i] = (long) convertedValue;
+ }
+ return rangeArray.create(data);
+ } catch (BindingException e) {
+ throw new AdaptException(e);
+ }
+ }
+ };
+ } else {
+ result = new AbstractAdapter() {
+ @Override
+ public Object adapt(Object obj) throws AdaptException {
+ try {
+ long[] data = domainArray.getArray(obj);\r
+ if (mustClone) data = data.clone();
+ return rangeArray.create(data);
+ } catch (BindingException e) {
+ throw new AdaptException(e);
+ }
+ }
+ };
+ }
+
+ result.clones = true;
+ addToCache(req, result);
+ return result;
+ }
+
+ if (domain instanceof FloatArrayBinding && range instanceof FloatArrayBinding)
+ {
+ final FloatArrayBinding domainArray = (FloatArrayBinding) domain;
+ final FloatArrayBinding rangeArray = (FloatArrayBinding) range;
+
+ String domainUnit = ((NumberType) ((ArrayType)domainArray.type()).componentType).getUnit();
+ String rangeUnit = ((NumberType) ((ArrayType)rangeArray.type()).componentType).getUnit();
+ IUnitConverter unitConverter = ObjectUtils.objectEquals(domainUnit, rangeUnit) ||\r
+ domainUnit == null || rangeUnit == null ? null : Units.createConverter(domainUnit, rangeUnit);
+ boolean doUnitConversion = unitConverter != null && unitConverter != IdentityConverter.INSTANCE;
+
+ AbstractAdapter result;
+ if (doUnitConversion) {
+ final IUnitConverter _unitConverter = unitConverter;
+ result = new AbstractAdapter() {
+ @Override
+ public Object adapt(Object obj) throws AdaptException {
+ try {
+ float[] data = domainArray.getArray(obj);
+ for (int i=0; i<data.length; i++) {
+ float value = data[i];
+ double convertedValue = _unitConverter.convert((double)value);
+ data[i] = (float) convertedValue;
+ }
+ return rangeArray.create(data);
+ } catch (BindingException e) {
+ throw new AdaptException(e);
+ }
+ }
+ };
+ } else {
+ result = new AbstractAdapter() {
+ @Override
+ public Object adapt(Object obj) throws AdaptException {
+ try {
+ float[] data = domainArray.getArray(obj);\r
+ if (mustClone) data = data.clone();
+ return rangeArray.create(data);
+ } catch (BindingException e) {
+ throw new AdaptException(e);
+ }
+ }
+ };
+ }
+
+ result.clones = true;
+ addToCache(req, result);
+ return result;
+ }
+
+ if (domain instanceof DoubleArrayBinding && range instanceof DoubleArrayBinding)
+ {
+ final DoubleArrayBinding domainArray = (DoubleArrayBinding) domain;
+ final DoubleArrayBinding rangeArray = (DoubleArrayBinding) range;
+
+ String domainUnit = ((NumberType) ((ArrayType)domainArray.type()).componentType).getUnit();
+ String rangeUnit = ((NumberType) ((ArrayType)rangeArray.type()).componentType).getUnit();
+ IUnitConverter unitConverter = ObjectUtils.objectEquals(domainUnit, rangeUnit) \r
+ || domainUnit == null || rangeUnit == null ? null : Units.createConverter(domainUnit, rangeUnit);
+ boolean doUnitConversion = unitConverter != null && unitConverter != IdentityConverter.INSTANCE;
+
+ AbstractAdapter result;
+ if (doUnitConversion) {
+ final IUnitConverter _unitConverter = unitConverter;
+ result = new AbstractAdapter() {
+ @Override
+ public Object adapt(Object obj) throws AdaptException {
+ try {
+ double[] data = domainArray.getArray(obj);
+ for (int i=0; i<data.length; i++) {
+ double value = data[i];
+ double convertedValue = _unitConverter.convert(value);
+ data[i] = convertedValue;
+ }
+ return rangeArray.create(data);
+ } catch (BindingException e) {
+ throw new AdaptException(e);
+ }
+ }
+ };
+ } else {
+ result = new AbstractAdapter() {
+ @Override
+ public Object adapt(Object obj) throws AdaptException {
+ try {
+ double[] data = domainArray.getArray(obj);\r
+ if (mustClone) data = data.clone();
+ return rangeArray.create(data);
+ } catch (BindingException e) {
+ throw new AdaptException(e);
+ }
+ }
+ };
+ }
+
+ result.clones = true;
+ addToCache(req, result);
+ return result;
+ }
+
+ if (domain instanceof ArrayBinding && range instanceof ArrayBinding)
+ {
+ final ArrayBinding domainBinding = (ArrayBinding) domain;
+ final ArrayBinding rangeBinding = (ArrayBinding) range;
+ final AbstractAdapter componentAdapter = getAdapterUnsynchronized(domainBinding.getComponentBinding(), rangeBinding.getComponentBinding(), typeAdapter, mustClone);
+ AbstractAdapter result = new AbstractAdapter() {
+ @Override
+ public Object adapt(Object obj) throws AdaptException {
+ try {
+ int len = domainBinding.size(obj);
+ ArrayList<Object> array = new ArrayList<Object>(len);
+ for (int i=0; i<len; i++)
+ {
+ Object srcValue = domainBinding.get(obj, i);
+ Object dstValue = componentAdapter.adapt(srcValue);
+ array.add(dstValue);
+ }
+ return rangeBinding instanceof ArrayListBinding ? array : rangeBinding.create(array);
+ } catch (BindingException e) {
+ throw new AdaptException(e);
+ }
+ }
+ };
+
+ result.clones = componentAdapter.clones;
+ addToCache(req, result);
+ return result;
+ }
+
+ if (domain instanceof OptionalBinding && range instanceof OptionalBinding)
+ {
+ final OptionalBinding domainBinding = (OptionalBinding) domain;
+ final OptionalBinding rangeBinding = (OptionalBinding) range;
+ final AbstractAdapter componentAdapter = getAdapterUnsynchronized(domainBinding.componentBinding, rangeBinding.componentBinding, typeAdapter, mustClone);
+ AbstractAdapter result = new AbstractAdapter() {
+ @Override
+ public Object adapt(Object obj) throws AdaptException {
+ try {
+ if (!domainBinding.hasValue(obj)) return rangeBinding.createNoValue();
+ Object value = domainBinding.getValue(obj);
+ value = componentAdapter.adapt(value);
+ return rangeBinding.createValue(value);
+ } catch (BindingException e) {
+ throw new AdaptException(e);
+ }
+ }
+ };
+ result.typeAdapter = componentAdapter.typeAdapter;
+ result.clones = componentAdapter.clones;
+ addToCache(req, result);
+ return result;
+ }\r
+ \r
+ // Adapt a non-optional value to an optional value\r
+ if (range instanceof OptionalBinding && !(domain instanceof OptionalBinding))\r
+ {\r
+ final Binding domainBinding = domain;\r
+ final OptionalBinding rangeBinding = (OptionalBinding) range;\r
+ final AbstractAdapter componentAdapter = getAdapterUnsynchronized(domainBinding, rangeBinding.componentBinding, typeAdapter, mustClone);\r
+ AbstractAdapter result = new AbstractAdapter() {\r
+ @Override\r
+ public Object adapt(Object obj) throws AdaptException {\r
+ try {\r
+ obj = componentAdapter.adapt(obj); \r
+ return rangeBinding.createValue(obj);\r
+ } catch (BindingException e) {\r
+ throw new AdaptException(e);\r
+ }\r
+ }\r
+ };\r
+ result.typeAdapter = componentAdapter.typeAdapter;\r
+ result.clones = componentAdapter.clones;\r
+ addToCache(req, result);\r
+ return result; \r
+ }
+
+ if (domain instanceof VariantBinding && range instanceof VariantBinding)
+ {
+ final VariantBinding domainBinding = (VariantBinding) domain;
+ final VariantBinding rangeBinding = (VariantBinding) range;
+ AbstractAdapter result = new AbstractAdapter() {
+ @Override
+ public Object adapt(Object obj) throws AdaptException {
+ try {\r
+
+ Binding domainValueBinding = domainBinding.getContentBinding(obj);
+ Object domainObject = domainBinding.getContent(obj, domainValueBinding);\r
+ if (mustClone && domainObject!=obj) {\r
+ Adapter valueAdapter = getAdapterUnsynchronized(domainValueBinding, domainValueBinding, false, true);\r
+ domainObject = valueAdapter.adapt(domainObject); \r
+ }
+ Object rangeVariant = rangeBinding.create(domainValueBinding, domainObject);
+ return rangeVariant;
+ } catch (BindingException e) {
+ throw new AdaptException(e);
+ } catch (AdapterConstructionException e) {\r
+ throw new AdaptException(e);\r
+ }
+ }
+ };
+ result.clones = mustClone;
+ addToCache(req, result);
+ return result;
+ }\r
+ \r
+ if (range instanceof VariantBinding && !(domain instanceof VariantBinding))\r
+ {\r
+ // Default to just wrapping the domain type\r
+ final VariantBinding rangeBinding = (VariantBinding)range;\r
+ final Binding domainBinding = domain;\r
+ AbstractAdapter result = new AbstractAdapter() {\r
+ @Override\r
+ public Object adapt(Object obj) throws AdaptException {\r
+ try {\r
+ if (mustClone) {\r
+ Adapter valueAdapter;\r
+ valueAdapter = getAdapterUnsynchronized(domainBinding, domainBinding, false, true);\r
+ obj = valueAdapter.adapt(obj);\r
+ }\r
+ return rangeBinding.create(domainBinding, obj);\r
+ } catch (AdapterConstructionException | BindingException e) {\r
+ throw new AdaptException(e);\r
+ }\r
+ }\r
+ };\r
+ result.clones = mustClone;\r
+ addToCache(req, result);\r
+ return result;\r
+ }
+
+ if (domain instanceof MapBinding && range instanceof MapBinding)
+ {
+ final MapBinding domainBinding = (MapBinding) domain;
+ final MapBinding rangeBinding = (MapBinding) range;
+ final AbstractAdapter keyAdapter = getAdapterUnsynchronized(domainBinding.getKeyBinding(), rangeBinding.getKeyBinding(), typeAdapter, mustClone);
+ final AbstractAdapter valueAdapter = getAdapterUnsynchronized(domainBinding.getValueBinding(), rangeBinding.getValueBinding(), typeAdapter, mustClone);
+ AbstractAdapter result = new AbstractAdapter() {
+ @Override
+ public Object adapt(Object obj) throws AdaptException {
+ try {
+ int len = domainBinding.size(obj);
+ Object domainKeys[] = domainBinding.getKeys(obj);
+ Object domainValues[] = domainBinding.getValues(obj);
+ Object rangeKeys[] = new Object[len];
+ Object rangeValues[] = new Object[len];
+ for (int i=0; i<len; i++) {
+ Object domainKey = domainKeys[i];
+ Object domainValue = domainValues[i];
+ Object rangeKey = keyAdapter.adapt(domainKey);
+ Object rangeValue = valueAdapter.adapt(domainValue);
+ rangeKeys[i] = rangeKey;
+ rangeValues[i] = rangeValue;
+ }
+ Object rangeMap = rangeBinding.create(rangeKeys, rangeValues);
+ return rangeMap;
+ } catch (BindingException e) {
+ throw new AdaptException(e);
+ }
+ }
+ };
+ result.typeAdapter |= keyAdapter.typeAdapter | valueAdapter.typeAdapter;
+ result.clones = keyAdapter.clones & valueAdapter.clones;
+ addToCache(req, result);
+ return result;
+ }
+/*
+ // Special-Case: Convert Union to its Composite
+ if (domain instanceof UnionBinding) {
+ final UnionType ut = (UnionType) domain.getDataType();
+ final UnionBinding ub = (UnionBinding) domain;
+ Binding[] cbs = ub.getComponentBindings();
+ for (int i=0; i<cbs.length; i++)
+ {
+ Binding domainCompositeBinding = cbs[i];
+ if (ut.getComponent(i).type.equals(range.getDataType())) {
+ final AbstractAdapter union2CompositeAdapter = getAdapterUnsynchronized(domainCompositeBinding, range, allowPrimitiveConversion);
+ final int tag = i;
+ AbstractAdapter result = new AbstractAdapter() {
+ @Override
+ public Object adapt(Object obj) throws AdaptException {
+ try {
+ Object domainUnion = obj;
+ int domainTag = ub.getTag(domainUnion);
+ if (domainTag != tag) {
+ throw new AdaptException("This adapter can adapt only "+ub.getDataType().getComponents()[tag].name+"s");
+ }
+ Object domainComposite = ub.getValue(domainUnion);
+ Object rangeComposite = union2CompositeAdapter.adapt(domainComposite);
+ return rangeComposite;
+ } catch (BindingException e) {
+ throw new AdaptException(e);
+ }
+ }
+ };
+ result.hasPrimitiveConversion = union2CompositeAdapter.hasPrimitiveConversion;
+ addToCache(pair, result);
+ return result;
+ }
+ }
+ }
+
+ // Special-Case: Convert Composite to Union
+ if (range instanceof UnionBinding) {
+ final UnionType ut = (UnionType) range.getDataType();
+ final UnionBinding ub = (UnionBinding) range;
+ Binding cbs[] = ub.getComponentBindings();
+ for (int i=0; i<cbs.length; i++) {
+ Binding rangeCompositeBinding = cbs[i];
+ if (ut.getComponent(i).type.equals(domain.getDataType())) {
+ final AbstractAdapter domainObject2RangeCompositeAdapter = getAdapterUnsynchronized(rangeCompositeBinding, domain, allowPrimitiveConversion);
+ final int tag = i;
+ AbstractAdapter result = new AbstractAdapter() {
+ @Override
+ public Object adapt(Object obj) throws AdaptException {
+ try {
+ Object domainObject = obj;
+ Object rangeComposite = domainObject2RangeCompositeAdapter.adapt(domainObject);
+ Object rangeUnion = ub.create(tag, rangeComposite);
+ return rangeUnion;
+ } catch (BindingException e) {
+ throw new AdaptException(e);
+ }
+ }
+ };
+ result.hasPrimitiveConversion = domainObject2RangeCompositeAdapter.hasPrimitiveConversion;
+ addToCache(pair, result);
+ return result;
+ }
+ }
+ }
+*/
+
+ } catch (UnitParseException e) {
+ throw new AdapterConstructionException(e.getMessage(), e);
+ }
+
+ StringBuilder error = new StringBuilder();
+ error.append("Could not create ");
+ if (mustClone) error.append("cloning ");
+ if (typeAdapter) error.append("type");
+ error.append("adapter (");
+ error.append("domain=");
+ error.append(domain.type().toSingleLineString());
+ error.append(", range=");
+ error.append(range.type().toSingleLineString());
+ error.append(")");
+
+ throw new AdapterConstructionException(error.toString());
+ }
+
+
+ \r
+ /**\r
+ * Adapt a value of one type to another. \r
+ * \r
+ * @param value\r
+ * @param domain\r
+ * @param range\r
+ * @return adapted value\r
+ * @throws AdapterConstructionException\r
+ * @throws AdaptException\r
+ */\r
+ public Object adapt(Object value, Binding domain, Binding range)\r
+ throws AdaptException\r
+ {\r
+ if (domain == range) return value;\r
+ try {\r
+ if (range instanceof VariantBinding && !(domain instanceof VariantBinding)) {\r
+ // Default to just wrapping the value (avoid adapter construction to save memory)\r
+ return ((VariantBinding)range).create(domain, value);\r
+ }\r
+ return getAdapter(domain, range, true, false).adapt(value);\r
+ } catch (AdapterConstructionException | BindingException e) {\r
+ throw new AdaptException(e);\r
+ }\r
+ }\r
+ \r
+ /**\r
+ * Adapt a value of one type to another\r
+ * \r
+ * @param value\r
+ * @param domain\r
+ * @param range\r
+ * @return adapted value\r
+ * @throws AdapterConstructionException\r
+ * @throws AdaptException\r
+ */\r
+ public Object adaptUnchecked(Object value, Binding domain, Binding range)\r
+ throws RuntimeAdapterConstructionException, RuntimeAdaptException\r
+ {\r
+ if (domain == range) return value;\r
+ try {\r
+ if (range instanceof VariantBinding && !(domain instanceof VariantBinding)) {\r
+ // Default to just wrapping the value (avoid adapter construction to save memory)\r
+ return ((VariantBinding)range).create(domain, value);\r
+ }\r
+ return getAdapter(domain, range, true, false).adaptUnchecked(value);\r
+ } catch (RuntimeAdapterConstructionException | RuntimeBindingException e) {\r
+ throw new RuntimeAdaptException(new AdaptException(e.getCause()));\r
+ } catch (AdapterConstructionException | BindingException e) {\r
+ throw new RuntimeAdaptException(new AdaptException(e));\r
+ }\r
+ }\r
+ \r
+ /**\r
+ * Clone a value to a type to another. Bindings that handle immutable values\r
+ * may return the same instance, others will guarantee a complete copy. \r
+ * \r
+ * @param value\r
+ * @param domain\r
+ * @param range\r
+ * @return adapted value\r
+ * @throws AdapterConstructionException\r
+ * @throws AdaptException\r
+ */\r
+ public Object clone(Object value, Binding domain, Binding range)\r
+ throws AdaptException\r
+ {\r
+ try {\r
+ return getAdapter(domain, range, true, true).adapt(value);\r
+ } catch (AdapterConstructionException e) {\r
+ throw new AdaptException(e);\r
+ }\r
+ }\r
+ \r
+\r
+ /**\r
+ * Clone a value of one binding to another. Bindings that handle immutable values\r
+ * may return the same instance, others will guarantee a complete copy.\r
+ * \r
+ * @param value\r
+ * @param domain\r
+ * @param range\r
+ * @return adapted value\r
+ * @throws AdapterConstructionException\r
+ * @throws AdaptException\r
+ */\r
+ public Object cloneUnchecked(Object value, Binding domain, Binding range)\r
+ throws RuntimeAdapterConstructionException, RuntimeAdaptException\r
+ {\r
+ try {\r
+ return getAdapter(domain, range, true, true).adapt(value);\r
+ } catch (AdaptException e) {\r
+ throw new RuntimeAdaptException(e);\r
+ } catch (RuntimeAdapterConstructionException e) {\r
+ throw new RuntimeAdaptException(new AdaptException(e.getCause()));\r
+ } catch (AdapterConstructionException e) {\r
+ throw new RuntimeAdaptException(new AdaptException(e));\r
+ }\r
+ } \r
+
+}
+