1 /******************************************************************************* 2 * Copyright (c) 2010 - 2013 by Timotei Dolean <timotei21@gmail.com> 3 * 4 * This program and the accompanying materials are made available 5 * under the terms of the Eclipse Public License v1.0 6 * which accompanies this distribution, and is available at 7 * http://www.eclipse.org/legal/epl-v10.html 8 *******************************************************************************/ 9 package org.wesnoth.preprocessor; 10 11 import java.io.File; 12 import java.io.IOException; 13 import java.util.ArrayList; 14 import java.util.HashMap; 15 import java.util.Iterator; 16 import java.util.List; 17 import java.util.Map; 18 import java.util.Map.Entry; 19 20 import org.eclipse.core.filesystem.EFS; 21 import org.eclipse.core.filesystem.IFileStore; 22 import org.eclipse.core.resources.IFile; 23 import org.eclipse.core.resources.IResource; 24 import org.eclipse.core.runtime.Path; 25 import org.eclipse.jface.dialogs.DialogSettings; 26 27 import org.wesnoth.Logger; 28 import org.wesnoth.Messages; 29 import org.wesnoth.preferences.Preferences; 30 import org.wesnoth.preferences.Preferences.Paths; 31 import org.wesnoth.projects.ProjectUtils; 32 import org.wesnoth.utils.EditorUtils; 33 import org.wesnoth.utils.ExternalToolInvoker; 34 import org.wesnoth.utils.ResourceUtils; 35 import org.wesnoth.utils.WorkspaceUtils; 36 37 /** 38 * Utilities class for handling with the preprocessor 39 */ 40 public class PreprocessorUtils 41 { 42 private static class PreprocessorUtilsInstance 43 { 44 private static PreprocessorUtils instance_ = new PreprocessorUtils( ); 45 } 46 47 private Map< String, Long > filesTimeStamps_ = new HashMap< String, Long >( ); 48 49 private static final String PREPROCESSED_FILE_PATH = WorkspaceUtils 50 .getTemporaryFolder( ) 51 + "preprocessed.txt"; 52 PreprocessorUtils( )53 private PreprocessorUtils( ) 54 { 55 filesTimeStamps_ = new HashMap< String, Long >( ); 56 restoreTimestamps( ); 57 } 58 59 /** 60 * Returns the singleton instance. 61 * 62 * @return The {@link PreprocessorUtils} singleton instance. 63 */ getInstance( )64 public static PreprocessorUtils getInstance( ) 65 { 66 return PreprocessorUtilsInstance.instance_; 67 } 68 69 /** 70 * preprocesses a file using the wesnoth's executable, only 71 * if the file was modified since last time checked. 72 * The target directory is the temporary directory + files's path relative 73 * to project 74 * 75 * @param file 76 * the file to process 77 * @param defines 78 * the list of additional defines to be added when preprocessing 79 * the file 80 * @return 81 * -1 - we skipped preprocessing - file was already preprocessed 82 * 0 - preprocessed succesfully 83 * 1 - there was an error 84 */ preprocessFile( IFile file, List< String > defines )85 public int preprocessFile( IFile file, List< String > defines ) 86 { 87 return preprocessFile( file, getPreprocessedFileLocation( file ), 88 getMacrosLocation( file ), defines, true ); 89 } 90 91 /** 92 * preprocesses a file using the wesnoth's executable, only 93 * if the file was modified since last time checked. 94 * The target directory is the temporary directory + files's path relative 95 * to project 96 * 97 * @param file 98 * the file to process 99 * @param macrosFile 100 * The file where macros are stored 101 * @param defines 102 * the list of additional defines to be added when preprocessing 103 * the file 104 * @return 105 * -1 - we skipped preprocessing - file was already preprocessed 106 * 0 - preprocessed succesfully 107 * 1 - there was an error 108 */ preprocessFile( IFile file, String macrosFile, List< String > defines )109 public int preprocessFile( IFile file, String macrosFile, 110 List< String > defines ) 111 { 112 return preprocessFile( file, getPreprocessedFileLocation( file ), 113 macrosFile, defines, true ); 114 } 115 116 /** 117 * preprocesses a file using the wesnoth's executable, only 118 * if the file was modified since last time checked. 119 * 120 * @param file 121 * the file to process 122 * @param targetDirectory 123 * target directory where should be put the results 124 * @param macrosFile 125 * The file where macros are stored 126 * @param defines 127 * the list of additional defines to be added when preprocessing 128 * the file 129 * @param waitForIt 130 * true to wait for the preprocessing to finish 131 * @return 132 * -1 - we skipped preprocessing - file was already preprocessed 133 * 0 - preprocessed succesfully 134 * 1 - there was an error 135 */ preprocessFile( IFile file, String targetDirectory, String macrosFile, List< String > defines, boolean waitForIt )136 public int preprocessFile( IFile file, String targetDirectory, 137 String macrosFile, List< String > defines, boolean waitForIt ) 138 { 139 String filePath = file.getLocation( ).toOSString( ); 140 if( filesTimeStamps_.containsKey( filePath ) 141 && filesTimeStamps_.get( filePath ) >= new File( filePath ) 142 .lastModified( ) ) { 143 Logger.getInstance( ).logTool( 144 "skipped preprocessing a non-modified file: " + filePath ); //$NON-NLS-1$ 145 return - 1; 146 } 147 148 filesTimeStamps_.put( filePath, new File( filePath ).lastModified( ) ); 149 150 Paths paths = Preferences 151 .getPaths( ProjectUtils.getCacheForProject( 152 file.getProject( ) ).getInstallName( ) ); 153 154 List< String > arguments = new ArrayList< String >( ); 155 156 arguments.add( "--config-dir" ); //$NON-NLS-1$ 157 arguments.add( paths.getUserDir( ) ); 158 159 arguments.add( "--data-dir" ); //$NON-NLS-1$ 160 arguments.add( paths.getWorkingDir( ) ); 161 162 if( macrosFile != null && macrosFile.isEmpty( ) == false ) { 163 ResourceUtils.createNewFile( macrosFile ); 164 165 // add the _MACROS_.cfg file 166 arguments.add( "--preprocess-input-macros" ); //$NON-NLS-1$ 167 arguments.add( macrosFile ); 168 169 170 arguments.add( "--preprocess-output-macros" ); //$NON-NLS-1$ 171 arguments.add( macrosFile ); 172 } 173 174 if( Preferences.getBool( Preferences.NO_TERRAIN_GFX ) ) { 175 if( defines == null ) { 176 defines = new ArrayList< String >( ); 177 } 178 defines.add( "NO_TERRAIN_GFX" ); //$NON-NLS-1$ 179 } 180 181 // --preprocess 182 arguments.add( "-p" ); //$NON-NLS-1$ 183 arguments.add( filePath ); 184 arguments.add( targetDirectory ); 185 186 // --preprocess-defines 187 if( defines != null && ! defines.isEmpty( ) ) { 188 arguments.add( "--preprocess-defines" ); //$NON-NLS-1$ 189 190 StringBuilder definesArg = new StringBuilder( ); 191 for( Iterator< String > itor = defines.iterator( ); itor 192 .hasNext( ); ) { 193 if( definesArg.length( ) > 0 ) { 194 definesArg.append( "," ); //$NON-NLS-1$ 195 } 196 197 definesArg.append( itor.next( ) ); 198 } 199 200 arguments.add( definesArg.toString( ) ); 201 } 202 203 Logger.getInstance( ).logTool( "preprocessing file: " + filePath ); //$NON-NLS-1$ 204 ExternalToolInvoker wesnoth = new ExternalToolInvoker( 205 paths.getWesnothExecutablePath( ), arguments ); 206 wesnoth.runTool( ); 207 if( waitForIt ) { 208 return wesnoth.waitForTool( ); 209 } 210 return 0; 211 } 212 213 /** 214 * Opens the preprocessed version of the specified file 215 * 216 * @param file 217 * the file to show preprocessed output 218 * @param openPlain 219 * true if it should open the plain preprocessed version 220 * or false for the normal one 221 */ openPreprocessedFileInEditor( IFile file, boolean openPlain )222 public void openPreprocessedFileInEditor( IFile file, boolean openPlain ) 223 { 224 if( file == null || ! file.exists( ) ) { 225 Logger.getInstance( ).log( "file null or non existent.", //$NON-NLS-1$ 226 Messages.PreprocessorUtils_12 ); 227 return; 228 } 229 EditorUtils 230 .openEditor( getPreprocessedFilePath( file, openPlain, true ) ); 231 } 232 233 /** 234 * Returns the path of the preprocessed file of the specified file 235 * 236 * @param file 237 * The file whom preprocessed file to get 238 * @param plain 239 * True to return the plain version file's file 240 * @param create 241 * if this is true, if the target preprocessed file 242 * doesn't exist it will be created. 243 * @return The {@link IFileStore} which contains the preprocessed file 244 */ getPreprocessedFilePath( IFile file, boolean plain, boolean create )245 public IFileStore getPreprocessedFilePath( IFile file, boolean plain, 246 boolean create ) 247 { 248 IFileStore preprocFile = EFS.getLocalFileSystem( ).getStore( 249 new Path( getPreprocessedFileLocation( file ) ) ); 250 preprocFile = preprocFile.getChild( file.getName( ) 251 + ( plain == true ? ".plain": "" ) ); //$NON-NLS-1$ //$NON-NLS-2$ 252 if( create && ! preprocFile.fetchInfo( ).exists( ) ) { 253 preprocessFile( file, null ); 254 } 255 return preprocFile; 256 } 257 258 /** 259 * Gets the temporary location where that file should be preprocessed 260 * 261 * @param file 262 * The file to get the location for. 263 * @return A string representing the temporary location. 264 */ getPreprocessedFileLocation( IFile file )265 public String getPreprocessedFileLocation( IFile file ) 266 { 267 String targetDirectory = WorkspaceUtils.getTemporaryFolder( ); 268 targetDirectory += file.getProject( ).getName( ) + "/"; //$NON-NLS-1$ 269 targetDirectory += file.getParent( ).getProjectRelativePath( ) 270 .toOSString( ) 271 + "/"; //$NON-NLS-1$ 272 return targetDirectory; 273 } 274 275 /** 276 * Gets the location where the '_MACROS_.cfg' file is for the 277 * specified resource. 278 * 279 * Currently we store just a defines file per project. 280 * 281 * @param resource 282 * The resource to get the location for 283 * @return A string that points to the macros file. 284 */ getMacrosLocation( IResource resource )285 public String getMacrosLocation( IResource resource ) 286 { 287 return WorkspaceUtils 288 .getProjectTemporaryFolder( resource.getProject( ) ) 289 + "/_MACROS_.cfg"; //$NON-NLS-1$ 290 } 291 292 /** 293 * Saves the current timestamps for preprocessed files 294 * to filesystem 295 */ saveTimestamps( )296 public void saveTimestamps( ) 297 { 298 DialogSettings settings = new DialogSettings( "preprocessed" ); //$NON-NLS-1$ 299 try { 300 settings 301 .put( "files", //$NON-NLS-1$ 302 filesTimeStamps_.keySet( ).toArray( 303 new String[filesTimeStamps_.size( )] ) ); 304 List< String > timestamps = new ArrayList< String >( ); 305 for( Long timestamp: filesTimeStamps_.values( ) ) { 306 timestamps.add( timestamp.toString( ) ); 307 } 308 settings 309 .put( "timestamps", //$NON-NLS-1$ 310 timestamps.toArray( new String[timestamps.size( )] ) ); 311 settings.save( PREPROCESSED_FILE_PATH ); 312 } catch( Exception e ) { 313 Logger.getInstance( ).logException( e ); 314 } 315 } 316 317 /** 318 * Restores the timestamps for preprocessed files from 319 * the filesystem 320 */ restoreTimestamps( )321 public void restoreTimestamps( ) 322 { 323 DialogSettings settings = new DialogSettings( "preprocessed" ); //$NON-NLS-1$ 324 filesTimeStamps_.clear( ); 325 326 try { 327 // ensure the creation of a valid file if it doesn't exist 328 if( ! new File( PREPROCESSED_FILE_PATH ).exists( ) ) { 329 settings.save( PREPROCESSED_FILE_PATH ); 330 } 331 332 settings.load( PREPROCESSED_FILE_PATH ); 333 String[] timestamps = settings.getArray( "timestamps" ); //$NON-NLS-1$ 334 String[] files = settings.getArray( "files" ); //$NON-NLS-1$ 335 if( timestamps != null && files != null 336 && timestamps.length == files.length ) { 337 for( int index = 0; index < files.length; ++index ) { 338 filesTimeStamps_.put( files[index], 339 Long.valueOf( timestamps[index] ) ); 340 } 341 } 342 } catch( IOException e ) { 343 Logger.getInstance( ).logException( e ); 344 } 345 } 346 347 /** 348 * Clears all timestamps cached for files that are located in that path 349 * 350 * @param path 351 * The path to match the files to clear their timestamp 352 */ clearTimestampsForPath( String path )353 public void clearTimestampsForPath( String path ) 354 { 355 Iterator< Entry< String, Long >> itor = filesTimeStamps_.entrySet( ) 356 .iterator( ); 357 358 while( itor.hasNext( ) ) { 359 Entry< String, Long > entry = itor.next( ); 360 if( entry.getKey( ).startsWith( path ) ) { 361 itor.remove( ); 362 } 363 } 364 365 saveTimestamps( ); 366 } 367 } 368