    001// Licensed under the Apache License, Version 2.0 (the "License");
    002// you may not use this file except in compliance with the License.
    003// You may obtain a copy of the License at
    004//
    005// http://www.apache.org/licenses/LICENSE-2.0
    006//
    007// Unless required by applicable law or agreed to in writing, software
    008// distributed under the License is distributed on an "AS IS" BASIS,
    009// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    010// See the License for the specific language governing permissions and
    011// limitations under the License.
    012
    013package org.apache.tapestry5.internal.services.javascript;
    014
    015import org.apache.tapestry5.Asset;
    016import org.apache.tapestry5.SymbolConstants;
    017import org.apache.tapestry5.func.F;
    018import org.apache.tapestry5.func.Mapper;
    019import org.apache.tapestry5.internal.services.RequestConstants;
    020import org.apache.tapestry5.internal.services.assets.JavaScriptStackAssembler;
    021import org.apache.tapestry5.ioc.annotations.Symbol;
    022import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
    023import org.apache.tapestry5.ioc.services.ThreadLocale;
    024import org.apache.tapestry5.ioc.util.ExceptionUtils;
    025import org.apache.tapestry5.services.ResponseCompressionAnalyzer;
    026import org.apache.tapestry5.services.assets.AssetPathConstructor;
    027import org.apache.tapestry5.services.assets.StreamableResource;
    028import org.apache.tapestry5.services.javascript.JavaScriptStack;
    029import org.apache.tapestry5.services.javascript.JavaScriptStackSource;
    030
    031import java.io.IOException;
    032import java.util.List;
    033
    034public class JavaScriptStackPathConstructorImpl implements JavaScriptStackPathConstructor
    035{
    036    private final ThreadLocale threadLocale;
    037
    038    private final AssetPathConstructor assetPathConstructor;
    039
    040    private final JavaScriptStackSource javascriptStackSource;
    041
    042    private final JavaScriptStackAssembler assembler;
    043
    044    private final ResponseCompressionAnalyzer compressionAnalyzer;
    045
    046    private final boolean combineScripts;
    047
    048    private final Mapper<Asset, String> toPath = new Mapper<Asset, String>()
    049    {
    050        public String map(Asset input)
    051        {
    052            return input.toClientURL();
    053        }
    054    };
    055
    056    public JavaScriptStackPathConstructorImpl(ThreadLocale threadLocale, AssetPathConstructor assetPathConstructor,
    057                                              JavaScriptStackSource javascriptStackSource,
    058                                              JavaScriptStackAssembler assembler,
    059                                              ResponseCompressionAnalyzer compressionAnalyzer,
    060                                              @Symbol(SymbolConstants.COMBINE_SCRIPTS)
    061                                              boolean combineScripts)
    062    {
    063        this.threadLocale = threadLocale;
    064        this.assetPathConstructor = assetPathConstructor;
    065        this.javascriptStackSource = javascriptStackSource;
    066        this.assembler = assembler;
    067        this.compressionAnalyzer = compressionAnalyzer;
    068        this.combineScripts = combineScripts;
    069    }
    070
    071    public List<String> constructPathsForJavaScriptStack(String stackName)
    072    {
    073        JavaScriptStack stack = javascriptStackSource.getStack(stackName);
    074
    075        List<Asset> assets = stack.getJavaScriptLibraries();
    076
    077        // When combine scripts is true, we want to build the virtual aggregated JavaScript ... but only
    078        // if there is more than one library asset, or any modules.
    079        if (combineScripts && stack.getJavaScriptAggregationStrategy().enablesCombine())
    080        {
    081            boolean needsVirtual = (assets.size() > 1) || (!stack.getModules().isEmpty());
    082
    083            if (needsVirtual)
    084            {
    085                return combinedStackURL(stackName, stack);
    086            }
    087        }
    088
    089        return toPaths(assets);
    090    }
    091
    092    private List<String> toPaths(List<Asset> assets)
    093    {
    094        assert assets != null;
    095
    096        return F.flow(assets).map(toPath).toList();
    097    }
    098
    099    private List<String> combinedStackURL(String stackName, JavaScriptStack stack)
    100    {
    101        try
    102        {
    103            StreamableResource assembled = assembler.assembleJavaScriptResourceForStack(stackName, compressionAnalyzer.isGZipSupported(),
    104                    stack.getJavaScriptAggregationStrategy());
    105
    106            String path = threadLocale.getLocale().toString() + '/' + stackName + ".js";
    107
    108            String stackURL = assetPathConstructor.constructAssetPath(RequestConstants.STACK_FOLDER, path, assembled);
    109
    110            return CollectionFactory.newList(stackURL);
    111        } catch (IOException ex)
    112        {
    113            throw new RuntimeException(String.format("Unable to construct path for '%s' JavaScript stack: %s",
    114                    stackName,
    115                    ExceptionUtils.toMessage(ex)), ex);
    116        }
    117    }
    118
    119}