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.File;
029    import java.io.IOException;
030    import java.util.HashMap;
031    import java.util.Map;
032    
033    import org.apache.commons.io.FileUtils;
034    import org.apache.log4j.Logger;
035    
036    import com.softwarementors.extjs.djn.StringUtils;
037    import com.softwarementors.extjs.djn.api.RegisteredApi;
038    import com.softwarementors.extjs.djn.api.Registry;
039    import com.softwarementors.extjs.djn.config.GlobalConfiguration;
040    
041    public class CodeFileGenerator {
042      private Registry registry;
043      Map<String, StringBuilder> debugFileOutputs = new HashMap<String,StringBuilder>();
044      Map<String, StringBuilder> noCommentsFileOutputs = new HashMap<String,StringBuilder>();
045      
046      private GlobalConfiguration getGlobalConfiguration() {
047        return this.registry.getGlobalConfiguration();
048      }
049      
050      private CodeFileGenerator( Registry registry) {
051        assert registry != null;
052        
053        this.registry = registry;
054      }
055      
056      private static final Logger logger = Logger.getLogger( CodeFileGenerator.class);
057      
058      public static void updateApiFiles( Registry registry ) throws IOException {
059        assert registry != null;
060        
061        CodeFileGenerator generator = new CodeFileGenerator(registry);
062        
063        generator.generateCode();
064        generator.saveCode();
065      }
066    
067      private void generateCode() {
068        for( RegisteredApi api : this.registry.getApis() ) {
069          String fileName = api.getFullApiFileName();
070          StringBuilder debugOutput = this.debugFileOutputs.get(fileName);
071          StringBuilder noCommentsOutput = this.noCommentsFileOutputs.get(fileName);
072          assert (debugOutput == null) == (noCommentsOutput == null);
073          
074          if( debugOutput == null) {
075            debugOutput = new StringBuilder();
076            this.debugFileOutputs.put( fileName, debugOutput);
077            
078            noCommentsOutput = new StringBuilder();
079            this.noCommentsFileOutputs.put( fileName, noCommentsOutput);
080          }
081          
082          ApiCodeGenerator generator = new ApiCodeGenerator( getGlobalConfiguration(), api );
083          generator.appendCode(debugOutput, false);
084          generator.appendCode(noCommentsOutput, true);
085        }
086      }
087    
088      private void saveCode() throws IOException 
089      {
090        for( String defaultFileName : this.debugFileOutputs.keySet() ) {
091          String debugFileName = getDebugFileName(defaultFileName);
092          String minifiedFileName = Minifier.getMinifiedFileName(defaultFileName);
093    
094          boolean minify = getGlobalConfiguration().getMinify();
095          boolean debug = getGlobalConfiguration().getDebug();
096          
097          String debugCode = this.debugFileOutputs.get(defaultFileName).toString();
098          String defaultCode = debugCode;
099          String minifiedCode = debugCode;
100          
101          boolean haveMinifiedCode = false;
102          if( minify ) {
103            String noCommentsCode = this.noCommentsFileOutputs.get(defaultFileName).toString();
104            minifiedCode = Minifier.minify(noCommentsCode, defaultFileName, debugCode.length());
105            if( minifiedCode == null ) {
106              logger.warn( "Unable to minify code: using Debug code for '" + minifiedFileName + "'.");
107              minifiedCode = debugCode;
108            }
109            else {
110              haveMinifiedCode = true;
111            }
112          }
113          
114          if( !debug && haveMinifiedCode) {
115            defaultCode = minifiedCode;
116            if( logger.isDebugEnabled()) {
117              logger.debug( "Production mode: using minified code for '" + defaultFileName + "'");
118            }
119          }
120          else {
121            if( logger.isDebugEnabled()) {
122              logger.debug( "Using Debug code for '" + defaultFileName + "'");
123            }
124          }
125    
126          updateFile( debugFileName, debugCode );
127          updateFile( defaultFileName, defaultCode );
128          if( minify ) {
129            updateFile( minifiedFileName, minifiedCode );
130          }
131          else {
132            deleteFile( minifiedFileName );
133          }
134          
135        }
136      }
137    
138      private void deleteFile( String fullFileName ) {
139        assert !StringUtils.isEmpty(fullFileName);
140    
141        File file = new File( fullFileName );
142        if( file.exists()) {
143          file.delete();
144          if( logger.isDebugEnabled() ) {
145            logger.debug( "Api file deleted: '" + file.getAbsolutePath() + "'");
146          }
147        }
148      }
149      
150      private void updateFile( String  fullFileName, String code ) throws IOException {
151        assert !StringUtils.isEmpty(fullFileName);
152        assert code != null;
153        
154        File file = new File( fullFileName );    
155        if( !fileContentEquals(file, code) ) {
156          FileUtils.writeStringToFile(file, code);
157          if( logger.isDebugEnabled() ) {
158            logger.debug( "Api file updated: '" + file.getAbsolutePath() + "'");
159          }
160        }
161        else {
162          if( logger.isDebugEnabled() ) {
163            logger.debug( "Api file '" + file.getAbsolutePath() + '\'' + " is up to date: it was not rewritten.");
164          }
165        }
166      }
167    
168      private boolean fileContentEquals(File file, String code) throws IOException {
169        assert file != null;
170        assert code != null;
171        
172        if( file.exists()) {
173          String contents = FileUtils.readFileToString(file);
174          boolean result = contents.equals( code );
175          return result;
176        }
177        return false;
178      }
179      
180      private String getDebugFileName( String file ) {
181        String result = file.replace( ".js", "-debug.js");
182        return result;
183      }
184    }