com.googlecode.htmlcompressor.compressor.ClosureJavaScriptCompressor Maven / Gradle / Ivy
Show all versions of htmlcompressor Show documentation
/**
* 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;
}
}