/*
* Copyright 2011 PrimeFaces Extensions.
*
* 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.
*
* $Id: ClosureCompilerOptimizer.java 513 2011-12-01 18:58:10Z ovaraksin@googlemail.com $
*/
package org.primefaces.extensions.optimizerplugin;
import java.io.File;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.logging.Log;
import org.codehaus.plexus.util.FileUtils;
import org.codehaus.plexus.util.StringUtils;
import com.google.common.io.Files;
import com.google.javascript.jscomp.CompilationLevel;
import com.google.javascript.jscomp.Compiler;
import com.google.javascript.jscomp.CompilerOptions;
import com.google.javascript.jscomp.JSError;
import com.google.javascript.jscomp.JSSourceFile;
import com.google.javascript.jscomp.Result;
import com.google.javascript.jscomp.WarningLevel;
/**
* Class for Google Closure Compiler doing JavaScript optimization.
*
* @author Oleg Varaksin / last modified by $Author: ovaraksin@googlemail.com $
* @version $Revision: 513 $
* @since 0.1
*/
public class ClosureCompilerOptimizer extends AbstractOptimizer {
// Note: empty externs, probably CommandLineRunner.getDefaultExterns() would be better
private static final List<JSSourceFile> EXTERNS_EMPTY = new ArrayList<JSSourceFile>();
private static final String OPTIMIZED_FILE_EXTENSION = ".optjs";
@Override
public void optimize(final ResourcesSetAdapter rsa, final Log log) throws MojoExecutionException {
CompilationLevel compLevel = rsa.getCompilationLevel();
CompilerOptions options = new CompilerOptions();
compLevel.setOptionsForCompilationLevel(options);
WarningLevel warnLevel = rsa.getWarningLevel();
warnLevel.setOptionsForWarningLevel(options);
com.google.javascript.jscomp.Compiler.setLoggingLevel(Level.WARNING);
try {
Charset cset = Charset.forName(rsa.getEncoding());
if (rsa.getAggregation() == null) {
// no aggregation
for (File file : rsa.getFiles()) {
log.info("Optimize JS file " + file.getName() + " ...");
addToOriginalSize(file);
JSSourceFile jsSourceFile = JSSourceFile.fromFile(file, cset);
List<JSSourceFile> interns = new ArrayList<JSSourceFile>();
interns.add(jsSourceFile);
// compile
Compiler compiler = compile(log, interns, options, rsa.isFailOnWarning());
// generate output
String path = file.getCanonicalPath();
if (StringUtils.isNotBlank(rsa.getSuffix())) {
// write compiled content into the new file
File outputFile = getFileWithSuffix(path, rsa.getSuffix());
Files.write(compiler.toSource(), outputFile, cset);
// statistic
addToOptimizedSize(outputFile);
} else {
// path of temp. file
String pathOptimized = FileUtils.removeExtension(path) + OPTIMIZED_FILE_EXTENSION;
// create a new temp. file
File outputFile = new File(pathOptimized);
Files.touch(outputFile);
// write compiled content into the new file and rename it (overwrite the original file)
Files.write(compiler.toSource(), outputFile, cset);
FileUtils.rename(outputFile, file);
// statistic
addToOptimizedSize(file);
}
}
} else if (rsa.getAggregation().getOutputFile() != null) {
// aggregation to one output file
File outputFile;
if (!rsa.getAggregation().isWithoutCompress()) {
// with compressing before aggregation
List<JSSourceFile> interns = new ArrayList<JSSourceFile>();
for (File file : rsa.getFiles()) {
log.info("Optimize JS file " + file.getName() + " ...");
addToOriginalSize(file);
interns.add(JSSourceFile.fromFile(file, cset));
}
// compile
Compiler compiler = compile(log, interns, options, rsa.isFailOnWarning());
int filesCount = rsa.getFiles().size();
if (rsa.getAggregation().getPrependedFile() != null) {
filesCount++;
}
if (filesCount > 1) {
log.info("Aggregation is running ...");
}
// get right output file
outputFile = getOutputFile(rsa);
long sizeBefore = outputFile.length();
if (rsa.getAggregation().getPrependedFile() != null) {
// write / append to be prepended file into / to the output file
prependFile(rsa.getAggregation().getPrependedFile(), outputFile, cset, rsa.getEncoding());
}
// write / append compiled content into / to the output file
Files.append(compiler.toSource(), outputFile, cset);
// statistic
addToOptimizedSize(outputFile.length() - sizeBefore);
if (filesCount > 1) {
log.info(filesCount + " files were successfully aggregated.");
}
} else {
// only aggregation without compressing
outputFile = aggregateFiles(rsa, cset, log);
}
// delete single files if necessary
deleteFilesIfNecessary(rsa, log);
// rename aggregated file if necessary
renameOutputFileIfNecessary(rsa, outputFile);
} else {
// should not happen
log.error("Wrong plugin's internal state.");
}
} catch (Exception e) {
throw new MojoExecutionException("Resources optimization failure: " + e.getLocalizedMessage(), e);
}
}
protected Compiler compile(final Log log, final List<JSSourceFile> interns, final CompilerOptions options,
final boolean failOnWarning) throws MojoExecutionException {
// compile
com.google.javascript.jscomp.Compiler compiler = new Compiler();
Result result = compiler.compile(EXTERNS_EMPTY, interns, options);
// evaluate result
evalResult(result, log, failOnWarning);
return compiler;
}
protected void evalResult(final Result result, final Log log, final boolean failOnWarning) throws MojoExecutionException {
if (result.warnings != null) {
for (JSError warning : result.warnings) {
log.warn(warning.toString());
}
}
if (result.warnings != null && result.warnings.length > 0 && failOnWarning) {
throw new MojoExecutionException("Resources optimization failure. Please fix warnings and try again.");
}
if (result.errors != null) {
for (JSError error : result.errors) {
log.error(error.toString());
}
}
if (result.errors != null && result.errors.length > 0) {
throw new MojoExecutionException("Resources optimization failure. Please fix errors and try again.");
}
if (!result.success) {
throw new MojoExecutionException("Resources optimization failure. Please fix errors and try again.");
}
}
}