001    /*
002     * Copyright © 2008, 2009 Pedro Agulló Soliveres.
003     * 
004     * This file is part of DirectJNgine.
005     *
006     * DirectJNgine is free software: you can redistribute it and/or modify
007     * it under the terms of the GNU General Public License as published by
008     * the Free Software Foundation, either version 3 of the License.
009     * 
010     * Commercial use is permitted to the extent that the code/component(s)
011     * do NOT become part of another Open Source or Commercially developed
012     * licensed development library or toolkit without explicit permission.
013     *
014     * DirectJNgine is distributed in the hope that it will be useful,
015     * but WITHOUT ANY WARRANTY; without even the implied warranty of
016     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
017     * GNU General Public License for more details.
018     *
019     * You should have received a copy of the GNU General Public License
020     * along with DirectJNgine.  If not, see <http://www.gnu.org/licenses/>.
021     * 
022     * This software uses the ExtJs library (http://extjs.com), which is 
023     * distributed under the GPL v3 license (see http://extjs.com/license).
024     */
025    
026    package com.softwarementors.extjs.djn.jscodegen;
027    
028    import java.io.IOException;
029    import java.io.Reader;
030    import java.io.StringReader;
031    import java.io.StringWriter;
032    import java.io.UnsupportedEncodingException;
033    import java.io.Writer;
034    
035    import org.apache.log4j.Logger;
036    import org.mozilla.javascript.ErrorReporter;
037    import org.mozilla.javascript.EvaluatorException;
038    
039    import com.softwarementors.extjs.djn.StringUtils;
040    import com.softwarementors.extjs.djn.Timer;
041    import com.yahoo.platform.yui.compressor.JavaScriptCompressor;
042    
043    public class Minifier {
044    
045      private static final Logger logger = Logger.getLogger( Minifier.class);
046    
047      private Minifier() {
048        // Avoid instantation
049      }
050    
051      public static String getMinifiedFileName(String file) {
052        assert !StringUtils.isEmpty(file);
053        
054        String result = file.replace( ".js", "-min.js" );
055        return result;
056      }
057    
058      public static final String minify( String input, String inputFilename, int debugCodeLength ) throws IOException {
059        assert input != null;
060        assert !StringUtils.isEmpty(inputFilename);
061        assert debugCodeLength > 0;
062    
063        try {
064          Timer timer = new Timer();      
065          // logger.debug( "Starting minification for '" + inputFilename + "'...");
066          Reader in = new StringReader( input );
067          JavaScriptCompressor compressor = new JavaScriptCompressor(in, new ErrorReporter() {
068            public void warning(String message, String sourceName,
069                int line, String lineSource, int lineOffset) {
070              if (line < 0) {
071                logger.warn("Minifier Warning: " + message);
072              } else {
073                logger.warn("Minifier Warning, " + line + ':' + lineOffset + ':' + message);
074              }
075            }
076    
077            public void error(String message, String sourceName,
078                int line, String lineSource, int lineOffset) {
079              if (line < 0) {
080                logger.warn("Minifier Error: " + message);
081              } else {
082                logger.warn("Minifier Error, " + line + ':' + lineOffset + ':' + message);
083              }
084            }
085    
086            public EvaluatorException runtimeError(String message, String sourceName,
087                int line, String lineSource, int lineOffset) {
088              error(message, sourceName, line, lineSource, lineOffset);
089              return new EvaluatorException(message);
090            }
091          });
092    
093          // Close the input stream first, and then open the output stream,
094          // in case the output file should override the input file.
095          in.close(); in = null;
096    
097          try {
098            Writer out = new StringWriter();
099            boolean munge = true;
100            boolean preserveAllSemiColons = false;
101            boolean disableOptimizations = false;
102            boolean verbose = false;
103            int linebreakpos = 0;
104    
105            compressor.compress(out, linebreakpos, munge, verbose,
106                preserveAllSemiColons, disableOptimizations);
107            out.close();
108            String result = out.toString();
109            if( logger.isDebugEnabled() ) {
110              timer.stop();
111              int compressionPercentage = 100 - (result.length() * 100 / debugCodeLength);
112              timer.logDebugTimeInMilliseconds( "Finished minification for '" + inputFilename + "'. Debug code length: " + debugCodeLength + ", Minified length: " + result.length() + ", Compression: " + compressionPercentage + "%. Time");
113            }
114            return result;
115          }
116          catch (UnsupportedEncodingException e) {
117            logger.warn( "Unable to minify '" + inputFilename + "'.", e );
118            return null;
119          }
120        } 
121        catch (EvaluatorException e) {
122          logger.warn( "Unable to minify '" + inputFilename + "' due to a problem with the Javascript evaluator.", e );
123          return null;
124        }
125      }
126    
127    }