com.googlecode.osde.internal.jscompiler.ClosureCompiler.java Source code

Java tutorial

Introduction

Here is the source code for com.googlecode.osde.internal.jscompiler.ClosureCompiler.java

Source

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership. The ASF licenses this file
 * to you 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.
 */

package com.googlecode.osde.internal.jscompiler;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.logging.Level;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;

import com.google.common.base.Charsets;
import com.google.common.io.LimitInputStream;
import com.google.javascript.jscomp.BasicErrorManager;
import com.google.javascript.jscomp.CheckLevel;
import com.google.javascript.jscomp.CompilationLevel;
import com.google.javascript.jscomp.Compiler;
import com.google.javascript.jscomp.CompilerOptions;
import com.google.javascript.jscomp.DefaultCodingConvention;
import com.google.javascript.jscomp.JSError;
import com.google.javascript.jscomp.JSSourceFile;
import com.google.javascript.jscomp.WarningLevel;

/**
 * A wrapper class around the Google Closure Compiler providing a simplified
 * API for OSDE. This implementation is a simplified version of
 * <code>com.google.javascript.jscomp.AbstractCompilerRunner</code>.
 * Note that all binary dependencies to the compiler are hidden inside this
 * class.
 *
 * @author Dolphin Chi-Ngai Wan
 */
public class ClosureCompiler implements JavaScriptCompiler {
    private final Charset inputCharset;
    private final List<String> inputFilePaths;
    private final String outputFilePath;
    private final Reporter reporter;

    /**
     * Creates an compiler instance. Note that compiler instances are not
     * reusable.
     *
     * @param input The absolute path to an input JavaScript source file.
     * @param outputFile The absolute path to compiled source file.
     * @param charset The encoding of the input file.
     * @param reporter
     */
    public ClosureCompiler(String input, String outputFile, String charset, Reporter reporter) {
        this.reporter = reporter;
        this.inputCharset = Charset.forName(charset);
        this.inputFilePaths = Collections.singletonList(input);
        this.outputFilePath = outputFile;
    }

    public InputStream compile() throws IOException {
        com.google.javascript.jscomp.Compiler.setLoggingLevel(Level.WARNING);

        Compiler compiler = createCompiler();
        CompilerOptions options = createOptions();

        JSSourceFile[] inputs = createInputs();
        JSSourceFile[] externs = createExterns();

        com.google.javascript.jscomp.Result result = compiler.compile(externs, inputs, options);

        reportIssues(result.errors, result.warnings);

        String compiledSource = result.success ? compiler.toSource() : "";
        return new ByteArrayInputStream(compiledSource.getBytes(options.outputCharset.name()));
    }

    private void reportIssues(JSError[] errors, JSError[] warnings) {
        for (JSError error : errors) {
            reporter.reportIssue(true, error.description, error.lineNumber);
        }

        for (JSError warning : warnings) {
            reporter.reportIssue(false, warning.description, warning.lineNumber);
        }
    }

    private CompilerOptions createOptions() {
        CompilerOptions options = new CompilerOptions();

        CompilationLevel.SIMPLE_OPTIMIZATIONS.setOptionsForCompilationLevel(options);
        WarningLevel.DEFAULT.setOptionsForWarningLevel(options);

        options.setCodingConvention(new DefaultCodingConvention());
        options.closurePass = true;
        options.jsOutputFile = outputFilePath;

        // Let the outputCharset be the same as the input charset... except if
        // we're reading in UTF-8 by default.  By tradition, we've always
        // output ASCII to avoid various hiccups with different browsers,
        // proxies and firewalls.
        options.outputCharset = (inputCharset == Charsets.UTF_8) ? Charsets.US_ASCII : inputCharset;

        return options;
    }

    /**
     * Creates a compiler replacing the default error manager because it writes
     * to stdout and stderr which affects the Eclipse's 'Console' view.
     */
    private Compiler createCompiler() {
        return new Compiler(new BasicErrorManager() {
            @Override
            public void println(CheckLevel level, JSError error) {
                // noop.
            }

            @Override
            protected void printSummary() {
                // noop.
            }
        });
    }

    /**
     * Creates a list of JavaScript files which contains browser-predefined
     * JavaScript object names that should be preserved under optimization.
     */
    private JSSourceFile[] createExterns() throws IOException {
        // The externs.zip file is bundled inside the compiler's jar.
        InputStream input = Compiler.class.getResourceAsStream("/externs.zip");
        ZipInputStream zip = new ZipInputStream(input);
        List<JSSourceFile> externs = new ArrayList<JSSourceFile>();
        for (ZipEntry entry; (entry = zip.getNextEntry()) != null;) {
            LimitInputStream entryStream = new LimitInputStream(zip, entry.getSize());
            externs.add(JSSourceFile.fromInputStream(entry.getName(), entryStream));
        }

        return externs.toArray(new JSSourceFile[externs.size()]);
    }

    private JSSourceFile[] createInputs() throws IOException {
        if (inputFilePaths.isEmpty()) {
            throw new IllegalArgumentException("source files not specified");
        }

        List<JSSourceFile> inputs = new ArrayList<JSSourceFile>(inputFilePaths.size());
        for (String filename : inputFilePaths) {
            JSSourceFile newFile = JSSourceFile.fromFile(filename, inputCharset);
            inputs.add(newFile);
        }

        return inputs.toArray(new JSSourceFile[inputs.size()]);
    }

}