LessCompilerImpl.java 10.3 KB
/*
 * Decompiled with CFR 0_118.
 * 
 * Could not load the following classes:
 *  com.adobe.granite.ui.clientlibs.script.CompilerContext
 *  com.adobe.granite.ui.clientlibs.script.ScriptCompiler
 *  com.adobe.granite.ui.clientlibs.script.ScriptResource
 *  com.adobe.granite.ui.clientlibs.script.ScriptResourceProvider
 *  com.adobe.granite.ui.clientlibs.script.Utils
 *  org.apache.commons.io.IOUtils
 *  org.apache.commons.lang.StringEscapeUtils
 *  org.apache.felix.scr.annotations.Component
 *  org.apache.felix.scr.annotations.Service
 *  org.apache.jackrabbit.util.Text
 *  org.mozilla.javascript.Context
 *  org.mozilla.javascript.JavaScriptException
 *  org.mozilla.javascript.Script
 *  org.mozilla.javascript.Scriptable
 *  org.mozilla.javascript.ScriptableObject
 *  org.mozilla.javascript.Undefined
 *  org.slf4j.Logger
 *  org.slf4j.LoggerFactory
 */
package com.adobe.granite.ui.clientlibs.compiler.less.impl;

import com.adobe.granite.ui.clientlibs.script.CompilerContext;
import com.adobe.granite.ui.clientlibs.script.ScriptCompiler;
import com.adobe.granite.ui.clientlibs.script.ScriptResource;
import com.adobe.granite.ui.clientlibs.script.ScriptResourceProvider;
import com.adobe.granite.ui.clientlibs.script.Utils;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.Writer;
import java.util.Collection;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringEscapeUtils;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Service;
import org.apache.jackrabbit.util.Text;
import org.mozilla.javascript.Context;
import org.mozilla.javascript.JavaScriptException;
import org.mozilla.javascript.Script;
import org.mozilla.javascript.Scriptable;
import org.mozilla.javascript.ScriptableObject;
import org.mozilla.javascript.Undefined;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/*
 * This class specifies class file version 49.0 but uses Java 6 signatures.  Assumed Java 6.
 */
@Component
@Service(value={ScriptCompiler.class})
public class LessCompilerImpl
implements ScriptCompiler {
    protected static final Logger log = LoggerFactory.getLogger(LessCompilerImpl.class);
    private Script lessJs;
    private Script helperJs;
    private boolean includeSourceMarkers;

    /*
     * WARNING - Removed try catching itself - possible behaviour change.
     */
    public LessCompilerImpl() {
        Context cx = Context.enter();
        try {
            long t0 = System.currentTimeMillis();
            cx.setOptimizationLevel(9);
            this.lessJs = this.processSource(cx, "/com/adobe/granite/ui/clientlibs/less/less-rhino-1.7.5.js");
            this.helperJs = this.processSource(cx, "/com/adobe/granite/ui/clientlibs/less/helper.js");
            long t1 = System.currentTimeMillis();
            log.info("Initialized LessCompiler in {}ms", (Object)(t1 - t0));
        }
        catch (IOException e) {
            log.error("Error while loading sources.", (Throwable)e);
        }
        finally {
            Context.exit();
        }
    }

    public boolean isIncludeSourceMarkers() {
        return this.includeSourceMarkers;
    }

    public void setIncludeSourceMarkers(boolean includeSourceMarkers) {
        this.includeSourceMarkers = includeSourceMarkers;
    }

    public String getName() {
        return "less";
    }

    public String getMimeType() {
        return "text/css";
    }

    public boolean handles(String s) {
        return "less".equals(s) || ".less".equals(s);
    }

    public String getOutputExtension() {
        return "css";
    }

    /*
     * WARNING - Removed try catching itself - possible behaviour change.
     */
    public void compile(Collection<ScriptResource> src, Writer dst, CompilerContext ctx) throws IOException {
        try {
            Context cx = Context.enter();
            long t0 = System.currentTimeMillis();
            JsTestGlobalScope globalScope = new JsTestGlobalScope();
            cx.initStandardObjects((ScriptableObject)globalScope);
            globalScope.put("resourceLoader", (Scriptable)globalScope, Context.javaToJS((Object)new ResourceLoader(ctx), (Scriptable)globalScope));
            this.lessJs.exec(cx, (Scriptable)globalScope);
            this.helperJs.exec(cx, (Scriptable)globalScope);
            long t1 = System.currentTimeMillis();
            log.info("Setup less compiler environment in {}ms", (Object)(t1 - t0));
            for (ScriptResource r : src) {
                log.info("Compiling {}...", (Object)r.getName());
                String less = LessCompilerImpl.retrieveInputString(r);
                if (log.isDebugEnabled()) {
                    log.debug("less source: {}", (Object)less);
                }
                try {
                    t1 = System.currentTimeMillis();
                    globalScope.put("lessSourceCode", (Scriptable)globalScope, (Object)less);
                    String js = String.format("var result=''; var lesserror;try {var p = new less.Parser({paths:['%s/'], filename: '%s',relativeUrls: true});p.parse(lessSourceCode, function(e, tree){if (e) {lesserror = formatError(e);} else {result=tree.toCSS(); }});} catch (e) {lesserror = formatError(e);}", StringEscapeUtils.escapeJavaScript((String)Text.getRelativeParent((String)r.getName(), (int)1)), r.getName());
                    cx.evaluateString((Scriptable)globalScope, js, "generated.js", 1, (Object)null);
                    long t2 = System.currentTimeMillis();
                    Object result = globalScope.get("result", (Scriptable)globalScope);
                    Object lesserror = globalScope.get("lesserror", (Scriptable)globalScope);
                    if (result instanceof Undefined) {
                        this.dumpError(dst, r.getName(), "result was Undefined", less);
                        continue;
                    }
                    if (!(lesserror instanceof Undefined)) {
                        this.dumpError(dst, r.getName(), String.valueOf(lesserror), less);
                        continue;
                    }
                    String css = result.toString();
                    css = Utils.rewriteUrlsInCss((String)ctx.getDestinationPath(), (String)r.getName(), (String)css);
                    if (log.isDebugEnabled()) {
                        log.debug("compiled output is: {}", (Object)css);
                    }
                    if (this.includeSourceMarkers) {
                        dst.write(String.format("/*---------------------------------------------------------------< %s >---*/%n", Text.getName((String)r.getName())));
                        dst.write(String.format("/* %s (%dms) */%n", r.getName(), t2 - t1));
                    }
                    dst.write(css);
                }
                catch (JavaScriptException ex) {
                    String message = "[could not determine error message]";
                    log.error("failed to compile {}: {} exception:{}", new Object[]{r.getName(), message, ex});
                    this.dumpError(dst, r.getName(), message, less);
                }
                catch (Exception ex) {
                    log.error("unexpected error during compile", (Throwable)ex);
                }
            }
        }
        finally {
            Context.exit();
        }
    }

    private void dumpError(Writer out, String name, String message, String lessSrc) throws IOException {
        log.error("failed to compile less {}: {}", (Object)name, (Object)message);
        out.write("/*****************************************************\n");
        out.write("LESS compilation failed due a JavaScript error!\n\n");
        out.write("Input: " + name + "\n");
        out.write("Error: " + message + "\n\n");
        out.write("(uncompiled LESS src is included below)\n");
        out.write("*****************************************************/\n");
        out.write(lessSrc);
    }

    /*
     * WARNING - Removed try catching itself - possible behaviour change.
     */
    private Script processSource(Context cx, String resourcePath) throws IOException {
        InputStreamReader in = null;
        try {
            InputStream is = this.getClass().getResourceAsStream(resourcePath);
            in = new InputStreamReader(is);
            Script script = cx.compileReader((Reader)in, resourcePath, 0, (Object)null);
            return script;
        }
        finally {
            IOUtils.closeQuietly((Reader)in);
        }
    }

    /*
     * WARNING - Removed try catching itself - possible behaviour change.
     */
    private static String retrieveInputString(ScriptResource r) throws IOException {
        String src = "";
        Reader in = r.getReader();
        try {
            src = IOUtils.toString((Reader)in);
            src = src.replace("\r", "");
            src = src.replace("@import-once", "@import (once)");
        }
        finally {
            IOUtils.closeQuietly((Reader)in);
        }
        return src;
    }

    public static class ResourceLoader {
        private final CompilerContext ctx;

        public ResourceLoader(CompilerContext ctx) {
            this.ctx = ctx;
        }

        public String load(String path) throws FileNotFoundException {
            if (!path.startsWith("/")) {
                LessCompilerImpl.log.warn("Only absolute paths supported in @import statements: {}", (Object)path);
                throw new FileNotFoundException(path);
            }
            String name = Text.getName((String)path);
            String ext = Text.getName((String)name, (char)'.');
            if (ext.length() == 0) {
                path = path + ".less";
            }
            try {
                ScriptResource r = this.ctx.getResourceProvider().getResource(path);
                if (r == null) {
                    throw new FileNotFoundException(path);
                }
                this.ctx.getDependencies().add(path);
                return LessCompilerImpl.retrieveInputString(r);
            }
            catch (Exception e) {
                LessCompilerImpl.log.error("Error while loading @import resource {}", (Object)path, (Object)e);
                throw new FileNotFoundException(path);
            }
        }
    }

    private static class JsTestGlobalScope
    extends ScriptableObject {
        private JsTestGlobalScope() {
        }

        public String getClassName() {
            return "global";
        }
    }

}