/* * Copyright 2006 The Apache Software Foundation * * 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 com.ibatis.sqlmap.engine.mapping.result; import com.ibatis.common.resources.*; import java.util.*; /** * This class is used to create instances of result objects. It will * use the configured ResultObjectFactory if there is one, otherwise * it will use iBATIS' normal methods. * * Note that this class is somewhat tightly coupled with * SqlExecuter - SqlExecute must call the setStatementId() and * setResultObjectFactory() methods before executing a statement. * This is a result of using a ThreadLocal to hold the current * configuration for the statement under execution. Using a * ThreadLocal is a solution for IBATIS-366. Without a ThreadLocal, * the current factory and statement id would have to be added to * many method signatures - often in inappropriate places. * * @author Jeff Butler */ public class ResultObjectFactoryUtil { /** * Use a ThreadLocal to hold the current statementId and * factory. This is much easier than passing these * items all over the place, and it has no measurable impact on * performance (I did a test with 100000 result rows and found * no impact - Jeff Butler). */ private static ThreadLocal factorySettings = new ThreadLocal(); /** * Utility class - no instances */ private ResultObjectFactoryUtil() { super(); } /** * Algorithm: * * <ul> * <li>If factory is null, then create object internally()</li> * <li>Otherwise try to create object through factory</li> * <li>If null returned from factory, then create object internally</li> * </ul> * * This allows the factory to selectively create objects, also allows for * the common possibility that a factory is not configured. * * @param factory * the factory to use. May be null! * @param statementId * the ID of the statement that generated the call to this method * @param clazz * the type of object to create * @return a new instance of the specified class. The instance must * be castable to the specified class. * @throws InstantiationException * if the instance cannot be created. If you throw this Exception, * iBATIS will throw a runtime exception in response and will end. * @throws IllegalAccessException * if the constructor cannot be accessed. If you throw this * Exception, iBATIS will throw a runtime exception in response and * will end. */ public static Object createObjectThroughFactory(Class clazz) throws InstantiationException, IllegalAccessException { FactorySettings fs = getFactorySettings(); Object obj; if (fs.getResultObjectFactory() == null) { obj = createObjectInternally(clazz); } else { obj = fs.getResultObjectFactory().createInstance(fs.getStatementId(), clazz); if (obj == null) { obj = createObjectInternally(clazz); } } return obj; } /** * This method creates object using iBATIS' normal mechanism. We * translate List and Collection to ArrayList, and Set to HashSet * because these interfaces may be requested in nested resultMaps * and we want to supply default implementations. * * @param clazz * @return * @throws InstantiationException * @throws IllegalAccessException */ private static Object createObjectInternally(Class clazz) throws InstantiationException, IllegalAccessException { Class classToCreate; if (clazz == List.class || clazz == Collection.class) { classToCreate = ArrayList.class; } else if (clazz == Set.class) { classToCreate = HashSet.class; } else { classToCreate = clazz; } Object obj = Resources.instantiate(classToCreate); return obj; } public static void setResultObjectFactory(ResultObjectFactory resultObjectFactory) { getFactorySettings().setResultObjectFactory(resultObjectFactory); } public static void setStatementId(String statementId) { getFactorySettings().setStatementId(statementId); } private static FactorySettings getFactorySettings() { FactorySettings fs = (FactorySettings) factorySettings.get(); if (fs == null) { fs = new FactorySettings(); factorySettings.set(fs); } return fs; } private static class FactorySettings { private ResultObjectFactory resultObjectFactory; private String statementId; public ResultObjectFactory getResultObjectFactory() { return resultObjectFactory; } public void setResultObjectFactory(ResultObjectFactory resultObjectFactory) { this.resultObjectFactory = resultObjectFactory; } public String getStatementId() { return statementId; } public void setStatementId(String statementId) { this.statementId = statementId; } } }