001// Copyright 2013 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.webresources.modules;
016
017import com.github.sommeri.less4j.LessCompiler;
018import com.github.sommeri.less4j.core.parser.AntlrException;
019import org.apache.tapestry5.MarkupWriter;
020import org.apache.tapestry5.internal.webresources.*;
021import org.apache.tapestry5.ioc.MappedConfiguration;
022import org.apache.tapestry5.ioc.ServiceBinder;
023import org.apache.tapestry5.ioc.annotations.Autobuild;
024import org.apache.tapestry5.ioc.annotations.Contribute;
025import org.apache.tapestry5.ioc.annotations.Primary;
026import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
027import org.apache.tapestry5.ioc.internal.util.InternalUtils;
028import org.apache.tapestry5.ioc.services.FactoryDefaults;
029import org.apache.tapestry5.ioc.services.SymbolProvider;
030import org.apache.tapestry5.services.ObjectRenderer;
031import org.apache.tapestry5.services.assets.ResourceMinimizer;
032import org.apache.tapestry5.services.assets.ResourceTransformer;
033import org.apache.tapestry5.services.assets.StreamableResourceSource;
034import org.apache.tapestry5.webresources.WebResourcesSymbols;
035
036import java.util.List;
037
038/**
039 * Configures use of various transformers and mimimizers to support:
040 * <ul>
041 * <li>Less to CSS</li>
042 * <li>CoffeeScript to JavaScript</li>
043 * <li>CSS minimization via YUI Compressor</li>
044 * <li>JavaScript minimization via Google Closure</li>
045 * </ul>
046 *
047 * @since 5.4
048 */
049public class WebResourcesModule
050{
051    public static void bind(ServiceBinder binder)
052    {
053        binder.bind(ResourceTransformerFactory.class, ResourceTransformerFactoryImpl.class);
054    }
055
056    @Contribute(SymbolProvider.class)
057    @FactoryDefaults
058    public static void setupDefaultCacheDirectory(MappedConfiguration<String, Object> configuration)
059    {
060        configuration.add(WebResourcesSymbols.CACHE_DIR, "${java.io.tmpdir}");
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            public void render(LessCompiler.Problem problem, MarkupWriter writer)
100            {
101                List<String> strings = CollectionFactory.newList();
102
103                if (InternalUtils.isNonBlank(problem.getMessage()))
104                {
105                    strings.add(problem.getMessage());
106                }
107
108                // Inside WRO4J we see that the LessSource is a StringSource with no useful toString(), so
109                // it is omitted. We may need to create our own processors, stripping away a couple of layers of
110                // WRO4J to get proper exception reporting!
111
112                if (problem.getLine() > 0)
113                {
114                    strings.add("line " + problem.getLine());
115                }
116
117                if (problem.getCharacter() > 0)
118                {
119                    strings.add("position " + problem.getCharacter());
120                }
121
122                writer.write(InternalUtils.join(strings, " - "));
123            }
124        });
125    }
126}