    001    // Copyright 2011 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.tapestry5.internal.plastic;
    016
    017    import java.lang.reflect.Constructor;
    018    import java.lang.reflect.Modifier;
    019    import java.util.HashMap;
    020    import java.util.Map;
    021
    022    import org.apache.tapestry5.plastic.ClassInstantiator;
    023    import org.apache.tapestry5.plastic.InstanceContext;
    024
    025    @SuppressWarnings("all")
    026    public class ClassInstantiatorImpl<T> implements ClassInstantiator<T>, InstanceContext
    027    {
    028        private final Class clazz;
    029
    030        private final Constructor<T> ctor;
    031
    032        private final StaticContext staticContext;
    033
    034        private final ClassInstantiatorImpl<T> parent;
    035
    036        private final Class withType;
    037
    038        private final Object withValue;
    039
    040        ClassInstantiatorImpl(Class<T> clazz, Constructor ctor, StaticContext staticContext)
    041        {
    042            this(clazz, ctor, staticContext, null, null, null);
    043        }
    044
    045        private <W> ClassInstantiatorImpl(Class clazz, Constructor ctor, StaticContext staticContext,
    046                ClassInstantiatorImpl<T> parent, Class<W> withType, W withValue)
    047        {
    048            this.clazz = clazz;
    049            this.ctor = ctor;
    050            this.staticContext = staticContext;
    051            this.parent = parent;
    052            this.withType = withType;
    053            this.withValue = withValue;
    054        }
    055
    056        public <V> ClassInstantiator<T> with(Class<V> valueType, V instanceContextValue)
    057        {
    058            assert valueType != null;
    059            assert instanceContextValue != null;
    060
    061            Object existing = find(valueType);
    062
    063            if (existing != null)
    064                throw new IllegalStateException(String.format(
    065                        "An instance context value of type %s has already been added.", valueType.getName()));
    066
    067            // A little optimization: the new CI doesn't need this CI as a parent, if this CI has no type/value pair
    068
    069            return new ClassInstantiatorImpl(clazz, ctor, staticContext, withType == null ? null : this, valueType,
    070                    instanceContextValue);
    071        }
    072
    073        public <V> V get(Class<V> valueType)
    074        {
    075            V result = find(valueType);
    076
    077            if (result == null)
    078                throw new IllegalArgumentException(String.format(
    079                        "Instance context for class %s does not contain a value for type %s.", clazz.getName(), valueType));
    080
    081            return result;
    082        }
    083
    084        private <V> V find(Class<V> valueType)
    085        {
    086            ClassInstantiatorImpl cursor = this;
    087
    088            while (cursor != null)
    089            {
    090                if (cursor.withType == valueType) { return valueType.cast(cursor.withValue); }
    091
    092                cursor = cursor.parent;
    093            }
    094
    095            return null;
    096        }
    097
    098        public T newInstance()
    099        {
    100            if (Modifier.isAbstract(clazz.getModifiers()))
    101                throw new IllegalStateException(String.format("Class %s is abstract and can not be instantiated.",
    102                        clazz.getName()));
    103
    104            try
    105            {
    106                return ctor.newInstance(staticContext, this);
    107            }
    108            catch (Throwable ex)
    109            {
    110                throw new RuntimeException(String.format("Unable to instantiate instance of transformed class %s: %s",
    111                        clazz.getName(), PlasticInternalUtils.toMessage(ex)), ex);
    112            }
    113        }
    114
    115        public Class<T> getInstanceType()
    116        {
    117            return clazz;
    118        }
    119
    120        public String toString()
    121        {
    122            return String.format("ClassInstantiator[%s]", clazz.getName());
    123        }
    124    }