    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    }