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.collections4.map.AbstractReferenceMap.ReferenceStrength;
18 import org.apache.commons.collections4.map.ReferenceMap;
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 Map<AdapterRequest, AbstractAdapter> cache = new ReferenceMap<>(ReferenceStrength.SOFT, ReferenceStrength.HARD);
57 public synchronized Adapter getAdapter(Binding domain, Binding range, boolean typeAdapter, boolean mustClone)
58 throws AdapterConstructionException
60 if ((!mustClone || domain.isImmutable()) && domain.equals(range)) return PassThruAdapter.PASSTHRU;
62 if (domain.getClass() == range.getClass() &&
63 ( !mustClone || domain.isImmutable() ) &&
64 NumberBinding.class.isAssignableFrom( domain.getClass() ) ) {
66 NumberBinding db = (NumberBinding) domain;
67 NumberBinding rb = (NumberBinding) range;
68 String u1 = db.type().getUnit();
69 String u2 = rb.type().getUnit();
70 if (u1==null || u2==null || u1.equals("") || u2.equals("") || u1.equals(u2)) return PassThruAdapter.PASSTHRU;
73 return getAdapterUnsynchronized(domain, range, typeAdapter, mustClone);
76 private AbstractAdapter getCached(AdapterRequest type)
78 return cache.get(type);
81 private void cache(AdapterRequest type, AbstractAdapter binding) {
82 cache.put(type, binding);
85 private void addToCache(AdapterRequest request, AbstractAdapter impl) {
86 impl.request = request;
89 // This request applies to "must clone" request aswell, remember this implementation
90 if (!request.mustClone && impl.clones) {
91 request = new AdapterRequest(request.domain, request.range, true);
97 * Create adapter, does not cache the result.
101 * @param typeAdapter if true, primitive conversion is allowed (e.g. int -> double)
104 private AbstractAdapter getAdapterUnsynchronized(Binding domain, Binding range, boolean typeAdapter, final boolean mustClone)
105 throws AdapterConstructionException
107 if ( !mustClone && domain.equals(range) ) return PassThruAdapter.PASSTHRU;
109 AdapterRequest req = new AdapterRequest(domain, range, mustClone);
110 AbstractAdapter cachedResult = getCached(req);
111 if (cachedResult!=null) return cachedResult;
115 if (domain instanceof RecordBinding && range instanceof RecordBinding)
117 final RecordBinding domainRecord = (RecordBinding) domain;
118 final RecordBinding rangeRecord = (RecordBinding) range;
119 RecordType domainType = domainRecord.type();
120 RecordType rangeType = rangeRecord.type();
122 // Field-Map describes the index of the fields in domain for each field in range
123 boolean requiresTypeAdapting = domainType.getComponentCount() != rangeType.getComponentCount();
124 int fieldMap[] = new int[rangeType.getComponentCount()];
125 for (int rangeIndex=0; rangeIndex<fieldMap.length; rangeIndex++)
127 String fieldName = rangeType.getComponent(rangeIndex).name;
128 Integer domainIndex = domainType.getComponentIndex(fieldName);
129 if (domainIndex!=null) {
130 fieldMap[rangeIndex] = domainIndex;
131 requiresTypeAdapting |= rangeIndex != domainIndex;
133 fieldMap[rangeIndex] = -1;
134 requiresTypeAdapting = true;
138 if (requiresTypeAdapting && !typeAdapter) {
139 throw new AdapterConstructionException("Type Adapter required.");
142 final int len = rangeRecord.componentBindings.length;
143 final AbstractAdapter[] componentAdapters = new AbstractAdapter[len];
144 AbstractAdapter result = null;
146 if (!requiresTypeAdapting) {
148 result = new AbstractAdapter() {
150 public Object adapt(Object src) throws AdaptException {
152 Object values[] = new Object[len];
153 for (int i=0; i<len; i++)
155 Object srcValue = domainRecord.getComponent(src, i);
156 Object dstValue = componentAdapters[i].adapt(srcValue);
157 values[i] = dstValue;
159 return rangeRecord.create(values);
160 } catch (BindingException e) {
161 throw new AdaptException(e);
166 // Type Adapter - Type adapter maps fields of different order
167 final int _fieldMap[] = fieldMap;
168 result = new AbstractAdapter() {
170 public Object adapt(Object src) throws AdaptException {
172 Object values[] = new Object[len];
173 for (int rangeIndex=0; rangeIndex<len; rangeIndex++)
175 int domainIndex = _fieldMap[rangeIndex];
176 if (domainIndex>=0) {
177 Object srcValue = domainRecord.getComponent(src, domainIndex);
178 Object dstValue = componentAdapters[rangeIndex].adapt(srcValue);
179 values[rangeIndex] = dstValue;
182 values[rangeIndex] = rangeRecord.componentBindings[rangeIndex].createDefault();
185 return rangeRecord.create(values);
186 } catch (BindingException e) {
187 throw new AdaptException(e);
191 result.typeAdapter = true;
194 addToCache(req, result);
195 result.clones = true;
196 for (int rangeIndex=0; rangeIndex<len; rangeIndex++)
198 int domainIndex = fieldMap[rangeIndex];
199 if (domainIndex>=0) {
200 componentAdapters[rangeIndex] = getAdapterUnsynchronized(domainRecord.componentBindings[domainIndex], rangeRecord.componentBindings[rangeIndex], typeAdapter, mustClone);
201 result.typeAdapter |= componentAdapters[rangeIndex].typeAdapter;
202 result.clones &= componentAdapters[rangeIndex].clones;
208 if (domain instanceof UnionBinding && range instanceof UnionBinding)
210 final UnionBinding domainBinding = (UnionBinding) domain;
211 final UnionBinding rangeBinding = (UnionBinding) range;
212 UnionType domainType = domainBinding.type();
213 UnionType rangeType = rangeBinding.type();
215 // Tag-Map describes the index of the tag-types in domain for each tag-type in range
216 boolean requiresTypeAdapting = domainType.getComponentCount() != rangeType.getComponentCount();
217 int tagMap[] = new int[domainType.getComponentCount()];
218 for (int domainIndex=0; domainIndex<tagMap.length; domainIndex++)
220 String fieldName = domainType.getComponent(domainIndex).name;
221 Integer rangeIndex = rangeType.getComponentIndex(fieldName);
222 if (rangeIndex==null) throw new AdapterConstructionException("The range UnionType does not have expected tag \""+fieldName+"\"");
223 tagMap[domainIndex] = rangeIndex;
224 requiresTypeAdapting |= rangeIndex != domainIndex;
227 if (requiresTypeAdapting && !typeAdapter) {
228 throw new AdapterConstructionException("Type Adapter required.");
231 final AbstractAdapter[] componentAdapters = new AbstractAdapter[domainType.getComponentCount()];
233 AbstractAdapter result = null;
235 if (!requiresTypeAdapting) {
237 result = new AbstractAdapter() {
239 public Object adapt(Object obj) throws AdaptException {
241 int tag = domainBinding.getTag(obj);
242 Object srcValue = domainBinding.getValue(obj);
243 Object dstValue = componentAdapters[tag].adapt(srcValue);
244 return rangeBinding.create(tag, dstValue);
245 } catch (BindingException e) {
246 throw new AdaptException(e);
251 // Type adapter, type adapter rearranges tag indices
252 final int _tagMap[] = tagMap;
253 result = new AbstractAdapter() {
255 public Object adapt(Object obj) throws AdaptException {
257 int domainTag = domainBinding.getTag(obj);
258 int rangeTag = _tagMap[domainTag];
259 // Domain Component Binding
260 Object srcValue = domainBinding.getValue(obj);
261 Object dstValue = componentAdapters[domainTag].adapt(srcValue);
262 return rangeBinding.create(rangeTag, dstValue);
263 } catch (BindingException e) {
264 throw new AdaptException(e);
270 addToCache(req, result);
271 result.clones = true;
272 for (int domainIndex=0; domainIndex<domainType.getComponentCount(); domainIndex++)
274 int rangeIndex = tagMap[domainIndex];
275 componentAdapters[domainIndex] = getAdapterUnsynchronized(domainBinding.getComponentBindings()[domainIndex], rangeBinding.getComponentBindings()[rangeIndex], typeAdapter, mustClone);
276 result.typeAdapter |= componentAdapters[domainIndex].typeAdapter;
277 result.clones &= componentAdapters[domainIndex].clones;
282 if (domain instanceof BooleanBinding && range instanceof BooleanBinding)
284 final BooleanBinding domainBoolean = (BooleanBinding) domain;
285 final BooleanBinding rangeBoolean = (BooleanBinding) range;
286 AbstractAdapter result = new AbstractAdapter() {
288 public Object adapt(Object obj) throws AdaptException {
290 boolean value = domainBoolean.getValue_(obj);
291 return rangeBoolean.create(value);
292 } catch (BindingException e) {
293 throw new AdaptException(e);
297 result.clones = mustClone;
298 result.typeAdapter = true;
299 addToCache(req, result);
303 if (domain instanceof BooleanBinding && range instanceof NumberBinding)
306 final BooleanBinding domainBoolean = (BooleanBinding) domain;
307 final NumberBinding rangeNumber = (NumberBinding) range;
308 final Object falseValue = rangeNumber.create( Integer.valueOf(0) );
309 final Object trueValue = rangeNumber.create( Integer.valueOf(1) );
310 AbstractAdapter result = new AbstractAdapter() {
312 public Object adapt(Object obj) throws AdaptException {
314 boolean value = domainBoolean.getValue_(obj);
315 return value ? trueValue : falseValue;
316 } catch (BindingException e) {
317 throw new AdaptException(e);
321 result.clones = true;
322 result.typeAdapter = true;
323 addToCache(req, result);
325 } catch (BindingException e1) {
326 throw new AdapterConstructionException(e1);
330 if (domain instanceof NumberBinding && range instanceof BooleanBinding)
333 final NumberBinding domainNumber = (NumberBinding) domain;
334 final BooleanBinding rangeBoolean = (BooleanBinding) range;
335 final Object zeroValue = domainNumber.create( Integer.valueOf(0) );
336 AbstractAdapter result = new AbstractAdapter() {
338 public Object adapt(Object obj) throws AdaptException {
340 Object value = domainNumber.getValue(obj);
341 boolean bool = !domainNumber.equals(value, zeroValue);
342 return rangeBoolean.create(bool);
343 } catch (BindingException e) {
344 throw new AdaptException(e);
348 result.clones = true;
349 addToCache(req, result);
351 } catch (BindingException e1) {
352 throw new AdapterConstructionException(e1);
356 if (domain instanceof StringBinding && range instanceof StringBinding)
358 final StringBinding domainString = (StringBinding) domain;
359 final StringBinding rangeString = (StringBinding) range;
360 AbstractAdapter result = new AbstractAdapter() {
362 public Object adapt(Object obj) throws AdaptException {
364 String value = domainString.getValue(obj);
365 return rangeString.create(value);
366 } catch (BindingException e) {
367 throw new AdaptException(e);
371 result.clones = true;
372 addToCache(req, result);
376 if(domain instanceof StringBinding && range instanceof NumberBinding)
378 final StringBinding domainString = (StringBinding) domain;
379 final NumberBinding rangeString = (NumberBinding) range;
380 AbstractAdapter result = new AbstractAdapter() {
382 public Object adapt(Object obj) throws AdaptException {
384 String value = domainString.getValue(obj);
385 return rangeString.create(value);
386 } catch (BindingException e) {
387 throw new AdaptException(e);
391 result.clones = true;
392 addToCache(req, result);
396 if(domain instanceof StringBinding && range instanceof BooleanBinding)
398 final StringBinding domainString = (StringBinding) domain;
399 final BooleanBinding rangeString = (BooleanBinding) range;
400 AbstractAdapter result = new AbstractAdapter() {
402 public Object adapt(Object obj) throws AdaptException {
404 String value = domainString.getValue(obj);
405 return rangeString.create(Boolean.parseBoolean(value));
406 } catch (BindingException e) {
407 throw new AdaptException(e);
411 result.clones = true;
412 addToCache(req, result);
416 // XXX We can optimizes here by using primitives
417 if (domain instanceof NumberBinding && range instanceof NumberBinding)
419 final NumberBinding domainNumber = (NumberBinding) domain;
420 final NumberBinding rangeNumber = (NumberBinding) range;
422 String domainUnit = ((NumberType) domainNumber.type()).getUnit();
423 String rangeUnit = ((NumberType) rangeNumber.type()).getUnit();
424 IUnitConverter unitConverter;
425 if(domainUnit == null || rangeUnit == null || domainUnit.equals(rangeUnit))
426 unitConverter = null;
428 unitConverter = Units.createConverter(domainUnit, rangeUnit);
429 /*if(domainUnit == null || domainUnit.equals("")) {
430 if(rangeUnit == null || rangeUnit.equals(""))
431 unitConverter = null;
433 unitConverter = null;
434 // throw new AdapterConstructionException("Cannot convert from a unitless type to a type with unit.");
437 if(rangeUnit == null || rangeUnit.equals(""))
438 unitConverter = null;
439 // throw new AdapterConstructionException("Cannot convert from a type with unit to unitless type.");
441 unitConverter = Units.createConverter(domainUnit, rangeUnit);
443 boolean doUnitConversion = unitConverter != null && unitConverter != IdentityConverter.INSTANCE;
444 boolean doPrimitiveConversion = !domainNumber.type().getClass().equals( rangeNumber.type().getClass() );
445 if (doPrimitiveConversion && !typeAdapter)
446 throw new AdapterConstructionException("Type mismatch, use Type Adapter instead.");
448 AbstractAdapter result;
449 if (!doUnitConversion) {
450 result = new AbstractAdapter() {
452 public Object adapt(Object obj) throws AdaptException {
455 value = domainNumber.getValue(obj);
456 return rangeNumber.create(value);
457 } catch (BindingException e) {
458 throw new AdaptException(e);
463 final IUnitConverter _unitConverter = unitConverter;
464 result = new AbstractAdapter() {
466 public Object adapt(Object obj) throws AdaptException {
468 Number value = domainNumber.getValue(obj);
469 double convertedValue = _unitConverter.convert(value.doubleValue());
470 return rangeNumber.create(Double.valueOf(convertedValue));
471 } catch (BindingException e) {
472 throw new AdaptException(e);
477 result.typeAdapter = doPrimitiveConversion;
478 result.clones = true;
479 addToCache(req, result);
483 if (domain instanceof BooleanArrayBinding && range instanceof BooleanArrayBinding)
485 final BooleanArrayBinding domainArray = (BooleanArrayBinding) domain;
486 final BooleanArrayBinding rangeArray = (BooleanArrayBinding) range;
487 AbstractAdapter result = new AbstractAdapter() {
489 public Object adapt(Object obj) throws AdaptException {
491 boolean[] data = domainArray.getArray(obj);
492 if (mustClone) data = data.clone();
493 return rangeArray.create(data);
494 } catch (BindingException e) {
495 throw new AdaptException(e);
499 result.clones = true;
500 addToCache(req, result);
504 if (domain instanceof ByteArrayBinding && range instanceof ByteArrayBinding)
506 final ByteArrayBinding domainArray = (ByteArrayBinding) domain;
507 final ByteArrayBinding rangeArray = (ByteArrayBinding) range;
509 String domainUnit = ((NumberType) ((ArrayType)domainArray.type()).componentType).getUnit();
510 String rangeUnit = ((NumberType) ((ArrayType)rangeArray.type()).componentType).getUnit();
511 IUnitConverter unitConverter = ObjectUtils.objectEquals(domainUnit, rangeUnit) ? null : Units.createConverter(domainUnit, rangeUnit);
512 boolean doUnitConversion = unitConverter != null && unitConverter != IdentityConverter.INSTANCE;
514 AbstractAdapter result;
515 if (doUnitConversion) {
516 final IUnitConverter _unitConverter = unitConverter;
517 result = new AbstractAdapter() {
519 public Object adapt(Object obj) throws AdaptException {
521 byte[] data = domainArray.getArray(obj);
522 for (int i=0; i<data.length; i++) {
523 byte value = data[i];
524 double convertedValue = _unitConverter.convert((double)value);
525 data[i] = (byte) convertedValue;
527 return rangeArray.create(data);
528 } catch (BindingException e) {
529 throw new AdaptException(e);
534 result = new AbstractAdapter() {
536 public Object adapt(Object obj) throws AdaptException {
538 byte[] data = domainArray.getArray(obj);
539 if (mustClone) data = data.clone();
540 return rangeArray.create(data);
541 } catch (BindingException e) {
542 throw new AdaptException(e);
547 result.clones = true;
548 addToCache(req, result);
552 if (domain instanceof IntArrayBinding && range instanceof IntArrayBinding)
554 final IntArrayBinding domainArray = (IntArrayBinding) domain;
555 final IntArrayBinding rangeArray = (IntArrayBinding) range;
557 String domainUnit = ((NumberType) ((ArrayType)domainArray.type()).componentType).getUnit();
558 String rangeUnit = ((NumberType) ((ArrayType)rangeArray.type()).componentType).getUnit();
559 IUnitConverter unitConverter = ObjectUtils.objectEquals(domainUnit, rangeUnit) ||
560 domainUnit == null || rangeUnit == null ? null : Units.createConverter(domainUnit, rangeUnit);
561 boolean doUnitConversion = unitConverter != null && unitConverter != IdentityConverter.INSTANCE;
563 AbstractAdapter result;
564 if (doUnitConversion) {
565 final IUnitConverter _unitConverter = unitConverter;
566 result = new AbstractAdapter() {
568 public Object adapt(Object obj) throws AdaptException {
570 int[] data = domainArray.getArray(obj);
571 for (int i=0; i<data.length; i++) {
573 double convertedValue = _unitConverter.convert((double)value);
574 data[i] = (int) convertedValue;
576 return rangeArray.create(data);
577 } catch (BindingException e) {
578 throw new AdaptException(e);
583 result = new AbstractAdapter() {
585 public Object adapt(Object obj) throws AdaptException {
587 int[] data = domainArray.getArray(obj);
588 if (mustClone) data = data.clone();
589 return rangeArray.create(data);
590 } catch (BindingException e) {
591 throw new AdaptException(e);
597 result.clones = true;
598 addToCache(req, result);
602 if (domain instanceof LongArrayBinding && range instanceof LongArrayBinding)
604 final LongArrayBinding domainArray = (LongArrayBinding) domain;
605 final LongArrayBinding rangeArray = (LongArrayBinding) range;
607 String domainUnit = ((NumberType) ((ArrayType)domainArray.type()).componentType).getUnit();
608 String rangeUnit = ((NumberType) ((ArrayType)rangeArray.type()).componentType).getUnit();
609 IUnitConverter unitConverter = ObjectUtils.objectEquals(domainUnit, rangeUnit) ||
610 domainUnit == null || rangeUnit == null ? null : Units.createConverter(domainUnit, rangeUnit);
611 boolean doUnitConversion = unitConverter != null && unitConverter != IdentityConverter.INSTANCE;
613 AbstractAdapter result;
614 if (doUnitConversion) {
615 final IUnitConverter _unitConverter = unitConverter;
616 result = new AbstractAdapter() {
618 public Object adapt(Object obj) throws AdaptException {
620 long[] data = domainArray.getArray(obj);
621 for (int i=0; i<data.length; i++) {
622 long value = data[i];
623 double convertedValue = _unitConverter.convert((double)value);
624 data[i] = (long) convertedValue;
626 return rangeArray.create(data);
627 } catch (BindingException e) {
628 throw new AdaptException(e);
633 result = new AbstractAdapter() {
635 public Object adapt(Object obj) throws AdaptException {
637 long[] data = domainArray.getArray(obj);
638 if (mustClone) data = data.clone();
639 return rangeArray.create(data);
640 } catch (BindingException e) {
641 throw new AdaptException(e);
647 result.clones = true;
648 addToCache(req, result);
652 if (domain instanceof FloatArrayBinding && range instanceof FloatArrayBinding)
654 final FloatArrayBinding domainArray = (FloatArrayBinding) domain;
655 final FloatArrayBinding rangeArray = (FloatArrayBinding) range;
657 String domainUnit = ((NumberType) ((ArrayType)domainArray.type()).componentType).getUnit();
658 String rangeUnit = ((NumberType) ((ArrayType)rangeArray.type()).componentType).getUnit();
659 IUnitConverter unitConverter = ObjectUtils.objectEquals(domainUnit, rangeUnit) ||
660 domainUnit == null || rangeUnit == null ? null : Units.createConverter(domainUnit, rangeUnit);
661 boolean doUnitConversion = unitConverter != null && unitConverter != IdentityConverter.INSTANCE;
663 AbstractAdapter result;
664 if (doUnitConversion) {
665 final IUnitConverter _unitConverter = unitConverter;
666 result = new AbstractAdapter() {
668 public Object adapt(Object obj) throws AdaptException {
670 float[] data = domainArray.getArray(obj);
671 for (int i=0; i<data.length; i++) {
672 float value = data[i];
673 double convertedValue = _unitConverter.convert((double)value);
674 data[i] = (float) convertedValue;
676 return rangeArray.create(data);
677 } catch (BindingException e) {
678 throw new AdaptException(e);
683 result = new AbstractAdapter() {
685 public Object adapt(Object obj) throws AdaptException {
687 float[] data = domainArray.getArray(obj);
688 if (mustClone) data = data.clone();
689 return rangeArray.create(data);
690 } catch (BindingException e) {
691 throw new AdaptException(e);
697 result.clones = true;
698 addToCache(req, result);
702 if (domain instanceof DoubleArrayBinding && range instanceof DoubleArrayBinding)
704 final DoubleArrayBinding domainArray = (DoubleArrayBinding) domain;
705 final DoubleArrayBinding rangeArray = (DoubleArrayBinding) range;
707 String domainUnit = ((NumberType) ((ArrayType)domainArray.type()).componentType).getUnit();
708 String rangeUnit = ((NumberType) ((ArrayType)rangeArray.type()).componentType).getUnit();
709 IUnitConverter unitConverter = ObjectUtils.objectEquals(domainUnit, rangeUnit)
710 || domainUnit == null || rangeUnit == null ? null : Units.createConverter(domainUnit, rangeUnit);
711 boolean doUnitConversion = unitConverter != null && unitConverter != IdentityConverter.INSTANCE;
713 AbstractAdapter result;
714 if (doUnitConversion) {
715 final IUnitConverter _unitConverter = unitConverter;
716 result = new AbstractAdapter() {
718 public Object adapt(Object obj) throws AdaptException {
720 double[] data = domainArray.getArray(obj);
721 for (int i=0; i<data.length; i++) {
722 double value = data[i];
723 double convertedValue = _unitConverter.convert(value);
724 data[i] = convertedValue;
726 return rangeArray.create(data);
727 } catch (BindingException e) {
728 throw new AdaptException(e);
733 result = new AbstractAdapter() {
735 public Object adapt(Object obj) throws AdaptException {
737 double[] data = domainArray.getArray(obj);
738 if (mustClone) data = data.clone();
739 return rangeArray.create(data);
740 } catch (BindingException e) {
741 throw new AdaptException(e);
747 result.clones = true;
748 addToCache(req, result);
752 if (domain instanceof ArrayBinding && range instanceof ArrayBinding)
754 final ArrayBinding domainBinding = (ArrayBinding) domain;
755 final ArrayBinding rangeBinding = (ArrayBinding) range;
756 final AbstractAdapter componentAdapter = getAdapterUnsynchronized(domainBinding.getComponentBinding(), rangeBinding.getComponentBinding(), typeAdapter, mustClone);
757 AbstractAdapter result = new AbstractAdapter() {
759 public Object adapt(Object obj) throws AdaptException {
761 int len = domainBinding.size(obj);
762 ArrayList<Object> array = new ArrayList<Object>(len);
763 for (int i=0; i<len; i++)
765 Object srcValue = domainBinding.get(obj, i);
766 Object dstValue = componentAdapter.adapt(srcValue);
769 return rangeBinding instanceof ArrayListBinding ? array : rangeBinding.create(array);
770 } catch (BindingException e) {
771 throw new AdaptException(e);
776 result.clones = componentAdapter.clones;
777 addToCache(req, result);
781 if (domain instanceof OptionalBinding && range instanceof OptionalBinding)
783 final OptionalBinding domainBinding = (OptionalBinding) domain;
784 final OptionalBinding rangeBinding = (OptionalBinding) range;
785 final AbstractAdapter componentAdapter = getAdapterUnsynchronized(domainBinding.componentBinding, rangeBinding.componentBinding, typeAdapter, mustClone);
786 AbstractAdapter result = new AbstractAdapter() {
788 public Object adapt(Object obj) throws AdaptException {
790 if (!domainBinding.hasValue(obj)) return rangeBinding.createNoValue();
791 Object value = domainBinding.getValue(obj);
792 value = componentAdapter.adapt(value);
793 return rangeBinding.createValue(value);
794 } catch (BindingException e) {
795 throw new AdaptException(e);
799 result.typeAdapter = componentAdapter.typeAdapter;
800 result.clones = componentAdapter.clones;
801 addToCache(req, result);
805 // Adapt a non-optional value to an optional value
806 if (range instanceof OptionalBinding && !(domain instanceof OptionalBinding))
808 final Binding domainBinding = domain;
809 final OptionalBinding rangeBinding = (OptionalBinding) range;
810 final AbstractAdapter componentAdapter = getAdapterUnsynchronized(domainBinding, rangeBinding.componentBinding, typeAdapter, mustClone);
811 AbstractAdapter result = new AbstractAdapter() {
813 public Object adapt(Object obj) throws AdaptException {
815 obj = componentAdapter.adapt(obj);
816 return rangeBinding.createValue(obj);
817 } catch (BindingException e) {
818 throw new AdaptException(e);
822 result.typeAdapter = componentAdapter.typeAdapter;
823 result.clones = componentAdapter.clones;
824 addToCache(req, result);
828 if (domain instanceof VariantBinding && range instanceof VariantBinding)
830 final VariantBinding domainBinding = (VariantBinding) domain;
831 final VariantBinding rangeBinding = (VariantBinding) range;
832 AbstractAdapter result = new AbstractAdapter() {
834 public Object adapt(Object obj) throws AdaptException {
837 Binding domainValueBinding = domainBinding.getContentBinding(obj);
838 Object domainObject = domainBinding.getContent(obj, domainValueBinding);
839 if (mustClone && domainObject!=obj) {
840 Adapter valueAdapter = getAdapterUnsynchronized(domainValueBinding, domainValueBinding, false, true);
841 domainObject = valueAdapter.adapt(domainObject);
843 Object rangeVariant = rangeBinding.create(domainValueBinding, domainObject);
845 } catch (BindingException e) {
846 throw new AdaptException(e);
847 } catch (AdapterConstructionException e) {
848 throw new AdaptException(e);
852 result.clones = mustClone;
853 addToCache(req, result);
857 if (domain instanceof VariantBinding && !(range instanceof VariantBinding))
859 // Make a recursive adaptation from a variant source
860 final VariantBinding domainBinding = (VariantBinding)domain;
861 final Binding rangeBinding = range;
862 AbstractAdapter result = new AbstractAdapter() {
864 public Object adapt(Object obj) throws AdaptException {
866 Object value = domainBinding.getContent(obj);
867 Binding contentBinding = domainBinding.getContentBinding(obj);
868 AbstractAdapter adapter = (AbstractAdapter) getAdapter(contentBinding, rangeBinding, typeAdapter, mustClone);
869 return adapter.adapt(value);
870 } catch (BindingException | AdapterConstructionException e) {
871 throw new AdaptException(e);
875 result.clones = mustClone;
876 addToCache(req, result);
880 if (range instanceof VariantBinding && !(domain instanceof VariantBinding))
882 // Default to just wrapping the domain type
883 final VariantBinding rangeBinding = (VariantBinding)range;
884 final Binding domainBinding = domain;
885 AbstractAdapter result = new AbstractAdapter() {
887 public Object adapt(Object obj) throws AdaptException {
890 Adapter valueAdapter;
891 valueAdapter = getAdapterUnsynchronized(domainBinding, domainBinding, false, true);
892 obj = valueAdapter.adapt(obj);
894 return rangeBinding.create(domainBinding, obj);
895 } catch (AdapterConstructionException | BindingException e) {
896 throw new AdaptException(e);
900 result.clones = mustClone;
901 addToCache(req, result);
905 if (domain instanceof MapBinding && range instanceof MapBinding)
907 final MapBinding domainBinding = (MapBinding) domain;
908 final MapBinding rangeBinding = (MapBinding) range;
909 final AbstractAdapter keyAdapter = getAdapterUnsynchronized(domainBinding.getKeyBinding(), rangeBinding.getKeyBinding(), typeAdapter, mustClone);
910 final AbstractAdapter valueAdapter = getAdapterUnsynchronized(domainBinding.getValueBinding(), rangeBinding.getValueBinding(), typeAdapter, mustClone);
911 AbstractAdapter result = new AbstractAdapter() {
913 public Object adapt(Object obj) throws AdaptException {
915 int len = domainBinding.size(obj);
916 Object domainKeys[] = domainBinding.getKeys(obj);
917 Object domainValues[] = domainBinding.getValues(obj);
918 Object rangeKeys[] = new Object[len];
919 Object rangeValues[] = new Object[len];
920 for (int i=0; i<len; i++) {
921 Object domainKey = domainKeys[i];
922 Object domainValue = domainValues[i];
923 Object rangeKey = keyAdapter.adapt(domainKey);
924 Object rangeValue = valueAdapter.adapt(domainValue);
925 rangeKeys[i] = rangeKey;
926 rangeValues[i] = rangeValue;
928 Object rangeMap = rangeBinding.create(rangeKeys, rangeValues);
930 } catch (BindingException e) {
931 throw new AdaptException(e);
935 result.typeAdapter |= keyAdapter.typeAdapter | valueAdapter.typeAdapter;
936 result.clones = keyAdapter.clones & valueAdapter.clones;
937 addToCache(req, result);
941 // Special-Case: Convert Union to its Composite
942 if (domain instanceof UnionBinding) {
943 final UnionType ut = (UnionType) domain.getDataType();
944 final UnionBinding ub = (UnionBinding) domain;
945 Binding[] cbs = ub.getComponentBindings();
946 for (int i=0; i<cbs.length; i++)
948 Binding domainCompositeBinding = cbs[i];
949 if (ut.getComponent(i).type.equals(range.getDataType())) {
950 final AbstractAdapter union2CompositeAdapter = getAdapterUnsynchronized(domainCompositeBinding, range, allowPrimitiveConversion);
952 AbstractAdapter result = new AbstractAdapter() {
954 public Object adapt(Object obj) throws AdaptException {
956 Object domainUnion = obj;
957 int domainTag = ub.getTag(domainUnion);
958 if (domainTag != tag) {
959 throw new AdaptException("This adapter can adapt only "+ub.getDataType().getComponents()[tag].name+"s");
961 Object domainComposite = ub.getValue(domainUnion);
962 Object rangeComposite = union2CompositeAdapter.adapt(domainComposite);
963 return rangeComposite;
964 } catch (BindingException e) {
965 throw new AdaptException(e);
969 result.hasPrimitiveConversion = union2CompositeAdapter.hasPrimitiveConversion;
970 addToCache(pair, result);
976 // Special-Case: Convert Composite to Union
977 if (range instanceof UnionBinding) {
978 final UnionType ut = (UnionType) range.getDataType();
979 final UnionBinding ub = (UnionBinding) range;
980 Binding cbs[] = ub.getComponentBindings();
981 for (int i=0; i<cbs.length; i++) {
982 Binding rangeCompositeBinding = cbs[i];
983 if (ut.getComponent(i).type.equals(domain.getDataType())) {
984 final AbstractAdapter domainObject2RangeCompositeAdapter = getAdapterUnsynchronized(rangeCompositeBinding, domain, allowPrimitiveConversion);
986 AbstractAdapter result = new AbstractAdapter() {
988 public Object adapt(Object obj) throws AdaptException {
990 Object domainObject = obj;
991 Object rangeComposite = domainObject2RangeCompositeAdapter.adapt(domainObject);
992 Object rangeUnion = ub.create(tag, rangeComposite);
994 } catch (BindingException e) {
995 throw new AdaptException(e);
999 result.hasPrimitiveConversion = domainObject2RangeCompositeAdapter.hasPrimitiveConversion;
1000 addToCache(pair, result);
1007 } catch (UnitParseException e) {
1008 throw new AdapterConstructionException(e.getMessage(), e);
1011 StringBuilder error = new StringBuilder();
1012 error.append("Could not create ");
1013 if (mustClone) error.append("cloning ");
1014 if (typeAdapter) error.append("type");
1015 error.append("adapter (");
1016 error.append("domain=");
1017 error.append(domain.type().toSingleLineString());
1018 error.append(", range=");
1019 error.append(range.type().toSingleLineString());
1022 throw new AdapterConstructionException(error.toString());
1028 * Adapt a value of one type to another.
1033 * @return adapted value
1034 * @throws AdapterConstructionException
1035 * @throws AdaptException
1037 public Object adapt(Object value, Binding domain, Binding range)
1038 throws AdaptException
1040 if (domain == range) return value;
1042 if (range instanceof VariantBinding && !(domain instanceof VariantBinding)) {
1043 // Default to just wrapping the value (avoid adapter construction to save memory)
1044 return ((VariantBinding)range).create(domain, value);
1046 return getAdapter(domain, range, true, false).adapt(value);
1047 } catch (AdapterConstructionException | BindingException e) {
1048 throw new AdaptException(e);
1053 * Adapt a value of one type to another
1058 * @return adapted value
1059 * @throws AdapterConstructionException
1060 * @throws AdaptException
1062 public Object adaptUnchecked(Object value, Binding domain, Binding range)
1063 throws RuntimeAdapterConstructionException, RuntimeAdaptException
1065 if (domain == range) return value;
1067 if (range instanceof VariantBinding && !(domain instanceof VariantBinding)) {
1068 // Default to just wrapping the value (avoid adapter construction to save memory)
1069 return ((VariantBinding)range).create(domain, value);
1071 return getAdapter(domain, range, true, false).adaptUnchecked(value);
1072 } catch (RuntimeAdapterConstructionException | RuntimeBindingException e) {
1073 throw new RuntimeAdaptException(new AdaptException(e.getCause()));
1074 } catch (AdapterConstructionException | BindingException e) {
1075 throw new RuntimeAdaptException(new AdaptException(e));
1080 * Clone a value to a type to another. Bindings that handle immutable values
1081 * may return the same instance, others will guarantee a complete copy.
1086 * @return adapted value
1087 * @throws AdapterConstructionException
1088 * @throws AdaptException
1090 public Object clone(Object value, Binding domain, Binding range)
1091 throws AdaptException
1094 return getAdapter(domain, range, true, true).adapt(value);
1095 } catch (AdapterConstructionException e) {
1096 throw new AdaptException(e);
1102 * Clone a value of one binding to another. Bindings that handle immutable values
1103 * may return the same instance, others will guarantee a complete copy.
1108 * @return adapted value
1109 * @throws AdapterConstructionException
1110 * @throws AdaptException
1112 public Object cloneUnchecked(Object value, Binding domain, Binding range)
1113 throws RuntimeAdapterConstructionException, RuntimeAdaptException
1116 return getAdapter(domain, range, true, true).adapt(value);
1117 } catch (AdaptException e) {
1118 throw new RuntimeAdaptException(e);
1119 } catch (RuntimeAdapterConstructionException e) {
1120 throw new RuntimeAdaptException(new AdaptException(e.getCause()));
1121 } catch (AdapterConstructionException e) {
1122 throw new RuntimeAdaptException(new AdaptException(e));