001    /*
002     * Copyright © 2008, 2012 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 Lesser 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 Lesser General Public License for more details.
018     *
019     * You should have received a copy of the GNU Lesser 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.RegisteredCode;
039    import com.softwarementors.extjs.djn.api.Registry;
040    import com.softwarementors.extjs.djn.config.GlobalConfiguration;
041    
042    import edu.umd.cs.findbugs.annotations.NonNull;
043    
044    public class CodeFileGenerator {
045      @NonNull private Registry registry;
046    
047      private GlobalConfiguration getGlobalConfiguration() {
048        return this.registry.getGlobalConfiguration();
049      }
050      
051      private CodeFileGenerator( Registry registry) {
052        assert registry != null;
053        
054        this.registry = registry;
055      }
056      
057      @NonNull
058      private static final Logger logger = Logger.getLogger( CodeFileGenerator.class);
059      
060      public static void updateSource( Registry registry, boolean saveApiFiles ) throws IOException {
061        assert registry != null;
062        
063        CodeFileGenerator generator = new CodeFileGenerator(registry);
064        generator.generateCode(saveApiFiles);
065      }
066    
067      private void generateCode(boolean saveApiFiles) throws IOException {
068        boolean minify = getGlobalConfiguration().getMinify();
069        boolean debug = getGlobalConfiguration().getDebug();
070        
071        // Generate code
072        Map<String, RegisteredCode> registeredCodes = new HashMap<String,RegisteredCode>();
073        for( RegisteredApi api : this.registry.getApis() ) {
074          String apiFile = api.getApiFile();
075          String fullApiFileName = api.getFullApiFileName();
076          RegisteredCode code = registeredCodes.get( apiFile );
077          if( code == null ) {
078            code = new RegisteredCode( apiFile, fullApiFileName, minify, debug );
079            registeredCodes.put( apiFile, code);
080          }
081          ApiCodeGenerator generator = new ApiCodeGenerator( getGlobalConfiguration(), api );
082          generator.appendCode(code.getDebugCodeBuilder(), false);
083          generator.appendCode(code.getNonCommentsCodeBuilder(), true);
084        }
085    
086        // Return source-code pairs!
087        for( Map.Entry<String, RegisteredCode> entry : registeredCodes.entrySet()) {
088          String sourceName = entry.getKey();
089          RegisteredCode code = entry.getValue();
090    
091          // Return source name-code pairs!
092          String debugSourceName = getDebugFileName(sourceName);
093          this.registry.addSource(sourceName, code.getCode());
094          this.registry.addSource(debugSourceName, code.getDebugCode());
095          if( minify ) {
096            String minifiedSourceName = Minifier.getMinifiedFileName(sourceName);
097            this.registry.addSource( minifiedSourceName, code.getMinifiedCode());
098          }
099          
100          // Save files, if needed
101          if( saveApiFiles ) {
102            if( logger.isInfoEnabled()) {
103              logger.info( "Creating source files for APIs...");
104            }
105            String defaultFileName = code.getFullApiFileName();  
106            String debugFileName = getDebugFileName(defaultFileName);
107            String minifiedFileName = Minifier.getMinifiedFileName(defaultFileName);
108            updateFile( debugFileName, code.getDebugCode() );
109            updateFile( defaultFileName, code.getCode() );
110            if( minify ) {
111              updateFile( minifiedFileName, code.getMinifiedCode() );
112            }
113            else {
114              deleteFile( minifiedFileName );
115            }
116          }
117          else {
118            if( logger.isInfoEnabled()) {
119              logger.info( "Source files for APIs have not been created: you will need to reference them dynamically");
120            }
121          }
122        }
123      }
124    
125    
126      private void deleteFile( String fullFileName ) throws IOException {
127        assert !StringUtils.isEmpty(fullFileName);
128    
129        File file = new File( fullFileName );
130        if( file.exists()) {
131          if( !file.delete() ) {
132            throw new IOException( "Unable to delete " + fullFileName);
133          }
134          if( logger.isDebugEnabled() ) {
135            logger.debug( "Api file deleted: '" + file.getAbsolutePath() + "'");
136          }
137        }
138      }
139      
140      private void updateFile( String  fullFileName, String code ) throws IOException {
141        assert !StringUtils.isEmpty(fullFileName);
142        assert code != null;
143        
144        File file = new File( fullFileName );    
145        if( !fileContentEquals(file, code) ) {
146          FileUtils.writeStringToFile(file, code);
147          if( logger.isDebugEnabled() ) {
148            logger.debug( "Api file updated: '" + file.getAbsolutePath() + "'");
149          }
150        }
151        else {
152          if( logger.isDebugEnabled() ) {
153            logger.debug( "Api file '" + file.getAbsolutePath() + '\'' + " is up to date: it was not rewritten.");
154          }
155        }
156      }
157    
158      private boolean fileContentEquals(File file, String code) throws IOException {
159        assert file != null;
160        assert code != null;
161        
162        if( file.exists()) {
163          String contents = FileUtils.readFileToString(file);
164          boolean result = contents.equals( code );
165          return result;
166        }
167        return false;
168      }
169      
170      private String getDebugFileName( String file ) {
171        String result = file.replace( ".js", "-debug.js");
172        return result;
173      }
174    }