1 /*******************************************************************************
\r
2 * Copyright (c) 2010 Association for Decentralized Information Management in
\r
4 * All rights reserved. This program and the accompanying materials
\r
5 * are made available under the terms of the Eclipse Public License v1.0
\r
6 * which accompanies this distribution, and is available at
\r
7 * http://www.eclipse.org/legal/epl-v10.html
\r
10 * VTT Technical Research Centre of Finland - initial API and implementation
\r
11 *******************************************************************************/
\r
12 package org.simantics.databoard.adapter;
14 import java.util.ArrayList;
\r
15 import java.util.Map;
\r
17 import org.apache.commons.collections.map.ReferenceMap;
\r
18 import org.simantics.databoard.Bindings;
\r
19 import org.simantics.databoard.Units;
\r
20 import org.simantics.databoard.binding.ArrayBinding;
\r
21 import org.simantics.databoard.binding.Binding;
\r
22 import org.simantics.databoard.binding.BooleanBinding;
\r
23 import org.simantics.databoard.binding.MapBinding;
\r
24 import org.simantics.databoard.binding.NumberBinding;
\r
25 import org.simantics.databoard.binding.OptionalBinding;
\r
26 import org.simantics.databoard.binding.RecordBinding;
\r
27 import org.simantics.databoard.binding.StringBinding;
\r
28 import org.simantics.databoard.binding.UnionBinding;
\r
29 import org.simantics.databoard.binding.VariantBinding;
\r
30 import org.simantics.databoard.binding.error.BindingException;
\r
31 import org.simantics.databoard.binding.error.RuntimeBindingException;
\r
32 import org.simantics.databoard.binding.impl.ArrayListBinding;
\r
33 import org.simantics.databoard.binding.impl.BooleanArrayBinding;
\r
34 import org.simantics.databoard.binding.impl.ByteArrayBinding;
\r
35 import org.simantics.databoard.binding.impl.DoubleArrayBinding;
\r
36 import org.simantics.databoard.binding.impl.FloatArrayBinding;
\r
37 import org.simantics.databoard.binding.impl.IntArrayBinding;
\r
38 import org.simantics.databoard.binding.impl.LongArrayBinding;
\r
39 import org.simantics.databoard.type.ArrayType;
\r
40 import org.simantics.databoard.type.NumberType;
\r
41 import org.simantics.databoard.type.RecordType;
\r
42 import org.simantics.databoard.type.UnionType;
\r
43 import org.simantics.databoard.units.IUnitConverter;
\r
44 import org.simantics.databoard.units.IdentityConverter;
\r
45 import org.simantics.databoard.units.internal.UnitParseException;
\r
46 import org.simantics.databoard.util.ObjectUtils;
\r
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" )
\r
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;
\r
63 if (domain.getClass() == range.getClass() &&
\r
64 ( !mustClone || domain.isImmutable() ) &&
\r
65 NumberBinding.class.isAssignableFrom( domain.getClass() ) ) {
\r
67 NumberBinding db = (NumberBinding) domain;
\r
68 NumberBinding rb = (NumberBinding) range;
\r
69 String u1 = db.type().getUnit();
\r
70 String u2 = rb.type().getUnit();
\r
71 if (u1==null || u2==null || u1.equals("") || u2.equals("") || u1.equals(u2)) return PassThruAdapter.PASSTHRU;
\r
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) {
\r
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();
\r
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);
\r
130 if (domainIndex!=null) {
\r
131 fieldMap[rangeIndex] = domainIndex;
\r
132 requiresTypeAdapting |= rangeIndex != domainIndex;
\r
134 fieldMap[rangeIndex] = -1;
\r
135 requiresTypeAdapting = true;
\r
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];
\r
177 if (domainIndex>=0) {
\r
178 Object srcValue = domainRecord.getComponent(src, domainIndex);
\r
179 Object dstValue = componentAdapters[rangeIndex].adapt(srcValue);
\r
180 values[rangeIndex] = dstValue;
\r
183 values[rangeIndex] = rangeRecord.componentBindings[rangeIndex].createDefault();
\r
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];
\r
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;
\r
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;
\r
300 addToCache(req, result);
304 if (domain instanceof BooleanBinding && range instanceof NumberBinding)
\r
307 final BooleanBinding domainBoolean = (BooleanBinding) domain;
\r
308 final NumberBinding rangeNumber = (NumberBinding) range;
\r
309 final Object falseValue = rangeNumber.create( Integer.valueOf(0) );
\r
310 final Object trueValue = rangeNumber.create( Integer.valueOf(1) );
\r
311 AbstractAdapter result = new AbstractAdapter() {
\r
313 public Object adapt(Object obj) throws AdaptException {
\r
315 boolean value = domainBoolean.getValue_(obj);
\r
316 return value ? trueValue : falseValue;
\r
317 } catch (BindingException e) {
\r
318 throw new AdaptException(e);
\r
322 result.clones = true;
\r
323 result.typeAdapter = true;
\r
324 addToCache(req, result);
\r
326 } catch (BindingException e1) {
\r
327 throw new AdapterConstructionException(e1);
\r
331 if (domain instanceof NumberBinding && range instanceof BooleanBinding)
\r
334 final NumberBinding domainNumber = (NumberBinding) domain;
\r
335 final BooleanBinding rangeBoolean = (BooleanBinding) range;
\r
336 final Object zeroValue = domainNumber.create( Integer.valueOf(0) );
\r
337 AbstractAdapter result = new AbstractAdapter() {
\r
339 public Object adapt(Object obj) throws AdaptException {
\r
341 Object value = domainNumber.getValue(obj);
\r
342 boolean bool = !domainNumber.equals(value, zeroValue);
\r
343 return rangeBoolean.create(bool);
\r
344 } catch (BindingException e) {
\r
345 throw new AdaptException(e);
\r
349 result.clones = true;
\r
350 addToCache(req, result);
\r
352 } catch (BindingException e1) {
\r
353 throw new AdapterConstructionException(e1);
\r
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)
\r
379 final StringBinding domainString = (StringBinding) domain;
\r
380 final NumberBinding rangeString = (NumberBinding) range;
\r
381 AbstractAdapter result = new AbstractAdapter() {
\r
383 public Object adapt(Object obj) throws AdaptException {
\r
385 String value = domainString.getValue(obj);
\r
386 return rangeString.create(value);
\r
387 } catch (BindingException e) {
\r
388 throw new AdaptException(e);
\r
392 result.clones = true;
\r
393 addToCache(req, result);
\r
397 if(domain instanceof StringBinding && range instanceof BooleanBinding)
\r
399 final StringBinding domainString = (StringBinding) domain;
\r
400 final BooleanBinding rangeString = (BooleanBinding) range;
\r
401 AbstractAdapter result = new AbstractAdapter() {
\r
403 public Object adapt(Object obj) throws AdaptException {
\r
405 String value = domainString.getValue(obj);
\r
406 return rangeString.create(Boolean.parseBoolean(value));
\r
407 } catch (BindingException e) {
\r
408 throw new AdaptException(e);
\r
412 result.clones = true;
\r
413 addToCache(req, result);
\r
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();
\r
425 IUnitConverter unitConverter;
\r
426 if(domainUnit == null || rangeUnit == null || domainUnit.equals(rangeUnit))
\r
427 unitConverter = null;
\r
429 unitConverter = Units.createConverter(domainUnit, rangeUnit);
\r
430 /*if(domainUnit == null || domainUnit.equals("")) {
\r
431 if(rangeUnit == null || rangeUnit.equals(""))
\r
432 unitConverter = null;
\r
434 unitConverter = null;
\r
435 // throw new AdapterConstructionException("Cannot convert from a unitless type to a type with unit.");
\r
438 if(rangeUnit == null || rangeUnit.equals(""))
\r
439 unitConverter = null;
\r
440 // throw new AdapterConstructionException("Cannot convert from a type with unit to unitless type.");
\r
442 unitConverter = Units.createConverter(domainUnit, rangeUnit);
\r
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);
\r
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);
\r
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) ||
\r
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);
\r
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) ||
\r
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);
\r
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) ||
\r
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);
\r
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)
\r
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);
\r
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
\r
807 if (range instanceof OptionalBinding && !(domain instanceof OptionalBinding))
\r
809 final Binding domainBinding = domain;
\r
810 final OptionalBinding rangeBinding = (OptionalBinding) range;
\r
811 final AbstractAdapter componentAdapter = getAdapterUnsynchronized(domainBinding, rangeBinding.componentBinding, typeAdapter, mustClone);
\r
812 AbstractAdapter result = new AbstractAdapter() {
\r
814 public Object adapt(Object obj) throws AdaptException {
\r
816 obj = componentAdapter.adapt(obj);
\r
817 return rangeBinding.createValue(obj);
\r
818 } catch (BindingException e) {
\r
819 throw new AdaptException(e);
\r
823 result.typeAdapter = componentAdapter.typeAdapter;
\r
824 result.clones = componentAdapter.clones;
\r
825 addToCache(req, result);
\r
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);
\r
840 if (mustClone && domainObject!=obj) {
\r
841 Adapter valueAdapter = getAdapterUnsynchronized(domainValueBinding, domainValueBinding, false, true);
\r
842 domainObject = valueAdapter.adapt(domainObject);
\r
844 Object rangeVariant = rangeBinding.create(domainValueBinding, domainObject);
846 } catch (BindingException e) {
847 throw new AdaptException(e);
848 } catch (AdapterConstructionException e) {
\r
849 throw new AdaptException(e);
\r
853 result.clones = mustClone;
854 addToCache(req, result);
858 if (domain instanceof VariantBinding && !(range instanceof VariantBinding))
\r
860 // Make a recursive adaptation from a variant source
\r
861 final VariantBinding domainBinding = (VariantBinding)domain;
\r
862 final Binding rangeBinding = range;
\r
863 AbstractAdapter result = new AbstractAdapter() {
\r
865 public Object adapt(Object obj) throws AdaptException {
\r
867 Object value = domainBinding.getContent(obj);
\r
868 Binding contentBinding = domainBinding.getContentBinding(obj);
\r
869 AbstractAdapter adapter = (AbstractAdapter) getAdapter(contentBinding, rangeBinding, typeAdapter, mustClone);
\r
870 return adapter.adapt(value);
\r
871 } catch (BindingException | AdapterConstructionException e) {
\r
872 throw new AdaptException(e);
\r
876 result.clones = mustClone;
\r
877 addToCache(req, result);
\r
881 if (range instanceof VariantBinding && !(domain instanceof VariantBinding))
\r
883 // Default to just wrapping the domain type
\r
884 final VariantBinding rangeBinding = (VariantBinding)range;
\r
885 final Binding domainBinding = domain;
\r
886 AbstractAdapter result = new AbstractAdapter() {
\r
888 public Object adapt(Object obj) throws AdaptException {
\r
891 Adapter valueAdapter;
\r
892 valueAdapter = getAdapterUnsynchronized(domainBinding, domainBinding, false, true);
\r
893 obj = valueAdapter.adapt(obj);
\r
895 return rangeBinding.create(domainBinding, obj);
\r
896 } catch (AdapterConstructionException | BindingException e) {
\r
897 throw new AdaptException(e);
\r
901 result.clones = mustClone;
\r
902 addToCache(req, result);
\r
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.
\r
1034 * @return adapted value
\r
1035 * @throws AdapterConstructionException
\r
1036 * @throws AdaptException
\r
1038 public Object adapt(Object value, Binding domain, Binding range)
\r
1039 throws AdaptException
\r
1041 if (domain == range) return value;
\r
1043 if (range instanceof VariantBinding && !(domain instanceof VariantBinding)) {
\r
1044 // Default to just wrapping the value (avoid adapter construction to save memory)
\r
1045 return ((VariantBinding)range).create(domain, value);
\r
1047 return getAdapter(domain, range, true, false).adapt(value);
\r
1048 } catch (AdapterConstructionException | BindingException e) {
\r
1049 throw new AdaptException(e);
\r
1054 * Adapt a value of one type to another
\r
1059 * @return adapted value
\r
1060 * @throws AdapterConstructionException
\r
1061 * @throws AdaptException
\r
1063 public Object adaptUnchecked(Object value, Binding domain, Binding range)
\r
1064 throws RuntimeAdapterConstructionException, RuntimeAdaptException
\r
1066 if (domain == range) return value;
\r
1068 if (range instanceof VariantBinding && !(domain instanceof VariantBinding)) {
\r
1069 // Default to just wrapping the value (avoid adapter construction to save memory)
\r
1070 return ((VariantBinding)range).create(domain, value);
\r
1072 return getAdapter(domain, range, true, false).adaptUnchecked(value);
\r
1073 } catch (RuntimeAdapterConstructionException | RuntimeBindingException e) {
\r
1074 throw new RuntimeAdaptException(new AdaptException(e.getCause()));
\r
1075 } catch (AdapterConstructionException | BindingException e) {
\r
1076 throw new RuntimeAdaptException(new AdaptException(e));
\r
1081 * Clone a value to a type to another. Bindings that handle immutable values
\r
1082 * may return the same instance, others will guarantee a complete copy.
\r
1087 * @return adapted value
\r
1088 * @throws AdapterConstructionException
\r
1089 * @throws AdaptException
\r
1091 public Object clone(Object value, Binding domain, Binding range)
\r
1092 throws AdaptException
\r
1095 return getAdapter(domain, range, true, true).adapt(value);
\r
1096 } catch (AdapterConstructionException e) {
\r
1097 throw new AdaptException(e);
\r
1103 * Clone a value of one binding to another. Bindings that handle immutable values
\r
1104 * may return the same instance, others will guarantee a complete copy.
\r
1109 * @return adapted value
\r
1110 * @throws AdapterConstructionException
\r
1111 * @throws AdaptException
\r
1113 public Object cloneUnchecked(Object value, Binding domain, Binding range)
\r
1114 throws RuntimeAdapterConstructionException, RuntimeAdaptException
\r
1117 return getAdapter(domain, range, true, true).adapt(value);
\r
1118 } catch (AdaptException e) {
\r
1119 throw new RuntimeAdaptException(e);
\r
1120 } catch (RuntimeAdapterConstructionException e) {
\r
1121 throw new RuntimeAdaptException(new AdaptException(e.getCause()));
\r
1122 } catch (AdapterConstructionException e) {
\r
1123 throw new RuntimeAdaptException(new AdaptException(e));
\r