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}