/******************************************************************************* * Copyright (c) 2010 Association for Decentralized Information Management in * Industry THTH ry. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * VTT Technical Research Centre of Finland - initial API and implementation *******************************************************************************/ package org.simantics.databoard.adapter; import java.util.ArrayList; import java.util.Map; import org.apache.commons.collections.map.ReferenceMap; import org.simantics.databoard.Bindings; import org.simantics.databoard.Units; import org.simantics.databoard.binding.ArrayBinding; import org.simantics.databoard.binding.Binding; import org.simantics.databoard.binding.BooleanBinding; import org.simantics.databoard.binding.MapBinding; import org.simantics.databoard.binding.NumberBinding; import org.simantics.databoard.binding.OptionalBinding; import org.simantics.databoard.binding.RecordBinding; import org.simantics.databoard.binding.StringBinding; import org.simantics.databoard.binding.UnionBinding; import org.simantics.databoard.binding.VariantBinding; import org.simantics.databoard.binding.error.BindingException; import org.simantics.databoard.binding.error.RuntimeBindingException; import org.simantics.databoard.binding.impl.ArrayListBinding; import org.simantics.databoard.binding.impl.BooleanArrayBinding; import org.simantics.databoard.binding.impl.ByteArrayBinding; import org.simantics.databoard.binding.impl.DoubleArrayBinding; import org.simantics.databoard.binding.impl.FloatArrayBinding; import org.simantics.databoard.binding.impl.IntArrayBinding; import org.simantics.databoard.binding.impl.LongArrayBinding; import org.simantics.databoard.type.ArrayType; import org.simantics.databoard.type.NumberType; import org.simantics.databoard.type.RecordType; import org.simantics.databoard.type.UnionType; import org.simantics.databoard.units.IUnitConverter; import org.simantics.databoard.units.IdentityConverter; import org.simantics.databoard.units.internal.UnitParseException; import org.simantics.databoard.util.ObjectUtils; /** * AdapterRepository is a factory and a collection of adapters. * * @author Toni Kalajainen */ public class AdapterFactory { @SuppressWarnings( "unchecked" ) Map cache = (Map) new ReferenceMap(ReferenceMap.SOFT, ReferenceMap.HARD); public synchronized Adapter getAdapter(Binding domain, Binding range, boolean typeAdapter, boolean mustClone) throws AdapterConstructionException { if ((!mustClone || domain.isImmutable()) && domain.equals(range)) return PassThruAdapter.PASSTHRU; if (domain.getClass() == range.getClass() && ( !mustClone || domain.isImmutable() ) && NumberBinding.class.isAssignableFrom( domain.getClass() ) ) { NumberBinding db = (NumberBinding) domain; NumberBinding rb = (NumberBinding) range; String u1 = db.type().getUnit(); String u2 = rb.type().getUnit(); if (u1==null || u2==null || u1.equals("") || u2.equals("") || u1.equals(u2)) return PassThruAdapter.PASSTHRU; } return getAdapterUnsynchronized(domain, range, typeAdapter, mustClone); } private AbstractAdapter getCached(AdapterRequest type) { return cache.get(type); } private void cache(AdapterRequest type, AbstractAdapter binding) { cache.put(type, binding); } private void addToCache(AdapterRequest request, AbstractAdapter impl) { impl.request = request; cache(request, impl); // This request applies to "must clone" request aswell, remember this implementation if (!request.mustClone && impl.clones) { request = new AdapterRequest(request.domain, request.range, true); cache(request, impl); } } /** * Create adapter, does not cache the result. * * @param domain * @param range * @param typeAdapter if true, primitive conversion is allowed (e.g. int -> double) * @return */ private AbstractAdapter getAdapterUnsynchronized(Binding domain, Binding range, boolean typeAdapter, final boolean mustClone) throws AdapterConstructionException { if ( !mustClone && domain.equals(range) ) return PassThruAdapter.PASSTHRU; AdapterRequest req = new AdapterRequest(domain, range, mustClone); AbstractAdapter cachedResult = getCached(req); if (cachedResult!=null) return cachedResult; try { if (domain instanceof RecordBinding && range instanceof RecordBinding) { final RecordBinding domainRecord = (RecordBinding) domain; final RecordBinding rangeRecord = (RecordBinding) range; RecordType domainType = domainRecord.type(); RecordType rangeType = rangeRecord.type(); // Field-Map describes the index of the fields in domain for each field in range boolean requiresTypeAdapting = domainType.getComponentCount() != rangeType.getComponentCount(); int fieldMap[] = new int[rangeType.getComponentCount()]; for (int rangeIndex=0; rangeIndex=0) { Object srcValue = domainRecord.getComponent(src, domainIndex); Object dstValue = componentAdapters[rangeIndex].adapt(srcValue); values[rangeIndex] = dstValue; } else { // Optional value values[rangeIndex] = rangeRecord.componentBindings[rangeIndex].createDefault(); } } return rangeRecord.create(values); } catch (BindingException e) { throw new AdaptException(e); } } }; result.typeAdapter = true; } addToCache(req, result); result.clones = true; for (int rangeIndex=0; rangeIndex=0) { componentAdapters[rangeIndex] = getAdapterUnsynchronized(domainRecord.componentBindings[domainIndex], rangeRecord.componentBindings[rangeIndex], typeAdapter, mustClone); result.typeAdapter |= componentAdapters[rangeIndex].typeAdapter; result.clones &= componentAdapters[rangeIndex].clones; } } return result; } if (domain instanceof UnionBinding && range instanceof UnionBinding) { final UnionBinding domainBinding = (UnionBinding) domain; final UnionBinding rangeBinding = (UnionBinding) range; UnionType domainType = domainBinding.type(); UnionType rangeType = rangeBinding.type(); // Tag-Map describes the index of the tag-types in domain for each tag-type in range boolean requiresTypeAdapting = domainType.getComponentCount() != rangeType.getComponentCount(); int tagMap[] = new int[domainType.getComponentCount()]; for (int domainIndex=0; domainIndex array = new ArrayList(len); for (int i=0; i