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