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    }