org.lesscss.LessCompiler Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of knowgate-xhtml Show documentation
Show all versions of knowgate-xhtml Show documentation
KnowGate HTTP, HTML, XSLT and CSS utilities
The newest version!
package org.lesscss;
/* Copyright 2011-2012 The Apache Software Foundation.
*
* 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.
*/
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.net.URLConnection;
import java.nio.file.Files;
import static java.nio.file.StandardOpenOption.CREATE;
import static java.nio.file.StandardOpenOption.WRITE;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.mozilla.javascript.Context;
import org.mozilla.javascript.JavaScriptException;
import org.mozilla.javascript.Scriptable;
import org.mozilla.javascript.ScriptableObject;
import org.mozilla.javascript.tools.shell.Global;
/**
* The LESS compiler to compile LESS sources to CSS stylesheets.
*
* The compiler uses Rhino (JavaScript implementation written in Java), Envjs
* (simulated browser environment written in JavaScript), and the official LESS
* JavaScript compiler.
* Note that the compiler is not a Java implementation of LESS itself, but rather
* integrates the LESS JavaScript compiler within a Java/JavaScript browser
* environment provided by Rhino and Envjs.
*
*
* The compiler comes bundled with the Envjs and LESS JavaScript, so there is
* no need to include them yourself. But if needed they can be overridden.
*
* Basic code example:
*
* LessCompiler lessCompiler = new LessCompiler();
* String css = lessCompiler.compile("@color: #4D926F; #header { color: @color; }");
*
*
* @author Marcel Overdijk
* @see LESS - The Dynamic Stylesheet language
* @see Rhino - JavaScript for Java
* @see Envjs - Bringing the Browser
*/
public class LessCompiler implements AutoCloseable {
private static final String COMPILE_STRING = "var result; var parser = new(less.Parser); parser.parse(input, function (e, tree) { if (e instanceof Object) { throw e } result = tree.toCSS({compress: %b}) });";
private URL envJs = LessCompiler.class.getClassLoader().getResource("META-INF/env.rhino.js");
private URL lessJs = LessCompiler.class.getClassLoader().getResource("META-INF/less.js");
private List customJs = Collections.emptyList();
private boolean compress = false;
private String encoding = null;
private Context cx;
private Scriptable scope;
/**
* Constructs a new LessCompiler
.
*/
public LessCompiler() {
}
/**
* Returns the Envjs JavaScript file used by the compiler.
*
* @return The Envjs JavaScript file used by the compiler.
*/
public URL getEnvJs() {
return envJs;
}
/**
* Sets the Envjs JavaScript file used by the compiler.
* Must be set before {@link #init()} is called.
*
* @param envJs The Envjs JavaScript file used by the compiler.
*/
public void setEnvJs(URL envJs) {
this.envJs = envJs;
}
/**
* Returns the LESS JavaScript file used by the compiler.
*
* @return The LESS JavaScript file used by the compiler.
*/
public URL getLessJs() {
return lessJs;
}
/**
* Sets the LESS JavaScript file used by the compiler.
* Must be set before {@link #init()} is called.
*
* @param lessJs The LESS JavaScript file used by the compiler.
*/
public void setLessJs(URL lessJs) {
this.lessJs = lessJs;
}
/**
* Returns the custom JavaScript files used by the compiler.
*
* @return The custom JavaScript files used by the compiler.
*/
public List getCustomJs() {
return customJs;
}
/**
* Sets a single custom JavaScript file used by the compiler.
* Must be set before {@link #init()} is called.
*
* @param customJs A single custom JavaScript file used by the compiler.
*/
public void setCustomJs(URL customJs) {
this.customJs = new ArrayList();
this.customJs.add(customJs);
}
/**
* Sets the custom JavaScript files used by the compiler.
* Must be set before {@link #init()} is called.
*
* @param customJs The custom JavaScript files used by the compiler.
*/
public void setCustomJs(List customJs) {
this.customJs = customJs;
}
/**
* Returns whether the compiler will compress the CSS.
*
* @return Whether the compiler will compress the CSS.
*/
public boolean isCompress() {
return compress;
}
/**
* Sets the compiler to compress the CSS.
*
* @param compress If true
, sets the compiler to compress the CSS.
*/
public void setCompress(boolean compress) {
this.compress = compress;
}
/**
* Returns the character encoding used by the compiler when writing the output File
.
*
* @return The character encoding used by the compiler when writing the output File
.
*/
public String getEncoding() {
return encoding;
}
/**
* Sets the character encoding used by the compiler when writing the output File
.
* If not set the platform default will be used.
*
* @param encoding The character encoding used by the compiler when writing the output File
.
*/
public void setEncoding(String encoding) {
this.encoding = encoding;
}
/**
* Initializes this LessCompiler
.
*
* It is not needed to call this method manually, as it is called implicitly by the compile methods if needed.
*
*/
private void init() {
long start = System.currentTimeMillis();
cx = Context.enter();
cx.setOptimizationLevel(-1);
cx.setLanguageVersion(Context.VERSION_1_7);
Global global = new Global();
global.init(cx);
scope = cx.initStandardObjects(global);
try {
URLConnection envCon = envJs.openConnection();
URLConnection lesCon = lessJs.openConnection();
InputStream envStm = envCon.getInputStream();
InputStream lesStm = lesCon.getInputStream();
cx.evaluateReader(scope, new InputStreamReader(envStm), "env.rhino.js", 1, null);
cx.evaluateReader(scope, new InputStreamReader(lesStm), "less.js", 1, null);
for (URL url : customJs) {
cx.evaluateReader(scope, new InputStreamReader(url.openConnection().getInputStream()), url.toString(), 1, null);
}
}
catch (Exception e) {
String message = "Failed to initialize LESS compiler.";
throw new IllegalStateException(message, e);
}
}
public void close() {
if (cx!=null) Context.exit();
}
/**
* Compiles the LESS input String
to CSS.
*
* @param input The LESS input String
to compile.
* @return The CSS.
* @throws LessException
*/
public String compile(String input) throws LessException {
if (cx == null)
init();
try {
scope.put("input", scope, input);
scope.put("result", scope, "");
cx.evaluateString(scope, String.format(COMPILE_STRING, compress), "compile.js", 1, null);
Object result = scope.get("result", scope);
return result.toString();
}
catch (Exception e) {
if (e instanceof JavaScriptException) {
Scriptable value = (Scriptable)((JavaScriptException)e).getValue();
if (value != null && ScriptableObject.hasProperty(value, "message")) {
String message = (String)ScriptableObject.getProperty(value, "message");
throw new LessException(message, e);
}
}
throw new LessException(e);
}
}
/**
* Compiles the LESS input File
to CSS.
*
* @param input The LESS input File
to compile.
* @return The CSS.
* @throws IOException If the LESS file cannot be read.
* @throws LessException
*/
public String compile(File input) throws IOException, LessException {
LessSource lessSource = new LessSource(input);
return compile(lessSource);
}
/**
* Compiles the LESS input File
to CSS and writes it to the specified output File
.
*
* @param input The LESS input File
to compile.
* @param output The output File
to write the CSS to.
* @throws IOException If the LESS file cannot be read or the output file cannot be written.
* @throws LessException
*/
public void compile(File input, File output) throws IOException, LessException {
this.compile(input, output, true);
}
/**
* Compiles the LESS input File
to CSS and writes it to the specified output File
.
*
* @param input The LESS input File
to compile.
* @param output The output File
to write the CSS to.
* @param force 'false' to only compile the LESS input file in case the LESS source has been modified (including imports) or the output file does not exists.
* @throws IOException If the LESS file cannot be read or the output file cannot be written.
* @throws LessException
*/
public void compile(File input, File output, boolean force) throws IOException, LessException {
LessSource lessSource = new LessSource(input);
compile(lessSource, output, force);
}
/**
* Compiles the input LessSource
to CSS.
*
* @param input The input LessSource
to compile.
* @return The CSS.
* @throws LessException
*/
public String compile(LessSource input) throws LessException {
return compile(input.getNormalizedContent());
}
/**
* Compiles the input LessSource
to CSS and writes it to the specified output File
.
*
* @param input The input LessSource
to compile.
* @param output The output File
to write the CSS to.
* @throws IOException If the LESS file cannot be read or the output file cannot be written.
* @throws LessException
*/
public void compile(LessSource input, File output) throws IOException, LessException {
compile(input, output, true);
}
/**
* Compiles the input LessSource
to CSS and writes it to the specified output File
.
*
* @param input The input LessSource
to compile.
* @param output The output File
to write the CSS to.
* @param force 'false' to only compile the input LessSource
in case the LESS source has been modified (including imports) or the output file does not exists.
* @throws IOException If the LESS file cannot be read or the output file cannot be written.
* @throws LessException
*/
public void compile(LessSource input, File output, boolean force) throws IOException, LessException {
if (force || !output.exists() || output.lastModified() < input.getLastModifiedIncludingImports()) {
Files.write(output.toPath(), compile(input).getBytes("UTF8"), CREATE, WRITE);
}
}
}