/* vim: set ts=2 et sw=2 cindent fo=qroca: */
package com.globant.katari.jsmodule.domain;
import java.io.CharArrayWriter;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.List;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.Validate;
import org.mozilla.javascript.ErrorReporter;
import org.mozilla.javascript.EvaluatorException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.yahoo.platform.yui.compressor.JavaScriptCompressor;
/** Bundles a list of files by minifying each one and then concatenating the
* resulting files.
*/
public class DependenciesBundler {
/**
* {@link Logger} used for logging the {@link CompressorErrorReporter}.
*/
private static Logger log =
LoggerFactory.getLogger(DependenciesBundler.class.getName());
/** Column number where to insert a break line.
*
* If it's -1 no break line will be inserted. The default value is 80.
*/
private static final int LINE_BREAK_POSTION = 80;
/** Indicates if the file will be minified or not.
*
* The default value is true.
*/
private static final boolean MUNGE = true;
/** Indicates if messages and warnings will be logged or not.
*
* The default value is false.
*/
private static final boolean VERBOSE = false;
/** Indicates if all semicolons will be preserved or not.
*
* The default value is false.
*/
private static final boolean PRESERVE_ALL_SEMICOLONS = false;
/** Indicates if all micro optimizations will be disabled or not.
*
* The default value is true.
*/
private static final boolean DISABLE_OPTIMIZATIONS = false;
/** Bundles a list of files by minifying each of them, and then concatenating
* the minified files.
*
* @param resources The list of resources to be bundled. Cannot be null.
* @return A string representing the content of the bundled file.
*/
public String bundleFiles(final List<String> resources) {
Validate.notNull(resources, "The files to be bundled cannot be null.");
StringBuilder bundle = new StringBuilder();
for (String resource : resources) {
bundle.append("/***************************************************\n");
bundle.append(" * Bundled from '" + resource + "'\n");
bundle.append(" ***************************************************/\n");
bundle.append(compressFile(resource));
bundle.append("\n");
}
return bundle.toString();
}
/** Compresses a single js file.
*
* @param resource the resource name of the file to compress. The resource
* will be loaded from the current class' class loader. It cannot be null.
*
* @return a string with the compressed file. It never returns null.
*/
private String compressFile(final String resource) {
Validate.notNull(resource, "The resource cannot be null.");
JavaScriptCompressor compressor;
InputStream resourceContent = getClass().getResourceAsStream(resource);
// This writer does not need to be closed.
CharArrayWriter compressedFile;
try {
compressor = new JavaScriptCompressor(
new InputStreamReader(resourceContent, "UTF-8"),
new CompressorErrorReporter());
compressedFile = new CharArrayWriter();
compressor.compress(compressedFile, LINE_BREAK_POSTION, MUNGE,
VERBOSE, PRESERVE_ALL_SEMICOLONS, DISABLE_OPTIMIZATIONS);
} catch (RuntimeException e) {
// Rethrow, don't wrap a runtime.
throw e;
} catch (Exception e) {
throw new RuntimeException("Error compressing " + resource, e);
} finally {
IOUtils.closeQuietly(resourceContent);
}
return compressedFile.toString();
}
/** Contain callback functions that will be called when the compressor finds
* warnings or errors in the JavaScript code.
*
* It's used by the constructor of the {@link JavaScriptCompressor}.
*/
private static class CompressorErrorReporter implements ErrorReporter {
/** {@inheritDoc} */
public void warning(final String message, final String sourceName,
final int line, final String lineSource, final int lineOffset) {
if (line < 0) {
log.warn(message);
} else {
log.warn(line + ':' + lineOffset + ':' + message);
}
}
/** {@inheritDoc} */
public void error(final String message, final String sourceName,
final int line, final String lineSource, final int lineOffset) {
if (line < 0) {
log.error(message);
} else {
log.error(line + ':' + lineOffset + ':' + message);
}
}
/** {@inheritDoc} */
public EvaluatorException runtimeError(final String message,
final String sourceName, final int line, final String lineSource,
final int lineOffset) {
error(message, sourceName, line, lineSource, lineOffset);
return new EvaluatorException(message);
}
}
}