001package org.jszip.jetty; 002 003import org.codehaus.plexus.util.FileUtils; 004import org.eclipse.jetty.util.URIUtil; 005import org.eclipse.jetty.util.resource.Resource; 006import org.jruby.embed.EvalFailedException; 007import org.jszip.css.CssCompilationError; 008import org.jszip.css.CssEngine; 009import org.jszip.pseudo.io.PseudoFileSystem; 010 011import java.io.ByteArrayInputStream; 012import java.io.File; 013import java.io.IOException; 014import java.io.InputStream; 015import java.io.OutputStream; 016import java.io.UnsupportedEncodingException; 017import java.net.MalformedURLException; 018import java.net.URL; 019import java.net.URLConnection; 020import java.net.URLStreamHandler; 021 022/** 023 * @author stephenc 024 * @since 01/02/2013 09:38 025 */ 026public class CssEngineResource extends Resource { 027 028 private final CssEngine engine; 029 030 private final PseudoFileSystem fs; 031 032 private final String sourceFilename; 033 034 private final String name; 035 036 public CssEngineResource(PseudoFileSystem fs, CssEngine engine, String sourceFilename) { 037 this.fs = fs; 038 this.engine = engine; 039 this.sourceFilename = sourceFilename; 040 this.name = FileUtils.filename(engine.mapName(sourceFilename)); 041 } 042 043 @Override 044 public boolean isContainedIn(Resource r) throws MalformedURLException { 045 return false; 046 } 047 048 @Override 049 public void release() { 050 } 051 052 @Override 053 public boolean exists() { 054 return fs.getPseudoFile(sourceFilename).exists(); 055 } 056 057 @Override 058 public boolean isDirectory() { 059 return fs.getPseudoFile(sourceFilename).isDirectory(); 060 } 061 062 @Override 063 public long lastModified() { 064 return fs.getPseudoFile(sourceFilename).lastModified(); 065 } 066 067 @Override 068 public long length() { 069 try { 070 return engine.toCSS(sourceFilename).getBytes("utf-8").length; 071 } catch (Throwable t) { 072 return -1; 073 } 074 } 075 076 @Override 077 public URL getURL() { 078 try { 079 return new URL("css-engine", null, -1, sourceFilename, new CssEngineURLStreamHandler()); 080 } catch (MalformedURLException e) { 081 throw new IllegalStateException( 082 "MalformedURLException should not be thrown when a URLStreamHandler is provided"); 083 } 084 } 085 086 @Override 087 public File getFile() throws IOException { 088 return null; 089 } 090 091 @Override 092 public String getName() { 093 return name; 094 } 095 096 @Override 097 public InputStream getInputStream() throws IOException { 098 try { 099 return new ByteArrayInputStream(engine.toCSS(sourceFilename).getBytes("utf-8")); 100 } catch (CssCompilationError e) { 101 final IOException ioe = new IOException(e.getMessage()); 102 ioe.initCause(e); 103 throw ioe; 104 } 105 } 106 107 @Override 108 public OutputStream getOutputStream() throws IOException, SecurityException { 109 return null; 110 } 111 112 @Override 113 public boolean delete() throws SecurityException { 114 return false; 115 } 116 117 @Override 118 public boolean renameTo(Resource dest) throws SecurityException { 119 return false; 120 } 121 122 @Override 123 public String[] list() { 124 return null; 125 } 126 127 @Override 128 public Resource addPath(String path) throws IOException, MalformedURLException { 129 if (path == null) { 130 throw new MalformedURLException(); 131 } 132 if (path.length() == 0 || URIUtil.SLASH.equals(path)) { 133 return this; 134 } 135 if (name.equals(path) || (URIUtil.SLASH + name).equals(path)) { 136 return this; 137 } 138 return new BadResource(); 139 } 140 141 @Override 142 public boolean equals(Object o) { 143 if (this == o) { 144 return true; 145 } 146 if (o == null || getClass() != o.getClass()) { 147 return false; 148 } 149 150 CssEngineResource that = (CssEngineResource) o; 151 152 if (!sourceFilename.equals(that.sourceFilename)) { 153 return false; 154 } 155 if (!fs.getPseudoFile(sourceFilename).equals(that.fs.getPseudoFile(sourceFilename))) { 156 return false; 157 } 158 159 return true; 160 } 161 162 @Override 163 public String toString() { 164 final StringBuilder sb = new StringBuilder(); 165 sb.append("CssEngineResource"); 166 sb.append("{sourceFilename='").append(sourceFilename).append('\''); 167 sb.append(", name='").append(name).append('\''); 168 sb.append('}'); 169 return sb.toString(); 170 } 171 172 @Override 173 public int hashCode() { 174 return sourceFilename.hashCode(); 175 } 176 177 /** 178 * In order to ensure that we can create URLs with the {@code virtual:} protocol, we need to provide a dummy 179 * {@link java.net.URLStreamHandler} otherwise Java will try to look up the protocol and fail thereby throwing the 180 * dreaded {@link MalformedURLException}. 181 */ 182 private class CssEngineURLStreamHandler extends URLStreamHandler { 183 184 /** 185 * {@inheritDoc} 186 */ 187 @Override 188 protected URLConnection openConnection(URL u) throws IOException { 189 try { 190 final byte[] content = CssEngineResource.this.engine.toCSS(sourceFilename).getBytes("utf-8"); 191 return new URLConnection(u) { 192 @Override 193 public void connect() throws IOException { 194 // no-op 195 } 196 197 @Override 198 public InputStream getInputStream() throws IOException { 199 return new ByteArrayInputStream(content); 200 } 201 202 @Override 203 public int getContentLength() { 204 return content.length; 205 } 206 207 @Override 208 public String getContentEncoding() { 209 return "utf-8"; 210 } 211 212 @Override 213 public String getContentType() { 214 return "text/css"; 215 } 216 217 @Override 218 public long getLastModified() { 219 return CssEngineResource.this.lastModified(); 220 } 221 }; 222 } catch (CssCompilationError e) { 223 IOException ioe = new IOException(e.getMessage()); 224 ioe.initCause(e); 225 throw ioe; 226 } 227 } 228 } 229 230 231}