001 // Copyright 2006, 2007 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.ioc.internal.services;
016
017 import org.apache.tapestry5.ioc.services.*;
018
019 import static java.lang.String.format;
020 import java.lang.reflect.Constructor;
021 import java.lang.reflect.Modifier;
022
023 public class PropertyShadowBuilderImpl implements PropertyShadowBuilder
024 {
025 private final ClassFactory classFactory;
026
027 private final PropertyAccess propertyAccess;
028
029 public PropertyShadowBuilderImpl(@Builtin
030 ClassFactory classFactory,
031
032 PropertyAccess propertyAccess)
033 {
034 this.classFactory = classFactory;
035 this.propertyAccess = propertyAccess;
036 }
037
038 public <T> T build(Object source, String propertyName, Class<T> propertyType)
039 {
040 Class sourceClass = source.getClass();
041 PropertyAdapter adapter = propertyAccess.getAdapter(sourceClass).getPropertyAdapter(
042 propertyName);
043
044 // TODO: Perhaps extend ClassPropertyAdapter to do these checks?
045
046 if (adapter == null)
047 throw new RuntimeException(ServiceMessages.noSuchProperty(sourceClass, propertyName));
048
049 if (!adapter.isRead())
050 throw new RuntimeException(ServiceMessages.readNotSupported(source, propertyName));
051
052 if (!propertyType.isAssignableFrom(adapter.getType()))
053 throw new RuntimeException(ServiceMessages.propertyTypeMismatch(
054 propertyName,
055 sourceClass,
056 adapter.getType(),
057 propertyType));
058
059 ClassFab cf = classFactory.newClass(propertyType);
060
061 cf.addField("_source", Modifier.PRIVATE | Modifier.FINAL, sourceClass);
062
063 cf.addConstructor(new Class[]
064 { sourceClass }, null, "_source = $1;");
065
066 String body = format("return _source.%s();", adapter.getReadMethod().getName());
067
068 MethodSignature sig = new MethodSignature(propertyType, "_delegate", null, null);
069 cf.addMethod(Modifier.PRIVATE, sig, body);
070
071 String toString = format("<Shadow: property %s of %s>", propertyName, source);
072
073 cf.proxyMethodsToDelegate(propertyType, "_delegate()", toString);
074
075 Class shadowClass = cf.createClass();
076
077 try
078 {
079 Constructor cc = shadowClass.getConstructors()[0];
080
081 Object instance = cc.newInstance(source);
082
083 return propertyType.cast(instance);
084 }
085 catch (Exception ex)
086 {
087 // Should not be reachable
088 throw new RuntimeException(ex);
089 }
090
091 }
092
093 }