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 }