Java tutorial
/** * Copyright (c) 2013 Edgar Espina * * This file is part of amd4j (https://github.com/jknack/amd4j) * * 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.github.jknack.amd4j; import static org.apache.commons.io.FilenameUtils.removeExtension; import static org.apache.commons.lang3.Validate.notNull; import java.io.BufferedInputStream; import java.io.IOException; import java.io.InputStream; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.logging.Level; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; import org.apache.commons.io.IOUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Sets; import com.google.common.io.ByteStreams; import com.google.javascript.jscomp.CheckLevel; import com.google.javascript.jscomp.ClosureCodingConvention; import com.google.javascript.jscomp.CommandLineRunner; import com.google.javascript.jscomp.CompilationLevel; import com.google.javascript.jscomp.Compiler; import com.google.javascript.jscomp.CompilerOptions; import com.google.javascript.jscomp.DiagnosticGroups; import com.google.javascript.jscomp.JSError; import com.google.javascript.jscomp.Result; import com.google.javascript.jscomp.SourceFile; /** * An optimizer built on top of Google Closure Compiler. * * @author edgar.espina * @since 0.2.0 */ public class ClosureMinifier extends Minifier { /** * The logging system. */ private final Logger logger = LoggerFactory.getLogger(getClass()); /** * The compilation level. */ private CompilationLevel compilationLevel; /** * The externs expected in externs.zip, in sorted order. * Taken from: com.google.javascript.jscomp.CommandLineRunner */ private static final List<String> DEFAULT_EXTERNS_NAMES = ImmutableList.of( // JS externs "es3.js", "es5.js", // Event APIs "w3c_event.js", "w3c_event3.js", "gecko_event.js", "ie_event.js", "webkit_event.js", "w3c_device_sensor_event.js", // DOM apis "w3c_dom1.js", "w3c_dom2.js", "w3c_dom3.js", "gecko_dom.js", "ie_dom.js", "webkit_dom.js", // CSS apis "w3c_css.js", "gecko_css.js", "ie_css.js", "webkit_css.js", // Top-level namespaces "google.js", "chrome.js", "deprecated.js", "fileapi.js", "flash.js", "gears_symbols.js", "gears_types.js", "gecko_xml.js", "html5.js", "ie_vml.js", "iphone.js", "webstorage.js", "w3c_anim_timing.js", "w3c_css3d.js", "w3c_elementtraversal.js", "w3c_geolocation.js", "w3c_indexeddb.js", "w3c_navigation_timing.js", "w3c_range.js", "w3c_selectors.js", "w3c_xml.js", "window.js", "webkit_notifications.js", "webgl.js"); /** * A cached copy of default externs source files. */ List<SourceFile> defaultExterns = null; /** * Creates a new {@link ClosureMinifier}. * * @param compilationLevel The compilation level. Required. */ public ClosureMinifier(final CompilationLevel compilationLevel) { this.compilationLevel = notNull(compilationLevel, "The compilationLevel is required."); try { this.defaultExterns = getDefaultExterns(); } catch (IOException e) { logger.warn("Could not load externs.zip. No closure externs will be used.", e); this.defaultExterns = Collections.<SourceFile>emptyList(); } } @Override public CharSequence minify(final Config config, final CharSequence source) { final CompilerOptions options = new CompilerOptions(); options.setCodingConvention(new ClosureCodingConvention()); options.setOutputCharset("UTF-8"); options.setWarningLevel(DiagnosticGroups.CHECK_VARIABLES, CheckLevel.WARNING); compilationLevel.setOptionsForCompilationLevel(options); Compiler.setLoggingLevel(Level.SEVERE); Compiler compiler = new Compiler(); compiler.disableThreads(); compiler.initOptions(options); String fname = removeExtension(config.getName()) + ".js"; Result result = compiler.compile(defaultExterns, Arrays.asList(SourceFile.fromCode(fname, source.toString())), options); if (result.success) { return compiler.toSource(); } JSError[] errors = result.errors; throw new IllegalStateException(errors[0].toString()); } /** * Build the default list of google closure external variable files. * Taken from: com.google.javascript.jscomp.CommandLineRunner * * @return a mutable list of source files. * @throws IOException On error when working with externs.zip */ protected List<SourceFile> getDefaultExterns() throws IOException { ZipInputStream zip = null; try { InputStream input = CommandLineRunner.class.getResourceAsStream("/externs.zip"); notNull(input, "The externs.zip file was not found within the closure classpath"); zip = new ZipInputStream(input); Map<String, SourceFile> externsMap = Maps.newHashMap(); ZipEntry entry = zip.getNextEntry(); while (entry != null) { BufferedInputStream entryStream = new BufferedInputStream(ByteStreams.limit(zip, entry.getSize())); externsMap.put(entry.getName(), SourceFile.fromInputStream( // Give the files an odd prefix, so that they do not conflict // with the user's files. "externs.zip//" + entry.getName(), entryStream)); entry = zip.getNextEntry(); } Preconditions.checkState(externsMap.keySet().equals(Sets.newHashSet(DEFAULT_EXTERNS_NAMES)), "Externs zip must match our hard-coded list of externs."); // Order matters, so the resources must be added to the result list // in the expected order. List<SourceFile> externs = Lists.newArrayList(); for (String key : DEFAULT_EXTERNS_NAMES) { externs.add(externsMap.get(key)); } return externs; } finally { IOUtils.closeQuietly(zip); } } }