001: /*
002: * Copyright 2006 The Apache Software Foundation
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016: package com.ibatis.sqlmap.engine.mapping.result;
017:
018: import java.util.ArrayList;
019: import java.util.Collection;
020: import java.util.HashSet;
021: import java.util.List;
022: import java.util.Set;
023:
024: import com.ibatis.common.resources.Resources;
025:
026: /**
027: * This class is used to create instances of result objects. It will
028: * use the configured ResultObjectFactory if there is one, otherwise
029: * it will use iBATIS' normal methods.
030: *
031: * Note that this class is somewhat tightly coupled with
032: * SqlExecuter - SqlExecute must call the setStatementId() and
033: * setResultObjectFactory() methods before executing a statement.
034: * This is a result of using a ThreadLocal to hold the current
035: * configuration for the statement under execution. Using a
036: * ThreadLocal is a solution for IBATIS-366. Without a ThreadLocal,
037: * the current factory and statement id would have to be added to
038: * many method signatures - often in inappropriate places.
039: *
040: * @author Jeff Butler
041: */
042: public class ResultObjectFactoryUtil {
043:
044: /**
045: * Use a ThreadLocal to hold the current statementId and
046: * factory. This is much easier than passing these
047: * items all over the place, and it has no measurable impact on
048: * performance (I did a test with 100000 result rows and found
049: * no impact - Jeff Butler).
050: */
051: private static ThreadLocal factorySettings = new ThreadLocal();
052:
053: /**
054: * Utility class - no instances
055: */
056: private ResultObjectFactoryUtil() {
057: super ();
058: }
059:
060: /**
061: * Algorithm:
062: *
063: * <ul>
064: * <li>If factory is null, then create object internally()</li>
065: * <li>Otherwise try to create object through factory</li>
066: * <li>If null returned from factory, then create object internally</li>
067: * </ul>
068: *
069: * This allows the factory to selectively create objects, also allows for
070: * the common possibility that a factory is not configured.
071: *
072: * @param factory
073: * the factory to use. May be null!
074: * @param statementId
075: * the ID of the statement that generated the call to this method
076: * @param clazz
077: * the type of object to create
078: * @return a new instance of the specified class. The instance must
079: * be castable to the specified class.
080: * @throws InstantiationException
081: * if the instance cannot be created. If you throw this Exception,
082: * iBATIS will throw a runtime exception in response and will end.
083: * @throws IllegalAccessException
084: * if the constructor cannot be accessed. If you throw this
085: * Exception, iBATIS will throw a runtime exception in response and
086: * will end.
087: */
088: public static Object createObjectThroughFactory(Class clazz)
089: throws InstantiationException, IllegalAccessException {
090:
091: FactorySettings fs = getFactorySettings();
092:
093: Object obj;
094: if (fs.getResultObjectFactory() == null) {
095: obj = createObjectInternally(clazz);
096: } else {
097: obj = fs.getResultObjectFactory().createInstance(
098: fs.getStatementId(), clazz);
099: if (obj == null) {
100: obj = createObjectInternally(clazz);
101: }
102: }
103:
104: return obj;
105: }
106:
107: /**
108: * This method creates object using iBATIS' normal mechanism. We
109: * translate List and Collection to ArrayList, and Set to HashSet
110: * because these interfaces may be requested in nested resultMaps
111: * and we want to supply default implementations.
112: *
113: * @param clazz
114: * @return
115: * @throws InstantiationException
116: * @throws IllegalAccessException
117: */
118: private static Object createObjectInternally(Class clazz)
119: throws InstantiationException, IllegalAccessException {
120: Class classToCreate;
121: if (clazz == List.class || clazz == Collection.class) {
122: classToCreate = ArrayList.class;
123: } else if (clazz == Set.class) {
124: classToCreate = HashSet.class;
125: } else {
126: classToCreate = clazz;
127: }
128:
129: Object obj = Resources.instantiate(classToCreate);
130: return obj;
131: }
132:
133: public static void setResultObjectFactory(
134: ResultObjectFactory resultObjectFactory) {
135: getFactorySettings()
136: .setResultObjectFactory(resultObjectFactory);
137: }
138:
139: public static void setStatementId(String statementId) {
140: getFactorySettings().setStatementId(statementId);
141: }
142:
143: private static FactorySettings getFactorySettings() {
144: FactorySettings fs = (FactorySettings) factorySettings.get();
145: if (fs == null) {
146: fs = new FactorySettings();
147: factorySettings.set(fs);
148: }
149:
150: return fs;
151: }
152:
153: private static class FactorySettings {
154: private ResultObjectFactory resultObjectFactory;
155: private String statementId;
156:
157: public ResultObjectFactory getResultObjectFactory() {
158: return resultObjectFactory;
159: }
160:
161: public void setResultObjectFactory(
162: ResultObjectFactory resultObjectFactory) {
163: this .resultObjectFactory = resultObjectFactory;
164: }
165:
166: public String getStatementId() {
167: return statementId;
168: }
169:
170: public void setStatementId(String statementId) {
171: this.statementId = statementId;
172: }
173: }
174: }
|