]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.databoard/src/org/simantics/databoard/adapter/AdapterFactory.java
Tons of dependency fixes and updates
[simantics/platform.git] / bundles / org.simantics.databoard / src / org / simantics / databoard / adapter / AdapterFactory.java
1 /*******************************************************************************
2  *  Copyright (c) 2010 Association for Decentralized Information Management in
3  *  Industry THTH ry.
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
8  *
9  *  Contributors:
10  *      VTT Technical Research Centre of Finland - initial API and implementation
11  *******************************************************************************/
12 package org.simantics.databoard.adapter;
13
14 import java.util.ArrayList;
15 import java.util.Map;
16
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;
47
48 /**
49  * AdapterRepository is a factory and a collection of adapters.
50  *
51  * @author Toni Kalajainen <toni.kalajainen@vtt.fi>
52  */
53 public class AdapterFactory {
54
55         Map<AdapterRequest, AbstractAdapter> cache = new ReferenceMap<>(ReferenceStrength.SOFT, ReferenceStrength.HARD);
56
57         public synchronized Adapter getAdapter(Binding domain, Binding range, boolean typeAdapter, boolean mustClone)
58         throws AdapterConstructionException
59         {               
60                 if ((!mustClone || domain.isImmutable()) && domain.equals(range)) return PassThruAdapter.PASSTHRU;
61                 
62                 if (domain.getClass() == range.getClass() &&
63                                 ( !mustClone || domain.isImmutable() ) &&
64                                 NumberBinding.class.isAssignableFrom( domain.getClass() ) ) {
65                         
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;
71                 }
72                 
73                 return getAdapterUnsynchronized(domain, range, typeAdapter, mustClone);
74         }
75
76         private AbstractAdapter getCached(AdapterRequest type) 
77         {
78                 return cache.get(type);
79         }                       
80         
81         private void cache(AdapterRequest type, AbstractAdapter binding) {
82                 cache.put(type, binding);
83         }       
84         
85         private void addToCache(AdapterRequest request, AbstractAdapter impl) {
86             impl.request = request;
87                 cache(request, impl);
88                 
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);
92                         cache(request, impl);
93                 }
94         }
95         
96         /**
97          * Create adapter, does not cache the result.
98          * 
99          * @param domain
100          * @param range
101          * @param typeAdapter if true, primitive conversion is allowed (e.g. int -> double)
102          * @return
103          */
104         private AbstractAdapter getAdapterUnsynchronized(Binding domain, Binding range, boolean typeAdapter, final boolean mustClone)
105         throws AdapterConstructionException
106         {
107                 if ( !mustClone && domain.equals(range) ) return PassThruAdapter.PASSTHRU;
108
109                 AdapterRequest req = new AdapterRequest(domain, range, mustClone);
110                 AbstractAdapter cachedResult = getCached(req);
111                 if (cachedResult!=null) return cachedResult;
112
113                 try {
114                 
115         if (domain instanceof RecordBinding && range instanceof RecordBinding)
116         {               
117                 final RecordBinding domainRecord = (RecordBinding) domain;
118                 final RecordBinding rangeRecord = (RecordBinding) range;
119                 RecordType domainType = domainRecord.type();
120                 RecordType rangeType = rangeRecord.type();  
121                 
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++)
126                 {
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;
132                         } else {
133                             fieldMap[rangeIndex] = -1;
134                             requiresTypeAdapting = true;
135                         }
136                 }
137                 
138                 if (requiresTypeAdapting && !typeAdapter) {
139                         throw new AdapterConstructionException("Type Adapter required.");
140                 }
141                 
142                 final int len = rangeRecord.componentBindings.length;
143                 final AbstractAdapter[] componentAdapters = new AbstractAdapter[len];
144                 AbstractAdapter result = null;
145                 
146                 if (!requiresTypeAdapting) {
147                         // Normal Adapter
148                         result = new AbstractAdapter() {
149                                 @Override
150                                 public Object adapt(Object src) throws AdaptException {
151                                         try {
152                                                 Object values[] = new Object[len];
153                                                 for (int i=0; i<len; i++)
154                                                 {                                       
155                                                         Object srcValue = domainRecord.getComponent(src, i);
156                                                         Object dstValue = componentAdapters[i].adapt(srcValue);
157                                                         values[i] = dstValue;
158                                                 }
159                                                 return rangeRecord.create(values);
160                                         } catch (BindingException e) {
161                                                 throw new AdaptException(e);
162                                         }
163                                 }
164                         };
165                 } else {
166                         // Type Adapter - Type adapter maps fields of different order
167                         final int _fieldMap[] = fieldMap;
168                         result = new AbstractAdapter() {
169                                 @Override
170                                 public Object adapt(Object src) throws AdaptException {
171                                         try {
172                                                 Object values[] = new Object[len];
173                                                 for (int rangeIndex=0; rangeIndex<len; rangeIndex++)
174                                                 {                                                       
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;
180                                                         } else {
181                                                                 // Optional value
182                                                                 values[rangeIndex] = rangeRecord.componentBindings[rangeIndex].createDefault();
183                                                         }
184                                                 }
185                                                 return rangeRecord.create(values);
186                                         } catch (BindingException e) {
187                                                 throw new AdaptException(e);
188                                         }
189                                 }
190                         };
191                         result.typeAdapter = true;
192                 }
193                         
194                 addToCache(req, result);                
195                 result.clones = true;
196                 for (int rangeIndex=0; rangeIndex<len; rangeIndex++)
197                 {
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;
203                         }
204                 }
205                 return result;
206         }
207         
208         if (domain instanceof UnionBinding && range instanceof UnionBinding)
209         {
210                 final UnionBinding domainBinding = (UnionBinding) domain;
211                 final UnionBinding rangeBinding = (UnionBinding) range;
212                 UnionType domainType = domainBinding.type();
213                 UnionType rangeType = rangeBinding.type();
214                 
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++)
219                 {
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;
225                 }                       
226                 
227                 if (requiresTypeAdapting && !typeAdapter) {
228                         throw new AdapterConstructionException("Type Adapter required.");
229                 }
230                 
231                 final AbstractAdapter[] componentAdapters = new AbstractAdapter[domainType.getComponentCount()];
232
233                 AbstractAdapter result = null;
234                 
235                 if (!requiresTypeAdapting) {
236                         // Normal adapter
237                         result = new AbstractAdapter() {
238                                 @Override
239                                 public Object adapt(Object obj) throws AdaptException {
240                                         try { 
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);
247                                         }
248                                 }
249                         };
250                 } else {
251                         // Type adapter, type adapter rearranges tag indices
252                         final int _tagMap[] = tagMap; 
253                         result = new AbstractAdapter() {
254                                 @Override
255                                 public Object adapt(Object obj) throws AdaptException {
256                                         try { 
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);
265                                         }
266                                 }
267                         };
268                 }
269                 
270                 addToCache(req, result);
271                 result.clones = true;
272                 for (int domainIndex=0; domainIndex<domainType.getComponentCount(); domainIndex++)
273                 {
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;
278                 }
279                         return result;
280         }       
281         
282         if (domain instanceof BooleanBinding && range instanceof BooleanBinding)
283         {
284                 final BooleanBinding domainBoolean = (BooleanBinding) domain;
285                 final BooleanBinding rangeBoolean = (BooleanBinding) range;
286                 AbstractAdapter result = new AbstractAdapter() {
287                                 @Override
288                                 public Object adapt(Object obj) throws AdaptException {
289                                         try {
290                                                 boolean value = domainBoolean.getValue_(obj);
291                                                 return rangeBoolean.create(value);
292                                         } catch (BindingException e) {
293                                                 throw new AdaptException(e);
294                                         }                                                                                                       
295                                 }
296                 };
297                 result.clones = mustClone;
298                 result.typeAdapter = true;
299                 addToCache(req, result);
300                 return result;
301         }               
302
303         if (domain instanceof BooleanBinding && range instanceof NumberBinding)
304         {
305                 try {
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() {
311                                         @Override
312                                         public Object adapt(Object obj) throws AdaptException {
313                                                 try {
314                                                         boolean value = domainBoolean.getValue_(obj);
315                                                         return value ? trueValue : falseValue;
316                                                 } catch (BindingException e) {
317                                                         throw new AdaptException(e);
318                                                 }                                                                                                       
319                                         }
320                         };
321                         result.clones = true;
322                         result.typeAdapter = true;
323                         addToCache(req, result);
324                         return result;
325                         } catch (BindingException e1) {
326                                 throw new AdapterConstructionException(e1);
327                         }
328         }               
329         
330         if (domain instanceof NumberBinding && range instanceof BooleanBinding)
331         {
332                 try {
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() {
337                                         @Override
338                                         public Object adapt(Object obj) throws AdaptException {
339                                                 try {
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);
345                                                 }                                                                                                       
346                                         }
347                         };
348                         result.clones = true;
349                         addToCache(req, result);
350                         return result;
351                         } catch (BindingException e1) {
352                                 throw new AdapterConstructionException(e1);
353                         }
354         }
355         
356         if (domain instanceof StringBinding && range instanceof StringBinding)
357         {
358                 final StringBinding domainString = (StringBinding) domain;
359                 final StringBinding rangeString = (StringBinding) range;
360                 AbstractAdapter result = new AbstractAdapter() {
361                                 @Override
362                                 public Object adapt(Object obj) throws AdaptException {
363                                         try {
364                                                 String value = domainString.getValue(obj);
365                                                 return rangeString.create(value);
366                                         } catch (BindingException e) {
367                                                 throw new AdaptException(e);
368                                         }                                                                                                       
369                                 }
370                 };
371                 result.clones = true;
372                 addToCache(req, result);
373                 return result;
374         }           
375         
376         if(domain instanceof StringBinding && range instanceof NumberBinding)
377         {
378                 final StringBinding domainString = (StringBinding) domain;
379                 final NumberBinding rangeString = (NumberBinding) range;
380                 AbstractAdapter result = new AbstractAdapter() {
381                                 @Override
382                                 public Object adapt(Object obj) throws AdaptException {
383                                         try {
384                                                 String value = domainString.getValue(obj);
385                                                 return rangeString.create(value);
386                                         } catch (BindingException e) {
387                                                 throw new AdaptException(e);
388                                         }                                                                                                       
389                                 }
390                 };
391                 result.clones = true;
392                 addToCache(req, result);
393                 return result;
394         }
395
396         if(domain instanceof StringBinding && range instanceof BooleanBinding)
397         {
398                 final StringBinding domainString = (StringBinding) domain;
399                 final BooleanBinding rangeString = (BooleanBinding) range;
400                 AbstractAdapter result = new AbstractAdapter() {
401                                 @Override
402                                 public Object adapt(Object obj) throws AdaptException {
403                                         try {
404                                                 String value = domainString.getValue(obj);
405                                                 return rangeString.create(Boolean.parseBoolean(value));
406                                         } catch (BindingException e) {
407                                                 throw new AdaptException(e);
408                                         }                                                                                                       
409                                 }
410                 };
411                 result.clones = true;
412                 addToCache(req, result);
413                 return result;
414         }
415         
416         // XXX We can optimizes here by using primitives 
417         if (domain instanceof NumberBinding && range instanceof NumberBinding)
418         {
419                         final NumberBinding domainNumber = (NumberBinding) domain;
420                         final NumberBinding rangeNumber = (NumberBinding) range;
421                 
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;
427                         else
428                                 unitConverter = Units.createConverter(domainUnit, rangeUnit); 
429                         /*if(domainUnit == null || domainUnit.equals("")) {
430                             if(rangeUnit == null || rangeUnit.equals(""))
431                                 unitConverter = null;
432                             else
433                                 unitConverter = null;
434 //                              throw new AdapterConstructionException("Cannot convert from a unitless type to a type with unit.");
435                         }
436                         else {
437                             if(rangeUnit == null || rangeUnit.equals(""))
438                                 unitConverter = null;                                   
439 //                              throw new AdapterConstructionException("Cannot convert from a type with unit to unitless type.");
440                             else
441                                 unitConverter = Units.createConverter(domainUnit, rangeUnit); 
442                         }       */                      
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.");
447                                                         
448                         AbstractAdapter result;
449                         if (!doUnitConversion) {
450                                 result = new AbstractAdapter() {
451                                         @Override
452                                         public Object adapt(Object obj) throws AdaptException {
453                                                 Number value;
454                                                 try {                                                   
455                                                         value = domainNumber.getValue(obj);
456                                                         return rangeNumber.create(value);
457                                                 } catch (BindingException e) {
458                                                         throw new AdaptException(e);
459                                                 }
460                                         }
461                                 };
462                         } else {
463                                 final IUnitConverter _unitConverter = unitConverter;
464                                 result = new AbstractAdapter() {
465                                         @Override
466                                         public Object adapt(Object obj) throws AdaptException {
467                                                 try {
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);
473                                                 }
474                                         }
475                                 };
476                         }
477                         result.typeAdapter = doPrimitiveConversion;
478                         result.clones = true;
479                         addToCache(req, result);
480                         return result;                  
481         }               
482         
483         if (domain instanceof BooleanArrayBinding && range instanceof BooleanArrayBinding)
484         {
485                 final BooleanArrayBinding domainArray = (BooleanArrayBinding) domain;
486                 final BooleanArrayBinding rangeArray = (BooleanArrayBinding) range;
487                 AbstractAdapter result = new AbstractAdapter() {
488                                 @Override
489                                 public Object adapt(Object obj) throws AdaptException {
490                                         try {
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);
496                                         }                                               
497                                 }
498                 };
499                 result.clones = true;
500                 addToCache(req, result);
501                 return result;
502         }               
503
504         if (domain instanceof ByteArrayBinding && range instanceof ByteArrayBinding)
505         {
506                 final ByteArrayBinding domainArray = (ByteArrayBinding) domain;
507                 final ByteArrayBinding rangeArray = (ByteArrayBinding) range;
508                 
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;
513
514                         AbstractAdapter result;
515                         if (doUnitConversion) {
516                                 final IUnitConverter _unitConverter = unitConverter; 
517                                 result = new AbstractAdapter() {
518                                         @Override
519                                         public Object adapt(Object obj) throws AdaptException {
520                                                 try {
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;
526                                                         }
527                                                         return rangeArray.create(data);
528                                                 } catch (BindingException e) {
529                                                         throw new AdaptException(e);
530                                                 }
531                                         }
532                                 };
533                         } else {
534                                 result = new AbstractAdapter() {
535                                         @Override
536                                         public Object adapt(Object obj) throws AdaptException {
537                                                 try {
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);
543                                                 }                                                       
544                                         }
545                                 };
546                         }
547                 result.clones = true;
548                 addToCache(req, result);
549                 return result;
550         }               
551         
552         if (domain instanceof IntArrayBinding && range instanceof IntArrayBinding)
553         {
554                 final IntArrayBinding domainArray = (IntArrayBinding) domain;
555                 final IntArrayBinding rangeArray = (IntArrayBinding) range;
556                 
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;
562                 
563                         AbstractAdapter result;
564                         if (doUnitConversion) {
565                                 final IUnitConverter _unitConverter = unitConverter; 
566                                 result = new AbstractAdapter() {
567                                         @Override
568                                         public Object adapt(Object obj) throws AdaptException {
569                                                 try {
570                                                         int[] data = domainArray.getArray(obj);                         
571                                                         for (int i=0; i<data.length; i++) {
572                                                                 int value = data[i];
573                                                                 double convertedValue = _unitConverter.convert((double)value);
574                                                                 data[i] = (int) convertedValue;
575                                                         }
576                                                         return rangeArray.create(data);
577                                                 } catch (BindingException e) {
578                                                         throw new AdaptException(e);
579                                                 }
580                                         }
581                                 };
582                         } else {
583                                 result = new AbstractAdapter() {
584                                         @Override
585                                         public Object adapt(Object obj) throws AdaptException {
586                                                 try {
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);
592                                                 }                                                       
593                                         }
594                                 };
595                         }
596                 
597                 result.clones = true;
598                 addToCache(req, result);
599                 return result;
600         }               
601
602         if (domain instanceof LongArrayBinding && range instanceof LongArrayBinding)
603         {
604                 final LongArrayBinding domainArray = (LongArrayBinding) domain;
605                 final LongArrayBinding rangeArray = (LongArrayBinding) range;
606
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;
612                 
613                         AbstractAdapter result;
614                         if (doUnitConversion) {
615                                 final IUnitConverter _unitConverter = unitConverter; 
616                                 result = new AbstractAdapter() {
617                                         @Override
618                                         public Object adapt(Object obj) throws AdaptException {
619                                                 try {
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;
625                                                         }
626                                                         return rangeArray.create(data);
627                                                 } catch (BindingException e) {
628                                                         throw new AdaptException(e);
629                                                 }
630                                         }
631                                 };
632                         } else {
633                                 result = new AbstractAdapter() {
634                                         @Override
635                                         public Object adapt(Object obj) throws AdaptException {
636                                                 try {
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);
642                                                 }
643                                         }
644                                 };
645                         }
646
647                 result.clones = true;
648                 addToCache(req, result);
649                 return result;
650         }               
651
652         if (domain instanceof FloatArrayBinding && range instanceof FloatArrayBinding)
653         {
654                 final FloatArrayBinding domainArray = (FloatArrayBinding) domain;
655                 final FloatArrayBinding rangeArray = (FloatArrayBinding) range;
656
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;
662                 
663                         AbstractAdapter result;
664                         if (doUnitConversion) {
665                                 final IUnitConverter _unitConverter = unitConverter; 
666                                 result = new AbstractAdapter() {
667                                         @Override
668                                         public Object adapt(Object obj) throws AdaptException {
669                                                 try {
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;
675                                                         }
676                                                         return rangeArray.create(data);
677                                                 } catch (BindingException e) {
678                                                         throw new AdaptException(e);
679                                                 }
680                                         }
681                                 };
682                         } else {
683                                 result = new AbstractAdapter() {
684                                         @Override
685                                         public Object adapt(Object obj) throws AdaptException {
686                                                 try {
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);
692                                                 }
693                                         }
694                                 };
695                         }
696                         
697                 result.clones = true;
698                 addToCache(req, result);
699                 return result;
700         }               
701
702         if (domain instanceof DoubleArrayBinding && range instanceof DoubleArrayBinding)
703         {
704                 final DoubleArrayBinding domainArray = (DoubleArrayBinding) domain;
705                 final DoubleArrayBinding rangeArray = (DoubleArrayBinding) range;
706
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;
712                 
713                         AbstractAdapter result;
714                         if (doUnitConversion) {
715                                 final IUnitConverter _unitConverter = unitConverter; 
716                                 result = new AbstractAdapter() {
717                                         @Override
718                                         public Object adapt(Object obj) throws AdaptException {
719                                                 try {
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;
725                                                         }
726                                                         return rangeArray.create(data);
727                                                 } catch (BindingException e) {
728                                                         throw new AdaptException(e);
729                                                 }
730                                         }
731                                 };
732                         } else {
733                                 result = new AbstractAdapter() {
734                                         @Override
735                                         public Object adapt(Object obj) throws AdaptException {
736                                                 try {
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);
742                                                 }
743                                         }
744                                 };
745                         }
746                 
747                 result.clones = true;
748                 addToCache(req, result);
749                 return result;
750         }               
751
752         if (domain instanceof ArrayBinding && range instanceof ArrayBinding)
753         {
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() {
758                                 @Override
759                                 public Object adapt(Object obj) throws AdaptException {
760                                         try {
761                                                 int len = domainBinding.size(obj);
762                                                 ArrayList<Object> array = new ArrayList<Object>(len);
763                                                 for (int i=0; i<len; i++)
764                                                 {
765                                                         Object srcValue = domainBinding.get(obj, i);
766                                                         Object dstValue = componentAdapter.adapt(srcValue);
767                                                         array.add(dstValue);
768                                                 }                                       
769                                                 return rangeBinding instanceof ArrayListBinding ? array : rangeBinding.create(array);
770                                         } catch (BindingException e) {
771                                                 throw new AdaptException(e);
772                                         }
773                                 }
774                 };
775                 
776                 result.clones = componentAdapter.clones;
777                 addToCache(req, result);
778                 return result;
779         }
780         
781         if (domain instanceof OptionalBinding && range instanceof OptionalBinding)
782         {
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() {
787                                 @Override
788                                 public Object adapt(Object obj) throws AdaptException {
789                                         try {
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);
796                                         }
797                                 }
798                 };
799                 result.typeAdapter = componentAdapter.typeAdapter;
800                 result.clones = componentAdapter.clones;
801                 addToCache(req, result);
802                 return result;
803         }
804         
805         // Adapt a non-optional value to an optional value
806         if (range instanceof OptionalBinding && !(domain instanceof OptionalBinding))
807         {
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() {
812                                 @Override
813                                 public Object adapt(Object obj) throws AdaptException {
814                                         try {
815                                                 obj = componentAdapter.adapt(obj); 
816                                                 return rangeBinding.createValue(obj);
817                                         } catch (BindingException e) {
818                                                 throw new AdaptException(e);
819                                         }
820                                 }
821                 };
822                 result.typeAdapter = componentAdapter.typeAdapter;
823                 result.clones = componentAdapter.clones;
824                 addToCache(req, result);
825                 return result;                  
826         }
827         
828         if (domain instanceof VariantBinding && range instanceof VariantBinding)
829         {               
830                 final VariantBinding domainBinding = (VariantBinding) domain;
831                 final VariantBinding rangeBinding = (VariantBinding) range;
832                 AbstractAdapter result = new AbstractAdapter() {
833                                 @Override
834                                 public Object adapt(Object obj) throws AdaptException {                                 
835                                         try {
836                                                 
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); 
842                                                 }
843                                                 Object rangeVariant = rangeBinding.create(domainValueBinding, domainObject);
844                                                 return rangeVariant;
845                                         } catch (BindingException e) {
846                                                 throw new AdaptException(e);
847                                         } catch (AdapterConstructionException e) {
848                                                 throw new AdaptException(e);
849                                         }
850                                 }
851                 };
852                 result.clones = mustClone;
853                 addToCache(req, result);
854                 return result;
855         }
856         
857         if (domain instanceof VariantBinding && !(range instanceof VariantBinding))
858         {
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() {
863                                 @Override
864                                 public Object adapt(Object obj) throws AdaptException {
865                                         try {
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);
872                                         }
873                                 }
874                 };
875                 result.clones = mustClone;
876                 addToCache(req, result);
877                 return result;                  
878         }
879         
880         if (range instanceof VariantBinding && !(domain instanceof VariantBinding))
881         {
882                 // Default to just wrapping the domain type
883                 final VariantBinding rangeBinding = (VariantBinding)range;
884                 final Binding domainBinding = domain;
885                 AbstractAdapter result = new AbstractAdapter() {
886                                 @Override
887                                 public Object adapt(Object obj) throws AdaptException {
888                                         try {
889                                                 if (mustClone) {
890                                                         Adapter valueAdapter;
891                                                                 valueAdapter = getAdapterUnsynchronized(domainBinding, domainBinding, false, true);
892                                                         obj = valueAdapter.adapt(obj);
893                                                 }
894                                                 return rangeBinding.create(domainBinding, obj);
895                                         } catch (AdapterConstructionException | BindingException e) {
896                                                 throw new AdaptException(e);
897                                         }
898                                 }
899                 };
900                 result.clones = mustClone;
901                 addToCache(req, result);
902                 return result;
903         }
904
905         if (domain instanceof MapBinding && range instanceof MapBinding)
906         {
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() {
912                                 @Override
913                                 public Object adapt(Object obj) throws AdaptException {
914                                         try {
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;
927                                                 }
928                                                 Object rangeMap = rangeBinding.create(rangeKeys, rangeValues);
929                                                 return rangeMap;
930                                         } catch (BindingException e) {
931                                                 throw new AdaptException(e);
932                                         }
933                                 }
934                 };
935                 result.typeAdapter |= keyAdapter.typeAdapter | valueAdapter.typeAdapter;
936                 result.clones = keyAdapter.clones & valueAdapter.clones;                
937                 addToCache(req, result);
938                 return result;
939         }
940 /*      
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++)
947                 {
948                         Binding domainCompositeBinding = cbs[i];
949                         if (ut.getComponent(i).type.equals(range.getDataType())) {
950                                 final AbstractAdapter union2CompositeAdapter = getAdapterUnsynchronized(domainCompositeBinding, range, allowPrimitiveConversion);                               
951                                 final int tag = i;
952                         AbstractAdapter result = new AbstractAdapter() {
953                                         @Override
954                                         public Object adapt(Object obj) throws AdaptException {
955                                                 try {
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");
960                                                         }
961                                                         Object domainComposite = ub.getValue(domainUnion);
962                                                         Object rangeComposite = union2CompositeAdapter.adapt(domainComposite);
963                                                         return rangeComposite;
964                                                 } catch (BindingException e) {
965                                                         throw new AdaptException(e);
966                                                 }
967                                         }
968                         };
969                         result.hasPrimitiveConversion = union2CompositeAdapter.hasPrimitiveConversion;
970                         addToCache(pair, result);
971                         return result;
972                         }
973                 }
974         }
975         
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);                                   
985                                 final int tag = i;
986                         AbstractAdapter result = new AbstractAdapter() {
987                                         @Override
988                                         public Object adapt(Object obj) throws AdaptException {
989                                                 try {
990                                                         Object domainObject = obj;
991                                                         Object rangeComposite = domainObject2RangeCompositeAdapter.adapt(domainObject);
992                                                     Object rangeUnion = ub.create(tag, rangeComposite);
993                                                         return rangeUnion;
994                                                 } catch (BindingException e) {
995                                                         throw new AdaptException(e);
996                                                 }
997                                         }
998                         };
999                         result.hasPrimitiveConversion = domainObject2RangeCompositeAdapter.hasPrimitiveConversion;
1000                         addToCache(pair, result);
1001                         return result;
1002                         }
1003                 }
1004         }
1005 */      
1006         
1007                 } catch (UnitParseException e) {
1008                         throw new AdapterConstructionException(e.getMessage(), e); 
1009                 }                               
1010
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());
1020                 error.append(")");
1021                 
1022         throw new AdapterConstructionException(error.toString());
1023         }
1024         
1025         
1026         
1027     /**
1028      * Adapt a value of one type to another. 
1029      * 
1030      * @param value
1031      * @param domain
1032      * @param range
1033      * @return adapted value
1034      * @throws AdapterConstructionException
1035      * @throws AdaptException
1036      */
1037     public Object adapt(Object value, Binding domain, Binding range)
1038     throws AdaptException
1039     {
1040         if (domain == range) return value;
1041         try {
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);
1045                 }
1046                         return getAdapter(domain, range, true, false).adapt(value);
1047                 } catch (AdapterConstructionException | BindingException e) {
1048                         throw new AdaptException(e);
1049                 }
1050     }
1051     
1052     /**
1053      * Adapt a value of one type to another
1054      * 
1055      * @param value
1056      * @param domain
1057      * @param range
1058      * @return adapted value
1059      * @throws AdapterConstructionException
1060      * @throws AdaptException
1061      */
1062     public Object adaptUnchecked(Object value, Binding domain, Binding range)
1063     throws RuntimeAdapterConstructionException, RuntimeAdaptException
1064     {
1065         if (domain == range) return value;
1066         try {
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);
1070                 }
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));
1076                 }
1077     }
1078     
1079     /**
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.  
1082      * 
1083      * @param value
1084      * @param domain
1085      * @param range
1086      * @return adapted value
1087      * @throws AdapterConstructionException
1088      * @throws AdaptException
1089      */
1090     public Object clone(Object value, Binding domain, Binding range)
1091     throws AdaptException
1092     {
1093         try {
1094                         return getAdapter(domain, range, true, true).adapt(value);
1095                 } catch (AdapterConstructionException e) {
1096                         throw new AdaptException(e);
1097                 }
1098     }
1099     
1100
1101     /**
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.
1104      * 
1105      * @param value
1106      * @param domain
1107      * @param range
1108      * @return adapted value
1109      * @throws AdapterConstructionException
1110      * @throws AdaptException
1111      */
1112     public Object cloneUnchecked(Object value, Binding domain, Binding range)
1113     throws RuntimeAdapterConstructionException, RuntimeAdaptException
1114     {
1115         try {
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));
1123                 }
1124     }    
1125                 
1126 }
1127