001// Copyright 2013-2014 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.internal.webresources; 016 017import com.github.sommeri.less4j.Less4jException; 018import com.github.sommeri.less4j.LessCompiler; 019import com.github.sommeri.less4j.LessSource; 020import com.github.sommeri.less4j.core.DefaultLessCompiler; 021import org.apache.commons.io.IOUtils; 022import org.apache.tapestry5.internal.services.assets.BytestreamCache; 023import org.apache.tapestry5.ioc.Resource; 024import org.apache.tapestry5.ioc.internal.util.InternalUtils; 025import org.apache.tapestry5.services.assets.ResourceDependencies; 026import org.apache.tapestry5.services.assets.ResourceTransformer; 027 028import java.io.*; 029 030/** 031 * Direct wrapper around the LessCompiler, so that Less source files may use {@code @import}, which isn't 032 * supported by the normal WRO4J processor. 033 */ 034public class LessResourceTransformer implements ResourceTransformer 035{ 036 private final LessCompiler compiler = new DefaultLessCompiler(); 037 038 public String getTransformedContentType() 039 { 040 return "text/css"; 041 } 042 043 class ResourceLessSource extends LessSource 044 { 045 private final Resource resource; 046 047 private final ResourceDependencies dependencies; 048 049 050 ResourceLessSource(Resource resource, ResourceDependencies dependencies) 051 { 052 this.resource = resource; 053 this.dependencies = dependencies; 054 } 055 056 @Override 057 public LessSource relativeSource(String filename) throws FileNotFound, CannotReadFile, StringSourceException 058 { 059 Resource relative = resource.forFile(filename); 060 061 if (!relative.exists()) 062 { 063 throw new FileNotFound(); 064 } 065 066 dependencies.addDependency(relative); 067 068 return new ResourceLessSource(relative, dependencies); 069 } 070 071 @Override 072 public String getContent() throws FileNotFound, CannotReadFile 073 { 074 // Adapted from Less's URLSource 075 Reader input = null; 076 try 077 { 078 input = new InputStreamReader(resource.openStream()); 079 String content = IOUtils.toString(input).replace("\r\n", "\n"); 080 081 return content; 082 } catch (FileNotFoundException ex) 083 { 084 throw new FileNotFound(); 085 } catch (IOException ex) 086 { 087 throw new CannotReadFile(); 088 } finally 089 { 090 InternalUtils.close(input); 091 } 092 } 093 094 public byte[] getBytes() throws FileNotFound, CannotReadFile 095 { 096 Reader input = null; 097 try 098 { 099 input = new InputStreamReader(resource.openStream()); 100 101 return IOUtils.toByteArray(input); 102 } catch (FileNotFoundException ex) 103 { 104 throw new FileNotFound(); 105 } catch (IOException ex) 106 { 107 throw new CannotReadFile(); 108 } finally 109 { 110 InternalUtils.close(input); 111 } 112 113 } 114 } 115 116 117 public InputStream transform(Resource source, ResourceDependencies dependencies) throws IOException 118 { 119 BytestreamCache compiled = invokeLessCompiler(source, dependencies); 120 121 return compiled.openStream(); 122 } 123 124 private BytestreamCache invokeLessCompiler(Resource source, ResourceDependencies dependencies) throws IOException 125 { 126 try 127 { 128 LessSource lessSource = new ResourceLessSource(source, dependencies); 129 130 LessCompiler.CompilationResult compilationResult = compiler.compile(lessSource); 131 132 // Currently, ignoring any warnings. 133 134 return new BytestreamCache(compilationResult.getCss().getBytes("utf-8")); 135 136 } catch (Less4jException ex) 137 { 138 throw new IOException(ex); 139 } catch (UnsupportedEncodingException ex) 140 { 141 throw new IOException(ex); 142 } 143 } 144}