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

com.googlecode.htmlcompressor.compressor.ClosureJavaScriptCompressor Maven / Gradle / Ivy

Go to download

HtmlCompressor is a small, fast and very easy to use Java library that minifies given HTML or XML source by removing extra whitespaces, comments and other unneeded characters without breaking the content structure. As a result pages become smaller in size and load faster. A command-line version of the compressor is also available.

There is a newer version: 2.0.2
Show newest version
/**
 *    Copyright 2009-2017 the original author or authors.
 *
 *    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.
 */
package com.googlecode.htmlcompressor.compressor;

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

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.collect.Lists;
import com.google.common.io.ByteStreams;
import com.google.javascript.jscomp.CompilationLevel;
import com.google.javascript.jscomp.Compiler;
import com.google.javascript.jscomp.CompilerOptions;
import com.google.javascript.jscomp.SourceFile;
import com.google.javascript.jscomp.Result;
import com.google.javascript.jscomp.WarningLevel;

/**
 * Basic JavaScript compressor implementation using Google Closure
 * Compiler that could be used by {@link HtmlCompressor} for inline JavaScript compression.
 * 
 * @author Sergiy Kovalchuk
 * 
 * @see HtmlCompressor#setJavaScriptCompressor(Compressor)
 * @see Google Closure Compiler
 */
public class ClosureJavaScriptCompressor implements Compressor {

    /** The constant LOGGER. */
    private static final Logger logger                       = LoggerFactory
            .getLogger(ClosureJavaScriptCompressor.class);

    /** The Constant COMPILATION_LEVEL_SIMPLE. */
    public static final String  COMPILATION_LEVEL_SIMPLE     = "simple";

    /** The Constant COMPILATION_LEVEL_ADVANCED. */
    public static final String  COMPILATION_LEVEL_ADVANCED   = "advanced";

    /** The Constant COMPILATION_LEVEL_WHITESPACE. */
    public static final String  COMPILATION_LEVEL_WHITESPACE = "whitespace";

    // Closure compiler default settings

    /** The compiler options. */
    private CompilerOptions     compilerOptions              = new CompilerOptions();

    /** The compilation level. */
    private CompilationLevel    compilationLevel             = CompilationLevel.SIMPLE_OPTIMIZATIONS;

    /** The logging level. */
    private Level               loggingLevel                 = Level.SEVERE;

    /** The warning level. */
    private WarningLevel        warningLevel                 = WarningLevel.DEFAULT;

    /** The custom externs only. */
    private boolean             customExternsOnly;

    /** The externs. */
    private List    externs;

    /**
     * Instantiates a new closure java script compressor.
     */
    public ClosureJavaScriptCompressor() {
        // Required for override.
    }

    /**
     * Instantiates a new closure java script compressor.
     *
     * @param compilationLevel
     *            the compilation level
     */
    public ClosureJavaScriptCompressor(CompilationLevel compilationLevel) {
        this.compilationLevel = compilationLevel;
    }

    @Override
    public String compress(String source) {

        StringWriter writer = new StringWriter();

        // prepare source
        List input = new ArrayList<>();
        input.add(SourceFile.fromCode("source.js", source));

        // prepare externs
        List externsList = new ArrayList<>();
        if (compilationLevel.equals(CompilationLevel.ADVANCED_OPTIMIZATIONS)) {
            // default externs
            if (!customExternsOnly) {
                try {
                    externsList = getDefaultExterns();
                } catch (IOException e) {
                    logger.error("", e);
                }
            }
            // add user defined externs
            if (externs != null) {
                for (SourceFile extern : externs) {
                    externsList.add(extern);
                }
            }
            // add empty externs
            if (externsList.isEmpty()) {
                externsList.add(SourceFile.fromCode("externs.js", ""));
            }
        } else {
            // empty externs
            externsList.add(SourceFile.fromCode("externs.js", ""));
        }

        Compiler.setLoggingLevel(loggingLevel);

        Compiler compiler = new Compiler();
        compiler.disableThreads();

        compilationLevel.setOptionsForCompilationLevel(compilerOptions);
        warningLevel.setOptionsForWarningLevel(compilerOptions);

        Result result = compiler.compile(externsList, input, compilerOptions);

        if (result.success) {
            writer.write(compiler.toSource());
        } else {
            writer.write(source);
        }

        return writer.toString();

    }

    /**
     * Gets the default externs.
     *
     * @return the default externs
     * @throws IOException
     *             Signals that an I/O exception has occurred.
     */
    // read default externs from closure.jar
    private List getDefaultExterns() throws IOException {
        InputStream input = ClosureJavaScriptCompressor.class.getResourceAsStream("/externs.zip");
        List externList = Lists.newLinkedList();
        try (ZipInputStream zip = new ZipInputStream(input)) {
            for (ZipEntry entry; (entry = zip.getNextEntry()) != null;) {
                externList.add(SourceFile.fromInputStream(entry.getName(), ByteStreams.limit(zip, entry.getSize()),
                        Charset.defaultCharset()));
            }
        }
        return externList;
    }

    /**
     * Returns level of optimization that is applied when compiling JavaScript code.
     * 
     * @return CompilationLevel that is applied when compiling JavaScript code.
     * 
     * @see CompilationLevel
     */
    public CompilationLevel getCompilationLevel() {
        return compilationLevel;
    }

    /**
     * Sets level of optimization that should be applied when compiling JavaScript code. If none is provided,
     * CompilationLevel.SIMPLE_OPTIMIZATIONS will be used by default.
     * 
     * 

* Warning: Using CompilationLevel.ADVANCED_OPTIMIZATIONS could break inline JavaScript if * externs are not set properly. * * @param compilationLevel * Optimization level to use, could be set to CompilationLevel.ADVANCED_OPTIMIZATIONS, * CompilationLevel.SIMPLE_OPTIMIZATIONS, CompilationLevel.WHITESPACE_ONLY * * @see Advanced Compilation and * Externs * @see Closure Compiler Compilation * Levels * @see CompilationLevel */ public void setCompilationLevel(CompilationLevel compilationLevel) { this.compilationLevel = compilationLevel; } /** * Returns options that are used by the Closure compiler. * * @return CompilerOptions that are used by the compiler * * @see CompilerOptions */ public CompilerOptions getCompilerOptions() { return compilerOptions; } /** * Sets options that will be used by the Closure compiler. If none is provided, default options constructor will be * used: new CompilerOptions(). * * @param compilerOptions * CompilerOptions that will be used by the compiler * * @see CompilerOptions */ public void setCompilerOptions(CompilerOptions compilerOptions) { this.compilerOptions = compilerOptions; } /** * Returns logging level used by the Closure compiler. * * @return Level of logging used by the Closure compiler */ public Level getLoggingLevel() { return loggingLevel; } /** * Sets logging level for the Closure compiler. * * @param loggingLevel * logging level for the Closure compiler. * * @see java.util.logging.Level */ public void setLoggingLevel(Level loggingLevel) { this.loggingLevel = loggingLevel; } /** * Returns SourceFile used as a reference during the compression at * CompilationLevel.ADVANCED_OPTIMIZATIONS level. * * @return SourceFile used as a reference during compression */ public List getExterns() { return externs; } /** * Sets external JavaScript files that are used as a reference for function declarations if * CompilationLevel.ADVANCED_OPTIMIZATIONS compression level is used. * *

* A number of default externs defined inside Closure's jar will be used besides user defined ones, to use only user * defined externs set {@link #setCustomExternsOnly(boolean) setCustomExternsOnly(true)} * *

* Warning: Using CompilationLevel.ADVANCED_OPTIMIZATIONS could break inline JavaScript if * externs are not set properly. * * @param externs * SourceFile to use as a reference during compression * * @see #setCompilationLevel(CompilationLevel) * @see #setCustomExternsOnly(boolean) * @see Advanced Compilation and * Externs * @see SourceFile */ public void setExterns(List externs) { this.externs = externs; } /** * Returns WarningLevel used by the Closure compiler. * * @return WarningLevel used by the Closure compiler */ public WarningLevel getWarningLevel() { return warningLevel; } /** * Indicates the amount of information you want from the compiler about possible problems in your code. * * @param warningLevel * WarningLevel to use * * @see Google Closure Compiler */ public void setWarningLevel(WarningLevel warningLevel) { this.warningLevel = warningLevel; } /** * Returns true if default externs defined inside Closure's jar are ignored and only user defined ones * are used. * * @return true if default externs defined inside Closure's jar are ignored and only user defined ones * are used */ public boolean isCustomExternsOnly() { return customExternsOnly; } /** * If set to true, default externs defined inside Closure's jar will be ignored and only user defined * ones will be used. * * @param customExternsOnly * true to skip default externs and use only user defined ones * * @see #setExterns(List) * @see #setCompilationLevel(CompilationLevel) */ public void setCustomExternsOnly(boolean customExternsOnly) { this.customExternsOnly = customExternsOnly; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy