001 // Copyright 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.Asset;
018 import org.apache.tapestry5.ComponentResources;
019 import org.apache.tapestry5.annotations.Import;
020 import org.apache.tapestry5.annotations.SetupRender;
021 import org.apache.tapestry5.func.F;
022 import org.apache.tapestry5.func.Mapper;
023 import org.apache.tapestry5.func.Worker;
024 import org.apache.tapestry5.ioc.Resource;
025 import org.apache.tapestry5.ioc.services.SymbolSource;
026 import org.apache.tapestry5.model.MutableComponentModel;
027 import org.apache.tapestry5.plastic.*;
028 import org.apache.tapestry5.services.AssetSource;
029 import org.apache.tapestry5.services.TransformConstants;
030 import org.apache.tapestry5.services.javascript.JavaScriptSupport;
031 import org.apache.tapestry5.services.transform.ComponentClassTransformWorker2;
032 import org.apache.tapestry5.services.transform.TransformationSupport;
033
034 import java.util.Locale;
035
036 /**
037 * Implements the {@link Import} annotation, both at the class and at the method level.
038 *
039 * @since 5.2.0
040 */
041 public class ImportWorker implements ComponentClassTransformWorker2
042 {
043 private final JavaScriptSupport javascriptSupport;
044
045 private final SymbolSource symbolSource;
046
047 private final AssetSource assetSource;
048
049 private final Worker<Asset> importLibrary = new Worker<Asset>()
050 {
051 public void work(Asset asset)
052 {
053 javascriptSupport.importJavaScriptLibrary(asset);
054 }
055 };
056
057 private final Worker<Asset> importStylesheet = new Worker<Asset>()
058 {
059 public void work(Asset asset)
060 {
061 javascriptSupport.importStylesheet(asset);
062 }
063 };
064
065 private final Mapper<String, String> expandSymbols = new Mapper<String, String>()
066 {
067 public String map(String element)
068 {
069 return symbolSource.expandSymbols(element);
070 }
071 };
072
073 public ImportWorker(JavaScriptSupport javascriptSupport, SymbolSource symbolSource, AssetSource assetSource)
074 {
075 this.javascriptSupport = javascriptSupport;
076 this.symbolSource = symbolSource;
077 this.assetSource = assetSource;
078 }
079
080 public void transform(PlasticClass componentClass, TransformationSupport support, MutableComponentModel model)
081 {
082 processClassAnnotationAtSetupRenderPhase(componentClass, model);
083
084 for (PlasticMethod m : componentClass.getMethodsWithAnnotation(Import.class))
085 {
086 decorateMethod(componentClass, model, m);
087 }
088 }
089
090 private void processClassAnnotationAtSetupRenderPhase(PlasticClass componentClass, MutableComponentModel model)
091 {
092 Import annotation = componentClass.getAnnotation(Import.class);
093
094 if (annotation != null)
095 {
096 PlasticMethod setupRender = componentClass.introduceMethod(TransformConstants.SETUP_RENDER_DESCRIPTION);
097
098 decorateMethod(componentClass, model, setupRender, annotation);
099
100 model.addRenderPhase(SetupRender.class);
101 }
102 }
103
104 private void decorateMethod(PlasticClass componentClass, MutableComponentModel model, PlasticMethod method)
105 {
106 Import annotation = method.getAnnotation(Import.class);
107
108 decorateMethod(componentClass, model, method, annotation);
109 }
110
111 private void decorateMethod(PlasticClass componentClass, MutableComponentModel model, PlasticMethod method,
112 Import annotation)
113 {
114 importStacks(method, annotation.stack());
115
116 importLibraries(componentClass, model, method, annotation.library());
117
118 importStylesheets(componentClass, model, method, annotation.stylesheet());
119 }
120
121 private void importStacks(PlasticMethod method, String[] stacks)
122 {
123 if (stacks.length != 0)
124 {
125 method.addAdvice(createImportStackAdvice(stacks));
126 }
127 }
128
129 private MethodAdvice createImportStackAdvice(final String[] stacks)
130 {
131 return new MethodAdvice()
132 {
133 public void advise(MethodInvocation invocation)
134 {
135 for (String stack : stacks)
136 {
137 javascriptSupport.importStack(stack);
138 }
139
140 invocation.proceed();
141 }
142 };
143 }
144
145 private void importLibraries(PlasticClass plasticClass, MutableComponentModel model, PlasticMethod method,
146 String[] paths)
147 {
148 decorateMethodWithOperation(plasticClass, model, method, paths, importLibrary);
149 }
150
151 private void importStylesheets(PlasticClass plasticClass, MutableComponentModel model, PlasticMethod method,
152 String[] paths)
153 {
154 decorateMethodWithOperation(plasticClass, model, method, paths, importStylesheet);
155 }
156
157 private void decorateMethodWithOperation(PlasticClass componentClass, MutableComponentModel model,
158 PlasticMethod method, String[] paths, Worker<Asset> operation)
159 {
160 if (paths.length == 0)
161 return;
162
163 String[] expandedPaths = expandPaths(paths);
164
165 PlasticField assetListField = componentClass.introduceField(Asset[].class,
166 "importedAssets_" + method.getDescription().methodName);
167
168 initializeAssetsFromPaths(model.getBaseResource(), expandedPaths, assetListField);
169
170 addMethodAssetOperationAdvice(method, assetListField.getHandle(), operation);
171 }
172
173 private String[] expandPaths(String[] paths)
174 {
175 return F.flow(paths).map(expandSymbols).toArray(String.class);
176 }
177
178 private void initializeAssetsFromPaths(final Resource baseResource,
179 final String[] expandedPaths, final PlasticField assetsField)
180 {
181 assetsField.injectComputed(new ComputedValue<Asset[]>()
182 {
183 public Asset[] get(InstanceContext context)
184 {
185 ComponentResources resources = context.get(ComponentResources.class);
186
187 return convertPathsToAssetArray(baseResource, resources.getLocale(), expandedPaths);
188 }
189 });
190 }
191
192 private Asset[] convertPathsToAssetArray(final Resource baseResource, final Locale locale, String[] assetPaths)
193 {
194 return F.flow(assetPaths).map(new Mapper<String, Asset>()
195 {
196 public Asset map(String assetPath)
197 {
198 return assetSource.getAsset(baseResource, assetPath, locale);
199 }
200 }).toArray(Asset.class);
201 }
202
203 private void addMethodAssetOperationAdvice(PlasticMethod method, final FieldHandle access,
204 final Worker<Asset> operation)
205 {
206 method.addAdvice(new MethodAdvice()
207 {
208 public void advise(MethodInvocation invocation)
209 {
210 Asset[] assets = (Asset[]) access.get(invocation.getInstance());
211
212 F.flow(assets).each(operation);
213
214 invocation.proceed();
215 }
216 });
217 }
218 }