001 // Copyright 2008, 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.InjectComponent;
019 import org.apache.tapestry5.internal.services.ComponentClassCache;
020 import org.apache.tapestry5.ioc.internal.util.InternalUtils;
021 import org.apache.tapestry5.ioc.util.UnknownValueException;
022 import org.apache.tapestry5.model.MutableComponentModel;
023 import org.apache.tapestry5.plastic.*;
024 import org.apache.tapestry5.runtime.Component;
025 import org.apache.tapestry5.runtime.PageLifecycleAdapter;
026 import org.apache.tapestry5.services.transform.ComponentClassTransformWorker2;
027 import org.apache.tapestry5.services.transform.TransformationSupport;
028
029 /**
030 * Recognizes the {@link org.apache.tapestry5.annotations.InjectComponent} annotation, and converts the field into a
031 * read-only field containing the component. The id of the component may be explicitly stated or will be determined
032 * from the field name.
033 */
034 public class InjectComponentWorker implements ComponentClassTransformWorker2
035 {
036 private final class InjectedComponentFieldValueConduit extends ReadOnlyComponentFieldConduit
037 {
038 private final ComponentResources resources;
039 private final String fieldName, componentId, type;
040
041 private Component embedded;
042
043 private InjectedComponentFieldValueConduit(final ComponentResources resources, String fieldName, String type,
044 String componentId)
045 {
046 super(resources, fieldName);
047
048 this.resources = resources;
049 this.fieldName = fieldName;
050 this.componentId = componentId;
051 this.type = type;
052
053 resources.addPageLifecycleListener(new PageLifecycleAdapter()
054 {
055 public void containingPageDidLoad()
056 {
057 load();
058
059 resources.removePageLifecycleListener(this);
060 }
061 });
062 }
063
064 private void load()
065 {
066 try
067 {
068 embedded = resources.getEmbeddedComponent(componentId);
069 } catch (UnknownValueException ex)
070 {
071 throw new RuntimeException(String.format("Unable to inject component into field %s of class %s: %s",
072 fieldName, getComponentClassName(), ex.getMessage()), ex);
073 }
074
075 Class fieldType = classCache.forName(type);
076
077 if (!fieldType.isInstance(embedded))
078 throw new RuntimeException(
079 String
080 .format(
081 "Unable to inject component '%s' into field %s of %s. Class %s is not assignable to a field of type %s.",
082 componentId, fieldName, getComponentClassName(),
083 embedded.getClass().getName(), fieldType.getName()));
084 }
085
086 private String getComponentClassName()
087 {
088 return resources.getComponentModel().getComponentClassName();
089 }
090
091 public Object get(Object instance, InstanceContext context)
092 {
093 return embedded;
094 }
095 }
096
097 private final ComponentClassCache classCache;
098
099 public InjectComponentWorker(ComponentClassCache classCache)
100 {
101 this.classCache = classCache;
102 }
103
104 public void transform(PlasticClass plasticClass, TransformationSupport support, MutableComponentModel model)
105 {
106 for (PlasticField field : plasticClass.getFieldsWithAnnotation(InjectComponent.class))
107 {
108 InjectComponent annotation = field.getAnnotation(InjectComponent.class);
109
110 field.claim(annotation);
111
112 final String type = field.getTypeName();
113
114 final String componentId = getComponentId(field, annotation);
115
116 final String fieldName = field.getName();
117
118 ComputedValue<FieldConduit<Object>> provider = new ComputedValue<FieldConduit<Object>>()
119 {
120 public FieldConduit<Object> get(InstanceContext context)
121 {
122 ComponentResources resources = context.get(ComponentResources.class);
123
124 return new InjectedComponentFieldValueConduit(resources, fieldName, type, componentId);
125 }
126 };
127
128 field.setComputedConduit(provider);
129 }
130
131 }
132
133 private String getComponentId(PlasticField field, InjectComponent annotation)
134 {
135 String id = annotation.value();
136
137 if (InternalUtils.isNonBlank(id))
138 return id;
139
140 return InternalUtils.stripMemberName(field.getName());
141 }
142 }