001// Copyright 2010-2012 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 015package org.apache.tapestry5.internal.transform; 016 017import org.apache.tapestry5.Asset; 018import org.apache.tapestry5.ComponentResources; 019import org.apache.tapestry5.annotations.Import; 020import org.apache.tapestry5.annotations.SetupRender; 021import org.apache.tapestry5.func.F; 022import org.apache.tapestry5.func.Mapper; 023import org.apache.tapestry5.func.Worker; 024import org.apache.tapestry5.ioc.services.SymbolSource; 025import org.apache.tapestry5.model.MutableComponentModel; 026import org.apache.tapestry5.plastic.*; 027import org.apache.tapestry5.services.AssetSource; 028import org.apache.tapestry5.services.TransformConstants; 029import org.apache.tapestry5.services.javascript.Initialization; 030import org.apache.tapestry5.services.javascript.JavaScriptSupport; 031import org.apache.tapestry5.services.transform.ComponentClassTransformWorker2; 032import org.apache.tapestry5.services.transform.TransformationSupport; 033 034import java.util.ArrayList; 035import java.util.List; 036 037/** 038 * Implements the {@link Import} annotation, both at the class and at the method level. 039 * 040 * @since 5.2.0 041 */ 042public class ImportWorker implements ComponentClassTransformWorker2 043{ 044 private final JavaScriptSupport javascriptSupport; 045 046 private final SymbolSource symbolSource; 047 048 private final AssetSource assetSource; 049 050 private final Worker<Asset> importLibrary = new Worker<Asset>() 051 { 052 public void work(Asset asset) 053 { 054 javascriptSupport.importJavaScriptLibrary(asset); 055 } 056 }; 057 058 private final Worker<Asset> importStylesheet = new Worker<Asset>() 059 { 060 public void work(Asset asset) 061 { 062 javascriptSupport.importStylesheet(asset); 063 } 064 }; 065 066 private final Mapper<String, String> expandSymbols = new Mapper<String, String>() 067 { 068 public String map(String element) 069 { 070 return symbolSource.expandSymbols(element); 071 } 072 }; 073 074 public ImportWorker(JavaScriptSupport javascriptSupport, SymbolSource symbolSource, AssetSource assetSource) 075 { 076 this.javascriptSupport = javascriptSupport; 077 this.symbolSource = symbolSource; 078 this.assetSource = assetSource; 079 } 080 081 public void transform(PlasticClass componentClass, TransformationSupport support, MutableComponentModel model) 082 { 083 processClassAnnotationAtSetupRenderPhase(componentClass, model); 084 085 for (PlasticMethod m : componentClass.getMethodsWithAnnotation(Import.class)) 086 { 087 decorateMethod(componentClass, model, m); 088 } 089 } 090 091 private void processClassAnnotationAtSetupRenderPhase(PlasticClass componentClass, MutableComponentModel model) 092 { 093 Import annotation = componentClass.getAnnotation(Import.class); 094 095 if (annotation != null) 096 { 097 PlasticMethod setupRender = componentClass.introduceMethod(TransformConstants.SETUP_RENDER_DESCRIPTION); 098 099 decorateMethod(componentClass, model, setupRender, annotation); 100 101 model.addRenderPhase(SetupRender.class); 102 } 103 } 104 105 private void decorateMethod(PlasticClass componentClass, MutableComponentModel model, PlasticMethod method) 106 { 107 Import annotation = method.getAnnotation(Import.class); 108 109 decorateMethod(componentClass, model, method, annotation); 110 } 111 112 private void decorateMethod(PlasticClass componentClass, MutableComponentModel model, PlasticMethod method, 113 Import annotation) 114 { 115 importStacks(method, annotation.stack()); 116 117 importLibraries(componentClass, model, method, annotation.library()); 118 119 importStylesheets(componentClass, model, method, annotation.stylesheet()); 120 121 importModules(method, annotation.module()); 122 } 123 124 private void importStacks(PlasticMethod method, String[] stacks) 125 { 126 if (stacks.length != 0) 127 { 128 method.addAdvice(createImportStackAdvice(stacks)); 129 } 130 } 131 132 private MethodAdvice createImportStackAdvice(final String[] stacks) 133 { 134 return new MethodAdvice() 135 { 136 public void advise(MethodInvocation invocation) 137 { 138 for (String stack : stacks) 139 { 140 javascriptSupport.importStack(stack); 141 } 142 143 invocation.proceed(); 144 } 145 }; 146 } 147 148 private void importModules(PlasticMethod method, String[] moduleNames) 149 { 150 if (moduleNames.length != 0) 151 { 152 method.addAdvice(createImportModulesAdvice(moduleNames)); 153 } 154 } 155 156 class ModuleImport 157 { 158 final String moduleName, functionName; 159 160 ModuleImport(String moduleName, String functionName) 161 { 162 this.moduleName = moduleName; 163 this.functionName = functionName; 164 } 165 166 void apply(JavaScriptSupport javaScriptSupport) 167 { 168 Initialization initialization = javaScriptSupport.require(moduleName); 169 170 if (functionName != null) 171 { 172 initialization.invoke(functionName); 173 } 174 } 175 } 176 177 private MethodAdvice createImportModulesAdvice(final String[] moduleNames) 178 { 179 final List<ModuleImport> moduleImports = new ArrayList<ModuleImport>(moduleNames.length); 180 181 for (String name : moduleNames) 182 { 183 int colonx = name.indexOf(':'); 184 185 String moduleName = colonx < 0 ? name : name.substring(0, colonx); 186 String functionName = colonx < 0 ? null : name.substring(colonx + 1); 187 188 moduleImports.add(new ModuleImport(moduleName, functionName)); 189 } 190 191 return new MethodAdvice() 192 { 193 public void advise(MethodInvocation invocation) 194 { 195 for (ModuleImport moduleImport : moduleImports) 196 { 197 moduleImport.apply(javascriptSupport); 198 } 199 200 invocation.proceed(); 201 } 202 }; 203 } 204 205 private void importLibraries(PlasticClass plasticClass, MutableComponentModel model, PlasticMethod method, 206 String[] paths) 207 { 208 decorateMethodWithOperation(plasticClass, model, method, paths, importLibrary); 209 } 210 211 private void importStylesheets(PlasticClass plasticClass, MutableComponentModel model, PlasticMethod method, 212 String[] paths) 213 { 214 decorateMethodWithOperation(plasticClass, model, method, paths, importStylesheet); 215 } 216 217 private void decorateMethodWithOperation(PlasticClass componentClass, MutableComponentModel model, 218 PlasticMethod method, String[] paths, Worker<Asset> operation) 219 { 220 if (paths.length == 0) 221 { 222 return; 223 } 224 225 String[] expandedPaths = expandPaths(paths); 226 227 PlasticField assetListField = componentClass.introduceField(Asset[].class, 228 "importedAssets_" + method.getDescription().methodName); 229 230 initializeAssetsFromPaths(expandedPaths, assetListField); 231 232 addMethodAssetOperationAdvice(method, assetListField.getHandle(), operation); 233 } 234 235 private String[] expandPaths(String[] paths) 236 { 237 return F.flow(paths).map(expandSymbols).toArray(String.class); 238 } 239 240 private void initializeAssetsFromPaths(final String[] expandedPaths, PlasticField assetsField) 241 { 242 assetsField.injectComputed(new ComputedValue<Asset[]>() 243 { 244 public Asset[] get(InstanceContext context) 245 { 246 ComponentResources resources = context.get(ComponentResources.class); 247 248 return convertPathsToAssetArray(resources, expandedPaths); 249 } 250 }); 251 } 252 253 private Asset[] convertPathsToAssetArray(final ComponentResources resources, String[] assetPaths) 254 { 255 return F.flow(assetPaths).map(new Mapper<String, Asset>() 256 { 257 public Asset map(String assetPath) 258 { 259 return assetSource.getComponentAsset(resources, assetPath); 260 } 261 }).toArray(Asset.class); 262 } 263 264 private void addMethodAssetOperationAdvice(PlasticMethod method, final FieldHandle access, 265 final Worker<Asset> operation) 266 { 267 method.addAdvice(new MethodAdvice() 268 { 269 public void advise(MethodInvocation invocation) 270 { 271 Asset[] assets = (Asset[]) access.get(invocation.getInstance()); 272 273 F.flow(assets).each(operation); 274 275 invocation.proceed(); 276 } 277 }); 278 } 279}