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    @Override
057    public <V> ClassInstantiator<T> with(Class<V> valueType, V instanceContextValue)
058    {
059        assert valueType != null;
060        assert instanceContextValue != null;
061
062        Object existing = find(valueType);
063
064        if (existing != null)
065            throw new IllegalStateException(String.format(
066                    "An instance context value of type %s has already been added.", valueType.getName()));
067
068        // A little optimization: the new CI doesn't need this CI as a parent, if this CI has no type/value pair
069        
070        return new ClassInstantiatorImpl(clazz, ctor, staticContext, withType == null ? null : this, valueType,
071                instanceContextValue);
072    }
073
074    @Override
075    public <V> V get(Class<V> valueType)
076    {
077        V result = find(valueType);
078
079        if (result == null)
080            throw new IllegalArgumentException(String.format(
081                    "Instance context for class %s does not contain a value for type %s.", clazz.getName(), valueType));
082
083        return result;
084    }
085
086    private <V> V find(Class<V> valueType)
087    {
088        ClassInstantiatorImpl cursor = this;
089
090        while (cursor != null)
091        {
092            if (cursor.withType == valueType) { return valueType.cast(cursor.withValue); }
093
094            cursor = cursor.parent;
095        }
096
097        return null;
098    }
099
100    @Override
101    public T newInstance()
102    {
103        if (Modifier.isAbstract(clazz.getModifiers()))
104            throw new IllegalStateException(String.format("Class %s is abstract and can not be instantiated.",
105                    clazz.getName()));
106
107        try
108        {
109            return ctor.newInstance(staticContext, this);
110        }
111        catch (Throwable ex)
112        {
113            throw new RuntimeException(String.format("Unable to instantiate instance of transformed class %s: %s",
114                    clazz.getName(), PlasticInternalUtils.toMessage(ex)), ex);
115        }
116    }
117
118    @Override
119    public Class<T> getInstanceType()
120    {
121        return clazz;
122    }
123
124    @Override
125    public String toString()
126    {
127        return String.format("ClassInstantiator[%s]", clazz.getName());
128    }
129}