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 }