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.Units;
\r
19 import org.simantics.databoard.binding.ArrayBinding;
\r
20 import org.simantics.databoard.binding.Binding;
\r
21 import org.simantics.databoard.binding.BooleanBinding;
\r
22 import org.simantics.databoard.binding.MapBinding;
\r
23 import org.simantics.databoard.binding.NumberBinding;
\r
24 import org.simantics.databoard.binding.OptionalBinding;
\r
25 import org.simantics.databoard.binding.RecordBinding;
\r
26 import org.simantics.databoard.binding.StringBinding;
\r
27 import org.simantics.databoard.binding.UnionBinding;
\r
28 import org.simantics.databoard.binding.VariantBinding;
\r
29 import org.simantics.databoard.binding.error.BindingException;
\r
30 import org.simantics.databoard.binding.error.RuntimeBindingException;
\r
31 import org.simantics.databoard.binding.impl.ArrayListBinding;
\r
32 import org.simantics.databoard.binding.impl.BooleanArrayBinding;
\r
33 import org.simantics.databoard.binding.impl.ByteArrayBinding;
\r
34 import org.simantics.databoard.binding.impl.DoubleArrayBinding;
\r
35 import org.simantics.databoard.binding.impl.FloatArrayBinding;
\r
36 import org.simantics.databoard.binding.impl.IntArrayBinding;
\r
37 import org.simantics.databoard.binding.impl.LongArrayBinding;
\r
38 import org.simantics.databoard.type.ArrayType;
\r
39 import org.simantics.databoard.type.NumberType;
\r
40 import org.simantics.databoard.type.RecordType;
\r
41 import org.simantics.databoard.type.UnionType;
\r
42 import org.simantics.databoard.units.IUnitConverter;
\r
43 import org.simantics.databoard.units.IdentityConverter;
\r
44 import org.simantics.databoard.units.internal.UnitParseException;
\r
45 import org.simantics.databoard.util.ObjectUtils;
\r
48 * AdapterRepository is a factory and a collection of adapters.
50 * @author Toni Kalajainen <toni.kalajainen@vtt.fi>
52 public class AdapterFactory {
54 @SuppressWarnings( "unchecked" )
\r
55 Map<AdapterRequest, AbstractAdapter> cache = (Map<AdapterRequest, AbstractAdapter>) new ReferenceMap(ReferenceMap.SOFT, ReferenceMap.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;
\r
62 if (domain.getClass() == range.getClass() &&
\r
63 ( !mustClone || domain.isImmutable() ) &&
\r
64 NumberBinding.class.isAssignableFrom( domain.getClass() ) ) {
\r
66 NumberBinding db = (NumberBinding) domain;
\r
67 NumberBinding rb = (NumberBinding) range;
\r
68 String u1 = db.type().getUnit();
\r
69 String u2 = rb.type().getUnit();
\r
70 if (u1==null || u2==null || u1.equals("") || u2.equals("") || u1.equals(u2)) return PassThruAdapter.PASSTHRU;
\r
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) {
\r
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();
\r
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);
\r
129 if (domainIndex!=null) {
\r
130 fieldMap[rangeIndex] = domainIndex;
\r
131 requiresTypeAdapting |= rangeIndex != domainIndex;
\r
133 fieldMap[rangeIndex] = -1;
\r
134 requiresTypeAdapting = true;
\r
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];
\r
176 if (domainIndex>=0) {
\r
177 Object srcValue = domainRecord.getComponent(src, domainIndex);
\r
178 Object dstValue = componentAdapters[rangeIndex].adapt(srcValue);
\r
179 values[rangeIndex] = dstValue;
\r
182 values[rangeIndex] = rangeRecord.componentBindings[rangeIndex].createDefault();
\r
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];
\r
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;
\r
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;
\r
299 addToCache(req, result);
303 if (domain instanceof BooleanBinding && range instanceof NumberBinding)
\r
306 final BooleanBinding domainBoolean = (BooleanBinding) domain;
\r
307 final NumberBinding rangeNumber = (NumberBinding) range;
\r
308 final Object falseValue = rangeNumber.create( Integer.valueOf(0) );
\r
309 final Object trueValue = rangeNumber.create( Integer.valueOf(1) );
\r
310 AbstractAdapter result = new AbstractAdapter() {
\r
312 public Object adapt(Object obj) throws AdaptException {
\r
314 boolean value = domainBoolean.getValue_(obj);
\r
315 return value ? trueValue : falseValue;
\r
316 } catch (BindingException e) {
\r
317 throw new AdaptException(e);
\r
321 result.clones = true;
\r
322 result.typeAdapter = true;
\r
323 addToCache(req, result);
\r
325 } catch (BindingException e1) {
\r
326 throw new AdapterConstructionException(e1);
\r
330 if (domain instanceof NumberBinding && range instanceof BooleanBinding)
\r
333 final NumberBinding domainNumber = (NumberBinding) domain;
\r
334 final BooleanBinding rangeBoolean = (BooleanBinding) range;
\r
335 final Object zeroValue = domainNumber.create( Integer.valueOf(0) );
\r
336 AbstractAdapter result = new AbstractAdapter() {
\r
338 public Object adapt(Object obj) throws AdaptException {
\r
340 Object value = domainNumber.getValue(obj);
\r
341 boolean bool = !domainNumber.equals(value, zeroValue);
\r
342 return rangeBoolean.create(bool);
\r
343 } catch (BindingException e) {
\r
344 throw new AdaptException(e);
\r
348 result.clones = true;
\r
349 addToCache(req, result);
\r
351 } catch (BindingException e1) {
\r
352 throw new AdapterConstructionException(e1);
\r
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)
\r
378 final StringBinding domainString = (StringBinding) domain;
\r
379 final NumberBinding rangeString = (NumberBinding) range;
\r
380 AbstractAdapter result = new AbstractAdapter() {
\r
382 public Object adapt(Object obj) throws AdaptException {
\r
384 String value = domainString.getValue(obj);
\r
385 return rangeString.create(value);
\r
386 } catch (BindingException e) {
\r
387 throw new AdaptException(e);
\r
391 result.clones = true;
\r
392 addToCache(req, result);
\r
396 if(domain instanceof StringBinding && range instanceof BooleanBinding)
\r
398 final StringBinding domainString = (StringBinding) domain;
\r
399 final BooleanBinding rangeString = (BooleanBinding) range;
\r
400 AbstractAdapter result = new AbstractAdapter() {
\r
402 public Object adapt(Object obj) throws AdaptException {
\r
404 String value = domainString.getValue(obj);
\r
405 return rangeString.create(Boolean.parseBoolean(value));
\r
406 } catch (BindingException e) {
\r
407 throw new AdaptException(e);
\r
411 result.clones = true;
\r
412 addToCache(req, result);
\r
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();
\r
424 IUnitConverter unitConverter;
\r
425 if(domainUnit == null || rangeUnit == null || domainUnit.equals(rangeUnit))
\r
426 unitConverter = null;
\r
428 unitConverter = Units.createConverter(domainUnit, rangeUnit);
\r
429 /*if(domainUnit == null || domainUnit.equals("")) {
\r
430 if(rangeUnit == null || rangeUnit.equals(""))
\r
431 unitConverter = null;
\r
433 unitConverter = null;
\r
434 // throw new AdapterConstructionException("Cannot convert from a unitless type to a type with unit.");
\r
437 if(rangeUnit == null || rangeUnit.equals(""))
\r
438 unitConverter = null;
\r
439 // throw new AdapterConstructionException("Cannot convert from a type with unit to unitless type.");
\r
441 unitConverter = Units.createConverter(domainUnit, rangeUnit);
\r
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);
\r
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);
\r
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) ||
\r
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);
\r
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) ||
\r
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);
\r
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) ||
\r
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);
\r
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)
\r
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);
\r
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
\r
806 if (range instanceof OptionalBinding && !(domain instanceof OptionalBinding))
\r
808 final Binding domainBinding = domain;
\r
809 final OptionalBinding rangeBinding = (OptionalBinding) range;
\r
810 final AbstractAdapter componentAdapter = getAdapterUnsynchronized(domainBinding, rangeBinding.componentBinding, typeAdapter, mustClone);
\r
811 AbstractAdapter result = new AbstractAdapter() {
\r
813 public Object adapt(Object obj) throws AdaptException {
\r
815 obj = componentAdapter.adapt(obj);
\r
816 return rangeBinding.createValue(obj);
\r
817 } catch (BindingException e) {
\r
818 throw new AdaptException(e);
\r
822 result.typeAdapter = componentAdapter.typeAdapter;
\r
823 result.clones = componentAdapter.clones;
\r
824 addToCache(req, result);
\r
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);
\r
839 if (mustClone && domainObject!=obj) {
\r
840 Adapter valueAdapter = getAdapterUnsynchronized(domainValueBinding, domainValueBinding, false, true);
\r
841 domainObject = valueAdapter.adapt(domainObject);
\r
843 Object rangeVariant = rangeBinding.create(domainValueBinding, domainObject);
845 } catch (BindingException e) {
846 throw new AdaptException(e);
847 } catch (AdapterConstructionException e) {
\r
848 throw new AdaptException(e);
\r
852 result.clones = mustClone;
853 addToCache(req, result);
857 if (range instanceof VariantBinding && !(domain instanceof VariantBinding))
\r
859 // Default to just wrapping the domain type
\r
860 final VariantBinding rangeBinding = (VariantBinding)range;
\r
861 final Binding domainBinding = domain;
\r
862 AbstractAdapter result = new AbstractAdapter() {
\r
864 public Object adapt(Object obj) throws AdaptException {
\r
867 Adapter valueAdapter;
\r
868 valueAdapter = getAdapterUnsynchronized(domainBinding, domainBinding, false, true);
\r
869 obj = valueAdapter.adapt(obj);
\r
871 return rangeBinding.create(domainBinding, obj);
\r
872 } catch (AdapterConstructionException | BindingException e) {
\r
873 throw new AdaptException(e);
\r
877 result.clones = mustClone;
\r
878 addToCache(req, result);
\r
882 if (domain instanceof MapBinding && range instanceof MapBinding)
884 final MapBinding domainBinding = (MapBinding) domain;
885 final MapBinding rangeBinding = (MapBinding) range;
886 final AbstractAdapter keyAdapter = getAdapterUnsynchronized(domainBinding.getKeyBinding(), rangeBinding.getKeyBinding(), typeAdapter, mustClone);
887 final AbstractAdapter valueAdapter = getAdapterUnsynchronized(domainBinding.getValueBinding(), rangeBinding.getValueBinding(), typeAdapter, mustClone);
888 AbstractAdapter result = new AbstractAdapter() {
890 public Object adapt(Object obj) throws AdaptException {
892 int len = domainBinding.size(obj);
893 Object domainKeys[] = domainBinding.getKeys(obj);
894 Object domainValues[] = domainBinding.getValues(obj);
895 Object rangeKeys[] = new Object[len];
896 Object rangeValues[] = new Object[len];
897 for (int i=0; i<len; i++) {
898 Object domainKey = domainKeys[i];
899 Object domainValue = domainValues[i];
900 Object rangeKey = keyAdapter.adapt(domainKey);
901 Object rangeValue = valueAdapter.adapt(domainValue);
902 rangeKeys[i] = rangeKey;
903 rangeValues[i] = rangeValue;
905 Object rangeMap = rangeBinding.create(rangeKeys, rangeValues);
907 } catch (BindingException e) {
908 throw new AdaptException(e);
912 result.typeAdapter |= keyAdapter.typeAdapter | valueAdapter.typeAdapter;
913 result.clones = keyAdapter.clones & valueAdapter.clones;
914 addToCache(req, result);
918 // Special-Case: Convert Union to its Composite
919 if (domain instanceof UnionBinding) {
920 final UnionType ut = (UnionType) domain.getDataType();
921 final UnionBinding ub = (UnionBinding) domain;
922 Binding[] cbs = ub.getComponentBindings();
923 for (int i=0; i<cbs.length; i++)
925 Binding domainCompositeBinding = cbs[i];
926 if (ut.getComponent(i).type.equals(range.getDataType())) {
927 final AbstractAdapter union2CompositeAdapter = getAdapterUnsynchronized(domainCompositeBinding, range, allowPrimitiveConversion);
929 AbstractAdapter result = new AbstractAdapter() {
931 public Object adapt(Object obj) throws AdaptException {
933 Object domainUnion = obj;
934 int domainTag = ub.getTag(domainUnion);
935 if (domainTag != tag) {
936 throw new AdaptException("This adapter can adapt only "+ub.getDataType().getComponents()[tag].name+"s");
938 Object domainComposite = ub.getValue(domainUnion);
939 Object rangeComposite = union2CompositeAdapter.adapt(domainComposite);
940 return rangeComposite;
941 } catch (BindingException e) {
942 throw new AdaptException(e);
946 result.hasPrimitiveConversion = union2CompositeAdapter.hasPrimitiveConversion;
947 addToCache(pair, result);
953 // Special-Case: Convert Composite to Union
954 if (range instanceof UnionBinding) {
955 final UnionType ut = (UnionType) range.getDataType();
956 final UnionBinding ub = (UnionBinding) range;
957 Binding cbs[] = ub.getComponentBindings();
958 for (int i=0; i<cbs.length; i++) {
959 Binding rangeCompositeBinding = cbs[i];
960 if (ut.getComponent(i).type.equals(domain.getDataType())) {
961 final AbstractAdapter domainObject2RangeCompositeAdapter = getAdapterUnsynchronized(rangeCompositeBinding, domain, allowPrimitiveConversion);
963 AbstractAdapter result = new AbstractAdapter() {
965 public Object adapt(Object obj) throws AdaptException {
967 Object domainObject = obj;
968 Object rangeComposite = domainObject2RangeCompositeAdapter.adapt(domainObject);
969 Object rangeUnion = ub.create(tag, rangeComposite);
971 } catch (BindingException e) {
972 throw new AdaptException(e);
976 result.hasPrimitiveConversion = domainObject2RangeCompositeAdapter.hasPrimitiveConversion;
977 addToCache(pair, result);
984 } catch (UnitParseException e) {
985 throw new AdapterConstructionException(e.getMessage(), e);
988 StringBuilder error = new StringBuilder();
989 error.append("Could not create ");
990 if (mustClone) error.append("cloning ");
991 if (typeAdapter) error.append("type");
992 error.append("adapter (");
993 error.append("domain=");
994 error.append(domain.type().toSingleLineString());
995 error.append(", range=");
996 error.append(range.type().toSingleLineString());
999 throw new AdapterConstructionException(error.toString());
1005 * Adapt a value of one type to another.
\r
1010 * @return adapted value
\r
1011 * @throws AdapterConstructionException
\r
1012 * @throws AdaptException
\r
1014 public Object adapt(Object value, Binding domain, Binding range)
\r
1015 throws AdaptException
\r
1017 if (domain == range) return value;
\r
1019 if (range instanceof VariantBinding && !(domain instanceof VariantBinding)) {
\r
1020 // Default to just wrapping the value (avoid adapter construction to save memory)
\r
1021 return ((VariantBinding)range).create(domain, value);
\r
1023 return getAdapter(domain, range, true, false).adapt(value);
\r
1024 } catch (AdapterConstructionException | BindingException e) {
\r
1025 throw new AdaptException(e);
\r
1030 * Adapt a value of one type to another
\r
1035 * @return adapted value
\r
1036 * @throws AdapterConstructionException
\r
1037 * @throws AdaptException
\r
1039 public Object adaptUnchecked(Object value, Binding domain, Binding range)
\r
1040 throws RuntimeAdapterConstructionException, RuntimeAdaptException
\r
1042 if (domain == range) return value;
\r
1044 if (range instanceof VariantBinding && !(domain instanceof VariantBinding)) {
\r
1045 // Default to just wrapping the value (avoid adapter construction to save memory)
\r
1046 return ((VariantBinding)range).create(domain, value);
\r
1048 return getAdapter(domain, range, true, false).adaptUnchecked(value);
\r
1049 } catch (RuntimeAdapterConstructionException | RuntimeBindingException e) {
\r
1050 throw new RuntimeAdaptException(new AdaptException(e.getCause()));
\r
1051 } catch (AdapterConstructionException | BindingException e) {
\r
1052 throw new RuntimeAdaptException(new AdaptException(e));
\r
1057 * Clone a value to a type to another. Bindings that handle immutable values
\r
1058 * may return the same instance, others will guarantee a complete copy.
\r
1063 * @return adapted value
\r
1064 * @throws AdapterConstructionException
\r
1065 * @throws AdaptException
\r
1067 public Object clone(Object value, Binding domain, Binding range)
\r
1068 throws AdaptException
\r
1071 return getAdapter(domain, range, true, true).adapt(value);
\r
1072 } catch (AdapterConstructionException e) {
\r
1073 throw new AdaptException(e);
\r
1079 * Clone a value of one binding to another. Bindings that handle immutable values
\r
1080 * may return the same instance, others will guarantee a complete copy.
\r
1085 * @return adapted value
\r
1086 * @throws AdapterConstructionException
\r
1087 * @throws AdaptException
\r
1089 public Object cloneUnchecked(Object value, Binding domain, Binding range)
\r
1090 throws RuntimeAdapterConstructionException, RuntimeAdaptException
\r
1093 return getAdapter(domain, range, true, true).adapt(value);
\r
1094 } catch (AdaptException e) {
\r
1095 throw new RuntimeAdaptException(e);
\r
1096 } catch (RuntimeAdapterConstructionException e) {
\r
1097 throw new RuntimeAdaptException(new AdaptException(e.getCause()));
\r
1098 } catch (AdapterConstructionException e) {
\r
1099 throw new RuntimeAdaptException(new AdaptException(e));
\r