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.adapter;
14 import java.util.ArrayList;
17 import org.apache.commons.collections.map.ReferenceMap;
18 import org.simantics.databoard.Bindings;
19 import org.simantics.databoard.Units;
20 import org.simantics.databoard.binding.ArrayBinding;
21 import org.simantics.databoard.binding.Binding;
22 import org.simantics.databoard.binding.BooleanBinding;
23 import org.simantics.databoard.binding.MapBinding;
24 import org.simantics.databoard.binding.NumberBinding;
25 import org.simantics.databoard.binding.OptionalBinding;
26 import org.simantics.databoard.binding.RecordBinding;
27 import org.simantics.databoard.binding.StringBinding;
28 import org.simantics.databoard.binding.UnionBinding;
29 import org.simantics.databoard.binding.VariantBinding;
30 import org.simantics.databoard.binding.error.BindingException;
31 import org.simantics.databoard.binding.error.RuntimeBindingException;
32 import org.simantics.databoard.binding.impl.ArrayListBinding;
33 import org.simantics.databoard.binding.impl.BooleanArrayBinding;
34 import org.simantics.databoard.binding.impl.ByteArrayBinding;
35 import org.simantics.databoard.binding.impl.DoubleArrayBinding;
36 import org.simantics.databoard.binding.impl.FloatArrayBinding;
37 import org.simantics.databoard.binding.impl.IntArrayBinding;
38 import org.simantics.databoard.binding.impl.LongArrayBinding;
39 import org.simantics.databoard.type.ArrayType;
40 import org.simantics.databoard.type.NumberType;
41 import org.simantics.databoard.type.RecordType;
42 import org.simantics.databoard.type.UnionType;
43 import org.simantics.databoard.units.IUnitConverter;
44 import org.simantics.databoard.units.IdentityConverter;
45 import org.simantics.databoard.units.internal.UnitParseException;
46 import org.simantics.databoard.util.ObjectUtils;
49 * AdapterRepository is a factory and a collection of adapters.
51 * @author Toni Kalajainen <toni.kalajainen@vtt.fi>
53 public class AdapterFactory {
55 @SuppressWarnings( "unchecked" )
56 Map<AdapterRequest, AbstractAdapter> cache = (Map<AdapterRequest, AbstractAdapter>) new ReferenceMap(ReferenceMap.SOFT, ReferenceMap.HARD);
58 public synchronized Adapter getAdapter(Binding domain, Binding range, boolean typeAdapter, boolean mustClone)
59 throws AdapterConstructionException
61 if ((!mustClone || domain.isImmutable()) && domain.equals(range)) return PassThruAdapter.PASSTHRU;
63 if (domain.getClass() == range.getClass() &&
64 ( !mustClone || domain.isImmutable() ) &&
65 NumberBinding.class.isAssignableFrom( domain.getClass() ) ) {
67 NumberBinding db = (NumberBinding) domain;
68 NumberBinding rb = (NumberBinding) range;
69 String u1 = db.type().getUnit();
70 String u2 = rb.type().getUnit();
71 if (u1==null || u2==null || u1.equals("") || u2.equals("") || u1.equals(u2)) return PassThruAdapter.PASSTHRU;
74 return getAdapterUnsynchronized(domain, range, typeAdapter, mustClone);
77 private AbstractAdapter getCached(AdapterRequest type)
79 return cache.get(type);
82 private void cache(AdapterRequest type, AbstractAdapter binding) {
83 cache.put(type, binding);
86 private void addToCache(AdapterRequest request, AbstractAdapter impl) {
87 impl.request = request;
90 // This request applies to "must clone" request aswell, remember this implementation
91 if (!request.mustClone && impl.clones) {
92 request = new AdapterRequest(request.domain, request.range, true);
98 * Create adapter, does not cache the result.
102 * @param typeAdapter if true, primitive conversion is allowed (e.g. int -> double)
105 private AbstractAdapter getAdapterUnsynchronized(Binding domain, Binding range, boolean typeAdapter, final boolean mustClone)
106 throws AdapterConstructionException
108 if ( !mustClone && domain.equals(range) ) return PassThruAdapter.PASSTHRU;
110 AdapterRequest req = new AdapterRequest(domain, range, mustClone);
111 AbstractAdapter cachedResult = getCached(req);
112 if (cachedResult!=null) return cachedResult;
116 if (domain instanceof RecordBinding && range instanceof RecordBinding)
118 final RecordBinding domainRecord = (RecordBinding) domain;
119 final RecordBinding rangeRecord = (RecordBinding) range;
120 RecordType domainType = domainRecord.type();
121 RecordType rangeType = rangeRecord.type();
123 // Field-Map describes the index of the fields in domain for each field in range
124 boolean requiresTypeAdapting = domainType.getComponentCount() != rangeType.getComponentCount();
125 int fieldMap[] = new int[rangeType.getComponentCount()];
126 for (int rangeIndex=0; rangeIndex<fieldMap.length; rangeIndex++)
128 String fieldName = rangeType.getComponent(rangeIndex).name;
129 Integer domainIndex = domainType.getComponentIndex(fieldName);
130 if (domainIndex!=null) {
131 fieldMap[rangeIndex] = domainIndex;
132 requiresTypeAdapting |= rangeIndex != domainIndex;
134 fieldMap[rangeIndex] = -1;
135 requiresTypeAdapting = true;
139 if (requiresTypeAdapting && !typeAdapter) {
140 throw new AdapterConstructionException("Type Adapter required.");
143 final int len = rangeRecord.componentBindings.length;
144 final AbstractAdapter[] componentAdapters = new AbstractAdapter[len];
145 AbstractAdapter result = null;
147 if (!requiresTypeAdapting) {
149 result = new AbstractAdapter() {
151 public Object adapt(Object src) throws AdaptException {
153 Object values[] = new Object[len];
154 for (int i=0; i<len; i++)
156 Object srcValue = domainRecord.getComponent(src, i);
157 Object dstValue = componentAdapters[i].adapt(srcValue);
158 values[i] = dstValue;
160 return rangeRecord.create(values);
161 } catch (BindingException e) {
162 throw new AdaptException(e);
167 // Type Adapter - Type adapter maps fields of different order
168 final int _fieldMap[] = fieldMap;
169 result = new AbstractAdapter() {
171 public Object adapt(Object src) throws AdaptException {
173 Object values[] = new Object[len];
174 for (int rangeIndex=0; rangeIndex<len; rangeIndex++)
176 int domainIndex = _fieldMap[rangeIndex];
177 if (domainIndex>=0) {
178 Object srcValue = domainRecord.getComponent(src, domainIndex);
179 Object dstValue = componentAdapters[rangeIndex].adapt(srcValue);
180 values[rangeIndex] = dstValue;
183 values[rangeIndex] = rangeRecord.componentBindings[rangeIndex].createDefault();
186 return rangeRecord.create(values);
187 } catch (BindingException e) {
188 throw new AdaptException(e);
192 result.typeAdapter = true;
195 addToCache(req, result);
196 result.clones = true;
197 for (int rangeIndex=0; rangeIndex<len; rangeIndex++)
199 int domainIndex = fieldMap[rangeIndex];
200 if (domainIndex>=0) {
201 componentAdapters[rangeIndex] = getAdapterUnsynchronized(domainRecord.componentBindings[domainIndex], rangeRecord.componentBindings[rangeIndex], typeAdapter, mustClone);
202 result.typeAdapter |= componentAdapters[rangeIndex].typeAdapter;
203 result.clones &= componentAdapters[rangeIndex].clones;
209 if (domain instanceof UnionBinding && range instanceof UnionBinding)
211 final UnionBinding domainBinding = (UnionBinding) domain;
212 final UnionBinding rangeBinding = (UnionBinding) range;
213 UnionType domainType = domainBinding.type();
214 UnionType rangeType = rangeBinding.type();
216 // Tag-Map describes the index of the tag-types in domain for each tag-type in range
217 boolean requiresTypeAdapting = domainType.getComponentCount() != rangeType.getComponentCount();
218 int tagMap[] = new int[domainType.getComponentCount()];
219 for (int domainIndex=0; domainIndex<tagMap.length; domainIndex++)
221 String fieldName = domainType.getComponent(domainIndex).name;
222 Integer rangeIndex = rangeType.getComponentIndex(fieldName);
223 if (rangeIndex==null) throw new AdapterConstructionException("The range UnionType does not have expected tag \""+fieldName+"\"");
224 tagMap[domainIndex] = rangeIndex;
225 requiresTypeAdapting |= rangeIndex != domainIndex;
228 if (requiresTypeAdapting && !typeAdapter) {
229 throw new AdapterConstructionException("Type Adapter required.");
232 final AbstractAdapter[] componentAdapters = new AbstractAdapter[domainType.getComponentCount()];
234 AbstractAdapter result = null;
236 if (!requiresTypeAdapting) {
238 result = new AbstractAdapter() {
240 public Object adapt(Object obj) throws AdaptException {
242 int tag = domainBinding.getTag(obj);
243 Object srcValue = domainBinding.getValue(obj);
244 Object dstValue = componentAdapters[tag].adapt(srcValue);
245 return rangeBinding.create(tag, dstValue);
246 } catch (BindingException e) {
247 throw new AdaptException(e);
252 // Type adapter, type adapter rearranges tag indices
253 final int _tagMap[] = tagMap;
254 result = new AbstractAdapter() {
256 public Object adapt(Object obj) throws AdaptException {
258 int domainTag = domainBinding.getTag(obj);
259 int rangeTag = _tagMap[domainTag];
260 // Domain Component Binding
261 Object srcValue = domainBinding.getValue(obj);
262 Object dstValue = componentAdapters[domainTag].adapt(srcValue);
263 return rangeBinding.create(rangeTag, dstValue);
264 } catch (BindingException e) {
265 throw new AdaptException(e);
271 addToCache(req, result);
272 result.clones = true;
273 for (int domainIndex=0; domainIndex<domainType.getComponentCount(); domainIndex++)
275 int rangeIndex = tagMap[domainIndex];
276 componentAdapters[domainIndex] = getAdapterUnsynchronized(domainBinding.getComponentBindings()[domainIndex], rangeBinding.getComponentBindings()[rangeIndex], typeAdapter, mustClone);
277 result.typeAdapter |= componentAdapters[domainIndex].typeAdapter;
278 result.clones &= componentAdapters[domainIndex].clones;
283 if (domain instanceof BooleanBinding && range instanceof BooleanBinding)
285 final BooleanBinding domainBoolean = (BooleanBinding) domain;
286 final BooleanBinding rangeBoolean = (BooleanBinding) range;
287 AbstractAdapter result = new AbstractAdapter() {
289 public Object adapt(Object obj) throws AdaptException {
291 boolean value = domainBoolean.getValue_(obj);
292 return rangeBoolean.create(value);
293 } catch (BindingException e) {
294 throw new AdaptException(e);
298 result.clones = mustClone;
299 result.typeAdapter = true;
300 addToCache(req, result);
304 if (domain instanceof BooleanBinding && range instanceof NumberBinding)
307 final BooleanBinding domainBoolean = (BooleanBinding) domain;
308 final NumberBinding rangeNumber = (NumberBinding) range;
309 final Object falseValue = rangeNumber.create( Integer.valueOf(0) );
310 final Object trueValue = rangeNumber.create( Integer.valueOf(1) );
311 AbstractAdapter result = new AbstractAdapter() {
313 public Object adapt(Object obj) throws AdaptException {
315 boolean value = domainBoolean.getValue_(obj);
316 return value ? trueValue : falseValue;
317 } catch (BindingException e) {
318 throw new AdaptException(e);
322 result.clones = true;
323 result.typeAdapter = true;
324 addToCache(req, result);
326 } catch (BindingException e1) {
327 throw new AdapterConstructionException(e1);
331 if (domain instanceof NumberBinding && range instanceof BooleanBinding)
334 final NumberBinding domainNumber = (NumberBinding) domain;
335 final BooleanBinding rangeBoolean = (BooleanBinding) range;
336 final Object zeroValue = domainNumber.create( Integer.valueOf(0) );
337 AbstractAdapter result = new AbstractAdapter() {
339 public Object adapt(Object obj) throws AdaptException {
341 Object value = domainNumber.getValue(obj);
342 boolean bool = !domainNumber.equals(value, zeroValue);
343 return rangeBoolean.create(bool);
344 } catch (BindingException e) {
345 throw new AdaptException(e);
349 result.clones = true;
350 addToCache(req, result);
352 } catch (BindingException e1) {
353 throw new AdapterConstructionException(e1);
357 if (domain instanceof StringBinding && range instanceof StringBinding)
359 final StringBinding domainString = (StringBinding) domain;
360 final StringBinding rangeString = (StringBinding) range;
361 AbstractAdapter result = new AbstractAdapter() {
363 public Object adapt(Object obj) throws AdaptException {
365 String value = domainString.getValue(obj);
366 return rangeString.create(value);
367 } catch (BindingException e) {
368 throw new AdaptException(e);
372 result.clones = true;
373 addToCache(req, result);
377 if(domain instanceof StringBinding && range instanceof NumberBinding)
379 final StringBinding domainString = (StringBinding) domain;
380 final NumberBinding rangeString = (NumberBinding) range;
381 AbstractAdapter result = new AbstractAdapter() {
383 public Object adapt(Object obj) throws AdaptException {
385 String value = domainString.getValue(obj);
386 return rangeString.create(value);
387 } catch (BindingException e) {
388 throw new AdaptException(e);
392 result.clones = true;
393 addToCache(req, result);
397 if(domain instanceof StringBinding && range instanceof BooleanBinding)
399 final StringBinding domainString = (StringBinding) domain;
400 final BooleanBinding rangeString = (BooleanBinding) range;
401 AbstractAdapter result = new AbstractAdapter() {
403 public Object adapt(Object obj) throws AdaptException {
405 String value = domainString.getValue(obj);
406 return rangeString.create(Boolean.parseBoolean(value));
407 } catch (BindingException e) {
408 throw new AdaptException(e);
412 result.clones = true;
413 addToCache(req, result);
417 // XXX We can optimizes here by using primitives
418 if (domain instanceof NumberBinding && range instanceof NumberBinding)
420 final NumberBinding domainNumber = (NumberBinding) domain;
421 final NumberBinding rangeNumber = (NumberBinding) range;
423 String domainUnit = ((NumberType) domainNumber.type()).getUnit();
424 String rangeUnit = ((NumberType) rangeNumber.type()).getUnit();
425 IUnitConverter unitConverter;
426 if(domainUnit == null || rangeUnit == null || domainUnit.equals(rangeUnit))
427 unitConverter = null;
429 unitConverter = Units.createConverter(domainUnit, rangeUnit);
430 /*if(domainUnit == null || domainUnit.equals("")) {
431 if(rangeUnit == null || rangeUnit.equals(""))
432 unitConverter = null;
434 unitConverter = null;
435 // throw new AdapterConstructionException("Cannot convert from a unitless type to a type with unit.");
438 if(rangeUnit == null || rangeUnit.equals(""))
439 unitConverter = null;
440 // throw new AdapterConstructionException("Cannot convert from a type with unit to unitless type.");
442 unitConverter = Units.createConverter(domainUnit, rangeUnit);
444 boolean doUnitConversion = unitConverter != null && unitConverter != IdentityConverter.INSTANCE;
445 boolean doPrimitiveConversion = !domainNumber.type().getClass().equals( rangeNumber.type().getClass() );
446 if (doPrimitiveConversion && !typeAdapter)
447 throw new AdapterConstructionException("Type mismatch, use Type Adapter instead.");
449 AbstractAdapter result;
450 if (!doUnitConversion) {
451 result = new AbstractAdapter() {
453 public Object adapt(Object obj) throws AdaptException {
456 value = domainNumber.getValue(obj);
457 return rangeNumber.create(value);
458 } catch (BindingException e) {
459 throw new AdaptException(e);
464 final IUnitConverter _unitConverter = unitConverter;
465 result = new AbstractAdapter() {
467 public Object adapt(Object obj) throws AdaptException {
469 Number value = domainNumber.getValue(obj);
470 double convertedValue = _unitConverter.convert(value.doubleValue());
471 return rangeNumber.create(Double.valueOf(convertedValue));
472 } catch (BindingException e) {
473 throw new AdaptException(e);
478 result.typeAdapter = doPrimitiveConversion;
479 result.clones = true;
480 addToCache(req, result);
484 if (domain instanceof BooleanArrayBinding && range instanceof BooleanArrayBinding)
486 final BooleanArrayBinding domainArray = (BooleanArrayBinding) domain;
487 final BooleanArrayBinding rangeArray = (BooleanArrayBinding) range;
488 AbstractAdapter result = new AbstractAdapter() {
490 public Object adapt(Object obj) throws AdaptException {
492 boolean[] data = domainArray.getArray(obj);
493 if (mustClone) data = data.clone();
494 return rangeArray.create(data);
495 } catch (BindingException e) {
496 throw new AdaptException(e);
500 result.clones = true;
501 addToCache(req, result);
505 if (domain instanceof ByteArrayBinding && range instanceof ByteArrayBinding)
507 final ByteArrayBinding domainArray = (ByteArrayBinding) domain;
508 final ByteArrayBinding rangeArray = (ByteArrayBinding) range;
510 String domainUnit = ((NumberType) ((ArrayType)domainArray.type()).componentType).getUnit();
511 String rangeUnit = ((NumberType) ((ArrayType)rangeArray.type()).componentType).getUnit();
512 IUnitConverter unitConverter = ObjectUtils.objectEquals(domainUnit, rangeUnit) ? null : Units.createConverter(domainUnit, rangeUnit);
513 boolean doUnitConversion = unitConverter != null && unitConverter != IdentityConverter.INSTANCE;
515 AbstractAdapter result;
516 if (doUnitConversion) {
517 final IUnitConverter _unitConverter = unitConverter;
518 result = new AbstractAdapter() {
520 public Object adapt(Object obj) throws AdaptException {
522 byte[] data = domainArray.getArray(obj);
523 for (int i=0; i<data.length; i++) {
524 byte value = data[i];
525 double convertedValue = _unitConverter.convert((double)value);
526 data[i] = (byte) convertedValue;
528 return rangeArray.create(data);
529 } catch (BindingException e) {
530 throw new AdaptException(e);
535 result = new AbstractAdapter() {
537 public Object adapt(Object obj) throws AdaptException {
539 byte[] data = domainArray.getArray(obj);
540 if (mustClone) data = data.clone();
541 return rangeArray.create(data);
542 } catch (BindingException e) {
543 throw new AdaptException(e);
548 result.clones = true;
549 addToCache(req, result);
553 if (domain instanceof IntArrayBinding && range instanceof IntArrayBinding)
555 final IntArrayBinding domainArray = (IntArrayBinding) domain;
556 final IntArrayBinding rangeArray = (IntArrayBinding) range;
558 String domainUnit = ((NumberType) ((ArrayType)domainArray.type()).componentType).getUnit();
559 String rangeUnit = ((NumberType) ((ArrayType)rangeArray.type()).componentType).getUnit();
560 IUnitConverter unitConverter = ObjectUtils.objectEquals(domainUnit, rangeUnit) ||
561 domainUnit == null || rangeUnit == null ? null : Units.createConverter(domainUnit, rangeUnit);
562 boolean doUnitConversion = unitConverter != null && unitConverter != IdentityConverter.INSTANCE;
564 AbstractAdapter result;
565 if (doUnitConversion) {
566 final IUnitConverter _unitConverter = unitConverter;
567 result = new AbstractAdapter() {
569 public Object adapt(Object obj) throws AdaptException {
571 int[] data = domainArray.getArray(obj);
572 for (int i=0; i<data.length; i++) {
574 double convertedValue = _unitConverter.convert((double)value);
575 data[i] = (int) convertedValue;
577 return rangeArray.create(data);
578 } catch (BindingException e) {
579 throw new AdaptException(e);
584 result = new AbstractAdapter() {
586 public Object adapt(Object obj) throws AdaptException {
588 int[] data = domainArray.getArray(obj);
589 if (mustClone) data = data.clone();
590 return rangeArray.create(data);
591 } catch (BindingException e) {
592 throw new AdaptException(e);
598 result.clones = true;
599 addToCache(req, result);
603 if (domain instanceof LongArrayBinding && range instanceof LongArrayBinding)
605 final LongArrayBinding domainArray = (LongArrayBinding) domain;
606 final LongArrayBinding rangeArray = (LongArrayBinding) range;
608 String domainUnit = ((NumberType) ((ArrayType)domainArray.type()).componentType).getUnit();
609 String rangeUnit = ((NumberType) ((ArrayType)rangeArray.type()).componentType).getUnit();
610 IUnitConverter unitConverter = ObjectUtils.objectEquals(domainUnit, rangeUnit) ||
611 domainUnit == null || rangeUnit == null ? null : Units.createConverter(domainUnit, rangeUnit);
612 boolean doUnitConversion = unitConverter != null && unitConverter != IdentityConverter.INSTANCE;
614 AbstractAdapter result;
615 if (doUnitConversion) {
616 final IUnitConverter _unitConverter = unitConverter;
617 result = new AbstractAdapter() {
619 public Object adapt(Object obj) throws AdaptException {
621 long[] data = domainArray.getArray(obj);
622 for (int i=0; i<data.length; i++) {
623 long value = data[i];
624 double convertedValue = _unitConverter.convert((double)value);
625 data[i] = (long) convertedValue;
627 return rangeArray.create(data);
628 } catch (BindingException e) {
629 throw new AdaptException(e);
634 result = new AbstractAdapter() {
636 public Object adapt(Object obj) throws AdaptException {
638 long[] data = domainArray.getArray(obj);
639 if (mustClone) data = data.clone();
640 return rangeArray.create(data);
641 } catch (BindingException e) {
642 throw new AdaptException(e);
648 result.clones = true;
649 addToCache(req, result);
653 if (domain instanceof FloatArrayBinding && range instanceof FloatArrayBinding)
655 final FloatArrayBinding domainArray = (FloatArrayBinding) domain;
656 final FloatArrayBinding rangeArray = (FloatArrayBinding) range;
658 String domainUnit = ((NumberType) ((ArrayType)domainArray.type()).componentType).getUnit();
659 String rangeUnit = ((NumberType) ((ArrayType)rangeArray.type()).componentType).getUnit();
660 IUnitConverter unitConverter = ObjectUtils.objectEquals(domainUnit, rangeUnit) ||
661 domainUnit == null || rangeUnit == null ? null : Units.createConverter(domainUnit, rangeUnit);
662 boolean doUnitConversion = unitConverter != null && unitConverter != IdentityConverter.INSTANCE;
664 AbstractAdapter result;
665 if (doUnitConversion) {
666 final IUnitConverter _unitConverter = unitConverter;
667 result = new AbstractAdapter() {
669 public Object adapt(Object obj) throws AdaptException {
671 float[] data = domainArray.getArray(obj);
672 for (int i=0; i<data.length; i++) {
673 float value = data[i];
674 double convertedValue = _unitConverter.convert((double)value);
675 data[i] = (float) convertedValue;
677 return rangeArray.create(data);
678 } catch (BindingException e) {
679 throw new AdaptException(e);
684 result = new AbstractAdapter() {
686 public Object adapt(Object obj) throws AdaptException {
688 float[] data = domainArray.getArray(obj);
689 if (mustClone) data = data.clone();
690 return rangeArray.create(data);
691 } catch (BindingException e) {
692 throw new AdaptException(e);
698 result.clones = true;
699 addToCache(req, result);
703 if (domain instanceof DoubleArrayBinding && range instanceof DoubleArrayBinding)
705 final DoubleArrayBinding domainArray = (DoubleArrayBinding) domain;
706 final DoubleArrayBinding rangeArray = (DoubleArrayBinding) range;
708 String domainUnit = ((NumberType) ((ArrayType)domainArray.type()).componentType).getUnit();
709 String rangeUnit = ((NumberType) ((ArrayType)rangeArray.type()).componentType).getUnit();
710 IUnitConverter unitConverter = ObjectUtils.objectEquals(domainUnit, rangeUnit)
711 || domainUnit == null || rangeUnit == null ? null : Units.createConverter(domainUnit, rangeUnit);
712 boolean doUnitConversion = unitConverter != null && unitConverter != IdentityConverter.INSTANCE;
714 AbstractAdapter result;
715 if (doUnitConversion) {
716 final IUnitConverter _unitConverter = unitConverter;
717 result = new AbstractAdapter() {
719 public Object adapt(Object obj) throws AdaptException {
721 double[] data = domainArray.getArray(obj);
722 for (int i=0; i<data.length; i++) {
723 double value = data[i];
724 double convertedValue = _unitConverter.convert(value);
725 data[i] = convertedValue;
727 return rangeArray.create(data);
728 } catch (BindingException e) {
729 throw new AdaptException(e);
734 result = new AbstractAdapter() {
736 public Object adapt(Object obj) throws AdaptException {
738 double[] data = domainArray.getArray(obj);
739 if (mustClone) data = data.clone();
740 return rangeArray.create(data);
741 } catch (BindingException e) {
742 throw new AdaptException(e);
748 result.clones = true;
749 addToCache(req, result);
753 if (domain instanceof ArrayBinding && range instanceof ArrayBinding)
755 final ArrayBinding domainBinding = (ArrayBinding) domain;
756 final ArrayBinding rangeBinding = (ArrayBinding) range;
757 final AbstractAdapter componentAdapter = getAdapterUnsynchronized(domainBinding.getComponentBinding(), rangeBinding.getComponentBinding(), typeAdapter, mustClone);
758 AbstractAdapter result = new AbstractAdapter() {
760 public Object adapt(Object obj) throws AdaptException {
762 int len = domainBinding.size(obj);
763 ArrayList<Object> array = new ArrayList<Object>(len);
764 for (int i=0; i<len; i++)
766 Object srcValue = domainBinding.get(obj, i);
767 Object dstValue = componentAdapter.adapt(srcValue);
770 return rangeBinding instanceof ArrayListBinding ? array : rangeBinding.create(array);
771 } catch (BindingException e) {
772 throw new AdaptException(e);
777 result.clones = componentAdapter.clones;
778 addToCache(req, result);
782 if (domain instanceof OptionalBinding && range instanceof OptionalBinding)
784 final OptionalBinding domainBinding = (OptionalBinding) domain;
785 final OptionalBinding rangeBinding = (OptionalBinding) range;
786 final AbstractAdapter componentAdapter = getAdapterUnsynchronized(domainBinding.componentBinding, rangeBinding.componentBinding, typeAdapter, mustClone);
787 AbstractAdapter result = new AbstractAdapter() {
789 public Object adapt(Object obj) throws AdaptException {
791 if (!domainBinding.hasValue(obj)) return rangeBinding.createNoValue();
792 Object value = domainBinding.getValue(obj);
793 value = componentAdapter.adapt(value);
794 return rangeBinding.createValue(value);
795 } catch (BindingException e) {
796 throw new AdaptException(e);
800 result.typeAdapter = componentAdapter.typeAdapter;
801 result.clones = componentAdapter.clones;
802 addToCache(req, result);
806 // Adapt a non-optional value to an optional value
807 if (range instanceof OptionalBinding && !(domain instanceof OptionalBinding))
809 final Binding domainBinding = domain;
810 final OptionalBinding rangeBinding = (OptionalBinding) range;
811 final AbstractAdapter componentAdapter = getAdapterUnsynchronized(domainBinding, rangeBinding.componentBinding, typeAdapter, mustClone);
812 AbstractAdapter result = new AbstractAdapter() {
814 public Object adapt(Object obj) throws AdaptException {
816 obj = componentAdapter.adapt(obj);
817 return rangeBinding.createValue(obj);
818 } catch (BindingException e) {
819 throw new AdaptException(e);
823 result.typeAdapter = componentAdapter.typeAdapter;
824 result.clones = componentAdapter.clones;
825 addToCache(req, result);
829 if (domain instanceof VariantBinding && range instanceof VariantBinding)
831 final VariantBinding domainBinding = (VariantBinding) domain;
832 final VariantBinding rangeBinding = (VariantBinding) range;
833 AbstractAdapter result = new AbstractAdapter() {
835 public Object adapt(Object obj) throws AdaptException {
838 Binding domainValueBinding = domainBinding.getContentBinding(obj);
839 Object domainObject = domainBinding.getContent(obj, domainValueBinding);
840 if (mustClone && domainObject!=obj) {
841 Adapter valueAdapter = getAdapterUnsynchronized(domainValueBinding, domainValueBinding, false, true);
842 domainObject = valueAdapter.adapt(domainObject);
844 Object rangeVariant = rangeBinding.create(domainValueBinding, domainObject);
846 } catch (BindingException e) {
847 throw new AdaptException(e);
848 } catch (AdapterConstructionException e) {
849 throw new AdaptException(e);
853 result.clones = mustClone;
854 addToCache(req, result);
858 if (domain instanceof VariantBinding && !(range instanceof VariantBinding))
860 // Make a recursive adaptation from a variant source
861 final VariantBinding domainBinding = (VariantBinding)domain;
862 final Binding rangeBinding = range;
863 AbstractAdapter result = new AbstractAdapter() {
865 public Object adapt(Object obj) throws AdaptException {
867 Object value = domainBinding.getContent(obj);
868 Binding contentBinding = domainBinding.getContentBinding(obj);
869 AbstractAdapter adapter = (AbstractAdapter) getAdapter(contentBinding, rangeBinding, typeAdapter, mustClone);
870 return adapter.adapt(value);
871 } catch (BindingException | AdapterConstructionException e) {
872 throw new AdaptException(e);
876 result.clones = mustClone;
877 addToCache(req, result);
881 if (range instanceof VariantBinding && !(domain instanceof VariantBinding))
883 // Default to just wrapping the domain type
884 final VariantBinding rangeBinding = (VariantBinding)range;
885 final Binding domainBinding = domain;
886 AbstractAdapter result = new AbstractAdapter() {
888 public Object adapt(Object obj) throws AdaptException {
891 Adapter valueAdapter;
892 valueAdapter = getAdapterUnsynchronized(domainBinding, domainBinding, false, true);
893 obj = valueAdapter.adapt(obj);
895 return rangeBinding.create(domainBinding, obj);
896 } catch (AdapterConstructionException | BindingException e) {
897 throw new AdaptException(e);
901 result.clones = mustClone;
902 addToCache(req, result);
906 if (domain instanceof MapBinding && range instanceof MapBinding)
908 final MapBinding domainBinding = (MapBinding) domain;
909 final MapBinding rangeBinding = (MapBinding) range;
910 final AbstractAdapter keyAdapter = getAdapterUnsynchronized(domainBinding.getKeyBinding(), rangeBinding.getKeyBinding(), typeAdapter, mustClone);
911 final AbstractAdapter valueAdapter = getAdapterUnsynchronized(domainBinding.getValueBinding(), rangeBinding.getValueBinding(), typeAdapter, mustClone);
912 AbstractAdapter result = new AbstractAdapter() {
914 public Object adapt(Object obj) throws AdaptException {
916 int len = domainBinding.size(obj);
917 Object domainKeys[] = domainBinding.getKeys(obj);
918 Object domainValues[] = domainBinding.getValues(obj);
919 Object rangeKeys[] = new Object[len];
920 Object rangeValues[] = new Object[len];
921 for (int i=0; i<len; i++) {
922 Object domainKey = domainKeys[i];
923 Object domainValue = domainValues[i];
924 Object rangeKey = keyAdapter.adapt(domainKey);
925 Object rangeValue = valueAdapter.adapt(domainValue);
926 rangeKeys[i] = rangeKey;
927 rangeValues[i] = rangeValue;
929 Object rangeMap = rangeBinding.create(rangeKeys, rangeValues);
931 } catch (BindingException e) {
932 throw new AdaptException(e);
936 result.typeAdapter |= keyAdapter.typeAdapter | valueAdapter.typeAdapter;
937 result.clones = keyAdapter.clones & valueAdapter.clones;
938 addToCache(req, result);
942 // Special-Case: Convert Union to its Composite
943 if (domain instanceof UnionBinding) {
944 final UnionType ut = (UnionType) domain.getDataType();
945 final UnionBinding ub = (UnionBinding) domain;
946 Binding[] cbs = ub.getComponentBindings();
947 for (int i=0; i<cbs.length; i++)
949 Binding domainCompositeBinding = cbs[i];
950 if (ut.getComponent(i).type.equals(range.getDataType())) {
951 final AbstractAdapter union2CompositeAdapter = getAdapterUnsynchronized(domainCompositeBinding, range, allowPrimitiveConversion);
953 AbstractAdapter result = new AbstractAdapter() {
955 public Object adapt(Object obj) throws AdaptException {
957 Object domainUnion = obj;
958 int domainTag = ub.getTag(domainUnion);
959 if (domainTag != tag) {
960 throw new AdaptException("This adapter can adapt only "+ub.getDataType().getComponents()[tag].name+"s");
962 Object domainComposite = ub.getValue(domainUnion);
963 Object rangeComposite = union2CompositeAdapter.adapt(domainComposite);
964 return rangeComposite;
965 } catch (BindingException e) {
966 throw new AdaptException(e);
970 result.hasPrimitiveConversion = union2CompositeAdapter.hasPrimitiveConversion;
971 addToCache(pair, result);
977 // Special-Case: Convert Composite to Union
978 if (range instanceof UnionBinding) {
979 final UnionType ut = (UnionType) range.getDataType();
980 final UnionBinding ub = (UnionBinding) range;
981 Binding cbs[] = ub.getComponentBindings();
982 for (int i=0; i<cbs.length; i++) {
983 Binding rangeCompositeBinding = cbs[i];
984 if (ut.getComponent(i).type.equals(domain.getDataType())) {
985 final AbstractAdapter domainObject2RangeCompositeAdapter = getAdapterUnsynchronized(rangeCompositeBinding, domain, allowPrimitiveConversion);
987 AbstractAdapter result = new AbstractAdapter() {
989 public Object adapt(Object obj) throws AdaptException {
991 Object domainObject = obj;
992 Object rangeComposite = domainObject2RangeCompositeAdapter.adapt(domainObject);
993 Object rangeUnion = ub.create(tag, rangeComposite);
995 } catch (BindingException e) {
996 throw new AdaptException(e);
1000 result.hasPrimitiveConversion = domainObject2RangeCompositeAdapter.hasPrimitiveConversion;
1001 addToCache(pair, result);
1008 } catch (UnitParseException e) {
1009 throw new AdapterConstructionException(e.getMessage(), e);
1012 StringBuilder error = new StringBuilder();
1013 error.append("Could not create ");
1014 if (mustClone) error.append("cloning ");
1015 if (typeAdapter) error.append("type");
1016 error.append("adapter (");
1017 error.append("domain=");
1018 error.append(domain.type().toSingleLineString());
1019 error.append(", range=");
1020 error.append(range.type().toSingleLineString());
1023 throw new AdapterConstructionException(error.toString());
1029 * Adapt a value of one type to another.
1034 * @return adapted value
1035 * @throws AdapterConstructionException
1036 * @throws AdaptException
1038 public Object adapt(Object value, Binding domain, Binding range)
1039 throws AdaptException
1041 if (domain == range) return value;
1043 if (range instanceof VariantBinding && !(domain instanceof VariantBinding)) {
1044 // Default to just wrapping the value (avoid adapter construction to save memory)
1045 return ((VariantBinding)range).create(domain, value);
1047 return getAdapter(domain, range, true, false).adapt(value);
1048 } catch (AdapterConstructionException | BindingException e) {
1049 throw new AdaptException(e);
1054 * Adapt a value of one type to another
1059 * @return adapted value
1060 * @throws AdapterConstructionException
1061 * @throws AdaptException
1063 public Object adaptUnchecked(Object value, Binding domain, Binding range)
1064 throws RuntimeAdapterConstructionException, RuntimeAdaptException
1066 if (domain == range) return value;
1068 if (range instanceof VariantBinding && !(domain instanceof VariantBinding)) {
1069 // Default to just wrapping the value (avoid adapter construction to save memory)
1070 return ((VariantBinding)range).create(domain, value);
1072 return getAdapter(domain, range, true, false).adaptUnchecked(value);
1073 } catch (RuntimeAdapterConstructionException | RuntimeBindingException e) {
1074 throw new RuntimeAdaptException(new AdaptException(e.getCause()));
1075 } catch (AdapterConstructionException | BindingException e) {
1076 throw new RuntimeAdaptException(new AdaptException(e));
1081 * Clone a value to a type to another. Bindings that handle immutable values
1082 * may return the same instance, others will guarantee a complete copy.
1087 * @return adapted value
1088 * @throws AdapterConstructionException
1089 * @throws AdaptException
1091 public Object clone(Object value, Binding domain, Binding range)
1092 throws AdaptException
1095 return getAdapter(domain, range, true, true).adapt(value);
1096 } catch (AdapterConstructionException e) {
1097 throw new AdaptException(e);
1103 * Clone a value of one binding to another. Bindings that handle immutable values
1104 * may return the same instance, others will guarantee a complete copy.
1109 * @return adapted value
1110 * @throws AdapterConstructionException
1111 * @throws AdaptException
1113 public Object cloneUnchecked(Object value, Binding domain, Binding range)
1114 throws RuntimeAdapterConstructionException, RuntimeAdaptException
1117 return getAdapter(domain, range, true, true).adapt(value);
1118 } catch (AdaptException e) {
1119 throw new RuntimeAdaptException(e);
1120 } catch (RuntimeAdapterConstructionException e) {
1121 throw new RuntimeAdaptException(new AdaptException(e.getCause()));
1122 } catch (AdapterConstructionException e) {
1123 throw new RuntimeAdaptException(new AdaptException(e));