/*
* Copyright 2010 Richard Nichols.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* under the License.
*/
package com.visural.wicket.util.lesscss;
import com.visural.common.web.lesscss.LessCSS;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.Serializable;
import java.util.Locale;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.apache.wicket.WicketRuntimeException;
import org.apache.wicket.util.file.IResourceFinder;
import org.apache.wicket.util.resource.IResourceStream;
import org.apache.wicket.util.resource.ResourceStreamNotFoundException;
import org.apache.wicket.util.resource.locator.ResourceStreamLocator;
import org.apache.wicket.util.time.Time;
/**
* A stream locator that automatically runs ".less" files through the LessCSS
* compilation process.
*
* @version $Id: LessCSSResourceStreamLocator.java 217 2010-09-30 23:57:10Z tibes80@gmail.com $
* @author Richard Nichols
*/
public class LessCSSResourceStreamLocator extends ResourceStreamLocator {
private static final long serialVersionUID = 1L;
private Map<CacheBean, CacheResponse> cache = new ConcurrentHashMap<CacheBean, CacheResponse>();
public LessCSSResourceStreamLocator() {
}
public LessCSSResourceStreamLocator(final IResourceFinder finder) {
super(finder);
}
@Override
public IResourceStream locate(Class<?> clazz, String path) {
IResourceStream orig = super.locate(clazz, path);
if (orig == null) {
return null;
}
if (path.endsWith(".less")) {
CacheBean cb = new CacheBean(clazz, path, null, null, null);
if (cache.get(cb) == null) {
try {
cache.put(cb, getResponse(orig));
} catch (Exception ex) {
Logger.getLogger(LessCSSResourceStreamLocator.class.getName()).log(Level.SEVERE, null, ex);
throw new WicketRuntimeException("Failed running LessCSS", ex);
}
}
return new LessResourceStream(cache.get(cb), super.locate(clazz, path));
} else {
return orig;
}
}
@Override
public IResourceStream locate(Class<?> clazz, String path, String style, Locale locale, String extension) {
IResourceStream orig = super.locate(clazz, path, style, locale, extension);
if (orig == null) {
return null;
}
if (path.endsWith(".less")) {
CacheBean cb = new CacheBean(clazz, path, style, locale, extension);
if (cache.get(cb) == null) {
try {
cache.put(cb, getResponse(orig));
} catch (Exception ex) {
Logger.getLogger(LessCSSResourceStreamLocator.class.getName()).log(Level.SEVERE, null, ex);
throw new WicketRuntimeException("Failed running LessCSS", ex);
}
}
return new LessResourceStream(cache.get(cb), orig);
} else {
return orig;
}
}
private CacheResponse getResponse(IResourceStream stream) throws ResourceStreamNotFoundException, IOException {
byte[] data = null;
ByteArrayOutputStream baos = new ByteArrayOutputStream();
int r;
InputStream in = stream.getInputStream();
while ((r = in.read()) != -1) {
baos.write(r);
}
stream.close();
data = baos.toByteArray();
LessCSS engine = new LessCSS();
String lesscss = engine.less(new ByteArrayInputStream(data));
CacheResponse cr = new CacheResponse(lesscss.getBytes().length, lesscss);
return cr;
}
class LessResourceStream implements IResourceStream {
private final CacheResponse cr;
private final IResourceStream regular;
public LessResourceStream(CacheResponse cr, IResourceStream regular) {
this.cr = cr;
this.regular = regular;
}
public String getContentType() {
return "text/css";
}
public long length() {
return cr.getLength();
}
public InputStream getInputStream() throws ResourceStreamNotFoundException {
return new ByteArrayInputStream(cr.getData().getBytes());
}
public void close() throws IOException {
}
public Locale getLocale() {
return regular.getLocale();
}
public void setLocale(Locale locale) {
regular.setLocale(locale);
}
public Time lastModifiedTime() {
return regular.lastModifiedTime();
}
}
class CacheResponse implements Serializable {
private final long length;
private final String data;
public CacheResponse(long length, String data) {
this.length = length;
this.data = data;
}
public String getData() {
return data;
}
public long getLength() {
return length;
}
}
class CacheBean implements Serializable {
private final Class<?> clazz;
private final String path;
private final String style;
private final Locale locale;
private final String extension;
public CacheBean(Class<?> clazz, String path, String style, Locale locale, String extension) {
this.clazz = clazz;
this.path = path;
this.style = style;
this.locale = locale;
this.extension = extension;
}
public Class<?> getClazz() {
return clazz;
}
public String getExtension() {
return extension;
}
public Locale getLocale() {
return locale;
}
public String getPath() {
return path;
}
public String getStyle() {
return style;
}
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final CacheBean other = (CacheBean) obj;
if (this.clazz != other.clazz && (this.clazz == null || !this.clazz.equals(other.clazz))) {
return false;
}
if ((this.path == null) ? (other.path != null) : !this.path.equals(other.path)) {
return false;
}
if ((this.style == null) ? (other.style != null) : !this.style.equals(other.style)) {
return false;
}
if (this.locale != other.locale && (this.locale == null || !this.locale.equals(other.locale))) {
return false;
}
if ((this.extension == null) ? (other.extension != null) : !this.extension.equals(other.extension)) {
return false;
}
return true;
}
@Override
public int hashCode() {
int hash = 7;
hash = 11 * hash + (this.clazz != null ? this.clazz.hashCode() : 0);
hash = 11 * hash + (this.path != null ? this.path.hashCode() : 0);
hash = 11 * hash + (this.style != null ? this.style.hashCode() : 0);
hash = 11 * hash + (this.locale != null ? this.locale.hashCode() : 0);
hash = 11 * hash + (this.extension != null ? this.extension.hashCode() : 0);
return hash;
}
}
}