001: // Copyright 2007 The Apache Software Foundation
002: //
003: // Licensed under the Apache License, Version 2.0 (the "License");
004: // you may not use this file except in compliance with the License.
005: // You may obtain a copy of the License at
006: //
007: // http://www.apache.org/licenses/LICENSE-2.0
008: //
009: // Unless required by applicable law or agreed to in writing, software
010: // distributed under the License is distributed on an "AS IS" BASIS,
011: // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
012: // See the License for the specific language governing permissions and
013: // limitations under the License.
014:
015: package org.apache.tapestry.ioc.internal;
016:
017: import static org.apache.tapestry.ioc.internal.ConfigurationType.MAPPED;
018: import static org.apache.tapestry.ioc.internal.ConfigurationType.ORDERED;
019: import static org.apache.tapestry.ioc.internal.ConfigurationType.UNORDERED;
020: import static org.apache.tapestry.ioc.internal.util.CollectionFactory.newMap;
021:
022: import java.lang.reflect.ParameterizedType;
023: import java.lang.reflect.Type;
024: import java.util.Collection;
025: import java.util.List;
026: import java.util.Map;
027:
028: import org.apache.commons.logging.Log;
029: import org.apache.tapestry.ioc.ObjectCreator;
030: import org.apache.tapestry.ioc.ServiceBuilderResources;
031: import org.apache.tapestry.ioc.ServiceResources;
032:
033: /**
034: * Abstract implementation of {@link ObjectCreator} geared towards the creation of the core service
035: * implementation, either by invoking a service builder method on a module, or by invoking a
036: * constructor.
037: */
038: public abstract class AbstractServiceCreator implements ObjectCreator {
039: protected final String _serviceId;
040:
041: private final Map<Class, Object> _parameterDefaults = newMap();
042:
043: protected final ServiceBuilderResources _resources;
044:
045: protected final Log _log;
046:
047: private final static Map<Class, ConfigurationType> PARAMETER_TYPE_TO_CONFIGURATION_TYPE = newMap();
048:
049: protected final String _creatorDescription;
050:
051: static {
052: PARAMETER_TYPE_TO_CONFIGURATION_TYPE.put(Collection.class,
053: UNORDERED);
054: PARAMETER_TYPE_TO_CONFIGURATION_TYPE.put(List.class, ORDERED);
055: PARAMETER_TYPE_TO_CONFIGURATION_TYPE.put(Map.class, MAPPED);
056: }
057:
058: public AbstractServiceCreator(ServiceBuilderResources resources,
059: String creatorDescription) {
060: _serviceId = resources.getServiceId();
061: _resources = resources;
062: _creatorDescription = creatorDescription;
063: _log = resources.getServiceLog();
064:
065: _parameterDefaults.put(String.class, _serviceId);
066: _parameterDefaults.put(ServiceResources.class, resources);
067: _parameterDefaults.put(Log.class, _log);
068: _parameterDefaults.put(Class.class, resources
069: .getServiceInterface());
070: }
071:
072: /**
073: * Returns a map (based on _parameterDefaults) that includes (possibly) an additional mapping
074: * containing the collected configuration data. This involves scanning the parameters and
075: * generic types.
076: */
077: protected final Map<Class, Object> getParameterDefaultsWithConfiguration(
078: Class[] parameterTypes, Type[] genericParameterTypes) {
079: Map<Class, Object> result = newMap(_parameterDefaults);
080: ConfigurationType type = null;
081:
082: for (int i = 0; i < parameterTypes.length; i++) {
083: Class parameterType = parameterTypes[i];
084:
085: ConfigurationType this Type = PARAMETER_TYPE_TO_CONFIGURATION_TYPE
086: .get(parameterType);
087:
088: if (this Type == null)
089: continue;
090:
091: if (type != null) {
092: _log
093: .warn(IOCMessages
094: .tooManyConfigurationParameters(_creatorDescription));
095: break;
096: }
097:
098: // Remember that we've seen a configuration parameter, in case there
099: // is another.
100:
101: type = this Type;
102:
103: Type genericType = genericParameterTypes[i];
104:
105: switch (type) {
106:
107: case UNORDERED:
108:
109: addUnorderedConfigurationParameter(result, genericType);
110:
111: break;
112:
113: case ORDERED:
114:
115: addOrderedConfigurationParameter(result, genericType);
116:
117: break;
118:
119: case MAPPED:
120:
121: addMappedConfigurationParameter(result, genericType);
122:
123: break;
124: }
125: }
126:
127: return result;
128: }
129:
130: @SuppressWarnings("unchecked")
131: private final void addOrderedConfigurationParameter(
132: Map<Class, Object> parameterDefaults, Type genericType) {
133: Class valueType = findParameterizedTypeFromGenericType(genericType);
134: List configuration = _resources
135: .getOrderedConfiguration(valueType);
136:
137: parameterDefaults.put(List.class, configuration);
138: }
139:
140: @SuppressWarnings("unchecked")
141: private void addUnorderedConfigurationParameter(
142: Map<Class, Object> parameterDefaults, Type genericType) {
143: Class valueType = findParameterizedTypeFromGenericType(genericType);
144: Collection configuration = _resources
145: .getUnorderedConfiguration(valueType);
146:
147: parameterDefaults.put(Collection.class, configuration);
148: }
149:
150: @SuppressWarnings("unchecked")
151: private void addMappedConfigurationParameter(
152: Map<Class, Object> parameterDefaults, Type genericType) {
153: Class keyType = findParameterizedTypeFromGenericType(
154: genericType, 0);
155: Class valueType = findParameterizedTypeFromGenericType(
156: genericType, 1);
157:
158: if (keyType == null || valueType == null)
159: throw new IllegalArgumentException(IOCMessages
160: .genericTypeNotSupported(genericType));
161:
162: Map configuration = _resources.getMappedConfiguration(keyType,
163: valueType);
164:
165: parameterDefaults.put(Map.class, configuration);
166: }
167:
168: /**
169: * Extracts from a generic type the underlying parameterized type. I.e., for List<Runnable>,
170: * will return Runnable. This is limited to simple parameterized types, not the more complex
171: * cases involving wildcards and upper/lower boundaries.
172: *
173: * @param type
174: * the genetic type of the parameter, i.e., List<Runnable>
175: * @return the parameterize type (i.e. Runnable.class if type represents List<Runnable>).
176: */
177:
178: // package private for testing
179: static Class findParameterizedTypeFromGenericType(Type type) {
180: Class result = findParameterizedTypeFromGenericType(type, 0);
181:
182: if (result == null)
183: throw new IllegalArgumentException(IOCMessages
184: .genericTypeNotSupported(type));
185:
186: return result;
187: }
188:
189: /**
190: * "Sniffs" a generic type to find the underlying parameterized type. If the Type is a class,
191: * then Object.class is returned. Otherwise, the type must be a ParameterizedType. We check to
192: * make sure it has the correct number of a actual types (1 for a Collection or List, 2 for a
193: * Map). The actual types must be classes (wildcards just aren't supported)
194: *
195: * @param type
196: * a Class or ParameterizedType to inspect
197: * @param typeIndex
198: * the index within the ParameterizedType to extract
199: * @return the actual type, or Object.class if the input type is not generic, or null if any
200: * other pre-condition is not met
201: */
202: private static Class findParameterizedTypeFromGenericType(
203: Type type, int typeIndex) {
204: // For a raw Class type, it means the parameter is not parameterized (i.e. Collection, not
205: // Collection<Foo>), so we can return Object.class to allow no restriction.
206:
207: if (type instanceof Class)
208: return Object.class;
209:
210: if (!(type instanceof ParameterizedType))
211: return null;
212:
213: ParameterizedType pt = (ParameterizedType) type;
214:
215: Type[] types = pt.getActualTypeArguments();
216:
217: Type actualType = types[typeIndex];
218:
219: return actualType instanceof Class ? (Class) actualType : null;
220: }
221: }
|