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 }