]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.databoard/src/org/simantics/databoard/adapter/AdapterFactory.java
a674943d50b996e6e62cf654043bd83e9a4ff4d6
[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.collections.map.ReferenceMap;
18 import org.simantics.databoard.Bindings;
19 import org.simantics.databoard.Units;
20 import org.simantics.databoard.binding.ArrayBinding;
21 import org.simantics.databoard.binding.Binding;
22 import org.simantics.databoard.binding.BooleanBinding;
23 import org.simantics.databoard.binding.MapBinding;
24 import org.simantics.databoard.binding.NumberBinding;
25 import org.simantics.databoard.binding.OptionalBinding;
26 import org.simantics.databoard.binding.RecordBinding;
27 import org.simantics.databoard.binding.StringBinding;
28 import org.simantics.databoard.binding.UnionBinding;
29 import org.simantics.databoard.binding.VariantBinding;
30 import org.simantics.databoard.binding.error.BindingException;
31 import org.simantics.databoard.binding.error.RuntimeBindingException;
32 import org.simantics.databoard.binding.impl.ArrayListBinding;
33 import org.simantics.databoard.binding.impl.BooleanArrayBinding;
34 import org.simantics.databoard.binding.impl.ByteArrayBinding;
35 import org.simantics.databoard.binding.impl.DoubleArrayBinding;
36 import org.simantics.databoard.binding.impl.FloatArrayBinding;
37 import org.simantics.databoard.binding.impl.IntArrayBinding;
38 import org.simantics.databoard.binding.impl.LongArrayBinding;
39 import org.simantics.databoard.type.ArrayType;
40 import org.simantics.databoard.type.NumberType;
41 import org.simantics.databoard.type.RecordType;
42 import org.simantics.databoard.type.UnionType;
43 import org.simantics.databoard.units.IUnitConverter;
44 import org.simantics.databoard.units.IdentityConverter;
45 import org.simantics.databoard.units.internal.UnitParseException;
46 import org.simantics.databoard.util.ObjectUtils;
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         @SuppressWarnings( "unchecked" )
56     Map<AdapterRequest, AbstractAdapter> cache = (Map<AdapterRequest, AbstractAdapter>) new ReferenceMap(ReferenceMap.SOFT, ReferenceMap.HARD);
57                 
58         public synchronized Adapter getAdapter(Binding domain, Binding range, boolean typeAdapter, boolean mustClone)
59         throws AdapterConstructionException
60         {               
61                 if ((!mustClone || domain.isImmutable()) && domain.equals(range)) return PassThruAdapter.PASSTHRU;
62                 
63                 if (domain.getClass() == range.getClass() &&
64                                 ( !mustClone || domain.isImmutable() ) &&
65                                 NumberBinding.class.isAssignableFrom( domain.getClass() ) ) {
66                         
67                         NumberBinding db = (NumberBinding) domain;
68                         NumberBinding rb = (NumberBinding) range;
69                         String u1 = db.type().getUnit();
70                         String u2 = rb.type().getUnit();
71                         if (u1==null || u2==null || u1.equals("") || u2.equals("") || u1.equals(u2)) return PassThruAdapter.PASSTHRU;
72                 }
73                 
74                 return getAdapterUnsynchronized(domain, range, typeAdapter, mustClone);
75         }
76
77         private AbstractAdapter getCached(AdapterRequest type) 
78         {
79                 return cache.get(type);
80         }                       
81         
82         private void cache(AdapterRequest type, AbstractAdapter binding) {
83                 cache.put(type, binding);
84         }       
85         
86         private void addToCache(AdapterRequest request, AbstractAdapter impl) {
87             impl.request = request;
88                 cache(request, impl);
89                 
90                 // This request applies to "must clone" request aswell, remember this implementation
91                 if (!request.mustClone && impl.clones) {
92                         request = new AdapterRequest(request.domain, request.range, true);
93                         cache(request, impl);
94                 }
95         }
96         
97         /**
98          * Create adapter, does not cache the result.
99          * 
100          * @param domain
101          * @param range
102          * @param typeAdapter if true, primitive conversion is allowed (e.g. int -> double)
103          * @return
104          */
105         private AbstractAdapter getAdapterUnsynchronized(Binding domain, Binding range, boolean typeAdapter, final boolean mustClone)
106         throws AdapterConstructionException
107         {
108                 if ( !mustClone && domain.equals(range) ) return PassThruAdapter.PASSTHRU;
109
110                 AdapterRequest req = new AdapterRequest(domain, range, mustClone);
111                 AbstractAdapter cachedResult = getCached(req);
112                 if (cachedResult!=null) return cachedResult;
113
114                 try {
115                 
116         if (domain instanceof RecordBinding && range instanceof RecordBinding)
117         {               
118                 final RecordBinding domainRecord = (RecordBinding) domain;
119                 final RecordBinding rangeRecord = (RecordBinding) range;
120                 RecordType domainType = domainRecord.type();
121                 RecordType rangeType = rangeRecord.type();  
122                 
123                 // Field-Map describes the index of the fields in domain for each field in range
124                 boolean requiresTypeAdapting = domainType.getComponentCount() != rangeType.getComponentCount(); 
125                 int fieldMap[] = new int[rangeType.getComponentCount()];
126                 for (int rangeIndex=0; rangeIndex<fieldMap.length; rangeIndex++)
127                 {
128                         String fieldName = rangeType.getComponent(rangeIndex).name;
129                         Integer domainIndex = domainType.getComponentIndex(fieldName);
130                         if (domainIndex!=null) {
131                                 fieldMap[rangeIndex] = domainIndex;
132                                 requiresTypeAdapting |= rangeIndex != domainIndex;
133                         } else {
134                             fieldMap[rangeIndex] = -1;
135                             requiresTypeAdapting = true;
136                         }
137                 }
138                 
139                 if (requiresTypeAdapting && !typeAdapter) {
140                         throw new AdapterConstructionException("Type Adapter required.");
141                 }
142                 
143                 final int len = rangeRecord.componentBindings.length;
144                 final AbstractAdapter[] componentAdapters = new AbstractAdapter[len];
145                 AbstractAdapter result = null;
146                 
147                 if (!requiresTypeAdapting) {
148                         // Normal Adapter
149                         result = new AbstractAdapter() {
150                                 @Override
151                                 public Object adapt(Object src) throws AdaptException {
152                                         try {
153                                                 Object values[] = new Object[len];
154                                                 for (int i=0; i<len; i++)
155                                                 {                                       
156                                                         Object srcValue = domainRecord.getComponent(src, i);
157                                                         Object dstValue = componentAdapters[i].adapt(srcValue);
158                                                         values[i] = dstValue;
159                                                 }
160                                                 return rangeRecord.create(values);
161                                         } catch (BindingException e) {
162                                                 throw new AdaptException(e);
163                                         }
164                                 }
165                         };
166                 } else {
167                         // Type Adapter - Type adapter maps fields of different order
168                         final int _fieldMap[] = fieldMap;
169                         result = new AbstractAdapter() {
170                                 @Override
171                                 public Object adapt(Object src) throws AdaptException {
172                                         try {
173                                                 Object values[] = new Object[len];
174                                                 for (int rangeIndex=0; rangeIndex<len; rangeIndex++)
175                                                 {                                                       
176                                                         int domainIndex = _fieldMap[rangeIndex];
177                                                         if (domainIndex>=0) {
178                                                                 Object srcValue = domainRecord.getComponent(src, domainIndex);
179                                                                 Object dstValue = componentAdapters[rangeIndex].adapt(srcValue);
180                                                                 values[rangeIndex] = dstValue;
181                                                         } else {
182                                                                 // Optional value
183                                                                 values[rangeIndex] = rangeRecord.componentBindings[rangeIndex].createDefault();
184                                                         }
185                                                 }
186                                                 return rangeRecord.create(values);
187                                         } catch (BindingException e) {
188                                                 throw new AdaptException(e);
189                                         }
190                                 }
191                         };
192                         result.typeAdapter = true;
193                 }
194                         
195                 addToCache(req, result);                
196                 result.clones = true;
197                 for (int rangeIndex=0; rangeIndex<len; rangeIndex++)
198                 {
199                         int domainIndex = fieldMap[rangeIndex];
200                         if (domainIndex>=0) {
201                                 componentAdapters[rangeIndex] = getAdapterUnsynchronized(domainRecord.componentBindings[domainIndex], rangeRecord.componentBindings[rangeIndex], typeAdapter, mustClone);
202                                 result.typeAdapter |= componentAdapters[rangeIndex].typeAdapter;
203                                 result.clones &= componentAdapters[rangeIndex].clones;
204                         }
205                 }
206                 return result;
207         }
208         
209         if (domain instanceof UnionBinding && range instanceof UnionBinding)
210         {
211                 final UnionBinding domainBinding = (UnionBinding) domain;
212                 final UnionBinding rangeBinding = (UnionBinding) range;
213                 UnionType domainType = domainBinding.type();
214                 UnionType rangeType = rangeBinding.type();
215                 
216                 // Tag-Map describes the index of the tag-types in domain for each tag-type in range
217                 boolean requiresTypeAdapting = domainType.getComponentCount() != rangeType.getComponentCount(); 
218                 int tagMap[] = new int[domainType.getComponentCount()];
219                 for (int domainIndex=0; domainIndex<tagMap.length; domainIndex++)
220                 {
221                         String fieldName = domainType.getComponent(domainIndex).name;
222                         Integer rangeIndex = rangeType.getComponentIndex(fieldName);
223                         if (rangeIndex==null) throw new AdapterConstructionException("The range UnionType does not have expected tag \""+fieldName+"\"");
224                         tagMap[domainIndex] = rangeIndex;
225                         requiresTypeAdapting |= rangeIndex != domainIndex;
226                 }                       
227                 
228                 if (requiresTypeAdapting && !typeAdapter) {
229                         throw new AdapterConstructionException("Type Adapter required.");
230                 }
231                 
232                 final AbstractAdapter[] componentAdapters = new AbstractAdapter[domainType.getComponentCount()];
233
234                 AbstractAdapter result = null;
235                 
236                 if (!requiresTypeAdapting) {
237                         // Normal adapter
238                         result = new AbstractAdapter() {
239                                 @Override
240                                 public Object adapt(Object obj) throws AdaptException {
241                                         try { 
242                                                 int tag = domainBinding.getTag(obj);
243                                                 Object srcValue = domainBinding.getValue(obj);                                  
244                                                 Object dstValue = componentAdapters[tag].adapt(srcValue);
245                                                 return rangeBinding.create(tag, dstValue);
246                                         } catch (BindingException e) {
247                                                 throw new AdaptException(e);
248                                         }
249                                 }
250                         };
251                 } else {
252                         // Type adapter, type adapter rearranges tag indices
253                         final int _tagMap[] = tagMap; 
254                         result = new AbstractAdapter() {
255                                 @Override
256                                 public Object adapt(Object obj) throws AdaptException {
257                                         try { 
258                                                 int domainTag = domainBinding.getTag(obj);
259                                                 int rangeTag = _tagMap[domainTag];
260                                                 // Domain Component Binding
261                                                 Object srcValue = domainBinding.getValue(obj);
262                                                 Object dstValue = componentAdapters[domainTag].adapt(srcValue);
263                                                 return rangeBinding.create(rangeTag, dstValue);
264                                         } catch (BindingException e) {
265                                                 throw new AdaptException(e);
266                                         }
267                                 }
268                         };
269                 }
270                 
271                 addToCache(req, result);
272                 result.clones = true;
273                 for (int domainIndex=0; domainIndex<domainType.getComponentCount(); domainIndex++)
274                 {
275                         int rangeIndex = tagMap[domainIndex];
276                         componentAdapters[domainIndex] = getAdapterUnsynchronized(domainBinding.getComponentBindings()[domainIndex], rangeBinding.getComponentBindings()[rangeIndex], typeAdapter, mustClone);
277                         result.typeAdapter |= componentAdapters[domainIndex].typeAdapter;
278                         result.clones &= componentAdapters[domainIndex].clones;
279                 }
280                         return result;
281         }       
282         
283         if (domain instanceof BooleanBinding && range instanceof BooleanBinding)
284         {
285                 final BooleanBinding domainBoolean = (BooleanBinding) domain;
286                 final BooleanBinding rangeBoolean = (BooleanBinding) range;
287                 AbstractAdapter result = new AbstractAdapter() {
288                                 @Override
289                                 public Object adapt(Object obj) throws AdaptException {
290                                         try {
291                                                 boolean value = domainBoolean.getValue_(obj);
292                                                 return rangeBoolean.create(value);
293                                         } catch (BindingException e) {
294                                                 throw new AdaptException(e);
295                                         }                                                                                                       
296                                 }
297                 };
298                 result.clones = mustClone;
299                 result.typeAdapter = true;
300                 addToCache(req, result);
301                 return result;
302         }               
303
304         if (domain instanceof BooleanBinding && range instanceof NumberBinding)
305         {
306                 try {
307                         final BooleanBinding domainBoolean = (BooleanBinding) domain;
308                         final NumberBinding rangeNumber = (NumberBinding) range;
309                                 final Object falseValue = rangeNumber.create( Integer.valueOf(0) );
310                         final Object trueValue = rangeNumber.create( Integer.valueOf(1) );
311                         AbstractAdapter result = new AbstractAdapter() {
312                                         @Override
313                                         public Object adapt(Object obj) throws AdaptException {
314                                                 try {
315                                                         boolean value = domainBoolean.getValue_(obj);
316                                                         return value ? trueValue : falseValue;
317                                                 } catch (BindingException e) {
318                                                         throw new AdaptException(e);
319                                                 }                                                                                                       
320                                         }
321                         };
322                         result.clones = true;
323                         result.typeAdapter = true;
324                         addToCache(req, result);
325                         return result;
326                         } catch (BindingException e1) {
327                                 throw new AdapterConstructionException(e1);
328                         }
329         }               
330         
331         if (domain instanceof NumberBinding && range instanceof BooleanBinding)
332         {
333                 try {
334                         final NumberBinding domainNumber = (NumberBinding) domain;
335                         final BooleanBinding rangeBoolean = (BooleanBinding) range;
336                                 final Object zeroValue = domainNumber.create( Integer.valueOf(0) );
337                         AbstractAdapter result = new AbstractAdapter() {
338                                         @Override
339                                         public Object adapt(Object obj) throws AdaptException {
340                                                 try {
341                                                         Object value = domainNumber.getValue(obj);
342                                                         boolean bool = !domainNumber.equals(value, zeroValue);
343                                                         return rangeBoolean.create(bool);
344                                                 } catch (BindingException e) {
345                                                         throw new AdaptException(e);
346                                                 }                                                                                                       
347                                         }
348                         };
349                         result.clones = true;
350                         addToCache(req, result);
351                         return result;
352                         } catch (BindingException e1) {
353                                 throw new AdapterConstructionException(e1);
354                         }
355         }
356         
357         if (domain instanceof StringBinding && range instanceof StringBinding)
358         {
359                 final StringBinding domainString = (StringBinding) domain;
360                 final StringBinding rangeString = (StringBinding) range;
361                 AbstractAdapter result = new AbstractAdapter() {
362                                 @Override
363                                 public Object adapt(Object obj) throws AdaptException {
364                                         try {
365                                                 String value = domainString.getValue(obj);
366                                                 return rangeString.create(value);
367                                         } catch (BindingException e) {
368                                                 throw new AdaptException(e);
369                                         }                                                                                                       
370                                 }
371                 };
372                 result.clones = true;
373                 addToCache(req, result);
374                 return result;
375         }           
376         
377         if(domain instanceof StringBinding && range instanceof NumberBinding)
378         {
379                 final StringBinding domainString = (StringBinding) domain;
380                 final NumberBinding rangeString = (NumberBinding) range;
381                 AbstractAdapter result = new AbstractAdapter() {
382                                 @Override
383                                 public Object adapt(Object obj) throws AdaptException {
384                                         try {
385                                                 String value = domainString.getValue(obj);
386                                                 return rangeString.create(value);
387                                         } catch (BindingException e) {
388                                                 throw new AdaptException(e);
389                                         }                                                                                                       
390                                 }
391                 };
392                 result.clones = true;
393                 addToCache(req, result);
394                 return result;
395         }
396
397         if(domain instanceof StringBinding && range instanceof BooleanBinding)
398         {
399                 final StringBinding domainString = (StringBinding) domain;
400                 final BooleanBinding rangeString = (BooleanBinding) range;
401                 AbstractAdapter result = new AbstractAdapter() {
402                                 @Override
403                                 public Object adapt(Object obj) throws AdaptException {
404                                         try {
405                                                 String value = domainString.getValue(obj);
406                                                 return rangeString.create(Boolean.parseBoolean(value));
407                                         } catch (BindingException e) {
408                                                 throw new AdaptException(e);
409                                         }                                                                                                       
410                                 }
411                 };
412                 result.clones = true;
413                 addToCache(req, result);
414                 return result;
415         }
416         
417         // XXX We can optimizes here by using primitives 
418         if (domain instanceof NumberBinding && range instanceof NumberBinding)
419         {
420                         final NumberBinding domainNumber = (NumberBinding) domain;
421                         final NumberBinding rangeNumber = (NumberBinding) range;
422                 
423                         String domainUnit = ((NumberType) domainNumber.type()).getUnit();
424                         String rangeUnit = ((NumberType) rangeNumber.type()).getUnit();
425                         IUnitConverter unitConverter;
426                         if(domainUnit == null || rangeUnit == null || domainUnit.equals(rangeUnit))
427                                 unitConverter = null;
428                         else
429                                 unitConverter = Units.createConverter(domainUnit, rangeUnit); 
430                         /*if(domainUnit == null || domainUnit.equals("")) {
431                             if(rangeUnit == null || rangeUnit.equals(""))
432                                 unitConverter = null;
433                             else
434                                 unitConverter = null;
435 //                              throw new AdapterConstructionException("Cannot convert from a unitless type to a type with unit.");
436                         }
437                         else {
438                             if(rangeUnit == null || rangeUnit.equals(""))
439                                 unitConverter = null;                                   
440 //                              throw new AdapterConstructionException("Cannot convert from a type with unit to unitless type.");
441                             else
442                                 unitConverter = Units.createConverter(domainUnit, rangeUnit); 
443                         }       */                      
444                         boolean doUnitConversion = unitConverter != null && unitConverter != IdentityConverter.INSTANCE;
445                         boolean doPrimitiveConversion = !domainNumber.type().getClass().equals( rangeNumber.type().getClass() );                                
446                         if (doPrimitiveConversion && !typeAdapter)
447                                 throw new AdapterConstructionException("Type mismatch, use Type Adapter instead.");
448                                                         
449                         AbstractAdapter result;
450                         if (!doUnitConversion) {
451                                 result = new AbstractAdapter() {
452                                         @Override
453                                         public Object adapt(Object obj) throws AdaptException {
454                                                 Number value;
455                                                 try {                                                   
456                                                         value = domainNumber.getValue(obj);
457                                                         return rangeNumber.create(value);
458                                                 } catch (BindingException e) {
459                                                         throw new AdaptException(e);
460                                                 }
461                                         }
462                                 };
463                         } else {
464                                 final IUnitConverter _unitConverter = unitConverter;
465                                 result = new AbstractAdapter() {
466                                         @Override
467                                         public Object adapt(Object obj) throws AdaptException {
468                                                 try {
469                                                         Number value = domainNumber.getValue(obj);
470                                                         double convertedValue = _unitConverter.convert(value.doubleValue());
471                                                         return rangeNumber.create(Double.valueOf(convertedValue));
472                                                 } catch (BindingException e) {
473                                                         throw new AdaptException(e);
474                                                 }
475                                         }
476                                 };
477                         }
478                         result.typeAdapter = doPrimitiveConversion;
479                         result.clones = true;
480                         addToCache(req, result);
481                         return result;                  
482         }               
483         
484         if (domain instanceof BooleanArrayBinding && range instanceof BooleanArrayBinding)
485         {
486                 final BooleanArrayBinding domainArray = (BooleanArrayBinding) domain;
487                 final BooleanArrayBinding rangeArray = (BooleanArrayBinding) range;
488                 AbstractAdapter result = new AbstractAdapter() {
489                                 @Override
490                                 public Object adapt(Object obj) throws AdaptException {
491                                         try {
492                                                 boolean[] data = domainArray.getArray(obj);
493                                                 if (mustClone) data = data.clone();
494                                                 return rangeArray.create(data);
495                                         } catch (BindingException e) {
496                                                 throw new AdaptException(e);
497                                         }                                               
498                                 }
499                 };
500                 result.clones = true;
501                 addToCache(req, result);
502                 return result;
503         }               
504
505         if (domain instanceof ByteArrayBinding && range instanceof ByteArrayBinding)
506         {
507                 final ByteArrayBinding domainArray = (ByteArrayBinding) domain;
508                 final ByteArrayBinding rangeArray = (ByteArrayBinding) range;
509                 
510                         String domainUnit = ((NumberType) ((ArrayType)domainArray.type()).componentType).getUnit();
511                         String rangeUnit = ((NumberType) ((ArrayType)rangeArray.type()).componentType).getUnit();               
512                         IUnitConverter unitConverter = ObjectUtils.objectEquals(domainUnit, rangeUnit) ? null : Units.createConverter(domainUnit, rangeUnit);
513                         boolean doUnitConversion = unitConverter != null && unitConverter != IdentityConverter.INSTANCE;
514
515                         AbstractAdapter result;
516                         if (doUnitConversion) {
517                                 final IUnitConverter _unitConverter = unitConverter; 
518                                 result = new AbstractAdapter() {
519                                         @Override
520                                         public Object adapt(Object obj) throws AdaptException {
521                                                 try {
522                                                         byte[] data = domainArray.getArray(obj);                                
523                                                         for (int i=0; i<data.length; i++) {
524                                                                 byte value = data[i];
525                                                                 double convertedValue = _unitConverter.convert((double)value);
526                                                                 data[i] = (byte) convertedValue;
527                                                         }
528                                                         return rangeArray.create(data);
529                                                 } catch (BindingException e) {
530                                                         throw new AdaptException(e);
531                                                 }
532                                         }
533                                 };
534                         } else {
535                                 result = new AbstractAdapter() {
536                                         @Override
537                                         public Object adapt(Object obj) throws AdaptException {
538                                                 try {
539                                                         byte[] data = domainArray.getArray(obj);
540                                                         if (mustClone) data = data.clone();                                                     
541                                                         return rangeArray.create(data);
542                                                 } catch (BindingException e) {
543                                                         throw new AdaptException(e);
544                                                 }                                                       
545                                         }
546                                 };
547                         }
548                 result.clones = true;
549                 addToCache(req, result);
550                 return result;
551         }               
552         
553         if (domain instanceof IntArrayBinding && range instanceof IntArrayBinding)
554         {
555                 final IntArrayBinding domainArray = (IntArrayBinding) domain;
556                 final IntArrayBinding rangeArray = (IntArrayBinding) range;
557                 
558                         String domainUnit = ((NumberType) ((ArrayType)domainArray.type()).componentType).getUnit();
559                         String rangeUnit = ((NumberType) ((ArrayType)rangeArray.type()).componentType).getUnit();               
560                         IUnitConverter unitConverter = ObjectUtils.objectEquals(domainUnit, rangeUnit) ||
561                                         domainUnit == null || rangeUnit == null ? null : Units.createConverter(domainUnit, rangeUnit);
562                         boolean doUnitConversion = unitConverter != null && unitConverter != IdentityConverter.INSTANCE;
563                 
564                         AbstractAdapter result;
565                         if (doUnitConversion) {
566                                 final IUnitConverter _unitConverter = unitConverter; 
567                                 result = new AbstractAdapter() {
568                                         @Override
569                                         public Object adapt(Object obj) throws AdaptException {
570                                                 try {
571                                                         int[] data = domainArray.getArray(obj);                         
572                                                         for (int i=0; i<data.length; i++) {
573                                                                 int value = data[i];
574                                                                 double convertedValue = _unitConverter.convert((double)value);
575                                                                 data[i] = (int) convertedValue;
576                                                         }
577                                                         return rangeArray.create(data);
578                                                 } catch (BindingException e) {
579                                                         throw new AdaptException(e);
580                                                 }
581                                         }
582                                 };
583                         } else {
584                                 result = new AbstractAdapter() {
585                                         @Override
586                                         public Object adapt(Object obj) throws AdaptException {
587                                                 try {
588                                                         int[] data = domainArray.getArray(obj);
589                                                         if (mustClone) data = data.clone();                                                     
590                                                         return rangeArray.create(data);
591                                                 } catch (BindingException e) {
592                                                         throw new AdaptException(e);
593                                                 }                                                       
594                                         }
595                                 };
596                         }
597                 
598                 result.clones = true;
599                 addToCache(req, result);
600                 return result;
601         }               
602
603         if (domain instanceof LongArrayBinding && range instanceof LongArrayBinding)
604         {
605                 final LongArrayBinding domainArray = (LongArrayBinding) domain;
606                 final LongArrayBinding rangeArray = (LongArrayBinding) range;
607
608                         String domainUnit = ((NumberType) ((ArrayType)domainArray.type()).componentType).getUnit();
609                         String rangeUnit = ((NumberType) ((ArrayType)rangeArray.type()).componentType).getUnit();               
610                         IUnitConverter unitConverter = ObjectUtils.objectEquals(domainUnit, rangeUnit) ||
611                                         domainUnit == null || rangeUnit == null ? null : Units.createConverter(domainUnit, rangeUnit);
612                         boolean doUnitConversion = unitConverter != null && unitConverter != IdentityConverter.INSTANCE;
613                 
614                         AbstractAdapter result;
615                         if (doUnitConversion) {
616                                 final IUnitConverter _unitConverter = unitConverter; 
617                                 result = new AbstractAdapter() {
618                                         @Override
619                                         public Object adapt(Object obj) throws AdaptException {
620                                                 try {
621                                                         long[] data = domainArray.getArray(obj);                                
622                                                         for (int i=0; i<data.length; i++) {
623                                                                 long value = data[i];
624                                                                 double convertedValue = _unitConverter.convert((double)value);
625                                                                 data[i] = (long) convertedValue;
626                                                         }
627                                                         return rangeArray.create(data);
628                                                 } catch (BindingException e) {
629                                                         throw new AdaptException(e);
630                                                 }
631                                         }
632                                 };
633                         } else {
634                                 result = new AbstractAdapter() {
635                                         @Override
636                                         public Object adapt(Object obj) throws AdaptException {
637                                                 try {
638                                                         long[] data = domainArray.getArray(obj);
639                                                         if (mustClone) data = data.clone();                                                     
640                                                         return rangeArray.create(data);
641                                                 } catch (BindingException e) {
642                                                         throw new AdaptException(e);
643                                                 }
644                                         }
645                                 };
646                         }
647
648                 result.clones = true;
649                 addToCache(req, result);
650                 return result;
651         }               
652
653         if (domain instanceof FloatArrayBinding && range instanceof FloatArrayBinding)
654         {
655                 final FloatArrayBinding domainArray = (FloatArrayBinding) domain;
656                 final FloatArrayBinding rangeArray = (FloatArrayBinding) range;
657
658                         String domainUnit = ((NumberType) ((ArrayType)domainArray.type()).componentType).getUnit();
659                         String rangeUnit = ((NumberType) ((ArrayType)rangeArray.type()).componentType).getUnit();               
660                         IUnitConverter unitConverter = ObjectUtils.objectEquals(domainUnit, rangeUnit) ||
661                                         domainUnit == null || rangeUnit == null ? null : Units.createConverter(domainUnit, rangeUnit);
662                         boolean doUnitConversion = unitConverter != null && unitConverter != IdentityConverter.INSTANCE;
663                 
664                         AbstractAdapter result;
665                         if (doUnitConversion) {
666                                 final IUnitConverter _unitConverter = unitConverter; 
667                                 result = new AbstractAdapter() {
668                                         @Override
669                                         public Object adapt(Object obj) throws AdaptException {
670                                                 try {
671                                                         float[] data = domainArray.getArray(obj);                               
672                                                         for (int i=0; i<data.length; i++) {
673                                                                 float value = data[i];
674                                                                 double convertedValue = _unitConverter.convert((double)value);
675                                                                 data[i] = (float) convertedValue;
676                                                         }
677                                                         return rangeArray.create(data);
678                                                 } catch (BindingException e) {
679                                                         throw new AdaptException(e);
680                                                 }
681                                         }
682                                 };
683                         } else {
684                                 result = new AbstractAdapter() {
685                                         @Override
686                                         public Object adapt(Object obj) throws AdaptException {
687                                                 try {
688                                                         float[] data = domainArray.getArray(obj);
689                                                         if (mustClone) data = data.clone();                                                     
690                                                         return rangeArray.create(data);
691                                                 } catch (BindingException e) {
692                                                         throw new AdaptException(e);
693                                                 }
694                                         }
695                                 };
696                         }
697                         
698                 result.clones = true;
699                 addToCache(req, result);
700                 return result;
701         }               
702
703         if (domain instanceof DoubleArrayBinding && range instanceof DoubleArrayBinding)
704         {
705                 final DoubleArrayBinding domainArray = (DoubleArrayBinding) domain;
706                 final DoubleArrayBinding rangeArray = (DoubleArrayBinding) range;
707
708                         String domainUnit = ((NumberType) ((ArrayType)domainArray.type()).componentType).getUnit();
709                         String rangeUnit = ((NumberType) ((ArrayType)rangeArray.type()).componentType).getUnit();               
710                         IUnitConverter unitConverter = ObjectUtils.objectEquals(domainUnit, rangeUnit) 
711                                         || domainUnit == null || rangeUnit == null ? null : Units.createConverter(domainUnit, rangeUnit);
712                         boolean doUnitConversion = unitConverter != null && unitConverter != IdentityConverter.INSTANCE;
713                 
714                         AbstractAdapter result;
715                         if (doUnitConversion) {
716                                 final IUnitConverter _unitConverter = unitConverter; 
717                                 result = new AbstractAdapter() {
718                                         @Override
719                                         public Object adapt(Object obj) throws AdaptException {
720                                                 try {
721                                                         double[] data = domainArray.getArray(obj);                              
722                                                         for (int i=0; i<data.length; i++) {
723                                                                 double value = data[i];
724                                                                 double convertedValue = _unitConverter.convert(value);
725                                                                 data[i] = convertedValue;
726                                                         }
727                                                         return rangeArray.create(data);
728                                                 } catch (BindingException e) {
729                                                         throw new AdaptException(e);
730                                                 }
731                                         }
732                                 };
733                         } else {
734                                 result = new AbstractAdapter() {
735                                         @Override
736                                         public Object adapt(Object obj) throws AdaptException {
737                                                 try {
738                                                         double[] data = domainArray.getArray(obj);
739                                                         if (mustClone) data = data.clone();                                                     
740                                                         return rangeArray.create(data);
741                                                 } catch (BindingException e) {
742                                                         throw new AdaptException(e);
743                                                 }
744                                         }
745                                 };
746                         }
747                 
748                 result.clones = true;
749                 addToCache(req, result);
750                 return result;
751         }               
752
753         if (domain instanceof ArrayBinding && range instanceof ArrayBinding)
754         {
755                 final ArrayBinding domainBinding = (ArrayBinding) domain;
756                 final ArrayBinding rangeBinding = (ArrayBinding) range;
757                 final AbstractAdapter componentAdapter = getAdapterUnsynchronized(domainBinding.getComponentBinding(), rangeBinding.getComponentBinding(), typeAdapter, mustClone);
758                 AbstractAdapter result = new AbstractAdapter() {
759                                 @Override
760                                 public Object adapt(Object obj) throws AdaptException {
761                                         try {
762                                                 int len = domainBinding.size(obj);
763                                                 ArrayList<Object> array = new ArrayList<Object>(len);
764                                                 for (int i=0; i<len; i++)
765                                                 {
766                                                         Object srcValue = domainBinding.get(obj, i);
767                                                         Object dstValue = componentAdapter.adapt(srcValue);
768                                                         array.add(dstValue);
769                                                 }                                       
770                                                 return rangeBinding instanceof ArrayListBinding ? array : rangeBinding.create(array);
771                                         } catch (BindingException e) {
772                                                 throw new AdaptException(e);
773                                         }
774                                 }
775                 };
776                 
777                 result.clones = componentAdapter.clones;
778                 addToCache(req, result);
779                 return result;
780         }
781         
782         if (domain instanceof OptionalBinding && range instanceof OptionalBinding)
783         {
784                 final OptionalBinding domainBinding = (OptionalBinding) domain;
785                 final OptionalBinding rangeBinding = (OptionalBinding) range;
786                 final AbstractAdapter componentAdapter = getAdapterUnsynchronized(domainBinding.componentBinding, rangeBinding.componentBinding, typeAdapter, mustClone);
787                 AbstractAdapter result = new AbstractAdapter() {
788                                 @Override
789                                 public Object adapt(Object obj) throws AdaptException {
790                                         try {
791                                                 if (!domainBinding.hasValue(obj)) return rangeBinding.createNoValue();
792                                                 Object value = domainBinding.getValue(obj);
793                                                 value = componentAdapter.adapt(value); 
794                                                 return rangeBinding.createValue(value);
795                                         } catch (BindingException e) {
796                                                 throw new AdaptException(e);
797                                         }
798                                 }
799                 };
800                 result.typeAdapter = componentAdapter.typeAdapter;
801                 result.clones = componentAdapter.clones;
802                 addToCache(req, result);
803                 return result;
804         }
805         
806         // Adapt a non-optional value to an optional value
807         if (range instanceof OptionalBinding && !(domain instanceof OptionalBinding))
808         {
809                 final Binding domainBinding = domain;
810                 final OptionalBinding rangeBinding = (OptionalBinding) range;
811                 final AbstractAdapter componentAdapter = getAdapterUnsynchronized(domainBinding, rangeBinding.componentBinding, typeAdapter, mustClone);
812                 AbstractAdapter result = new AbstractAdapter() {
813                                 @Override
814                                 public Object adapt(Object obj) throws AdaptException {
815                                         try {
816                                                 obj = componentAdapter.adapt(obj); 
817                                                 return rangeBinding.createValue(obj);
818                                         } catch (BindingException e) {
819                                                 throw new AdaptException(e);
820                                         }
821                                 }
822                 };
823                 result.typeAdapter = componentAdapter.typeAdapter;
824                 result.clones = componentAdapter.clones;
825                 addToCache(req, result);
826                 return result;                  
827         }
828         
829         if (domain instanceof VariantBinding && range instanceof VariantBinding)
830         {               
831                 final VariantBinding domainBinding = (VariantBinding) domain;
832                 final VariantBinding rangeBinding = (VariantBinding) range;
833                 AbstractAdapter result = new AbstractAdapter() {
834                                 @Override
835                                 public Object adapt(Object obj) throws AdaptException {                                 
836                                         try {
837                                                 
838                                                 Binding domainValueBinding = domainBinding.getContentBinding(obj);
839                                                 Object domainObject = domainBinding.getContent(obj, domainValueBinding);
840                                                 if (mustClone && domainObject!=obj) {
841                                                         Adapter valueAdapter = getAdapterUnsynchronized(domainValueBinding, domainValueBinding, false, true);
842                                                         domainObject = valueAdapter.adapt(domainObject); 
843                                                 }
844                                                 Object rangeVariant = rangeBinding.create(domainValueBinding, domainObject);
845                                                 return rangeVariant;
846                                         } catch (BindingException e) {
847                                                 throw new AdaptException(e);
848                                         } catch (AdapterConstructionException e) {
849                                                 throw new AdaptException(e);
850                                         }
851                                 }
852                 };
853                 result.clones = mustClone;
854                 addToCache(req, result);
855                 return result;
856         }
857         
858         if (domain instanceof VariantBinding && !(range instanceof VariantBinding))
859         {
860                 // Make a recursive adaptation from a variant source
861                 final VariantBinding domainBinding = (VariantBinding)domain;
862                 final Binding rangeBinding = range;
863                 AbstractAdapter result = new AbstractAdapter() {
864                                 @Override
865                                 public Object adapt(Object obj) throws AdaptException {
866                                         try {
867                                                 Object value = domainBinding.getContent(obj);
868                                                 Binding contentBinding = domainBinding.getContentBinding(obj);
869                                                 AbstractAdapter adapter = (AbstractAdapter) getAdapter(contentBinding, rangeBinding, typeAdapter, mustClone);
870                                                 return adapter.adapt(value);
871                                         } catch (BindingException | AdapterConstructionException e) {
872                                                 throw new AdaptException(e);
873                                         }
874                                 }
875                 };
876                 result.clones = mustClone;
877                 addToCache(req, result);
878                 return result;                  
879         }
880         
881         if (range instanceof VariantBinding && !(domain instanceof VariantBinding))
882         {
883                 // Default to just wrapping the domain type
884                 final VariantBinding rangeBinding = (VariantBinding)range;
885                 final Binding domainBinding = domain;
886                 AbstractAdapter result = new AbstractAdapter() {
887                                 @Override
888                                 public Object adapt(Object obj) throws AdaptException {
889                                         try {
890                                                 if (mustClone) {
891                                                         Adapter valueAdapter;
892                                                                 valueAdapter = getAdapterUnsynchronized(domainBinding, domainBinding, false, true);
893                                                         obj = valueAdapter.adapt(obj);
894                                                 }
895                                                 return rangeBinding.create(domainBinding, obj);
896                                         } catch (AdapterConstructionException | BindingException e) {
897                                                 throw new AdaptException(e);
898                                         }
899                                 }
900                 };
901                 result.clones = mustClone;
902                 addToCache(req, result);
903                 return result;
904         }
905
906         if (domain instanceof MapBinding && range instanceof MapBinding)
907         {
908                 final MapBinding domainBinding = (MapBinding) domain;
909                 final MapBinding rangeBinding = (MapBinding) range;
910                 final AbstractAdapter keyAdapter = getAdapterUnsynchronized(domainBinding.getKeyBinding(), rangeBinding.getKeyBinding(), typeAdapter, mustClone);
911                 final AbstractAdapter valueAdapter = getAdapterUnsynchronized(domainBinding.getValueBinding(), rangeBinding.getValueBinding(), typeAdapter, mustClone);
912                 AbstractAdapter result = new AbstractAdapter() {
913                                 @Override
914                                 public Object adapt(Object obj) throws AdaptException {
915                                         try {
916                                                 int len = domainBinding.size(obj);
917                                                 Object domainKeys[] = domainBinding.getKeys(obj);
918                                                 Object domainValues[] = domainBinding.getValues(obj);
919                                                 Object rangeKeys[] = new Object[len];
920                                                 Object rangeValues[] = new Object[len];
921                                                 for (int i=0; i<len; i++) {
922                                                         Object domainKey = domainKeys[i];
923                                                         Object domainValue = domainValues[i];
924                                                         Object rangeKey = keyAdapter.adapt(domainKey);
925                                                         Object rangeValue = valueAdapter.adapt(domainValue);
926                                                         rangeKeys[i] = rangeKey;
927                                                         rangeValues[i] = rangeValue;
928                                                 }
929                                                 Object rangeMap = rangeBinding.create(rangeKeys, rangeValues);
930                                                 return rangeMap;
931                                         } catch (BindingException e) {
932                                                 throw new AdaptException(e);
933                                         }
934                                 }
935                 };
936                 result.typeAdapter |= keyAdapter.typeAdapter | valueAdapter.typeAdapter;
937                 result.clones = keyAdapter.clones & valueAdapter.clones;                
938                 addToCache(req, result);
939                 return result;
940         }
941 /*      
942         // Special-Case: Convert Union to its Composite
943         if (domain instanceof UnionBinding) {
944                 final UnionType ut = (UnionType) domain.getDataType();
945                 final UnionBinding ub = (UnionBinding) domain;
946                 Binding[] cbs = ub.getComponentBindings();              
947                 for (int i=0; i<cbs.length; i++)
948                 {
949                         Binding domainCompositeBinding = cbs[i];
950                         if (ut.getComponent(i).type.equals(range.getDataType())) {
951                                 final AbstractAdapter union2CompositeAdapter = getAdapterUnsynchronized(domainCompositeBinding, range, allowPrimitiveConversion);                               
952                                 final int tag = i;
953                         AbstractAdapter result = new AbstractAdapter() {
954                                         @Override
955                                         public Object adapt(Object obj) throws AdaptException {
956                                                 try {
957                                                         Object domainUnion = obj;
958                                                         int domainTag = ub.getTag(domainUnion);
959                                                         if (domainTag != tag) {
960                                                                 throw new AdaptException("This adapter can adapt only "+ub.getDataType().getComponents()[tag].name+"s");
961                                                         }
962                                                         Object domainComposite = ub.getValue(domainUnion);
963                                                         Object rangeComposite = union2CompositeAdapter.adapt(domainComposite);
964                                                         return rangeComposite;
965                                                 } catch (BindingException e) {
966                                                         throw new AdaptException(e);
967                                                 }
968                                         }
969                         };
970                         result.hasPrimitiveConversion = union2CompositeAdapter.hasPrimitiveConversion;
971                         addToCache(pair, result);
972                         return result;
973                         }
974                 }
975         }
976         
977         // Special-Case: Convert Composite to Union
978         if (range instanceof UnionBinding) {
979                 final UnionType ut = (UnionType) range.getDataType();
980                 final UnionBinding ub = (UnionBinding) range;
981                 Binding cbs[] = ub.getComponentBindings();
982                 for (int i=0; i<cbs.length; i++) {
983                         Binding rangeCompositeBinding = cbs[i];
984                         if (ut.getComponent(i).type.equals(domain.getDataType())) {
985                                 final AbstractAdapter domainObject2RangeCompositeAdapter = getAdapterUnsynchronized(rangeCompositeBinding, domain, allowPrimitiveConversion);                                   
986                                 final int tag = i;
987                         AbstractAdapter result = new AbstractAdapter() {
988                                         @Override
989                                         public Object adapt(Object obj) throws AdaptException {
990                                                 try {
991                                                         Object domainObject = obj;
992                                                         Object rangeComposite = domainObject2RangeCompositeAdapter.adapt(domainObject);
993                                                     Object rangeUnion = ub.create(tag, rangeComposite);
994                                                         return rangeUnion;
995                                                 } catch (BindingException e) {
996                                                         throw new AdaptException(e);
997                                                 }
998                                         }
999                         };
1000                         result.hasPrimitiveConversion = domainObject2RangeCompositeAdapter.hasPrimitiveConversion;
1001                         addToCache(pair, result);
1002                         return result;
1003                         }
1004                 }
1005         }
1006 */      
1007         
1008                 } catch (UnitParseException e) {
1009                         throw new AdapterConstructionException(e.getMessage(), e); 
1010                 }                               
1011
1012                 StringBuilder error = new StringBuilder();
1013                 error.append("Could not create ");
1014                 if (mustClone) error.append("cloning ");
1015                 if (typeAdapter) error.append("type");
1016                 error.append("adapter (");
1017                 error.append("domain=");
1018                 error.append(domain.type().toSingleLineString());
1019                 error.append(", range=");
1020                 error.append(range.type().toSingleLineString());
1021                 error.append(")");
1022                 
1023         throw new AdapterConstructionException(error.toString());
1024         }
1025         
1026         
1027         
1028     /**
1029      * Adapt a value of one type to another. 
1030      * 
1031      * @param value
1032      * @param domain
1033      * @param range
1034      * @return adapted value
1035      * @throws AdapterConstructionException
1036      * @throws AdaptException
1037      */
1038     public Object adapt(Object value, Binding domain, Binding range)
1039     throws AdaptException
1040     {
1041         if (domain == range) return value;
1042         try {
1043                 if (range instanceof VariantBinding && !(domain instanceof VariantBinding)) {
1044                         // Default to just wrapping the value (avoid adapter construction to save memory)
1045                         return ((VariantBinding)range).create(domain, value);
1046                 }
1047                         return getAdapter(domain, range, true, false).adapt(value);
1048                 } catch (AdapterConstructionException | BindingException e) {
1049                         throw new AdaptException(e);
1050                 }
1051     }
1052     
1053     /**
1054      * Adapt a value of one type to another
1055      * 
1056      * @param value
1057      * @param domain
1058      * @param range
1059      * @return adapted value
1060      * @throws AdapterConstructionException
1061      * @throws AdaptException
1062      */
1063     public Object adaptUnchecked(Object value, Binding domain, Binding range)
1064     throws RuntimeAdapterConstructionException, RuntimeAdaptException
1065     {
1066         if (domain == range) return value;
1067         try {
1068                 if (range instanceof VariantBinding && !(domain instanceof VariantBinding)) {
1069                         // Default to just wrapping the value (avoid adapter construction to save memory)
1070                         return ((VariantBinding)range).create(domain, value);
1071                 }
1072                 return getAdapter(domain, range, true, false).adaptUnchecked(value);
1073                 } catch (RuntimeAdapterConstructionException | RuntimeBindingException e) {
1074                         throw new RuntimeAdaptException(new AdaptException(e.getCause()));
1075                 } catch (AdapterConstructionException | BindingException e) {
1076                         throw new RuntimeAdaptException(new AdaptException(e));
1077                 }
1078     }
1079     
1080     /**
1081      * Clone a value to a type to another. Bindings that handle immutable values
1082      * may return the same instance, others will guarantee a complete copy.  
1083      * 
1084      * @param value
1085      * @param domain
1086      * @param range
1087      * @return adapted value
1088      * @throws AdapterConstructionException
1089      * @throws AdaptException
1090      */
1091     public Object clone(Object value, Binding domain, Binding range)
1092     throws AdaptException
1093     {
1094         try {
1095                         return getAdapter(domain, range, true, true).adapt(value);
1096                 } catch (AdapterConstructionException e) {
1097                         throw new AdaptException(e);
1098                 }
1099     }
1100     
1101
1102     /**
1103      * Clone a value of one binding to another. Bindings that handle immutable values
1104      * may return the same instance, others will guarantee a complete copy.
1105      * 
1106      * @param value
1107      * @param domain
1108      * @param range
1109      * @return adapted value
1110      * @throws AdapterConstructionException
1111      * @throws AdaptException
1112      */
1113     public Object cloneUnchecked(Object value, Binding domain, Binding range)
1114     throws RuntimeAdapterConstructionException, RuntimeAdaptException
1115     {
1116         try {
1117                         return getAdapter(domain, range, true, true).adapt(value);
1118                 } catch (AdaptException e) {
1119                         throw new RuntimeAdaptException(e);
1120                 } catch (RuntimeAdapterConstructionException e) {
1121                         throw new RuntimeAdaptException(new AdaptException(e.getCause()));
1122                 } catch (AdapterConstructionException e) {
1123                         throw new RuntimeAdaptException(new AdaptException(e));
1124                 }
1125     }    
1126                 
1127 }
1128