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
015package org.apache.tapestry5.internal.plastic;
016
017import java.lang.reflect.Constructor;
018import java.lang.reflect.Modifier;
019import java.util.HashMap;
020import java.util.Map;
021
022import org.apache.tapestry5.plastic.ClassInstantiator;
023import org.apache.tapestry5.plastic.InstanceContext;
024
025@SuppressWarnings("all")
026public 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}