/* * Copyright 2004-2015 the Seasar Foundation and the Others. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, * either express or implied. See the License for the specific language * governing permissions and limitations under the License. */ package org.seasar.extension.dxo.converter.impl; import java.util.HashMap; import java.util.Map; import java.util.TreeMap; import org.seasar.extension.dxo.converter.Converter; import org.seasar.extension.dxo.converter.ConverterFactory; import org.seasar.framework.container.S2Container; import org.seasar.framework.util.ClassUtil; import org.seasar.framework.util.Disposable; import org.seasar.framework.util.DisposableUtil; import org.seasar.framework.util.MapUtil; /** * コンバータファクトリの実装クラスです。 * * @author Satoshi Kimura * @author koichik */ public class ConverterFactoryImpl implements ConverterFactory, Disposable { /** プリミティブ型の配列クラスとラッパー型の配列クラスのマッピング */ protected static Map PRIMITIVE_ARRAY_TO_WRAPPER_ARRAY = new HashMap(); static { PRIMITIVE_ARRAY_TO_WRAPPER_ARRAY.put(boolean[].class, Boolean[].class); PRIMITIVE_ARRAY_TO_WRAPPER_ARRAY.put(char[].class, Character[].class); PRIMITIVE_ARRAY_TO_WRAPPER_ARRAY.put(byte[].class, Byte[].class); PRIMITIVE_ARRAY_TO_WRAPPER_ARRAY.put(short[].class, Short[].class); PRIMITIVE_ARRAY_TO_WRAPPER_ARRAY.put(int[].class, Integer[].class); PRIMITIVE_ARRAY_TO_WRAPPER_ARRAY.put(long[].class, Long[].class); PRIMITIVE_ARRAY_TO_WRAPPER_ARRAY.put(float[].class, Float[].class); PRIMITIVE_ARRAY_TO_WRAPPER_ARRAY.put(double[].class, Double[].class); } /** インスタンスが初期化済みであることを示します。 */ protected volatile boolean initialized; /** このファクトリを管理しているS2コンテナです。 */ protected S2Container container; /** S2コンテナに登録されているコンバータの配列です。 */ protected Converter[] converters; /** コンバータのキャッシュです。 */ protected final Map converterCache = MapUtil.createHashMap(); /** * <code>ConverterFactoryImpl</code>のインスタンスを構築します。 * */ public ConverterFactoryImpl() { } /** * S2コンテナを設定します。 * * @param container * S2コンテナ */ public void setContainer(final S2Container container) { this.container = container.getRoot(); } /** * インスタンスを初期化します。 * */ public void initialize() { if (initialized) { return; } converters = (Converter[]) container.findAllComponents(Converter.class); DisposableUtil.add(this); initialized = true; } /** * キャッシュ情報等を破棄し、インスタンスを未初期化状態に戻します。 * */ public void dispose() { converters = null; converterCache.clear(); initialized = false; } public Converter getConverter(final Class sourceClass, final Class destClass) { initialize(); final Class destType = ClassUtil.getWrapperClassIfPrimitive(destClass); final String cacheKey = sourceClass.getName() + destType.getName(); final Converter converter = (Converter) converterCache.get(cacheKey); if (converter != null) { return converter; } return detectConverter(sourceClass, destType); } private Converter detectConverter(final Class sourceClass, final Class destClass) { final Converter converter = getDistanceTable(sourceClass, destClass); final String cacheKey = sourceClass.getName() + destClass.getName(); converterCache.put(cacheKey, converter); return converter; } private Converter getDistanceTable(final Class sourceClass, final Class destClass) { final Map distanceTable = new TreeMap(); for (int i = 0; i < converters.length; ++i) { final Converter converter = converters[i]; if (!canConvert(sourceClass, destClass, converter)) { continue; } final double distance = getDistance(converter.getDestClass(), destClass); distanceTable.put(new Double(distance), converter); } if (distanceTable.isEmpty()) { throw new IllegalStateException("converter was not found."); // TODO } return (Converter) distanceTable.values().iterator().next(); } private boolean canConvert(final Class sourceClass, final Class destClass, final Converter converter) { if (!converter.getDestClass().isAssignableFrom(destClass)) { final Class wrapperArray = (Class) PRIMITIVE_ARRAY_TO_WRAPPER_ARRAY .get(destClass); if (wrapperArray != null) { return canConvert(sourceClass, wrapperArray, converter); } return false; } final Class[] sourceClasses = converter.getSourceClasses(); for (int i = 0; i < sourceClasses.length; i++) { if (sourceClasses[i].isAssignableFrom(sourceClass)) { return true; } } return false; } private double getDistance(final Class assigner, final Class assignee) { if (assignee.isArray() && assigner.isArray()) { return getDistance(assigner.getComponentType(), assignee .getComponentType(), 0); } else if (assignee.isArray()) { return getDistance(assigner, assignee, 10); // TODO 一方だけ配列の時のクラス間の距離 // 一先ず10にしておく } return getDistance(assigner, assignee, 0); } private double getDistance(final Class assigner, final Class assignee, final double distance) { if (assignee.equals(assigner)) { return distance; } if (assigner.getName().equals("java.lang.Enum") && TigerSupport.instance.isEnum(assignee)) { return distance + 0.5; } if (isImplements(assigner, assignee)) { return distance + 0.5; } final Class superClass = assigner.getSuperclass(); if (superClass == null) { return distance + 1; } return getDistance(superClass, assignee, distance + 1); } private boolean isImplements(final Class assigner, final Class assignee) { return !assigner.isInterface() && assignee.isInterface() && assignee.isAssignableFrom(assigner); } }