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}