Coverage Report - org.seasar.cubby.converter.impl.ConverterFactoryImpl
 
Classes in this File Line Coverage Branch Coverage Complexity
ConverterFactoryImpl
95%
71/75
79%
33/42
0
 
 1  
 /*
 2  
  * Copyright 2004-2008 the Seasar Foundation and the Others.
 3  
  *
 4  
  * Licensed under the Apache License, Version 2.0 (the "License");
 5  
  * you may not use this file except in compliance with the License.
 6  
  * You may obtain a copy of the License at
 7  
  *
 8  
  *     http://www.apache.org/licenses/LICENSE-2.0
 9  
  *
 10  
  * Unless required by applicable law or agreed to in writing, software
 11  
  * distributed under the License is distributed on an "AS IS" BASIS,
 12  
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
 13  
  * either express or implied. See the License for the specific language
 14  
  * governing permissions and limitations under the License.
 15  
  */
 16  
 package org.seasar.cubby.converter.impl;
 17  
 
 18  
 import java.lang.reflect.Modifier;
 19  
 import java.util.HashMap;
 20  
 import java.util.LinkedHashSet;
 21  
 import java.util.Map;
 22  
 import java.util.Set;
 23  
 import java.util.TreeMap;
 24  
 
 25  
 import org.seasar.cubby.controller.ClassDetector;
 26  
 import org.seasar.cubby.controller.DetectClassProcessor;
 27  
 import org.seasar.cubby.converter.Converter;
 28  
 import org.seasar.cubby.converter.ConverterFactory;
 29  
 import org.seasar.framework.container.S2Container;
 30  
 import org.seasar.framework.convention.NamingConvention;
 31  
 import org.seasar.framework.util.ClassUtil;
 32  
 import org.seasar.framework.util.Disposable;
 33  
 import org.seasar.framework.util.DisposableUtil;
 34  
 
 35  
 /**
 36  
  * コンバータファクトリの実装クラスです。
 37  
  * 
 38  
  * @author baba
 39  
  * @since 1.1.0
 40  
  */
 41  
 public class ConverterFactoryImpl implements ConverterFactory, DetectClassProcessor,
 42  
                 Disposable {
 43  
 
 44  
         /** インスタンスが初期化済みであることを示します。 */
 45  
         private boolean initialized;
 46  
 
 47  
         /** このファクトリを管理しているコンテナです。 */
 48  
         private S2Container container;
 49  
 
 50  
         /** 命名規約。 */
 51  
         private NamingConvention namingConvention;
 52  
 
 53  
         /** クラスパスを走査してクラスを検出するクラス。 */
 54  
         private ClassDetector classDetector;
 55  
 
 56  
         /** コンバータのセットです。 */
 57  82
         private Set<Converter> converters = new LinkedHashSet<Converter>();
 58  
 
 59  
         /** コンバータのキャッシュです。 */
 60  82
         private Map<String, Converter> converterCache = new HashMap<String, Converter>();
 61  
 
 62  
         /**
 63  
          * <code>ConverterFactoryImpl</code>のインスタンスを構築します。
 64  
          */
 65  82
         public ConverterFactoryImpl() {
 66  82
         }
 67  
 
 68  
         /**
 69  
          * コンテナを設定します。
 70  
          * 
 71  
          * @param container
 72  
          *            コンテナ
 73  
          */
 74  
         public void setContainer(final S2Container container) {
 75  82
                 this.container = container.getRoot();
 76  82
         }
 77  
 
 78  
         /**
 79  
          * 命名規約を設定します。
 80  
          * 
 81  
          * @param namingConvention
 82  
          *            命名規約
 83  
          */
 84  
         public void setNamingConvention(final NamingConvention namingConvention) {
 85  82
                 this.namingConvention = namingConvention;
 86  82
         }
 87  
 
 88  
         /**
 89  
          * クラスパスを走査してクラスを検出するクラスを設定します。
 90  
          * 
 91  
          * @param classDetector
 92  
          *            クラスパスを走査してクラスを設定します。
 93  
          */
 94  
         public void setClassDetector(final ClassDetector classDetector) {
 95  82
                 this.classDetector = classDetector;
 96  82
         }
 97  
 
 98  
         /**
 99  
          * インスタンスを初期化します。
 100  
          */
 101  
         public void initialize() {
 102  134
                 if (initialized) {
 103  118
                         return;
 104  
                 }
 105  16
                 classDetector.detect();
 106  
 
 107  288
                 for (final Converter converter : Converter[].class.cast(container
 108  
                                 .findAllComponents(Converter.class))) {
 109  272
                         converters.add(converter);
 110  
                 }
 111  16
                 DisposableUtil.add(this);
 112  16
                 initialized = true;
 113  16
         }
 114  
 
 115  
         /**
 116  
          * キャッシュ情報等を破棄し、インスタンスを未初期化状態に戻します。
 117  
          * 
 118  
          */
 119  
         public void dispose() {
 120  16
                 converters.clear();
 121  16
                 converterCache.clear();
 122  16
                 initialized = false;
 123  16
         }
 124  
 
 125  
         /**
 126  
          * {@inheritDoc}
 127  
          */
 128  
         public Converter getConverter(final Class<?> parameterType,
 129  
                         final Class<?> objectType) {
 130  134
                 initialize();
 131  134
                 final Class<?> destType = ClassUtil
 132  
                                 .getWrapperClassIfPrimitive(objectType);
 133  134
                 final String cacheKey = cacheKey(parameterType, destType);
 134  134
                 final Converter converter = converterCache.get(cacheKey);
 135  134
                 if (converter != null) {
 136  66
                         return converter;
 137  
                 }
 138  68
                 return detectConverter(parameterType, destType);
 139  
         }
 140  
 
 141  
         private Converter detectConverter(final Class<?> parameterType,
 142  
                         final Class<?> objectType) {
 143  68
                 final Converter converter = getDistanceTable(parameterType, objectType);
 144  68
                 final String cacheKey = cacheKey(parameterType, objectType);
 145  68
                 converterCache.put(cacheKey, converter);
 146  68
                 return converter;
 147  
         }
 148  
 
 149  
         private static String cacheKey(final Class<?> parameterType,
 150  
                         final Class<?> objectType) {
 151  202
                 if (parameterType == null) {
 152  15
                         return objectType.getName();
 153  
                 }
 154  187
                 return parameterType.getName() + objectType.getName();
 155  
         }
 156  
 
 157  
         private Converter getDistanceTable(final Class<?> parameterType,
 158  
                         final Class<?> objectType) {
 159  68
                 final Map<Integer, Converter> distanceTable = new TreeMap<Integer, Converter>();
 160  68
                 for (final Converter converter : converters) {
 161  1156
                         if (!converter.canConvert(parameterType, objectType)) {
 162  1130
                                 continue;
 163  
                         }
 164  26
                         final int distance = getDistance(converter.getObjectType(),
 165  
                                         objectType);
 166  26
                         distanceTable.put(distance, converter);
 167  26
                 }
 168  68
                 if (distanceTable.isEmpty()) {
 169  45
                         return null;
 170  
                 }
 171  23
                 return distanceTable.values().iterator().next();
 172  
         }
 173  
 
 174  
         private int getDistance(final Class<?> assigner, final Class<?> assignee) {
 175  26
                 return getDistance(assigner, assignee, 0);
 176  
         }
 177  
 
 178  
         private int getDistance(final Class<?> assigner, final Class<?> assignee,
 179  
                         final int distance) {
 180  29
                 if (assignee.equals(assigner)) {
 181  22
                         return distance;
 182  
                 }
 183  7
                 if (Enum.class.equals(assigner) && assignee.isEnum()) {
 184  1
                         return distance + 5;
 185  
                 }
 186  6
                 if (isImplements(assigner, assignee)) {
 187  0
                         return distance + 5;
 188  
                 }
 189  
 
 190  6
                 final Class<?> superClass = assigner.getSuperclass();
 191  6
                 if (superClass == null) {
 192  3
                         return distance + 10;
 193  
                 }
 194  3
                 return getDistance(superClass, assignee, distance + 10);
 195  
         }
 196  
 
 197  
         private boolean isImplements(final Class<?> assigner,
 198  
                         final Class<?> assignee) {
 199  6
                 return !assigner.isInterface() && assignee.isInterface()
 200  
                                 && assignee.isAssignableFrom(assigner);
 201  
         }
 202  
 
 203  
         /**
 204  
          * {@inheritDoc}
 205  
          * <p>
 206  
          * 指定されたパッケージ名、クラス名から導出されるクラスがコンバータだった場合はファクトリにコンバータを登録します。
 207  
          * </p>
 208  
          */
 209  
         public void processClass(final String packageName,
 210  
                         final String shortClassName) {
 211  5814
                 if (shortClassName.indexOf('$') != -1) {
 212  1887
                         return;
 213  
                 }
 214  3927
                 final String className = ClassUtil.concatName(packageName,
 215  
                                 shortClassName);
 216  3927
                 if (!namingConvention.isTargetClassName(className)) {
 217  0
                         return;
 218  
                 }
 219  3927
                 if (!className.endsWith(namingConvention.getConverterSuffix())) {
 220  3570
                         return;
 221  
                 }
 222  357
                 final Class<?> clazz = ClassUtil.forName(className);
 223  357
                 if (namingConvention.isSkipClass(clazz)) {
 224  0
                         return;
 225  
                 }
 226  357
                 if ((clazz.getModifiers() & Modifier.ABSTRACT) != 0) {
 227  68
                         return;
 228  
                 }
 229  289
                 if (!Converter.class.isAssignableFrom(clazz)) {
 230  0
                         return;
 231  
                 }
 232  289
                 final Converter converter = Converter.class.cast(container
 233  
                                 .getComponent(clazz));
 234  289
                 converters.add(converter);
 235  289
         }
 236  
 
 237  
 }