    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}