blob: 2d72e7b7d5298acdc844fc3aa31501fb77d4f55f [file] [log] [blame]
// Copyright 2007, 2008 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 org.apache.tapestry5.ioc.internal;
import org.apache.tapestry5.ioc.ObjectCreator;
import org.apache.tapestry5.ioc.ObjectLocator;
import org.apache.tapestry5.ioc.ServiceBuilderResources;
import org.apache.tapestry5.ioc.ServiceResources;
import static org.apache.tapestry5.ioc.internal.ConfigurationType.*;
import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
import org.slf4j.Logger;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Collection;
import java.util.List;
import java.util.Map;
/**
* Abstract implementation of {@link ObjectCreator} geared towards the creation of the core service implementation,
* either by invoking a service builder method on a module, or by invoking a constructor.
*/
public abstract class AbstractServiceCreator implements ObjectCreator
{
protected final String serviceId;
private final Map<Class, Object> parameterDefaults = CollectionFactory.newMap();
protected final ServiceBuilderResources resources;
protected final Logger logger;
private final static Map<Class, ConfigurationType> PARAMETER_TYPE_TO_CONFIGURATION_TYPE = CollectionFactory.newMap();
protected final String creatorDescription;
static
{
PARAMETER_TYPE_TO_CONFIGURATION_TYPE.put(Collection.class, UNORDERED);
PARAMETER_TYPE_TO_CONFIGURATION_TYPE.put(List.class, ORDERED);
PARAMETER_TYPE_TO_CONFIGURATION_TYPE.put(Map.class, MAPPED);
}
public AbstractServiceCreator(ServiceBuilderResources resources, String creatorDescription)
{
serviceId = resources.getServiceId();
this.resources = resources;
this.creatorDescription = creatorDescription;
logger = resources.getLogger();
parameterDefaults.put(String.class, serviceId);
parameterDefaults.put(ObjectLocator.class, resources);
parameterDefaults.put(ServiceResources.class, resources);
parameterDefaults.put(Logger.class, logger);
parameterDefaults.put(Class.class, resources.getServiceInterface());
}
/**
* Returns a map (based on _parameterDefaults) that includes (possibly) an additional mapping containing the
* collected configuration data. This involves scanning the parameters and generic types.
*/
protected final Map<Class, Object> getParameterDefaultsWithConfiguration(Class[] parameterTypes,
Type[] genericParameterTypes)
{
Map<Class, Object> result = CollectionFactory.newMap(parameterDefaults);
ConfigurationType type = null;
for (int i = 0; i < parameterTypes.length; i++)
{
Class parameterType = parameterTypes[i];
ConfigurationType thisType = PARAMETER_TYPE_TO_CONFIGURATION_TYPE.get(parameterType);
if (thisType == null) continue;
if (type != null)
{
logger.warn(IOCMessages.tooManyConfigurationParameters(creatorDescription));
break;
}
// Remember that we've seen a configuration parameter, in case there
// is another.
type = thisType;
Type genericType = genericParameterTypes[i];
switch (type)
{
case UNORDERED:
addUnorderedConfigurationParameter(result, genericType);
break;
case ORDERED:
addOrderedConfigurationParameter(result, genericType);
break;
case MAPPED:
addMappedConfigurationParameter(result, genericType);
break;
}
}
return result;
}
@SuppressWarnings("unchecked")
private void addOrderedConfigurationParameter(Map<Class, Object> parameterDefaults, Type genericType)
{
Class valueType = findParameterizedTypeFromGenericType(genericType);
List configuration = resources.getOrderedConfiguration(valueType);
parameterDefaults.put(List.class, configuration);
}
@SuppressWarnings("unchecked")
private void addUnorderedConfigurationParameter(Map<Class, Object> parameterDefaults, Type genericType)
{
Class valueType = findParameterizedTypeFromGenericType(genericType);
Collection configuration = resources.getUnorderedConfiguration(valueType);
parameterDefaults.put(Collection.class, configuration);
}
@SuppressWarnings("unchecked")
private void addMappedConfigurationParameter(Map<Class, Object> parameterDefaults, Type genericType)
{
Class keyType = findParameterizedTypeFromGenericType(genericType, 0);
Class valueType = findParameterizedTypeFromGenericType(genericType, 1);
if (keyType == null || valueType == null)
throw new IllegalArgumentException(IOCMessages.genericTypeNotSupported(genericType));
Map configuration = resources.getMappedConfiguration(keyType, valueType);
parameterDefaults.put(Map.class, configuration);
}
/**
* Extracts from a generic type the underlying parameterized type. I.e., for List<Runnable>, will return Runnable.
* This is limited to simple parameterized types, not the more complex cases involving wildcards and upper/lower
* boundaries.
*
* @param type the genetic type of the parameter, i.e., List<Runnable>
* @return the parameterize type (i.e. Runnable.class if type represents List<Runnable>).
*/
// package private for testing
static Class findParameterizedTypeFromGenericType(Type type)
{
Class result = findParameterizedTypeFromGenericType(type, 0);
if (result == null) throw new IllegalArgumentException(IOCMessages.genericTypeNotSupported(type));
return result;
}
/**
* "Sniffs" a generic type to find the underlying parameterized type. If the Type is a class, then Object.class is
* returned. Otherwise, the type must be a ParameterizedType. We check to make sure it has the correct number of a
* actual types (1 for a Collection or List, 2 for a Map). The actual types must be classes (wildcards just aren't
* supported)
*
* @param type a Class or ParameterizedType to inspect
* @param typeIndex the index within the ParameterizedType to extract
* @return the actual type, or Object.class if the input type is not generic, or null if any other pre-condition is
* not met
*/
private static Class findParameterizedTypeFromGenericType(Type type, int typeIndex)
{
// For a raw Class type, it means the parameter is not parameterized (i.e. Collection, not
// Collection<Foo>), so we can return Object.class to allow no restriction.
if (type instanceof Class) return Object.class;
if (!(type instanceof ParameterizedType)) return null;
ParameterizedType pt = (ParameterizedType) type;
Type[] types = pt.getActualTypeArguments();
Type actualType = types[typeIndex];
return actualType instanceof Class ? (Class) actualType : null;
}
}