001 // Copyright 2006, 2008, 2009, 2010, 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.transform;
016
017 import org.apache.tapestry5.ComponentResources;
018 import org.apache.tapestry5.annotations.Mixin;
019 import org.apache.tapestry5.internal.InternalComponentResources;
020 import org.apache.tapestry5.ioc.internal.util.InternalUtils;
021 import org.apache.tapestry5.model.MutableComponentModel;
022 import org.apache.tapestry5.plastic.*;
023 import org.apache.tapestry5.services.ComponentClassResolver;
024 import org.apache.tapestry5.services.transform.ComponentClassTransformWorker2;
025 import org.apache.tapestry5.services.transform.TransformationSupport;
026
027 /**
028 * Supports the {@link org.apache.tapestry5.annotations.Mixin} annotation, which allows a mixin to
029 * be part of the
030 * implementation of a component. The annotation is applied to a field, which will become read-only,
031 * and contain a
032 * reference to the mixin instance.
033 */
034 public class MixinWorker implements ComponentClassTransformWorker2
035 {
036 private final ComponentClassResolver resolver;
037
038 public MixinWorker(final ComponentClassResolver resolver)
039 {
040 this.resolver = resolver;
041 }
042
043 public void transform(PlasticClass plasticClass, TransformationSupport support, MutableComponentModel model)
044 {
045 for (PlasticField field : plasticClass.getFieldsWithAnnotation(Mixin.class))
046 {
047 replaceFieldWithMixin(model, field);
048 }
049 }
050
051 private void replaceFieldWithMixin(MutableComponentModel model, PlasticField field)
052 {
053 Mixin annotation = field.getAnnotation(Mixin.class);
054
055 field.claim(annotation);
056
057 String mixinType = annotation.value();
058
059 String[] order = annotation.order();
060
061 String fieldType = field.getTypeName();
062
063 String mixinClassName = InternalUtils.isBlank(mixinType) ? fieldType : resolver
064 .resolveMixinTypeToClassName(mixinType);
065
066 model.addMixinClassName(mixinClassName, order);
067
068 replaceFieldAccessWithMixin(field, mixinClassName);
069 }
070
071 private void replaceFieldAccessWithMixin(PlasticField field, String mixinClassName)
072 {
073 ComputedValue<FieldConduit<Object>> provider = createMixinFieldProvider(field.getName(), mixinClassName);
074
075 field.setComputedConduit(provider);
076 }
077
078 private ComputedValue<FieldConduit<Object>> createMixinFieldProvider(final String fieldName,
079 final String mixinClassName)
080 {
081 return new ComputedValue<FieldConduit<Object>>()
082 {
083 public FieldConduit get(InstanceContext context)
084 {
085 ComponentResources resources = context.get(ComponentResources.class);
086 final InternalComponentResources icr = (InternalComponentResources) resources;
087
088 return new ReadOnlyComponentFieldConduit(resources, fieldName)
089 {
090 public Object get(Object instance, InstanceContext context)
091 {
092 return icr.getMixinByClassName(mixinClassName);
093 }
094 };
095 }
096 };
097 }
098 }