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