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}