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}