/* * ============================================================================ * GNU Lesser General Public License * ============================================================================ * * Beanlet - JSE Application Container. * Copyright (C) 2006 Leon van Zantvoort * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. * * Leon van Zantvoort * 243 Acalanes Drive #11 * Sunnyvale, CA 94086 * USA * * zantvoort@users.sourceforge.net * http://beanlet.org */ package org.beanlet.impl; import org.beanlet.common.ParameterizedTypeAwareDependencyInjectionFactory; import java.lang.reflect.Array; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.Set; import java.util.SortedMap; import java.util.SortedSet; import java.util.TreeMap; import java.util.TreeSet; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import org.beanlet.BeanletApplicationContext; import org.beanlet.BeanletTypeMismatchException; import org.beanlet.BeanletWiringException; import org.jargo.ComponentContext; import org.beanlet.Inject; import org.beanlet.CollectionValue; import org.beanlet.Entry; import org.beanlet.IgnoreDependency; import org.beanlet.MapValue; import org.beanlet.Value; import org.beanlet.annotation.AnnotationDomain; import org.beanlet.annotation.Element; import org.beanlet.annotation.ElementAnnotation; import org.beanlet.plugin.BeanletConfiguration; import org.beanlet.plugin.Injectant; import org.beanlet.common.InjectantImpl; /** * * @author Leon van Zantvoort */ public final class ValueDependencyInjectionFactoryImpl extends ParameterizedTypeAwareDependencyInjectionFactory { private final BeanletConfiguration<?> configuration; public ValueDependencyInjectionFactoryImpl( BeanletConfiguration<?> configuration) { super(configuration); this.configuration = configuration; } public boolean isSupported(ElementAnnotation<? extends Element, Inject> ea) { Inject inject = ea.getAnnotation(); Value value = inject.value(); CollectionValue collection = inject.collection(); MapValue map = inject.map(); Class<?> target = getType(ea.getElement()); final boolean supported; if (!inject.ref().equals("") || value.nill() || !value.value().equals("") || value.empty() || !value.ref().equals("")) { supported = true; Class<?> selectedType = getType(inject); if (inject.ref().equals("")) { if (target.isPrimitive() && value.nill()) { throw new BeanletWiringException( configuration.getComponentName(), ea.getElement().getMember(), "Primitive cannot be injected with null value."); } if (!Object.class.equals(value.type())) { selectedType = value.type(); } } if (!Object.class.equals(selectedType)) { final Class<?> objectType; if (target.isPrimitive()) { if (Boolean.TYPE.equals(target)) { objectType = Boolean.class; } else if (Byte.TYPE.equals(target)) { objectType = Byte.class; } else if (Short.TYPE.equals(target)) { objectType = Short.class; } else if (Integer.TYPE.equals(target)) { objectType = Integer.class; } else if (Long.TYPE.equals(target)) { objectType = Long.class; } else if (Float.TYPE.equals(target)) { objectType = Float.class; } else if (Double.TYPE.equals(target)) { objectType = Double.class; } else { objectType = target; } } else { objectType = target; } if (!objectType.isAssignableFrom(selectedType)) { throw new BeanletTypeMismatchException( configuration.getComponentName(), ea.getElement().getMember(), target, selectedType); } } } else if (collection.value().length > 0 || collection.empty()) { supported = true; if (!collection.type().equals(Collection.class)) { if (!target.isAssignableFrom(collection.type())) { throw new BeanletTypeMismatchException( configuration.getComponentName(), ea.getElement().getMember(), target, collection.type()); } } } else if (map.value().length > 0 || map.empty()) { supported = true; if (!map.type().equals(Map.class)) { if (!target.isAssignableFrom(map.type())) { throw new BeanletTypeMismatchException( configuration.getComponentName(), ea.getElement().getMember(), target, map.type()); } } } else { supported = false; } return supported; } public Set<String> getDependencies( ElementAnnotation<? extends Element, Inject> ea) { final Set<String> dependencies; AnnotationDomain domain = configuration.getAnnotationDomain(); if (domain.getDeclaration(IgnoreDependency.class). isAnnotationPresent(ea.getElement())) { dependencies = Collections.emptySet(); } else { Set<String> tmp = new HashSet<String>(); Inject inject = ea.getAnnotation(); if (!inject.ref().equals("")) { tmp.add(inject.ref()); } MapValue map = inject.map(); for (Entry entry : map.value()) { if (!entry.key().ref().equals("")) { tmp.add(entry.key().ref()); } if (!entry.value().ref().equals("")) { tmp.add(entry.value().ref()); } } CollectionValue collection = inject.collection(); for (Value value : collection.value()) { if (!value.ref().equals("")) { tmp.add(value.ref()); } } Value value = inject.value(); if (!value.ref().equals("")) { tmp.add(value.ref()); } dependencies = Collections.unmodifiableSet(tmp); } return dependencies; } private Class<?> getCollectionValueType(Element element) { return getTypeClass(getParameterizedType(element, 0, Collection.class)); } private Class<?> getMapKeyType(Element element) { return getTypeClass(getParameterizedType(element, 0, Map.class)); } private Class<?> getMapValueType(Element element) { return getTypeClass(getParameterizedType(element, 1, Map.class)); } @SuppressWarnings("unchecked") public Injectant<?> getInjectant( ElementAnnotation<? extends Element, Inject> ea, ComponentContext<?> ctx) { String name = configuration.getComponentName(); Inject inject = ea.getAnnotation(); Value value = inject.value(); if (!inject.ref().equals("")) { return new InjectantImpl<Object>( getValue(inject, getType(ea), ea.getElement()), false); } if (value.nill() || !value.ref().equals("") || !value.value().equals("") || value.empty()) { return new InjectantImpl<Object>( getValue(value, getType(ea), ea.getElement()), false); } try { CollectionValue collection = inject.collection(); if (collection.value().length > 0 || collection.empty()) { Class<?> type = getType(ea.getElement()); final Class<?> componentType; if (type.isArray()) { componentType = type.getComponentType(); } else { componentType = getCollectionValueType(ea.getElement()); } Collection c; if (collection.type().equals(Collection.class)) { try { try { Constructor constr = type.getConstructor(); c = (Collection) constr.newInstance(); } catch (NoSuchMethodException e) { if (type.isAssignableFrom(Collection.class)) { c = new ArrayList(); } else if (type.isAssignableFrom(List.class)) { c = new ArrayList(); } else if (type.isAssignableFrom(Set.class)) { c = new HashSet(); } else if (type.isAssignableFrom(SortedSet.class)) { c = new TreeSet(); } else { throw new BeanletWiringException(name, ea.getElement().getMember(), "Unable to select collection type for: '" + type.getName() + "'."); } } } catch (InvocationTargetException e) { throw new BeanletWiringException(name, ea.getElement().getMember(), e); } catch (InstantiationException e) { throw new BeanletWiringException(name, ea.getElement().getMember(), e); } catch (IllegalAccessException e) { throw new BeanletWiringException(name, ea.getElement().getMember(), e); } } else { if (collection.type().isInterface()) { throw new BeanletWiringException(name, ea.getElement().getMember(), "Type must not be " + "an interface: '" + collection.type().getName() + "'."); } c = (Collection) collection.type().newInstance(); } if (!collection.empty()) { Collection<Object> tmp = (Collection<Object>) Collections.checkedCollection(c, componentType); for (Value v : collection.value()) { Object o = getValue(v, componentType, ea.getElement()); tmp.add(o); } } if (type.isArray()) { Object[] a = (Object[]) Array.newInstance(componentType, c.size()); Collection<Object> tmp = (Collection<Object>) c; return new InjectantImpl<Object>(tmp.toArray(a), false); } else { try { if (collection.synced()) { if (type.isAssignableFrom(Collection.class)) { c = Collections.synchronizedCollection(c); } else if (type.isAssignableFrom(List.class)) { c = Collections.synchronizedList((List) c); } else if (type.isAssignableFrom(Set.class)) { c = Collections.synchronizedSet((Set) c); } else if (type.isAssignableFrom(SortedSet.class)) { c = Collections.synchronizedSortedSet((SortedSet) c); } else { throw new BeanletWiringException(name, ea.getElement().getMember(), "Synchronized collection " + "cannot be injected for type: '" + type.getName() + "'."); } } if (collection.unmodifiable()) { if (type.isAssignableFrom(Collection.class)) { c = Collections.unmodifiableCollection(c); } else if (type.isAssignableFrom(List.class)) { c = Collections.unmodifiableList((List) c); } else if (type.isAssignableFrom(Set.class)) { c = Collections.unmodifiableSet((Set) c); } else if (type.isAssignableFrom(SortedSet.class)) { c = Collections.unmodifiableSortedSet((SortedSet) c); } else { throw new BeanletWiringException(name, ea.getElement().getMember(), "Unmodifiable collection " + "cannot be injected for type: '" + type.getName() + "'."); } } } catch (ClassCastException e) { // No need to process this exception. The // ValidatingDependencyInjection class will detect that // this is a false injection. It is therefore safe to // return a collection that is not // synchronized / unmodifiable. } return new InjectantImpl<Object>(c, false); } } MapValue map = inject.map(); if (map.value().length > 0 || map.empty()) { Class<?> type = getType(ea.getElement()); Map m; if (map.type().equals(Map.class)) { try { try { Constructor constr = type.getConstructor(); m = (Map) constr.newInstance(); } catch (NoSuchMethodException e) { if (type.isAssignableFrom(Map.class)) { m = new HashMap(); } else if (type.isAssignableFrom(SortedMap.class)) { m = new TreeMap(); } else if (type.isAssignableFrom(ConcurrentMap.class)) { m = new ConcurrentHashMap(); } else { throw new BeanletWiringException(name, ea.getElement().getMember(), "Unable to select collection type for: '" + type.getName() + "'."); } } } catch (InvocationTargetException e) { throw new BeanletWiringException(name, ea.getElement().getMember(), e); } catch (InstantiationException e) { throw new BeanletWiringException(name, ea.getElement().getMember(), e); } catch (IllegalAccessException e) { throw new BeanletWiringException(name, ea.getElement().getMember(), e); } } else { if (map.type().isInterface()) { throw new BeanletWiringException(name, ea.getElement().getMember(), "Type must not be " + "an interface: '" + collection.type().getName() + "'."); } m = (Map) map.type().newInstance(); } if (!map.empty()) { Class<?> keyType = getMapKeyType(ea.getElement()); Class<?> valueType = getMapValueType(ea.getElement()); if (m instanceof Properties) { // Special case. if (keyType.equals(Object.class)) { keyType = String.class; } if (valueType.equals(Object.class)) { valueType = String.class; } } Map<Object, Object> tmp = (Map<Object, Object>) Collections.checkedMap(m, keyType, valueType); for (Entry entry : map.value()) { tmp.put(getValue(entry.key(), keyType, ea.getElement()), getValue(entry.value(), valueType, ea.getElement())); } } try { if (map.synced()) { if (type.isAssignableFrom(Map.class)) { m = Collections.synchronizedSortedMap((SortedMap) m); } else if (type.isAssignableFrom(SortedMap.class)) { m = Collections.synchronizedMap(m); } else { throw new BeanletWiringException(name, ea.getElement().getMember(), "Synchronized map " + "cannot be injected for type: '" + type.getName() + "'."); } } if (map.unmodifiable()) { if (type.isAssignableFrom(Map.class)) { m = Collections.unmodifiableSortedMap((SortedMap) m); } else if (type.isAssignableFrom(SortedMap.class)) { m = Collections.unmodifiableMap(m); } else { throw new BeanletWiringException(name, ea.getElement().getMember(), "Unmodifiable map " + "cannot be injected for type: '" + type.getName() + "'."); } } } catch (ClassCastException e) { // No need to process this exception. The // ValidatingDependencyInjection class will detect that // this is a false injection. It is therefor safe to // return a collection that is not // synchronized / unmodifiable. } return new InjectantImpl<Object>(m, false); } else { assert false; return new InjectantImpl<Object>(null, false); } } catch (ClassCastException e) { throw new BeanletWiringException(name, ea.getElement().getMember(), "Attempt to insert an incompatible value for parameterized " + "member.", e); } catch (IllegalAccessException e) { throw new BeanletWiringException(name, ea.getElement().getMember(), e); } catch (InstantiationException e) { throw new BeanletWiringException(name, ea.getElement().getMember(), e); } } private Object getValue(Inject inject, Class<?> type, Element element) { return getValue("", Object.class, false, inject.ref(), type, element); } private Object getValue(Value value, Class<?> type, Element element) { return getValue(value.value(), value.type(), value.nill(), value.ref(), type, element); } private Object getValue(String value, Class<?> valueType, boolean nill, String ref, Class<?> type, Element element) { final Object v; if (nill) { v = null; } else if (!ref.equals("")) { v = BeanletApplicationContext.instance().getBeanlet(ref); } else { Class<?> cls = valueType.equals(Object.class) ? type : valueType; v = getValue(value, cls, element); } return v; } private Object getValue(String value, Class<?> type, Element element) { assert value != null; assert type != null; final Object o; try { try { if (type.isPrimitive()) { if (Character.TYPE.equals(type)) { if (value.length() != 1) { throw new BeanletWiringException( configuration.getComponentName(), element.getMember(), "Invalid content for attribute type Character: " + value + "."); } else { o = Character.valueOf(value.charAt(0)); } } else if (Void.TYPE.equals(type)) { throw new BeanletWiringException( configuration.getComponentName(), element.getMember(), "Invalid attribute type: void."); } else { final Class<?> objectType; if (Boolean.TYPE.equals(type)) { objectType = Boolean.class; } else if (Byte.TYPE.equals(type)) { objectType = Byte.class; } else if (Short.TYPE.equals(type)) { objectType = Short.class; } else if (Integer.TYPE.equals(type)) { objectType = Integer.class; } else if (Long.TYPE.equals(type)) { objectType = Long.class; } else if (Float.TYPE.equals(type)) { objectType = Float.class; } else if (Double.TYPE.equals(type)) { objectType = Double.class; } else { objectType = null; assert false : type; } o = objectType.getMethod("valueOf", String.class). invoke(null, value); } } else if (type.equals(Class.class)) { o = configuration.getComponentUnit().getClassLoader().loadClass(value); } else { if (type.isInterface()) { throw new BeanletWiringException( configuration.getComponentName(), element.getMember(), "Failed to create instance for value \"" + value + "\". Interfaces " + "cannot be instantiated: " + type.getName() + "."); } Object tmp = null; try { tmp = type.getConstructor(String.class). newInstance(value); } catch (NoSuchMethodException e) { if (!value.equals("")) { throw new BeanletWiringException( configuration.getComponentName(), element.getMember(), "Failed to create instance for value \"" + value + "\". Class " + type.getName() + " does not declare a String constructor."); } tmp = type.newInstance(); } o = tmp; } } catch (InvocationTargetException e) { throw e.getTargetException(); } } catch (BeanletWiringException e) { throw e; } catch (Error e) { throw e; } catch (Throwable t) { // Errors are excluded. throw new BeanletWiringException(configuration.getComponentName(), element.getMember(), t); } return o; } }