    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.webresources.modules;
    014
    015import com.github.sommeri.less4j.LessCompiler;
    016import com.github.sommeri.less4j.core.parser.AntlrException;
    017import com.google.javascript.jscomp.CompilationLevel;
    018import org.apache.tapestry5.MarkupWriter;
    019import org.apache.tapestry5.internal.webresources.*;
    020import org.apache.tapestry5.ioc.MappedConfiguration;
    021import org.apache.tapestry5.ioc.ServiceBinder;
    022import org.apache.tapestry5.ioc.annotations.Autobuild;
    023import org.apache.tapestry5.ioc.annotations.Contribute;
    024import org.apache.tapestry5.ioc.annotations.Primary;
    025import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
    026import org.apache.tapestry5.ioc.internal.util.InternalUtils;
    027import org.apache.tapestry5.ioc.services.FactoryDefaults;
    028import org.apache.tapestry5.ioc.services.SymbolProvider;
    029import org.apache.tapestry5.services.ObjectRenderer;
    030import org.apache.tapestry5.services.assets.ResourceMinimizer;
    031import org.apache.tapestry5.services.assets.ResourceTransformer;
    032import org.apache.tapestry5.services.assets.StreamableResourceSource;
    033import org.apache.tapestry5.webresources.WebResourcesSymbols;
    034
    035import java.util.List;
    036
    037/**
    038 * Configures use of various transformers and mimimizers to support:
    039 * <ul>
    040 * <li>Less to CSS</li>
    041 * <li>CoffeeScript to JavaScript</li>
    042 * <li>CSS minimization via YUI Compressor</li>
    043 * <li>JavaScript minimization via Google Closure</li>
    044 * </ul>
    045 *
    046 * @since 5.4
    047 */
    048public class WebResourcesModule
    049{
    050    public static void bind(ServiceBinder binder)
    051    {
    052        binder.bind(ResourceTransformerFactory.class, ResourceTransformerFactoryImpl.class);
    053    }
    054
    055    @Contribute(SymbolProvider.class)
    056    @FactoryDefaults
    057    public static void setupDefaultCacheDirectory(MappedConfiguration<String, Object> configuration)
    058    {
    059        configuration.add(WebResourcesSymbols.CACHE_DIR, "${java.io.tmpdir}/tapestry-asset-cache");
    060        configuration.add(WebResourcesSymbols.COMPILATION_LEVEL, CompilationLevel.WHITESPACE_ONLY);
    061    }
    062
    063
    064    @Contribute(StreamableResourceSource.class)
    065    public static void provideCompilers(MappedConfiguration<String, ResourceTransformer> configuration, ResourceTransformerFactory factory,
    066                                        @Autobuild CoffeeScriptCompiler coffeeScriptCompiler)
    067    {
    068        // contribution ids are file extensions:
    069
    070        configuration.add("coffee",
    071                factory.createCompiler("text/javascript", "CoffeeScript", "JavaScript",
    072                        coffeeScriptCompiler,
    073                        CacheMode.SINGLE_FILE));
    074
    075        configuration.add("less",
    076                factory.createCompiler("text/css", "Less", "CSS", new LessResourceTransformer(),
    077                        CacheMode.MULTIPLE_FILE));
    078    }
    079
    080    @Contribute(ResourceMinimizer.class)
    081    @Primary
    082    public static void setupDefaultResourceMinimizers(MappedConfiguration<String, ResourceMinimizer> configuration)
    083    {
    084        configuration.addInstance("text/css", CSSMinimizer.class);
    085        configuration.addInstance("text/javascript", GoogleClosureMinimizer.class);
    086    }
    087
    088    /**
    089     * Alas {@link AntlrException}s do not have a useful toString() which makes them useless in the exception report;
    090     * here we provide an {@link ObjectRenderer} that breaks them apart into useful strings. Eventually we may be
    091     * able to synthesize a {@link org.apache.tapestry5.ioc.Location} from them as well and show some of the source .less file.
    092     */
    093    @Contribute(ObjectRenderer.class)
    094    @Primary
    095    public static void provideLessCompilerProblemRenderer(MappedConfiguration<Class, ObjectRenderer> configuration)
    096    {
    097        configuration.add(LessCompiler.Problem.class, new ObjectRenderer<LessCompiler.Problem>()
    098        {
    099            @Override
    100            public void render(LessCompiler.Problem problem, MarkupWriter writer)
    101            {
    102                List<String> strings = CollectionFactory.newList();
    103
    104                if (InternalUtils.isNonBlank(problem.getMessage()))
    105                {
    106                    strings.add(problem.getMessage());
    107                }
    108
    109                // Inside WRO4J we see that the LessSource is a StringSource with no useful toString(), so
    110                // it is omitted. We may need to create our own processors, stripping away a couple of layers of
    111                // WRO4J to get proper exception reporting!
    112
    113                if (problem.getLine() > 0)
    114                {
    115                    strings.add("line " + problem.getLine());
    116                }
    117
    118                if (problem.getCharacter() > 0)
    119                {
    120                    strings.add("position " + problem.getCharacter());
    121                }
    122
    123                writer.write(InternalUtils.join(strings, " - "));
    124            }
    125        });
    126    }
    127}