All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.lesscss.LessCompiler Maven / Gradle / Ivy

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); } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy