Subversion Repository Public Repository

litesoft

Diff Revisions 949 vs 950 for /trunk/Java/ScarPlus/src/com/esotericsoftware/scar/Project.java

Diff revisions: vs.
  @@ -1,1214 +1,1214 @@
1 - package com.esotericsoftware.scar;
2 -
3 - import org.litesoft.logger.*;
4 -
5 - import com.esotericsoftware.filesystem.*;
6 - import com.esotericsoftware.scar.support.*;
7 - import com.esotericsoftware.utils.*;
8 -
9 - import javax.tools.*;
10 - import java.io.*;
11 - import java.util.*;
12 - import java.util.jar.*;
13 - import java.util.zip.*;
14 -
15 - /**
16 - * Generic Data structure that contains information needed to perform tasks.
17 - */
18 - @SuppressWarnings("UnusedDeclaration")
19 - public class Project extends ProjectParameters
20 - {
21 - private static final String JAVA_SOURCE_TARGET_VERSION = "1.7";
22 -
23 - protected static final Logger LOGGER = LoggerFactory.getLogger( Project.class );
24 -
25 - private static final String META_INF_MANIFEST_MF = "META-INF/MANIFEST.MF";
26 -
27 - private static final String VERSIONED_URL_PATTERN_PREFIX = "<url-pattern>/v";
28 - private static final String VERSIONED_SCRIPT_PREFIX = "<script src='v";
29 - private static final String VERSIONED_SCRIPT_SUFFIX = ".nocache.js'></script>";
30 - private static final String VERSIONED_MODULE_PREFIX = "<module rename-to=\"v";
31 - private static final String VERSIONED_MODULE_SUFFIX = "\">";
32 - private static final String JAVA_HOME = "JAVA_HOME";
33 -
34 - public Project( ProjectParameters pParameters )
35 - {
36 - super( pParameters.validate() );
37 - applyDefaults();
38 - }
39 -
40 - public String getSourceJavaVersion()
41 - {
42 - return JAVA_SOURCE_TARGET_VERSION;
43 - }
44 -
45 - public String getTargetJavaVersion()
46 - {
47 - return JAVA_SOURCE_TARGET_VERSION;
48 - }
49 -
50 - protected void packageClean()
51 - {
52 - delete( getPhoneGapDirPath() );
53 - delete( getAppDirPath() );
54 - delete( getOneJarPath() );
55 - delete( getWarPath() );
56 - }
57 -
58 - protected boolean packageIt()
59 - {
60 - return phoneGapDir() | appDir() | oneJAR() | war(); // Note: SINGLE '|' ORs to force full execution!
61 - }
62 -
63 - /**
64 - * Unzips all JARs in the classpath and creates a single JAR containing those files and this Project's JAR (which MUST exist).
65 - * The manifest from the project's JAR is used. Putting everything into a single JAR makes it harder to see what libraries are
66 - * being used, but makes it easier for end users to distribute the application.
67 - * <p/>
68 - * Note: Files with the same path in different JARs will be overwritten. Files in the project's JAR will never be overwritten,
69 - * but may overwrite other files.
70 - *
71 - * @param pExcludeJARs The names of any JARs to exclude.
72 - *
73 - * @return True if the "OneJAR" was created / updated or false if no OneJar is NOT requested for this project or it was not needed.
74 - */
75 - public boolean oneJAR( String... pExcludeJARs )
76 - {
77 - File zOneJarPath = getOneJarPathFile();
78 -
79 - if ( zOneJarPath == null )
80 - {
81 - return false;
82 - }
83 -
84 - File zJarPath = getJarPathFile();
85 - if ( !zJarPath.isFile() )
86 - {
87 - throw new IllegalStateException( "One JAR: " + this + " requested, BUT NO jar File produced at: " + zJarPath.getPath() );
88 - }
89 -
90 - if ( zOneJarPath.isFile() && (zOneJarPath.lastModified() >= zJarPath.lastModified()) )
91 - {
92 - progress( "One JAR: " + this + " NOT Needed!" );
93 - return false;
94 - }
95 -
96 - Paths zClasspath = classpath();
97 - if ( zClasspath.isEmpty() )
98 - {
99 - progress( "One JAR: " + this + " No supporting Jars! Simply Copying to: " + zOneJarPath.getPath() );
100 - copyFile( zJarPath, zOneJarPath );
101 - return true;
102 - }
103 - progress( "One JAR: " + this );
104 -
105 - File zOnejarDir = mkdir( new File( path( "$target$/onejar/" ) ) );
106 -
107 - List<String> zExcludeJARs = Arrays.asList( pExcludeJARs );
108 - for ( File jarFile : zClasspath.getFiles() ) // All our Class Path (dependant) JARS
109 - {
110 - if ( !zExcludeJARs.contains( jarFile.getName() ) )
111 - {
112 - unzip( jarFile, zOnejarDir );
113 - }
114 - }
115 -
116 - unzip( zJarPath, zOnejarDir ); // Our Jar! - Our Manifest will be "the" Manifest !!!!!! Need to remove class PATH!
117 - innerJar( "'ONE' JAR", zOneJarPath.getPath(), new Paths( zOnejarDir.getPath() ) );
118 - return true;
119 - }
120 -
121 - /**
122 - * Collects the distribution files using the "dist" property, the project's JAR file, and everything on the project's classpath
123 - * (including dependency project classpaths) and places them into the specified directory. This is also done for depenency projects,
124 - * recursively. This is everything the application needs to be run from JAR files.
125 - *
126 - * @return True if the PhoneGapDir was populated or false if no distribution (App Dir) is requested for this project.
127 - */
128 - public boolean phoneGapDir()
129 - {
130 - String zPhoneGapDir = getPhoneGapDirPath();
131 - if ( zPhoneGapDir == null )
132 - {
133 - return false;
134 - }
135 - Paths zPaths = new Paths( getGWTwarPath(), "**.js", "**.gif" );
136 - zPaths.add( getDist() );
137 -
138 - File zPhoneGapDirFile = new File( zPhoneGapDir );
139 - if ( zPhoneGapDirFile.exists() )
140 - {
141 - if ( zPhoneGapDirFile.lastModified() >= zPaths.getGreatestLastModified() )
142 - {
143 - progress( "PhoneGapDir: " + this + " NOT Needed!" );
144 - return false;
145 - }
146 - delete( zPhoneGapDirFile );
147 - }
148 -
149 - progress( "PhoneGapDir: " + this + " -> " + zPhoneGapDir );
150 - String distDir = mkdir( zPhoneGapDir ); // Give it a new Timestamp
151 - zPaths.copyTo( distDir );
152 - return true;
153 - }
154 -
155 - /**
156 - * Collects the distribution files using the "dist" property, the project's JAR file, and everything on the project's classpath
157 - * (including dependency project classpaths) and places them into the specified directory. This is also done for depenency projects,
158 - * recursively. This is everything the application needs to be run from JAR files.
159 - *
160 - * @return True if the AppDir was populated or false if no distribution (App Dir) is requested for this project.
161 - */
162 - public boolean appDir()
163 - {
164 - String zAppDir = getAppDirPath();
165 - if ( zAppDir == null )
166 - {
167 - return false;
168 - }
169 - File zJarPath = getJarPathFile();
170 - if ( !zJarPath.isFile() )
171 - {
172 - progress( "AppDir: " + this + " BUT NO jar File produced at: " + zJarPath.getPath() );
173 - return false;
174 - }
175 - Paths zPaths = new Paths();
176 - addDependantProjectsDistPaths( zPaths );
177 - zPaths.add( classpath() );
178 - zPaths.add( FilePath.canonicalize( getJarPathFile() ) );
179 -
180 - File zAppDirFile = new File( zAppDir );
181 - if ( zAppDirFile.exists() )
182 - {
183 - if ( zAppDirFile.lastModified() >= zPaths.getGreatestLastModified() )
184 - {
185 - progress( "AppDir: " + this + " NOT Needed!" );
186 - return false;
187 - }
188 - delete( zAppDirFile );
189 - }
190 -
191 - progress( "AppDir: " + this + " -> " + zAppDir );
192 - String distDir = mkdir( zAppDir ); // Give it a new Timestamp
193 - zPaths.copyTo( distDir );
194 - return true;
195 - }
196 -
197 - /**
198 - * Produce either a 'war' directory or a '.war' file, in theory ready to deploy to a servlet/web container.
199 - *
200 - * @return true if the 'war' was created.
201 - */
202 - public boolean war()
203 - {
204 - String zWar = getWar();
205 - if ( zWar == null )
206 - {
207 - return false;
208 - }
209 - File zJarPath = getJarPathFile();
210 - if ( !zJarPath.isFile() )
211 - {
212 - progress( "WAR: " + this + " BUT NO jar File produced at: " + zJarPath.getPath() );
213 - return false;
214 - }
215 -
216 - Paths zDistPaths = new Paths();
217 - addDependantProjectsDistPaths( zDistPaths );
218 - if ( null != getGWT() )
219 - {
220 - zDistPaths.add( new Paths( getGWTwarPath() ) );
221 - }
222 -
223 - Paths zClassPath = new Paths();
224 - zClassPath.add( FilePath.canonical( getGWTatDir(), GWT_SERVLET ) );
225 - zClassPath.add( classpath() );
226 - zClassPath.add( FilePath.canonicalize( getJarPathFile() ) );
227 -
228 - File zWarPathFile = getWarPathFile();
229 - if ( zWarPathFile.exists() )
230 - {
231 - long zWarLastModified = zWarPathFile.lastModified();
232 - if ( (zWarLastModified >= zClassPath.getGreatestLastModified()) && (zWarLastModified >= zDistPaths.getGreatestLastModified()) )
233 - {
234 - progress( "WAR: " + this + " NOT Needed!" );
235 - return false;
236 - }
237 - delete( zWarPathFile );
238 - }
239 -
240 - boolean zWarIt = zWar.endsWith( ".war" );
241 -
242 - File zWarDir = zWarPathFile;
243 - if ( zWarIt )
244 - {
245 - zWarDir = new File( path( "$target$/war/" ) );
246 - delete( zWarDir );
247 - }
248 -
249 - progress( "WAR: " + this + " -> " + zWarDir.getPath() );
250 -
251 - File zWarDirLibPath = new File( zWarDir, "WEB-INF/lib" );
252 - mkdir( zWarDirLibPath );
253 -
254 - zDistPaths.copyTo( zWarDir.getPath() );
255 - zClassPath.copyTo( zWarDirLibPath.getPath() );
256 -
257 - if ( zWarIt )
258 - {
259 - innerJar( "WAR", zWarPathFile.getPath(), new Paths( zWarDir.getPath() ) );
260 - }
261 - return true;
262 - }
263 -
264 - /**
265 - * Computes the classpath for all the dependencies of the specified project, recursively.
266 - */
267 - protected void addDependantProjectsDistPaths( Paths pPathsToAddTo )
268 - {
269 - for ( Project zProject : mDependantProjects )
270 - {
271 - zProject.addDependantProjectsDistPaths( pPathsToAddTo );
272 - }
273 - pPathsToAddTo.add( getDist() );
274 - }
275 -
276 - protected boolean GWTcompileIt()
277 - {
278 - String[] args = //
279 - { //
280 - getPathJavaJRE(), //
281 - "-Xmx" + getGWTmx(), //
282 - "-cp", //
283 - buildGWTcompileClassPath(), //
284 - "com.google.gwt.dev.Compiler", //
285 - "-logLevel", //
286 - getGWTlogging(), //
287 - "-war", //
288 - getGWTwarPath(), //
289 - "-style", //
290 - getGWTstyle(), //
291 - getGWT() //
292 - };
293 -
294 - Utils.shell( args );
295 - return true;
296 - }
297 -
298 - private String buildGWTcompileClassPath()
299 - {
300 - Paths zGWTclassPath = new Paths();
301 - File zGWTatDir = getGWTatDir();
302 - zGWTclassPath.add( FilePath.canonical( zGWTatDir, GWT_DEV ) );
303 - zGWTclassPath.add( FilePath.canonical( zGWTatDir, GWT_USER ) );
304 - zGWTclassPath.add( FilePath.canonical( zGWTatDir, GWT_VALIDATION ) );
305 - zGWTclassPath.add( FilePath.canonical( zGWTatDir, GWT_VALIDATION_SOURCE ) );
306 - if ( mSources )
307 - {
308 - zGWTclassPath.add( FilePath.canonicalize( getJarPathFile() ) );
309 - }
310 - zGWTclassPath.add( classpath() );
311 - return zGWTclassPath.toString( File.pathSeparator );
312 - }
313 -
314 - protected String getPathJavaJRE()
315 - {
316 - File zJavaHomeDir = assertIsDirectory( JAVA_HOME, new File( assertNotEmpty( JAVA_HOME, System.getenv( JAVA_HOME ) ) ) );
317 - File zJavaDir = new File( zJavaHomeDir, "jre/bin" );
318 - if ( !zJavaDir.isDirectory() )
319 - {
320 - if ( !(zJavaDir = new File( zJavaHomeDir, "bin" )).isDirectory() )
321 - {
322 - throw new IllegalStateException( "Unable to find JAVA_HOME bin directory under: " + zJavaHomeDir );
323 - }
324 - }
325 - File zJavaExecutable = new File( zJavaDir, isWindows ? "java.exe" : "java" );
326 - if ( zJavaExecutable.isFile() )
327 - {
328 - return getCanonicalFile( zJavaExecutable ).getPath();
329 - }
330 - throw new IllegalStateException( "Unable to find JAVA_HOME based executable at: " + zJavaExecutable );
331 - }
332 -
333 - protected File getGeneratedGWT_nocache_jsFile()
334 - {
335 - String zGWTxmlRelativeFilePath = getGWT().replace( '.', '/' ) + ".gwt.xml"; // e.g. org.litesoft.sandbox.csapp.CSapp
336 - File zFound = getGeneratedGWT_nocache_jsFile( zGWTxmlRelativeFilePath, getSource() );
337 - if ( zFound == null )
338 - {
339 - if ( null == (zFound = getGeneratedGWT_nocache_jsFile( zGWTxmlRelativeFilePath, getResources() )) )
340 - {
341 - throw new IllegalArgumentException( "Unable to locate GWT module file: " + getGWT() );
342 - }
343 - }
344 - return zFound;
345 - }
346 -
347 - private File getGeneratedGWT_nocache_jsFile( String pGWTxmlRelativeFilePath, Paths pPaths )
348 - {
349 - RootedPaths[] zRootedPaths = pPaths.getRootedPaths();
350 - for ( RootedPaths zPath : zRootedPaths )
351 - {
352 - File zFile = new File( zPath.getCanonicalRootDirectory(), pGWTxmlRelativeFilePath );
353 - if ( zFile.isFile() )
354 - {
355 - String moduleName = extractModuleNameFrom( getGWT(), fileContents( zFile ) );
356 - return new File( getGWTwarPath(), moduleName + "/" + moduleName + ".nocache.js" );
357 - }
358 - }
359 - return null;
360 - }
361 -
362 - private String extractModuleNameFrom( String pGWTmoduleReference, String pGWTmoduleFileContents )
363 - {
364 - int at = pGWTmoduleFileContents.indexOf( " rename-to" );
365 - if ( at != -1 ) // . . . . . . . . . . . .01234567890
366 - {
367 - int upTo = pGWTmoduleFileContents.indexOf( '>', at += 10 );
368 - if ( upTo != -1 )
369 - {
370 - String stuff = pGWTmoduleFileContents.substring( at, upTo ).trim();
371 - if ( stuff.startsWith( "=" ) )
372 - {
373 - if ( (stuff = stuff.substring( 1 ).trim()).length() > 2 )
374 - {
375 - char c = stuff.charAt( 0 );
376 - if ( (c == '"') || (c == '\'') )
377 - {
378 - if ( -1 != (at = stuff.indexOf( c, 1 )) )
379 - {
380 - return stuff.substring( 1, at );
381 - }
382 - }
383 - }
384 - }
385 - }
386 - }
387 - String s = "." + pGWTmoduleReference;
388 - return s.substring( s.lastIndexOf( '.' ) + 1 );
389 - }
390 -
391 - protected boolean needToCompileGWT()
392 - {
393 - File zGeneratedGWT_nocache_jsFile = getGeneratedGWT_nocache_jsFile();
394 - return needToBuild( ((zGeneratedGWT_nocache_jsFile != null) && zGeneratedGWT_nocache_jsFile.isFile()) ?
395 - zGeneratedGWT_nocache_jsFile.lastModified() : forceBuildLastModified() );
396 - }
397 -
398 - public boolean GWTcompile()
399 - {
400 - String zGWT = getGWT();
401 - if ( zGWT == null )
402 - {
403 - return false;
404 - }
405 - if ( !needToCompileGWT() )
406 - {
407 - progress( "GWT Compile: " + this + " NOT Needed!" );
408 - return false;
409 - }
410 - progress( "GWT Compile: " + this );
411 - return GWTcompileIt();
412 - }
413 -
414 - /**
415 - * Assert that this project is currently a 'Versioned' GWT project, and then rev the version number by 1
416 - */
417 - public void versionGWT()
418 - {
419 - File zWarWebXmlFile = new File( mCanonicalProjectDir, "war/WEB-INF/web.xml" );
420 - String zWarWebXml = fileContents( assertIsFile( "web.xml", zWarWebXmlFile ) );
421 -
422 - int zCurVersion = extractVersionFromUrlPattern( zWarWebXml );
423 -
424 - String zWarResourceRelativePathCurrent = "warResources/v" + zCurVersion;
425 -
426 - File zIndexHtmlFile = new File( mCanonicalProjectDir, zWarResourceRelativePathCurrent + "/index.html" );
427 - String zIndexHtml = assertVersionedIndexHtml( zIndexHtmlFile, zCurVersion );
428 -
429 - List<File> zVersionedGwtXmlFiles = findVersionedGwtXmlFiles( zCurVersion );
430 -
431 - int zNewVersion = zCurVersion + 1;
432 -
433 - String zWarResourceRelativePathNew = "warResources/v" + zNewVersion;
434 - if ( new File( mCanonicalProjectDir, zWarResourceRelativePathNew ).exists() )
435 - {
436 - throw new IllegalStateException( "Project already contains a 'warResources/v" + zNewVersion + "' directory?" );
437 - }
438 -
439 - progress( "versionGWT: " + this + " | " + zCurVersion + " -> " + (zCurVersion + 1) );
440 - progress( " " + zWarWebXmlFile.getPath() );
441 - progress( " " + zIndexHtmlFile.getPath() );
442 - for ( File zFile : zVersionedGwtXmlFiles )
443 - {
444 - progress( " " + zFile.getPath() );
445 - }
446 - progress( " " + zWarResourceRelativePathCurrent + " -> " + zWarResourceRelativePathNew );
447 -
448 - new Paths( zWarResourceRelativePathCurrent ).copyTo( zWarResourceRelativePathNew );
449 -
450 - updateFileContents( new File( mCanonicalProjectDir, zWarResourceRelativePathNew + "/index.html" ),
451 - updateVersionedIndexHtml( zIndexHtml, zCurVersion, zNewVersion ) );
452 -
453 - updateFileContents( zWarWebXmlFile, updateVersionedWebXml( zWarWebXml, zCurVersion, zNewVersion ) );
454 -
455 - for ( File zFile : zVersionedGwtXmlFiles )
456 - {
457 - updateFileContents( zFile, updateVersionedGwtXmlFile( fileContents( zFile ), zCurVersion, zNewVersion ) );
458 - }
459 - // Update/Create the Current Version's redirect JavaScript file
460 - String zCurPathVersion = "/v" + zCurVersion;
461 - String redirectScript = "var loc = window.location.href;\n" + //
462 - "var at = loc.indexOf( '" + zCurPathVersion + "' );\n" + //
463 - "window.location.href = loc.substring(0, at) + '/v" + zNewVersion + "' + loc.substring(at + " + zCurPathVersion.length() + ");\n";
464 - updateFileContents( new File( mCanonicalProjectDir, zWarResourceRelativePathCurrent + "/v" + zCurVersion + ".nocache.js" ), redirectScript );
465 -
466 - // Update the "root" html (if it exists)
467 - File zRootHtmlFile = new File( mCanonicalProjectDir, "warResources/index.html" );
468 - if ( zRootHtmlFile.isFile() )
469 - {
470 - updateFileContents( zRootHtmlFile, updateRootHTML( fileContents( zRootHtmlFile ), zCurVersion, zNewVersion ) );
471 - }
472 - }
473 -
474 - protected String updateRootHTML( String pFileContents, int pCurVersion, int pNewVersion )
475 - {
476 - // <!DOCTYPE html>
477 - // <html>
478 - // <head>
479 - // <meta http-equiv="Refresh" content="1; url=v1/">
480 - // </head>
481 - // <body>
482 - // <script>window.location.href = 'v1/';</script>
483 - // </body>
484 - // </html>
485 - String zCurPathVersion = "v" + pCurVersion + "/";
486 - String zNewPathVersion = "v" + pNewVersion + "/";
487 - for ( int at; -1 != (at = pFileContents.indexOf( zCurPathVersion )); )
488 - {
489 - pFileContents = pFileContents.substring( 0, at ) + zNewPathVersion + pFileContents.substring( at + zCurPathVersion.length() );
490 - }
491 - return pFileContents;
492 - }
493 -
494 - protected String updateVersionedGwtXmlFile( String pFileContents, int pCurVersion, int pNewVersion )
495 - {
496 - String zCurVersionedModule = VERSIONED_MODULE_PREFIX + pCurVersion + VERSIONED_MODULE_SUFFIX;
497 - String zNewVersionedModule = VERSIONED_MODULE_PREFIX + pNewVersion + VERSIONED_MODULE_SUFFIX;
498 - int at = pFileContents.indexOf( zCurVersionedModule );
499 - return pFileContents.substring( 0, at ) + zNewVersionedModule + pFileContents.substring( at + zCurVersionedModule.length() );
500 - }
501 -
502 - protected List<File> findVersionedGwtXmlFiles( int pVersion )
503 - {
504 - String zVersionedModule = VERSIONED_MODULE_PREFIX + pVersion + VERSIONED_MODULE_SUFFIX;
505 -
506 - ArrayList<File> zVersionedFiles = new ArrayList<File>();
507 -
508 - String zSourceString = get( SOURCE.getName() ) + "|";
509 - Paths zGwtXml = new Paths( zSourceString.substring( 0, zSourceString.indexOf( '|' ) ), "**.gwt.xml" );
510 - for ( File zFile : zGwtXml.getFiles() )
511 - {
512 - if ( fileContents( zFile ).contains( zVersionedModule ) )
513 - {
514 - zVersionedFiles.add( zFile );
515 - }
516 - }
517 - if ( zVersionedFiles.isEmpty() )
518 - {
519 - throw new IllegalStateException(
520 - "Project does not appear to contain a 'gwt.xml' file with the current version module definition of: " + zVersionedModule );
521 - }
522 - return zVersionedFiles;
523 - }
524 -
525 - protected String updateVersionedIndexHtml( String pFileContents, int pCurVersion, int pNewVersion )
526 - {
527 - String zCurVersionedScript = VERSIONED_SCRIPT_PREFIX + pCurVersion + VERSIONED_SCRIPT_SUFFIX;
528 - String zNewVersionedScript = VERSIONED_SCRIPT_PREFIX + pNewVersion + VERSIONED_SCRIPT_SUFFIX;
529 - int at = pFileContents.indexOf( zCurVersionedScript );
530 - return pFileContents.substring( 0, at ) + zNewVersionedScript + pFileContents.substring( at + zCurVersionedScript.length() );
531 - }
532 -
533 - protected String assertVersionedIndexHtml( File pIndexHtmlFile, int pVersion )
534 - {
535 - String zVersionedScript = VERSIONED_SCRIPT_PREFIX + pVersion + VERSIONED_SCRIPT_SUFFIX;
536 -
537 - String zContents = fileContents( assertIsFile( "Versioned index.html", pIndexHtmlFile ) );
538 - if ( !zContents.contains( zVersionedScript ) )
539 - {
540 - throw new IllegalStateException(
541 - "Project's current versioned index.html file (" + pIndexHtmlFile.getPath() + ") does not contain a 'versioned' script element of: " +
542 - zVersionedScript );
543 - }
544 - return zContents;
545 - }
546 -
547 - protected String updateVersionedWebXml( String pFileContents, int pCurVersion, int pNewVersion )
548 - {
549 - String zCurVersionedUrlPattern = VERSIONED_URL_PATTERN_PREFIX + pCurVersion + "/";
550 - String zNewVersionedUrlPattern = VERSIONED_URL_PATTERN_PREFIX + pNewVersion + "/";
551 - for ( int at; -1 != (at = pFileContents.indexOf( zCurVersionedUrlPattern )); )
552 - {
553 - pFileContents = pFileContents.substring( 0, at ) + zNewVersionedUrlPattern + pFileContents.substring( at + zCurVersionedUrlPattern.length() );
554 - }
555 - return pFileContents;
556 - }
557 -
558 - protected int extractVersionFromUrlPattern( String pWarWebXml )
559 - {
560 - for ( int at, from = 0; -1 != (at = pWarWebXml.indexOf( VERSIONED_URL_PATTERN_PREFIX, from )); from = at + 1 )
561 - {
562 - int slashAt = pWarWebXml.indexOf( '/', at += VERSIONED_URL_PATTERN_PREFIX.length() );
563 - if ( slashAt > 0 )
564 - {
565 - try
566 - {
567 - return Integer.parseInt( pWarWebXml.substring( at, slashAt ) );
568 - }
569 - catch ( NumberFormatException acceptable )
570 - {
571 - // path starts w/ a 'v' but is not of pattern "v####"
572 - }
573 - }
574 - }
575 - throw new IllegalStateException( "Project's war/WEB-INF/web.xml does not appear to contain a 'versioned' <url-pattern>." );
576 - }
577 -
578 - /**
579 - * Executes the buildDependencies, clean, compile, jar, [GWTcompile], and then "packageIt" utility methods.
580 - */
581 - public synchronized boolean build()
582 - {
583 - if ( mBuilt )
584 - {
585 - return false;
586 - }
587 - mBuilt = true;
588 - mSources = !getSource().isEmpty();
589 - boolean zAnythingBuilt = false;
590 - boolean zBuildIt;
591 - try
592 - {
593 - zBuildIt = buildDependencies() || needToBuild();
594 - }
595 - catch ( RuntimeException e )
596 - {
597 - progress( "Build: " + this );
598 - throw e;
599 - }
600 - if ( !zBuildIt )
601 - {
602 - progress( "Build: " + this + " NOT Needed!" );
603 - }
604 - else
605 - {
606 - progress( "Build: " + this );
607 - clean();
608 - if ( mSources )
609 - {
610 - compile();
611 - jar();
612 - zAnythingBuilt = true;
613 - }
614 - }
615 - zAnythingBuilt |= GWTcompile();
616 - zAnythingBuilt |= packageIt();
617 - return zAnythingBuilt;
618 - }
619 -
620 - protected boolean needToBuild()
621 - {
622 - return needToBuild( determineOutputLastModified() );
623 - }
624 -
625 - protected boolean needToBuild( long pOutputLastModified )
626 - {
627 - return (mProjectFileLastModified > pOutputLastModified) || //
628 - checkNewer( pOutputLastModified, "ClassPath", compileClasspath() ) || //
629 - checkNewer( pOutputLastModified, "Source", getSource() ) || //
630 - checkNewer( pOutputLastModified, "Resources", getResources() ) || //
631 - checkNewer( pOutputLastModified, "Dist", getDist() );
632 - }
633 -
634 - protected boolean checkNewer( long pOutputLastModified, String pWhat, Paths pPaths )
635 - {
636 - Long zLastModified = pPaths.getGreatestLastModified();
637 - if ( (zLastModified != null) && (zLastModified > pOutputLastModified) )
638 - {
639 - System.out.println( this + ": " + pWhat + " - " + new NewerBy( pOutputLastModified, zLastModified ) );
640 - return true;
641 - }
642 - return false;
643 - }
644 -
645 - protected long forceBuildLastModified()
646 - {
647 - return (mProjectFileLastModified - 1);
648 - }
649 -
650 - protected long determineOutputLastModified()
651 - {
652 - if ( mSources )
653 - {
654 - File zJarFile = getJarPathFile();
655 - if ( zJarFile.isFile() )
656 - {
657 - return zJarFile.lastModified();
658 - }
659 - }
660 - else
661 - {
662 - String zPhoneGapDir = getPhoneGapDirPath();
663 - if ( zPhoneGapDir != null )
664 - {
665 - File zPhoneGapDirFile = new File( zPhoneGapDir );
666 - if ( zPhoneGapDirFile.isDirectory() )
667 - {
668 - return zPhoneGapDirFile.lastModified();
669 - }
670 - }
671 - }
672 - return forceBuildLastModified();
673 - }
674 -
675 - /**
676 - * Deletes the "target" directory and all files and directories under it.
677 - */
678 - public void clean()
679 - {
680 - progress( "Clean: " + this );
681 - packageClean();
682 - delete( getGWTwarPath() );
683 - delete( getJarPath() );
684 - delete( getTargetPath() );
685 - }
686 -
687 - /**
688 - * Collects the source files using the "source" property and compiles them into a "classes" directory under the target
689 - * directory. It uses "classpath" and "dependencies" to find the libraries required to compile the source.
690 - * <p/>
691 - * Note: Each dependency project is not built automatically. Each needs to be built before the dependent project.
692 - *
693 - * @return The path to the "classes" directory or null if there was no sources to compile
694 - */
695 - public String compile()
696 - {
697 - Paths source = getSource();
698 - if ( source.isEmpty() )
699 - {
700 - return null;
701 - }
702 - Paths classpath = compileClasspath();
703 -
704 - String classesDir = mkdir( path( "$target$/classes/" ) );
705 -
706 - String zMessage = "Compile: " + this;
707 - if ( LOGGER.debug.isEnabled() )
708 - {
709 - zMessage += " | " + source.count() + " source files";
710 - if ( !classpath.isEmpty() )
711 - {
712 - zMessage += "\n Classpath: " + classpath;
713 - }
714 - }
715 - progress( zMessage );
716 -
717 - List<String> zCompileArgs = createCompileJavaArgs( classpath, source, classesDir );
718 -
719 - compileJava( classpath, source, zCompileArgs );
720 -
721 - return classesDir;
722 - }
723 -
724 - protected List<String> createCompileJavaArgs( Paths pClasspath, Paths pSource, String pClassesDir )
725 - {
726 - List<String> args = new ArrayList<String>();
727 - if ( LOGGER.trace.isEnabled() )
728 - {
729 - args.add( "-verbose" );
730 - }
731 - args.add( "-d" );
732 - args.add( pClassesDir );
733 - args.add( "-g:source,lines" );
734 - args.add( "-source" );
735 - args.add( getSourceJavaVersion() );
736 - args.addAll( pSource.getFullPaths() );
737 - if ( !pClasspath.isEmpty() )
738 - {
739 - args.add( "-classpath" );
740 - args.add( pClasspath.toString( File.pathSeparator ) );
741 - }
742 - return args;
743 - }
744 -
745 - protected void compileJava( Paths pClasspath, Paths pSource, List<String> pCompileArgs )
746 - {
747 - JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
748 -
749 - if ( compiler == null )
750 - {
751 - throw new RuntimeException( "No compiler available. Ensure you are running from a " + getTargetJavaVersion() +
752 - "+ JDK, and not a JRE *and* that your class path includes tools.jar." );
753 - }
754 - int zError = compiler.run( getCompile_in(), getCompile_out(), getCompile_err(), pCompileArgs.toArray( new String[pCompileArgs.size()] ) );
755 - if ( zError != 0 )
756 - {
757 - String zMessage = "Error (" + zError + ") during compilation of project: " + this +
758 - "\nSource: " + pSource.count() + " files\nCompilerArgs: " + pCompileArgs;
759 - if ( LOGGER.debug.isEnabled() )
760 - {
761 - zMessage += "\nClasspath: " + pClasspath + "\nSource: " + pSource.toString( " " );
762 - }
763 - throw new RuntimeException( zMessage );
764 - }
765 - try
766 - {
767 - Thread.sleep( 100 );
768 - }
769 - catch ( InterruptedException ex )
770 - {
771 - // Whatever
772 - }
773 - }
774 -
775 - protected InputStream getCompile_in()
776 - {
777 - return null;
778 - }
779 -
780 - protected OutputStream getCompile_out()
781 - {
782 - return null;
783 - }
784 -
785 - protected OutputStream getCompile_err()
786 - {
787 - return new OutputStream()
788 - {
789 - private StringBuilder mBuffer = new StringBuilder();
790 - private boolean mLastWasCRtreatedAsLF = false;
791 -
792 - private void dumpLine( int pByte )
793 - {
794 - if ( pByte == 13 )
795 - {
796 - mLastWasCRtreatedAsLF = true;
797 - pByte = 10;
798 - }
799 - else
800 - {
801 - boolean zLastWasCRtreatedAsLF = mLastWasCRtreatedAsLF;
802 - mLastWasCRtreatedAsLF = false;
803 - if ( (pByte == 10) && zLastWasCRtreatedAsLF )
804 - {
805 - return;
806 - }
807 - }
808 - mBuffer.append( (char) pByte ); // Assuming Ascii!
809 - String line = mBuffer.toString();
810 - mBuffer = new StringBuilder();
811 - if ( !line.startsWith( "Note: " ) )
812 - {
813 - System.err.print( line );
814 - }
815 - }
816 -
817 - @Override
818 - public void write( int pByte ) // Not a Unicode character, just a BYTE! --- Asumming Ascii ---
819 - throws IOException
820 - {
821 - if ( (10 <= pByte) && (pByte <= 13) ) // New Line Indicator
822 - { // LF: Line Feed, U+000A
823 - dumpLine( pByte ); // VT: Vertical Tab, U+000B
824 - return; // FF: Form Feed, U+000C
825 - } // CR: Carriage Return, U+000D
826 - mBuffer.append( (char) pByte ); // Assuming Ascii!
827 - }
828 - };
829 - }
830 -
831 - /**
832 - * Collects the class files from the "classes" directory and all the resource files using the "resources" property and encodes
833 - * them into a JAR file.
834 - * <p/>
835 - * If the resources don't contain a META-INF/MANIFEST.MF file, one is generated. If the project has a main property, the
836 - * generated manifest will include "Main-Class" and "Class-Path" entries to allow the main class to be run with "java -jar".
837 - *
838 - * @return The path to the created JAR file or null if No JAR created.
839 - */
840 - public String jar()
841 - {
842 - String zJarPath = getJarPath();
843 -
844 - Paths zClasses = new Paths( path( "$target$/classes/" ), "**.class" );
845 - Paths zResources = getResources();
846 - if ( zClasses.isEmpty() && zResources.isEmpty() )
847 - {
848 - delete( zJarPath );
849 - return null;
850 - }
851 - progress( "JAR: " + this + " -> " + zJarPath );
852 -
853 - String jarDir = mkdir( path( "$target$/jar/" ) );
854 -
855 - zClasses.copyTo( jarDir );
856 - zResources.copyTo( jarDir );
857 -
858 - File manifestFile = new File( jarDir, "META-INF/MANIFEST.MF" );
859 - if ( !manifestFile.exists() )
860 - {
861 - createDefaultManifestFile( zJarPath, manifestFile );
862 - }
863 -
864 - return jar( zJarPath, new Paths( jarDir ) );
865 - }
866 -
867 - protected void createDefaultManifestFile( String pJarFile, File pManifestFile )
868 - {
869 - LOGGER.debug.log( "Generating JAR manifest: ", pManifestFile );
870 - mkdir( pManifestFile.getParent() );
871 - Manifest manifest = new Manifest();
872 - manifest.getMainAttributes().putValue( Attributes.Name.MANIFEST_VERSION.toString(), "1.0" );
873 - if ( hasMain() )
874 - {
875 - LOGGER.debug.log( "Main class: ", getMain() );
876 - manifest.getMainAttributes().putValue( Attributes.Name.MAIN_CLASS.toString(), getMain() );
877 - StringBuilder buffer = new StringBuilder( 512 );
878 - buffer.append( Utils.fileName( pJarFile ) );
879 - buffer.append( " ." );
880 - Paths classpath = classpath();
881 - for ( String name : classpath.getRelativePaths( pJarFile ) )
882 - {
883 - buffer.append( ' ' );
884 - buffer.append( name );
885 - }
886 - manifest.getMainAttributes().putValue( Attributes.Name.CLASS_PATH.toString(), buffer.toString() );
887 - }
888 - OutputStream output = createFileOutputStream( pManifestFile );
889 - try
890 - {
891 - manifest.write( output );
892 - Closeable zCloseable = output;
893 - output = null;
894 - close( zCloseable );
895 - }
896 - catch ( IOException e )
897 - {
898 - throw new WrappedIOException( e );
899 - }
900 - finally
901 - {
902 - dispose( output );
903 - }
904 - }
905 -
906 - /**
907 - * Encodes the specified paths into a JAR file.
908 - *
909 - * @return The path to the JAR file.
910 - */
911 - public String jar( String jarFile, Paths paths )
912 - {
913 - return innerJar( "JAR", jarFile, paths );
914 - }
915 -
916 - /**
917 - * Encodes the specified paths into a JAR/WAR file.
918 - *
919 - * @return The path to the JAR/WAR file.
920 - */
921 - protected String innerJar( String pType, String jarFile, Paths paths )
922 - {
923 - Util.assertNotNull( "jarFile", jarFile );
924 - Util.assertNotNull( "paths", paths );
925 -
926 - progress( "Creating " + pType + " (" + paths.count() + " entries): " + jarFile );
927 -
928 - int zZipped = paths.zip( jarFile, new ZipFactory()
929 - {
930 - @Override
931 - public ZipOutputStream createZOS( String pFilePath, List<FilePath> pPaths )
932 - {
933 - putManifestFirst( pPaths );
934 - try
935 - {
936 - return new JarOutputStream( FileUtil.createBufferedFileOutputStream( pFilePath ) );
937 - }
938 - catch ( IOException e )
939 - {
940 - throw new WrappedIOException( e );
941 - }
942 - }
943 -
944 - @Override
945 - public ZipEntry createZE( String pRelativePath )
946 - {
947 - return new JarEntry( pRelativePath );
948 - }
949 -
950 - private void putManifestFirst( List<FilePath> pPaths )
951 - {
952 - int at = findManifest( pPaths );
953 - if ( at > 0 )
954 - {
955 - FilePath zManifest = pPaths.remove( at );
956 - pPaths.add( 0, zManifest );
957 - }
958 - }
959 -
960 - private int findManifest( List<FilePath> pPaths )
961 - {
962 - for ( int i = 0; i < pPaths.size(); i++ )
963 - {
964 - if ( META_INF_MANIFEST_MF.equals( pPaths.get( i ).getFileSubPath() ) )
965 - {
966 - return i;
967 - }
968 - }
969 - return -1;
970 - }
971 - } );
972 - return zZipped == 0 ? null : jarFile;
973 - }
974 -
975 - /**
976 - * Decodes the specified ZIP file.
977 - *
978 - * @return The path to the output directory.
979 - */
980 - protected void quiteUnzip( File zipFile, File outputDir )
981 - {
982 - ZipInputStream input = new ZipInputStream( createFileInputStream( zipFile ) );
983 - try
984 - {
985 - for ( ZipEntry entry; null != (entry = input.getNextEntry()); )
986 - {
987 - File file = new File( outputDir, entry.getName() );
988 - if ( entry.isDirectory() )
989 - {
990 - mkdir( file.getPath() );
991 - continue;
992 - }
993 - writeStream( input, createFileOutputStream( file ) );
994 - }
995 - }
996 - catch ( IOException e )
997 - {
998 - throw new WrappedIOException( e );
999 - }
1000 - finally
1001 - {
1002 - dispose( input );
1003 - }
1004 - }
1005 -
1006 - /**
1007 - * Decodes the specified ZIP file.
1008 - */
1009 - public void unzip( File zipFile, File outputDir )
1010 - {
1011 - Util.assertNotNull( "zipFile", zipFile );
1012 - Util.assertNotNull( "outputDir", outputDir );
1013 - progress( "ZIP decoding: " + zipFile.getPath() + " -> " + outputDir.getPath() );
1014 - quiteUnzip( zipFile, outputDir );
1015 - }
1016 -
1017 - /**
1018 - * Decodes the specified ZIP file.
1019 - *
1020 - * @return The path to the output directory.
1021 - */
1022 - public String unzip( String zipFile, String outputDir )
1023 - {
1024 - zipFile = assertNotEmpty( "zipFile", zipFile );
1025 - outputDir = assertNotEmpty( "outputDir", outputDir );
1026 - progress( "ZIP decoding: " + zipFile + " -> " + outputDir );
1027 - quiteUnzip( new File( zipFile ), new File( outputDir ) );
1028 - return outputDir;
1029 - }
1030 -
1031 - /**
1032 - * Computes the classpath for the specified project and all its dependency projects, recursively.
1033 - */
1034 - protected Paths compileClasspath()
1035 - {
1036 - Paths classpath = getCompileClasspath();
1037 - classpath.add( getClasspath() );
1038 - for ( Project zProject : mDependantProjects )
1039 - {
1040 - zProject.addDependantProjectsCompileClassPaths( classpath );
1041 - }
1042 - return classpath;
1043 - }
1044 -
1045 - /**
1046 - * Computes the classpath for all the dependencies of the specified project, recursively.
1047 - */
1048 - protected void addDependantProjectsCompileClassPaths( Paths pPathsToAddTo )
1049 - {
1050 - addDependentProjectJar( pPathsToAddTo );
1051 - pPathsToAddTo.add( compileClasspath() );
1052 - }
1053 -
1054 - /**
1055 - * Computes the classpath for the specified project and all its dependency projects, recursively.
1056 - */
1057 - protected Paths classpath()
1058 - {
1059 - Paths classpath = getClasspath();
1060 - for ( Project zProject : mDependantProjects )
1061 - {
1062 - zProject.addDependantProjectsClassPaths( classpath );
1063 - }
1064 - return classpath;
1065 - }
1066 -
1067 - /**
1068 - * Computes the classpath for all the dependencies of the specified project, recursively.
1069 - */
1070 - protected void addDependantProjectsClassPaths( Paths pPathsToAddTo )
1071 - {
1072 - addDependentProjectJar( pPathsToAddTo );
1073 - pPathsToAddTo.add( classpath() );
1074 - }
1075 -
1076 - protected void addDependentProjectJar( Paths pPathsToAddTo )
1077 - {
1078 - if ( mSources )
1079 - {
1080 - File zJarFile = getJarPathFile();
1081 - if ( !zJarFile.isFile() )
1082 - {
1083 - throw new RuntimeException( "Dependency (" + this + ") Jar not found, not built?" );
1084 - }
1085 - pPathsToAddTo.add( new FilePath( zJarFile.getParentFile(), zJarFile.getName() ) );
1086 - }
1087 - }
1088 -
1089 - /**
1090 - * Calls {@link #build(Project)} for each dependency project in the specified project.
1091 - */
1092 - public boolean buildDependencies()
1093 - {
1094 - boolean anyBuilt = false;
1095 - for ( Project zProject : mDependantProjects )
1096 - {
1097 - anyBuilt |= zProject.build();
1098 - }
1099 - return anyBuilt;
1100 - }
1101 -
1102 - public void set( Object key, Object object )
1103 - {
1104 - mManager.put( updatableKey( key ), object );
1105 - }
1106 -
1107 - public void remove( Object key )
1108 - {
1109 - set( key, null );
1110 - }
1111 -
1112 - /**
1113 - * Removes an item from a list or map. If the mData under the specified key is a list, the entry equal to the specified value is
1114 - * removed. If the mData under the specified key is a map, the entry with the key specified by value is removed.
1115 - */
1116 - public void remove( Object key, Object value )
1117 - {
1118 - mManager.remove( updatableKey( key ), value );
1119 - }
1120 -
1121 - private Object updatableKey( Object pKey )
1122 - {
1123 - if ( pKey instanceof String )
1124 - {
1125 - String zStrKey = noEmpty( pKey.toString().toLowerCase() );
1126 - if ( Parameter.reservedNames().contains( zStrKey ) )
1127 - {
1128 - throw new IllegalArgumentException( zStrKey + " not updatable!" );
1129 - }
1130 - pKey = zStrKey;
1131 - }
1132 - Util.assertNotNull( "key", pKey );
1133 - return pKey;
1134 - }
1135 -
1136 - public synchronized void initialize( ProjectFactory pProjectFactory )
1137 - {
1138 - List<String> zDependencies = getDependencies();
1139 - if ( zDependencies != null )
1140 - {
1141 - for ( String zDependency : zDependencies )
1142 - {
1143 - mDependantProjects.add( pProjectFactory.project( mCanonicalProjectDir, zDependency ) );
1144 - }
1145 - }
1146 -
1147 - // Project defaults = new Project();
1148 - //
1149 - // File file = new File( canonical( pPath ) );
1150 - // if ( file.isDirectory() )
1151 - // {
1152 - // String name = file.getName();
1153 - // defaults.set( "name", name );
1154 - // defaults.set( "target", file.getParent() + "/target/" + name + "/" );
1155 - // }
1156 - // else
1157 - // {
1158 - // String name = file.getParentFile().getName();
1159 - // defaults.set( "name", name );
1160 - // defaults.set( "target", file.getParentFile().getParent() + "/target/" + name + "/" );
1161 - // }
1162 - // defaults.set( "classpath", "lib|**/*.jar" );
1163 - // defaults.set( "dist", "dist" );
1164 - //
1165 - // List<String> source = new ArrayList<String>();
1166 - // source.add( "src|**/*.java" );
1167 - // source.add( "src/main/java|**/*.java" );
1168 - // defaults.set( "source", source );
1169 - //
1170 - // List<String> resources = new ArrayList<String>();
1171 - // resources.add( "resources" );
1172 - // resources.add( "src/main/resources" );
1173 - // defaults.set( "resources", resources );
1174 - //
1175 - // Project project = project( pPath, defaults );
1176 - //
1177 - // // Remove dependency if a JAR of the same name is on the classpath.
1178 - // Paths classpath = project.getPaths( "classpath" );
1179 - // classpath.add( dependencyClasspaths( project, classpath, false, false ) );
1180 - // for ( String dependency : project.getDependencies() )
1181 - // {
1182 - // String dependencyName = project( project.path( dependency ) ).getName();
1183 - // for ( String classpathFile : classpath )
1184 - // {
1185 - // String name = fileWithoutExtension( classpathFile );
1186 - // int dashIndex = name.lastIndexOf( '-' );
1187 - // if ( dashIndex != -1 )
1188 - // {
1189 - // name = name.substring( 0, dashIndex );
1190 - // }
1191 - // if ( name.equals( dependencyName ) )
1192 - // {
1193 - // if ( DEBUG )
1194 - // {
1195 - // debug( "Ignoring " + project + " dependency: " + dependencyName + " (already on classpath: " + classpathFile + ")" );
1196 - // }
1197 - // project.remove( "dependencies", dependency );
1198 - // break;
1199 - // }
1200 - // }
1201 - // }
1202 - //
1203 - // if ( TRACE )
1204 - // {
1205 - // trace( "scar", "Project: " + project + "\n" + project );
1206 - // }
1207 - //
1208 - // return project;
1209 - }
1210 -
1211 - protected boolean mBuilt = false;
1212 - protected boolean mSources = false;
1213 - protected List<Project> mDependantProjects = new ArrayList<Project>();
1214 - }
1 + package com.esotericsoftware.scar;
2 +
3 + import org.litesoft.logger.*;
4 +
5 + import com.esotericsoftware.filesystem.*;
6 + import com.esotericsoftware.scar.support.*;
7 + import com.esotericsoftware.utils.*;
8 +
9 + import javax.tools.*;
10 + import java.io.*;
11 + import java.util.*;
12 + import java.util.jar.*;
13 + import java.util.zip.*;
14 +
15 + /**
16 + * Generic Data structure that contains information needed to perform tasks.
17 + */
18 + @SuppressWarnings("UnusedDeclaration")
19 + public class Project extends ProjectParameters
20 + {
21 + private static final String JAVA_SOURCE_TARGET_VERSION = "1.7";
22 +
23 + protected static final Logger LOGGER = LoggerFactory.getLogger( Project.class );
24 +
25 + private static final String META_INF_MANIFEST_MF = "META-INF/MANIFEST.MF";
26 +
27 + private static final String VERSIONED_URL_PATTERN_PREFIX = "<url-pattern>/v";
28 + private static final String VERSIONED_SCRIPT_PREFIX = "<script src='v";
29 + private static final String VERSIONED_SCRIPT_SUFFIX = ".nocache.js'></script>";
30 + private static final String VERSIONED_MODULE_PREFIX = "<module rename-to=\"v";
31 + private static final String VERSIONED_MODULE_SUFFIX = "\">";
32 + private static final String JAVA_HOME = "JAVA_HOME";
33 +
34 + public Project( ProjectParameters pParameters )
35 + {
36 + super( pParameters.validate() );
37 + applyDefaults();
38 + }
39 +
40 + public String getSourceJavaVersion()
41 + {
42 + return JAVA_SOURCE_TARGET_VERSION;
43 + }
44 +
45 + public String getTargetJavaVersion()
46 + {
47 + return JAVA_SOURCE_TARGET_VERSION;
48 + }
49 +
50 + protected void packageClean()
51 + {
52 + delete( getPhoneGapDirPath() );
53 + delete( getAppDirPath() );
54 + delete( getOneJarPath() );
55 + delete( getWarPath() );
56 + }
57 +
58 + protected boolean packageIt()
59 + {
60 + return phoneGapDir() | appDir() | oneJAR() | war(); // Note: SINGLE '|' ORs to force full execution!
61 + }
62 +
63 + /**
64 + * Unzips all JARs in the classpath and creates a single JAR containing those files and this Project's JAR (which MUST exist).
65 + * The manifest from the project's JAR is used. Putting everything into a single JAR makes it harder to see what libraries are
66 + * being used, but makes it easier for end users to distribute the application.
67 + * <p/>
68 + * Note: Files with the same path in different JARs will be overwritten. Files in the project's JAR will never be overwritten,
69 + * but may overwrite other files.
70 + *
71 + * @param pExcludeJARs The names of any JARs to exclude.
72 + *
73 + * @return True if the "OneJAR" was created / updated or false if no OneJar is NOT requested for this project or it was not needed.
74 + */
75 + public boolean oneJAR( String... pExcludeJARs )
76 + {
77 + File zOneJarPath = getOneJarPathFile();
78 +
79 + if ( zOneJarPath == null )
80 + {
81 + return false;
82 + }
83 +
84 + File zJarPath = getJarPathFile();
85 + if ( !zJarPath.isFile() )
86 + {
87 + throw new IllegalStateException( "One JAR: " + this + " requested, BUT NO jar File produced at: " + zJarPath.getPath() );
88 + }
89 +
90 + if ( zOneJarPath.isFile() && (zOneJarPath.lastModified() >= zJarPath.lastModified()) )
91 + {
92 + progress( "One JAR: " + this + " NOT Needed!" );
93 + return false;
94 + }
95 +
96 + Paths zClasspath = classpath();
97 + if ( zClasspath.isEmpty() )
98 + {
99 + progress( "One JAR: " + this + " No supporting Jars! Simply Copying to: " + zOneJarPath.getPath() );
100 + copyFile( zJarPath, zOneJarPath );
101 + return true;
102 + }
103 + progress( "One JAR: " + this );
104 +
105 + File zOnejarDir = mkdir( new File( path( "$target$/onejar/" ) ) );
106 +
107 + List<String> zExcludeJARs = Arrays.asList( pExcludeJARs );
108 + for ( File jarFile : zClasspath.getFiles() ) // All our Class Path (dependant) JARS
109 + {
110 + if ( !zExcludeJARs.contains( jarFile.getName() ) )
111 + {
112 + unzip( jarFile, zOnejarDir );
113 + }
114 + }
115 +
116 + unzip( zJarPath, zOnejarDir ); // Our Jar! - Our Manifest will be "the" Manifest !!!!!! Need to remove class PATH!
117 + innerJar( "'ONE' JAR", zOneJarPath.getPath(), new Paths( zOnejarDir.getPath() ) );
118 + return true;
119 + }
120 +
121 + /**
122 + * Collects the distribution files using the "dist" property, the project's JAR file, and everything on the project's classpath
123 + * (including dependency project classpaths) and places them into the specified directory. This is also done for depenency projects,
124 + * recursively. This is everything the application needs to be run from JAR files.
125 + *
126 + * @return True if the PhoneGapDir was populated or false if no distribution (App Dir) is requested for this project.
127 + */
128 + public boolean phoneGapDir()
129 + {
130 + String zPhoneGapDir = getPhoneGapDirPath();
131 + if ( zPhoneGapDir == null )
132 + {
133 + return false;
134 + }
135 + Paths zPaths = new Paths( getGWTwarPath(), "**.js", "**.gif" );
136 + zPaths.add( getDist() );
137 +
138 + File zPhoneGapDirFile = new File( zPhoneGapDir );
139 + if ( zPhoneGapDirFile.exists() )
140 + {
141 + if ( zPhoneGapDirFile.lastModified() >= zPaths.getGreatestLastModified() )
142 + {
143 + progress( "PhoneGapDir: " + this + " NOT Needed!" );
144 + return false;
145 + }
146 + delete( zPhoneGapDirFile );
147 + }
148 +
149 + progress( "PhoneGapDir: " + this + " -> " + zPhoneGapDir );
150 + String distDir = mkdir( zPhoneGapDir ); // Give it a new Timestamp
151 + zPaths.copyTo( distDir );
152 + return true;
153 + }
154 +
155 + /**
156 + * Collects the distribution files using the "dist" property, the project's JAR file, and everything on the project's classpath
157 + * (including dependency project classpaths) and places them into the specified directory. This is also done for depenency projects,
158 + * recursively. This is everything the application needs to be run from JAR files.
159 + *
160 + * @return True if the AppDir was populated or false if no distribution (App Dir) is requested for this project.
161 + */
162 + public boolean appDir()
163 + {
164 + String zAppDir = getAppDirPath();
165 + if ( zAppDir == null )
166 + {
167 + return false;
168 + }
169 + File zJarPath = getJarPathFile();
170 + if ( !zJarPath.isFile() )
171 + {
172 + progress( "AppDir: " + this + " BUT NO jar File produced at: " + zJarPath.getPath() );
173 + return false;
174 + }
175 + Paths zPaths = new Paths();
176 + addDependantProjectsDistPaths( zPaths );
177 + zPaths.add( classpath() );
178 + zPaths.add( FilePath.canonicalize( getJarPathFile() ) );
179 +
180 + File zAppDirFile = new File( zAppDir );
181 + if ( zAppDirFile.exists() )
182 + {
183 + if ( zAppDirFile.lastModified() >= zPaths.getGreatestLastModified() )
184 + {
185 + progress( "AppDir: " + this + " NOT Needed!" );
186 + return false;
187 + }
188 + delete( zAppDirFile );
189 + }
190 +
191 + progress( "AppDir: " + this + " -> " + zAppDir );
192 + String distDir = mkdir( zAppDir ); // Give it a new Timestamp
193 + zPaths.copyTo( distDir );
194 + return true;
195 + }
196 +
197 + /**
198 + * Produce either a 'war' directory or a '.war' file, in theory ready to deploy to a servlet/web container.
199 + *
200 + * @return true if the 'war' was created.
201 + */
202 + public boolean war()
203 + {
204 + String zWar = getWar();
205 + if ( zWar == null )
206 + {
207 + return false;
208 + }
209 + File zJarPath = getJarPathFile();
210 + if ( !zJarPath.isFile() )
211 + {
212 + progress( "WAR: " + this + " BUT NO jar File produced at: " + zJarPath.getPath() );
213 + return false;
214 + }
215 +
216 + Paths zDistPaths = new Paths();
217 + addDependantProjectsDistPaths( zDistPaths );
218 + if ( null != getGWT() )
219 + {
220 + zDistPaths.add( new Paths( getGWTwarPath() ) );
221 + }
222 +
223 + Paths zClassPath = new Paths();
224 + zClassPath.add( FilePath.canonical( getGWTatDir(), GWT_SERVLET ) );
225 + zClassPath.add( classpath() );
226 + zClassPath.add( FilePath.canonicalize( getJarPathFile() ) );
227 +
228 + File zWarPathFile = getWarPathFile();
229 + if ( zWarPathFile.exists() )
230 + {
231 + long zWarLastModified = zWarPathFile.lastModified();
232 + if ( (zWarLastModified >= zClassPath.getGreatestLastModified()) && (zWarLastModified >= zDistPaths.getGreatestLastModified()) )
233 + {
234 + progress( "WAR: " + this + " NOT Needed!" );
235 + return false;
236 + }
237 + delete( zWarPathFile );
238 + }
239 +
240 + boolean zWarIt = zWar.endsWith( ".war" );
241 +
242 + File zWarDir = zWarPathFile;
243 + if ( zWarIt )
244 + {
245 + zWarDir = new File( path( "$target$/war/" ) );
246 + delete( zWarDir );
247 + }
248 +
249 + progress( "WAR: " + this + " -> " + zWarDir.getPath() );
250 +
251 + File zWarDirLibPath = new File( zWarDir, "WEB-INF/lib" );
252 + mkdir( zWarDirLibPath );
253 +
254 + zDistPaths.copyTo( zWarDir.getPath() );
255 + zClassPath.copyTo( zWarDirLibPath.getPath() );
256 +
257 + if ( zWarIt )
258 + {
259 + innerJar( "WAR", zWarPathFile.getPath(), new Paths( zWarDir.getPath() ) );
260 + }
261 + return true;
262 + }
263 +
264 + /**
265 + * Computes the classpath for all the dependencies of the specified project, recursively.
266 + */
267 + protected void addDependantProjectsDistPaths( Paths pPathsToAddTo )
268 + {
269 + for ( Project zProject : mDependantProjects )
270 + {
271 + zProject.addDependantProjectsDistPaths( pPathsToAddTo );
272 + }
273 + pPathsToAddTo.add( getDist() );
274 + }
275 +
276 + protected boolean GWTcompileIt()
277 + {
278 + String[] args = //
279 + { //
280 + getPathJavaJRE(), //
281 + "-Xmx" + getGWTmx(), //
282 + "-cp", //
283 + buildGWTcompileClassPath(), //
284 + "com.google.gwt.dev.Compiler", //
285 + "-logLevel", //
286 + getGWTlogging(), //
287 + "-war", //
288 + getGWTwarPath(), //
289 + "-style", //
290 + getGWTstyle(), //
291 + getGWT() //
292 + };
293 +
294 + Utils.shell( args );
295 + return true;
296 + }
297 +
298 + private String buildGWTcompileClassPath()
299 + {
300 + Paths zGWTclassPath = new Paths();
301 + File zGWTatDir = getGWTatDir();
302 + zGWTclassPath.add( FilePath.canonical( zGWTatDir, GWT_DEV ) );
303 + zGWTclassPath.add( FilePath.canonical( zGWTatDir, GWT_USER ) );
304 + zGWTclassPath.add( FilePath.canonical( zGWTatDir, GWT_VALIDATION ) );
305 + zGWTclassPath.add( FilePath.canonical( zGWTatDir, GWT_VALIDATION_SOURCE ) );
306 + if ( mSources )
307 + {
308 + zGWTclassPath.add( FilePath.canonicalize( getJarPathFile() ) );
309 + }
310 + zGWTclassPath.add( classpath() );
311 + return zGWTclassPath.toString( File.pathSeparator );
312 + }
313 +
314 + protected String getPathJavaJRE()
315 + {
316 + File zJavaHomeDir = assertIsDirectory( JAVA_HOME, new File( assertNotEmpty( JAVA_HOME, System.getenv( JAVA_HOME ) ) ) );
317 + File zJavaDir = new File( zJavaHomeDir, "jre/bin" );
318 + if ( !zJavaDir.isDirectory() )
319 + {
320 + if ( !(zJavaDir = new File( zJavaHomeDir, "bin" )).isDirectory() )
321 + {
322 + throw new IllegalStateException( "Unable to find JAVA_HOME bin directory under: " + zJavaHomeDir );
323 + }
324 + }
325 + File zJavaExecutable = new File( zJavaDir, isWindows ? "java.exe" : "java" );
326 + if ( zJavaExecutable.isFile() )
327 + {
328 + return getCanonicalFile( zJavaExecutable ).getPath();
329 + }
330 + throw new IllegalStateException( "Unable to find JAVA_HOME based executable at: " + zJavaExecutable );
331 + }
332 +
333 + protected File getGeneratedGWT_nocache_jsFile()
334 + {
335 + String zGWTxmlRelativeFilePath = getGWT().replace( '.', '/' ) + ".gwt.xml"; // e.g. org.litesoft.sandbox.csapp.CSapp
336 + File zFound = getGeneratedGWT_nocache_jsFile( zGWTxmlRelativeFilePath, getSource() );
337 + if ( zFound == null )
338 + {
339 + if ( null == (zFound = getGeneratedGWT_nocache_jsFile( zGWTxmlRelativeFilePath, getResources() )) )
340 + {
341 + throw new IllegalArgumentException( "Unable to locate GWT module file: " + getGWT() );
342 + }
343 + }
344 + return zFound;
345 + }
346 +
347 + private File getGeneratedGWT_nocache_jsFile( String pGWTxmlRelativeFilePath, Paths pPaths )
348 + {
349 + RootedPaths[] zRootedPaths = pPaths.getRootedPaths();
350 + for ( RootedPaths zPath : zRootedPaths )
351 + {
352 + File zFile = new File( zPath.getCanonicalRootDirectory(), pGWTxmlRelativeFilePath );
353 + if ( zFile.isFile() )
354 + {
355 + String moduleName = extractModuleNameFrom( getGWT(), fileContents( zFile ) );
356 + return new File( getGWTwarPath(), moduleName + "/" + moduleName + ".nocache.js" );
357 + }
358 + }
359 + return null;
360 + }
361 +
362 + private String extractModuleNameFrom( String pGWTmoduleReference, String pGWTmoduleFileContents )
363 + {
364 + int at = pGWTmoduleFileContents.indexOf( " rename-to" );
365 + if ( at != -1 ) // . . . . . . . . . . . .01234567890
366 + {
367 + int upTo = pGWTmoduleFileContents.indexOf( '>', at += 10 );
368 + if ( upTo != -1 )
369 + {
370 + String stuff = pGWTmoduleFileContents.substring( at, upTo ).trim();
371 + if ( stuff.startsWith( "=" ) )
372 + {
373 + if ( (stuff = stuff.substring( 1 ).trim()).length() > 2 )
374 + {
375 + char c = stuff.charAt( 0 );
376 + if ( (c == '"') || (c == '\'') )
377 + {
378 + if ( -1 != (at = stuff.indexOf( c, 1 )) )
379 + {
380 + return stuff.substring( 1, at );
381 + }
382 + }
383 + }
384 + }
385 + }
386 + }
387 + String s = "." + pGWTmoduleReference;
388 + return s.substring( s.lastIndexOf( '.' ) + 1 );
389 + }
390 +
391 + protected boolean needToCompileGWT()
392 + {
393 + File zGeneratedGWT_nocache_jsFile = getGeneratedGWT_nocache_jsFile();
394 + return needToBuild( ((zGeneratedGWT_nocache_jsFile != null) && zGeneratedGWT_nocache_jsFile.isFile()) ?
395 + zGeneratedGWT_nocache_jsFile.lastModified() : forceBuildLastModified() );
396 + }
397 +
398 + public boolean GWTcompile()
399 + {
400 + String zGWT = getGWT();
401 + if ( zGWT == null )
402 + {
403 + return false;
404 + }
405 + if ( !needToCompileGWT() )
406 + {
407 + progress( "GWT Compile: " + this + " NOT Needed!" );
408 + return false;
409 + }
410 + progress( "GWT Compile: " + this );
411 + return GWTcompileIt();
412 + }
413 +
414 + /**
415 + * Assert that this project is currently a 'Versioned' GWT project, and then rev the version number by 1
416 + */
417 + public void versionGWT()
418 + {
419 + File zWarWebXmlFile = new File( mCanonicalProjectDir, "war/WEB-INF/web.xml" );
420 + String zWarWebXml = fileContents( assertIsFile( "web.xml", zWarWebXmlFile ) );
421 +
422 + int zCurVersion = extractVersionFromUrlPattern( zWarWebXml );
423 +
424 + String zWarResourceRelativePathCurrent = "warResources/v" + zCurVersion;
425 +
426 + File zIndexHtmlFile = new File( mCanonicalProjectDir, zWarResourceRelativePathCurrent + "/index.html" );
427 + String zIndexHtml = assertVersionedIndexHtml( zIndexHtmlFile, zCurVersion );
428 +
429 + List<File> zVersionedGwtXmlFiles = findVersionedGwtXmlFiles( zCurVersion );
430 +
431 + int zNewVersion = zCurVersion + 1;
432 +
433 + String zWarResourceRelativePathNew = "warResources/v" + zNewVersion;
434 + if ( new File( mCanonicalProjectDir, zWarResourceRelativePathNew ).exists() )
435 + {
436 + throw new IllegalStateException( "Project already contains a 'warResources/v" + zNewVersion + "' directory?" );
437 + }
438 +
439 + progress( "versionGWT: " + this + " | " + zCurVersion + " -> " + (zCurVersion + 1) );
440 + progress( " " + zWarWebXmlFile.getPath() );
441 + progress( " " + zIndexHtmlFile.getPath() );
442 + for ( File zFile : zVersionedGwtXmlFiles )
443 + {
444 + progress( " " + zFile.getPath() );
445 + }
446 + progress( " " + zWarResourceRelativePathCurrent + " -> " + zWarResourceRelativePathNew );
447 +
448 + new Paths( zWarResourceRelativePathCurrent ).copyTo( zWarResourceRelativePathNew );
449 +
450 + updateFileContents( new File( mCanonicalProjectDir, zWarResourceRelativePathNew + "/index.html" ),
451 + updateVersionedIndexHtml( zIndexHtml, zCurVersion, zNewVersion ) );
452 +
453 + updateFileContents( zWarWebXmlFile, updateVersionedWebXml( zWarWebXml, zCurVersion, zNewVersion ) );
454 +
455 + for ( File zFile : zVersionedGwtXmlFiles )
456 + {
457 + updateFileContents( zFile, updateVersionedGwtXmlFile( fileContents( zFile ), zCurVersion, zNewVersion ) );
458 + }
459 + // Update/Create the Current Version's redirect JavaScript file
460 + String zCurPathVersion = "/v" + zCurVersion;
461 + String redirectScript = "var loc = window.location.href;\n" + //
462 + "var at = loc.indexOf( '" + zCurPathVersion + "' );\n" + //
463 + "window.location.href = loc.substring(0, at) + '/v" + zNewVersion + "' + loc.substring(at + " + zCurPathVersion.length() + ");\n";
464 + updateFileContents( new File( mCanonicalProjectDir, zWarResourceRelativePathCurrent + "/v" + zCurVersion + ".nocache.js" ), redirectScript );
465 +
466 + // Update the "root" html (if it exists)
467 + File zRootHtmlFile = new File( mCanonicalProjectDir, "warResources/index.html" );
468 + if ( zRootHtmlFile.isFile() )
469 + {
470 + updateFileContents( zRootHtmlFile, updateRootHTML( fileContents( zRootHtmlFile ), zCurVersion, zNewVersion ) );
471 + }
472 + }
473 +
474 + protected String updateRootHTML( String pFileContents, int pCurVersion, int pNewVersion )
475 + {
476 + // <!DOCTYPE html>
477 + // <html>
478 + // <head>
479 + // <meta http-equiv="Refresh" content="1; url=v1/">
480 + // </head>
481 + // <body>
482 + // <script>window.location.href = 'v1/';</script>
483 + // </body>
484 + // </html>
485 + String zCurPathVersion = "v" + pCurVersion + "/";
486 + String zNewPathVersion = "v" + pNewVersion + "/";
487 + for ( int at; -1 != (at = pFileContents.indexOf( zCurPathVersion )); )
488 + {
489 + pFileContents = pFileContents.substring( 0, at ) + zNewPathVersion + pFileContents.substring( at + zCurPathVersion.length() );
490 + }
491 + return pFileContents;
492 + }
493 +
494 + protected String updateVersionedGwtXmlFile( String pFileContents, int pCurVersion, int pNewVersion )
495 + {
496 + String zCurVersionedModule = VERSIONED_MODULE_PREFIX + pCurVersion + VERSIONED_MODULE_SUFFIX;
497 + String zNewVersionedModule = VERSIONED_MODULE_PREFIX + pNewVersion + VERSIONED_MODULE_SUFFIX;
498 + int at = pFileContents.indexOf( zCurVersionedModule );
499 + return pFileContents.substring( 0, at ) + zNewVersionedModule + pFileContents.substring( at + zCurVersionedModule.length() );
500 + }
501 +
502 + protected List<File> findVersionedGwtXmlFiles( int pVersion )
503 + {
504 + String zVersionedModule = VERSIONED_MODULE_PREFIX + pVersion + VERSIONED_MODULE_SUFFIX;
505 +
506 + ArrayList<File> zVersionedFiles = new ArrayList<File>();
507 +
508 + String zSourceString = get( SOURCE.getName() ) + "|";
509 + Paths zGwtXml = new Paths( zSourceString.substring( 0, zSourceString.indexOf( '|' ) ), "**.gwt.xml" );
510 + for ( File zFile : zGwtXml.getFiles() )
511 + {
512 + if ( fileContents( zFile ).contains( zVersionedModule ) )
513 + {
514 + zVersionedFiles.add( zFile );
515 + }
516 + }
517 + if ( zVersionedFiles.isEmpty() )
518 + {
519 + throw new IllegalStateException(
520 + "Project does not appear to contain a 'gwt.xml' file with the current version module definition of: " + zVersionedModule );
521 + }
522 + return zVersionedFiles;
523 + }
524 +
525 + protected String updateVersionedIndexHtml( String pFileContents, int pCurVersion, int pNewVersion )
526 + {
527 + String zCurVersionedScript = VERSIONED_SCRIPT_PREFIX + pCurVersion + VERSIONED_SCRIPT_SUFFIX;
528 + String zNewVersionedScript = VERSIONED_SCRIPT_PREFIX + pNewVersion + VERSIONED_SCRIPT_SUFFIX;
529 + int at = pFileContents.indexOf( zCurVersionedScript );
530 + return pFileContents.substring( 0, at ) + zNewVersionedScript + pFileContents.substring( at + zCurVersionedScript.length() );
531 + }
532 +
533 + protected String assertVersionedIndexHtml( File pIndexHtmlFile, int pVersion )
534 + {
535 + String zVersionedScript = VERSIONED_SCRIPT_PREFIX + pVersion + VERSIONED_SCRIPT_SUFFIX;
536 +
537 + String zContents = fileContents( assertIsFile( "Versioned index.html", pIndexHtmlFile ) );
538 + if ( !zContents.contains( zVersionedScript ) )
539 + {
540 + throw new IllegalStateException(
541 + "Project's current versioned index.html file (" + pIndexHtmlFile.getPath() + ") does not contain a 'versioned' script element of: " +
542 + zVersionedScript );
543 + }
544 + return zContents;
545 + }
546 +
547 + protected String updateVersionedWebXml( String pFileContents, int pCurVersion, int pNewVersion )
548 + {
549 + String zCurVersionedUrlPattern = VERSIONED_URL_PATTERN_PREFIX + pCurVersion + "/";
550 + String zNewVersionedUrlPattern = VERSIONED_URL_PATTERN_PREFIX + pNewVersion + "/";
551 + for ( int at; -1 != (at = pFileContents.indexOf( zCurVersionedUrlPattern )); )
552 + {
553 + pFileContents = pFileContents.substring( 0, at ) + zNewVersionedUrlPattern + pFileContents.substring( at + zCurVersionedUrlPattern.length() );
554 + }
555 + return pFileContents;
556 + }
557 +
558 + protected int extractVersionFromUrlPattern( String pWarWebXml )
559 + {
560 + for ( int at, from = 0; -1 != (at = pWarWebXml.indexOf( VERSIONED_URL_PATTERN_PREFIX, from )); from = at + 1 )
561 + {
562 + int slashAt = pWarWebXml.indexOf( '/', at += VERSIONED_URL_PATTERN_PREFIX.length() );
563 + if ( slashAt > 0 )
564 + {
565 + try
566 + {
567 + return Integer.parseInt( pWarWebXml.substring( at, slashAt ) );
568 + }
569 + catch ( NumberFormatException acceptable )
570 + {
571 + // path starts w/ a 'v' but is not of pattern "v####"
572 + }
573 + }
574 + }
575 + throw new IllegalStateException( "Project's war/WEB-INF/web.xml does not appear to contain a 'versioned' <url-pattern>." );
576 + }
577 +
578 + /**
579 + * Executes the buildDependencies, clean, compile, jar, [GWTcompile], and then "packageIt" utility methods.
580 + */
581 + public synchronized boolean build()
582 + {
583 + if ( mBuilt )
584 + {
585 + return false;
586 + }
587 + mBuilt = true;
588 + mSources = !getSource().isEmpty();
589 + boolean zAnythingBuilt = false;
590 + boolean zBuildIt;
591 + try
592 + {
593 + zBuildIt = buildDependencies() || needToBuild();
594 + }
595 + catch ( RuntimeException e )
596 + {
597 + progress( "Build: " + this );
598 + throw e;
599 + }
600 + if ( !zBuildIt )
601 + {
602 + progress( "Build: " + this + " NOT Needed!" );
603 + }
604 + else
605 + {
606 + progress( "Build: " + this );
607 + clean();
608 + if ( mSources )
609 + {
610 + compile();
611 + jar();
612 + zAnythingBuilt = true;
613 + }
614 + }
615 + zAnythingBuilt |= GWTcompile();
616 + zAnythingBuilt |= packageIt();
617 + return zAnythingBuilt;
618 + }
619 +
620 + protected boolean needToBuild()
621 + {
622 + return needToBuild( determineOutputLastModified() );
623 + }
624 +
625 + protected boolean needToBuild( long pOutputLastModified )
626 + {
627 + return (mProjectFileLastModified > pOutputLastModified) || //
628 + checkNewer( pOutputLastModified, "ClassPath", compileClasspath() ) || //
629 + checkNewer( pOutputLastModified, "Source", getSource() ) || //
630 + checkNewer( pOutputLastModified, "Resources", getResources() ) || //
631 + checkNewer( pOutputLastModified, "Dist", getDist() );
632 + }
633 +
634 + protected boolean checkNewer( long pOutputLastModified, String pWhat, Paths pPaths )
635 + {
636 + Long zLastModified = pPaths.getGreatestLastModified();
637 + if ( (zLastModified != null) && (zLastModified > pOutputLastModified) )
638 + {
639 + System.out.println( this + ": " + pWhat + " - " + new NewerBy( pOutputLastModified, zLastModified ) );
640 + return true;
641 + }
642 + return false;
643 + }
644 +
645 + protected long forceBuildLastModified()
646 + {
647 + return (mProjectFileLastModified - 1);
648 + }
649 +
650 + protected long determineOutputLastModified()
651 + {
652 + if ( mSources )
653 + {
654 + File zJarFile = getJarPathFile();
655 + if ( zJarFile.isFile() )
656 + {
657 + return zJarFile.lastModified();
658 + }
659 + }
660 + else
661 + {
662 + String zPhoneGapDir = getPhoneGapDirPath();
663 + if ( zPhoneGapDir != null )
664 + {
665 + File zPhoneGapDirFile = new File( zPhoneGapDir );
666 + if ( zPhoneGapDirFile.isDirectory() )
667 + {
668 + return zPhoneGapDirFile.lastModified();
669 + }
670 + }
671 + }
672 + return forceBuildLastModified();
673 + }
674 +
675 + /**
676 + * Deletes the "target" directory and all files and directories under it.
677 + */
678 + public void clean()
679 + {
680 + progress( "Clean: " + this );
681 + packageClean();
682 + delete( getGWTwarPath() );
683 + delete( getJarPath() );
684 + delete( getTargetPath() );
685 + }
686 +
687 + /**
688 + * Collects the source files using the "source" property and compiles them into a "classes" directory under the target
689 + * directory. It uses "classpath" and "dependencies" to find the libraries required to compile the source.
690 + * <p/>
691 + * Note: Each dependency project is not built automatically. Each needs to be built before the dependent project.
692 + *
693 + * @return The path to the "classes" directory or null if there was no sources to compile
694 + */
695 + public String compile()
696 + {
697 + Paths source = getSource();
698 + if ( source.isEmpty() )
699 + {
700 + return null;
701 + }
702 + Paths classpath = compileClasspath();
703 +
704 + String classesDir = mkdir( path( "$target$/classes/" ) );
705 +
706 + String zMessage = "Compile: " + this;
707 + if ( LOGGER.debug.isEnabled() )
708 + {
709 + zMessage += " | " + source.count() + " source files";
710 + if ( !classpath.isEmpty() )
711 + {
712 + zMessage += "\n Classpath: " + classpath;
713 + }
714 + }
715 + progress( zMessage );
716 +
717 + List<String> zCompileArgs = createCompileJavaArgs( classpath, source, classesDir );
718 +
719 + compileJava( classpath, source, zCompileArgs );
720 +
721 + return classesDir;
722 + }
723 +
724 + protected List<String> createCompileJavaArgs( Paths pClasspath, Paths pSource, String pClassesDir )
725 + {
726 + List<String> args = new ArrayList<String>();
727 + if ( LOGGER.trace.isEnabled() )
728 + {
729 + args.add( "-verbose" );
730 + }
731 + args.add( "-d" );
732 + args.add( pClassesDir );
733 + args.add( "-g:source,lines" );
734 + args.add( "-source" );
735 + args.add( getSourceJavaVersion() );
736 + args.addAll( pSource.getFullPaths() );
737 + if ( !pClasspath.isEmpty() )
738 + {
739 + args.add( "-classpath" );
740 + args.add( pClasspath.toString( File.pathSeparator ) );
741 + }
742 + return args;
743 + }
744 +
745 + protected void compileJava( Paths pClasspath, Paths pSource, List<String> pCompileArgs )
746 + {
747 + JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
748 +
749 + if ( compiler == null )
750 + {
751 + throw new RuntimeException( "No compiler available. Ensure you are running from a " + getTargetJavaVersion() +
752 + "+ JDK, and not a JRE *and* that your class path includes tools.jar." );
753 + }
754 + int zError = compiler.run( getCompile_in(), getCompile_out(), getCompile_err(), pCompileArgs.toArray( new String[pCompileArgs.size()] ) );
755 + if ( zError != 0 )
756 + {
757 + String zMessage = "Error (" + zError + ") during compilation of project: " + this +
758 + "\nSource: " + pSource.count() + " files\nCompilerArgs: " + pCompileArgs;
759 + if ( LOGGER.debug.isEnabled() )
760 + {
761 + zMessage += "\nClasspath: " + pClasspath + "\nSource: " + pSource.toString( " " );
762 + }
763 + throw new RuntimeException( zMessage );
764 + }
765 + try
766 + {
767 + Thread.sleep( 100 );
768 + }
769 + catch ( InterruptedException ex )
770 + {
771 + // Whatever
772 + }
773 + }
774 +
775 + protected InputStream getCompile_in()
776 + {
777 + return null;
778 + }
779 +
780 + protected OutputStream getCompile_out()
781 + {
782 + return null;
783 + }
784 +
785 + protected OutputStream getCompile_err()
786 + {
787 + return new OutputStream()
788 + {
789 + private StringBuilder mBuffer = new StringBuilder();
790 + private boolean mLastWasCRtreatedAsLF = false;
791 +
792 + private void dumpLine( int pByte )
793 + {
794 + if ( pByte == 13 )
795 + {
796 + mLastWasCRtreatedAsLF = true;
797 + pByte = 10;
798 + }
799 + else
800 + {
801 + boolean zLastWasCRtreatedAsLF = mLastWasCRtreatedAsLF;
802 + mLastWasCRtreatedAsLF = false;
803 + if ( (pByte == 10) && zLastWasCRtreatedAsLF )
804 + {
805 + return;
806 + }
807 + }
808 + mBuffer.append( (char) pByte ); // Assuming Ascii!
809 + String line = mBuffer.toString();
810 + mBuffer = new StringBuilder();
811 + if ( !line.startsWith( "Note: " ) )
812 + {
813 + System.err.print( line );
814 + }
815 + }
816 +
817 + @Override
818 + public void write( int pByte ) // Not a Unicode character, just a BYTE! --- Asumming Ascii ---
819 + throws IOException
820 + {
821 + if ( (10 <= pByte) && (pByte <= 13) ) // New Line Indicator
822 + { // LF: Line Feed, U+000A
823 + dumpLine( pByte ); // VT: Vertical Tab, U+000B
824 + return; // FF: Form Feed, U+000C
825 + } // CR: Carriage Return, U+000D
826 + mBuffer.append( (char) pByte ); // Assuming Ascii!
827 + }
828 + };
829 + }
830 +
831 + /**
832 + * Collects the class files from the "classes" directory and all the resource files using the "resources" property and encodes
833 + * them into a JAR file.
834 + * <p/>
835 + * If the resources don't contain a META-INF/MANIFEST.MF file, one is generated. If the project has a main property, the
836 + * generated manifest will include "Main-Class" and "Class-Path" entries to allow the main class to be run with "java -jar".
837 + *
838 + * @return The path to the created JAR file or null if No JAR created.
839 + */
840 + public String jar()
841 + {
842 + String zJarPath = getJarPath();
843 +
844 + Paths zClasses = new Paths( path( "$target$/classes/" ), "**.class" );
845 + Paths zResources = getResources();
846 + if ( zClasses.isEmpty() && zResources.isEmpty() )
847 + {
848 + delete( zJarPath );
849 + return null;
850 + }
851 + progress( "JAR: " + this + " -> " + zJarPath );
852 +
853 + String jarDir = mkdir( path( "$target$/jar/" ) );
854 +
855 + zClasses.copyTo( jarDir );
856 + zResources.copyTo( jarDir );
857 +
858 + File manifestFile = new File( jarDir, "META-INF/MANIFEST.MF" );
859 + if ( !manifestFile.exists() )
860 + {
861 + createDefaultManifestFile( zJarPath, manifestFile );
862 + }
863 +
864 + return jar( zJarPath, new Paths( jarDir ) );
865 + }
866 +
867 + protected void createDefaultManifestFile( String pJarFile, File pManifestFile )
868 + {
869 + LOGGER.debug.log( "Generating JAR manifest: ", pManifestFile );
870 + mkdir( pManifestFile.getParent() );
871 + Manifest manifest = new Manifest();
872 + manifest.getMainAttributes().putValue( Attributes.Name.MANIFEST_VERSION.toString(), "1.0" );
873 + if ( hasMain() )
874 + {
875 + LOGGER.debug.log( "Main class: ", getMain() );
876 + manifest.getMainAttributes().putValue( Attributes.Name.MAIN_CLASS.toString(), getMain() );
877 + StringBuilder buffer = new StringBuilder( 512 );
878 + buffer.append( Utils.fileName( pJarFile ) );
879 + buffer.append( " ." );
880 + Paths classpath = classpath();
881 + for ( String name : classpath.getRelativePaths( pJarFile ) )
882 + {
883 + buffer.append( ' ' );
884 + buffer.append( name );
885 + }
886 + manifest.getMainAttributes().putValue( Attributes.Name.CLASS_PATH.toString(), buffer.toString() );
887 + }
888 + OutputStream output = createFileOutputStream( pManifestFile );
889 + try
890 + {
891 + manifest.write( output );
892 + Closeable zCloseable = output;
893 + output = null;
894 + close( zCloseable );
895 + }
896 + catch ( IOException e )
897 + {
898 + throw new WrappedIOException( e );
899 + }
900 + finally
901 + {
902 + dispose( output );
903 + }
904 + }
905 +
906 + /**
907 + * Encodes the specified paths into a JAR file.
908 + *
909 + * @return The path to the JAR file.
910 + */
911 + public String jar( String jarFile, Paths paths )
912 + {
913 + return innerJar( "JAR", jarFile, paths );
914 + }
915 +
916 + /**
917 + * Encodes the specified paths into a JAR/WAR file.
918 + *
919 + * @return The path to the JAR/WAR file.
920 + */
921 + protected String innerJar( String pType, String jarFile, Paths paths )
922 + {
923 + Util.assertNotNull( "jarFile", jarFile );
924 + Util.assertNotNull( "paths", paths );
925 +
926 + progress( "Creating " + pType + " (" + paths.count() + " entries): " + jarFile );
927 +
928 + int zZipped = paths.zip( jarFile, new ZipFactory()
929 + {
930 + @Override
931 + public ZipOutputStream createZOS( String pFilePath, List<FilePath> pPaths )
932 + {
933 + putManifestFirst( pPaths );
934 + try
935 + {
936 + return new JarOutputStream( FileUtil.createBufferedFileOutputStream( pFilePath ) );
937 + }
938 + catch ( IOException e )
939 + {
940 + throw new WrappedIOException( e );
941 + }
942 + }
943 +
944 + @Override
945 + public ZipEntry createZE( String pRelativePath )
946 + {
947 + return new JarEntry( pRelativePath );
948 + }
949 +
950 + private void putManifestFirst( List<FilePath> pPaths )
951 + {
952 + int at = findManifest( pPaths );
953 + if ( at > 0 )
954 + {
955 + FilePath zManifest = pPaths.remove( at );
956 + pPaths.add( 0, zManifest );
957 + }
958 + }
959 +
960 + private int findManifest( List<FilePath> pPaths )
961 + {
962 + for ( int i = 0; i < pPaths.size(); i++ )
963 + {
964 + if ( META_INF_MANIFEST_MF.equals( pPaths.get( i ).getFileSubPath() ) )
965 + {
966 + return i;
967 + }
968 + }
969 + return -1;
970 + }
971 + } );
972 + return zZipped == 0 ? null : jarFile;
973 + }
974 +
975 + /**
976 + * Decodes the specified ZIP file.
977 + *
978 + * @return The path to the output directory.
979 + */
980 + protected void quiteUnzip( File zipFile, File outputDir )
981 + {
982 + ZipInputStream input = new ZipInputStream( createFileInputStream( zipFile ) );
983 + try
984 + {
985 + for ( ZipEntry entry; null != (entry = input.getNextEntry()); )
986 + {
987 + File file = new File( outputDir, entry.getName() );
988 + if ( entry.isDirectory() )
989 + {
990 + mkdir( file.getPath() );
991 + continue;
992 + }
993 + writeStream( input, createFileOutputStream( file ) );
994 + }
995 + }
996 + catch ( IOException e )
997 + {
998 + throw new WrappedIOException( e );
999 + }
1000 + finally
1001 + {
1002 + dispose( input );
1003 + }
1004 + }
1005 +
1006 + /**
1007 + * Decodes the specified ZIP file.
1008 + */
1009 + public void unzip( File zipFile, File outputDir )
1010 + {
1011 + Util.assertNotNull( "zipFile", zipFile );
1012 + Util.assertNotNull( "outputDir", outputDir );
1013 + progress( "ZIP decoding: " + zipFile.getPath() + " -> " + outputDir.getPath() );
1014 + quiteUnzip( zipFile, outputDir );
1015 + }
1016 +
1017 + /**
1018 + * Decodes the specified ZIP file.
1019 + *
1020 + * @return The path to the output directory.
1021 + */
1022 + public String unzip( String zipFile, String outputDir )
1023 + {
1024 + zipFile = assertNotEmpty( "zipFile", zipFile );
1025 + outputDir = assertNotEmpty( "outputDir", outputDir );
1026 + progress( "ZIP decoding: " + zipFile + " -> " + outputDir );
1027 + quiteUnzip( new File( zipFile ), new File( outputDir ) );
1028 + return outputDir;
1029 + }
1030 +
1031 + /**
1032 + * Computes the classpath for the specified project and all its dependency projects, recursively.
1033 + */
1034 + protected Paths compileClasspath()
1035 + {
1036 + Paths classpath = getCompileClasspath();
1037 + classpath.add( getClasspath() );
1038 + for ( Project zProject : mDependantProjects )
1039 + {
1040 + zProject.addDependantProjectsCompileClassPaths( classpath );
1041 + }
1042 + return classpath;
1043 + }
1044 +
1045 + /**
1046 + * Computes the classpath for all the dependencies of the specified project, recursively.
1047 + */
1048 + protected void addDependantProjectsCompileClassPaths( Paths pPathsToAddTo )
1049 + {
1050 + addDependentProjectJar( pPathsToAddTo );
1051 + pPathsToAddTo.add( compileClasspath() );
1052 + }
1053 +
1054 + /**
1055 + * Computes the classpath for the specified project and all its dependency projects, recursively.
1056 + */
1057 + protected Paths classpath()
1058 + {
1059 + Paths classpath = getClasspath();
1060 + for ( Project zProject : mDependantProjects )
1061 + {
1062 + zProject.addDependantProjectsClassPaths( classpath );
1063 + }
1064 + return classpath;
1065 + }
1066 +
1067 + /**
1068 + * Computes the classpath for all the dependencies of the specified project, recursively.
1069 + */
1070 + protected void addDependantProjectsClassPaths( Paths pPathsToAddTo )
1071 + {
1072 + addDependentProjectJar( pPathsToAddTo );
1073 + pPathsToAddTo.add( classpath() );
1074 + }
1075 +
1076 + protected void addDependentProjectJar( Paths pPathsToAddTo )
1077 + {
1078 + if ( mSources )
1079 + {
1080 + File zJarFile = getJarPathFile();
1081 + if ( !zJarFile.isFile() )
1082 + {
1083 + throw new RuntimeException( "Dependency (" + this + ") Jar not found, not built?" );
1084 + }
1085 + pPathsToAddTo.add( new FilePath( zJarFile.getParentFile(), zJarFile.getName() ) );
1086 + }
1087 + }
1088 +
1089 + /**
1090 + * Calls {@link #build(Project)} for each dependency project in the specified project.
1091 + */
1092 + public boolean buildDependencies()
1093 + {
1094 + boolean anyBuilt = false;
1095 + for ( Project zProject : mDependantProjects )
1096 + {
1097 + anyBuilt |= zProject.build();
1098 + }
1099 + return anyBuilt;
1100 + }
1101 +
1102 + public void set( Object key, Object object )
1103 + {
1104 + mManager.put( updatableKey( key ), object );
1105 + }
1106 +
1107 + public void remove( Object key )
1108 + {
1109 + set( key, null );
1110 + }
1111 +
1112 + /**
1113 + * Removes an item from a list or map. If the mData under the specified key is a list, the entry equal to the specified value is
1114 + * removed. If the mData under the specified key is a map, the entry with the key specified by value is removed.
1115 + */
1116 + public void remove( Object key, Object value )
1117 + {
1118 + mManager.remove( updatableKey( key ), value );
1119 + }
1120 +
1121 + private Object updatableKey( Object pKey )
1122 + {
1123 + if ( pKey instanceof String )
1124 + {
1125 + String zStrKey = noEmpty( pKey.toString().toLowerCase() );
1126 + if ( Parameter.reservedNames().contains( zStrKey ) )
1127 + {
1128 + throw new IllegalArgumentException( zStrKey + " not updatable!" );
1129 + }
1130 + pKey = zStrKey;
1131 + }
1132 + Util.assertNotNull( "key", pKey );
1133 + return pKey;
1134 + }
1135 +
1136 + public synchronized void initialize( ProjectFactory pProjectFactory )
1137 + {
1138 + List<String> zDependencies = getDependencies();
1139 + if ( zDependencies != null )
1140 + {
1141 + for ( String zDependency : zDependencies )
1142 + {
1143 + mDependantProjects.add( pProjectFactory.project( mCanonicalProjectDir, zDependency ) );
1144 + }
1145 + }
1146 +
1147 + // Project defaults = new Project();
1148 + //
1149 + // File file = new File( canonical( pPath ) );
1150 + // if ( file.isDirectory() )
1151 + // {
1152 + // String name = file.getName();
1153 + // defaults.set( "name", name );
1154 + // defaults.set( "target", file.getParent() + "/target/" + name + "/" );
1155 + // }
1156 + // else
1157 + // {
1158 + // String name = file.getParentFile().getName();
1159 + // defaults.set( "name", name );
1160 + // defaults.set( "target", file.getParentFile().getParent() + "/target/" + name + "/" );
1161 + // }
1162 + // defaults.set( "classpath", "lib|**/*.jar" );
1163 + // defaults.set( "dist", "dist" );
1164 + //
1165 + // List<String> source = new ArrayList<String>();
1166 + // source.add( "src|**/*.java" );
1167 + // source.add( "src/main/java|**/*.java" );
1168 + // defaults.set( "source", source );
1169 + //
1170 + // List<String> resources = new ArrayList<String>();
1171 + // resources.add( "resources" );
1172 + // resources.add( "src/main/resources" );
1173 + // defaults.set( "resources", resources );
1174 + //
1175 + // Project project = project( pPath, defaults );
1176 + //
1177 + // // Remove dependency if a JAR of the same name is on the classpath.
1178 + // Paths classpath = project.getPaths( "classpath" );
1179 + // classpath.add( dependencyClasspaths( project, classpath, false, false ) );
1180 + // for ( String dependency : project.getDependencies() )
1181 + // {
1182 + // String dependencyName = project( project.path( dependency ) ).getName();
1183 + // for ( String classpathFile : classpath )
1184 + // {
1185 + // String name = fileWithoutExtension( classpathFile );
1186 + // int dashIndex = name.lastIndexOf( '-' );
1187 + // if ( dashIndex != -1 )
1188 + // {
1189 + // name = name.substring( 0, dashIndex );
1190 + // }
1191 + // if ( name.equals( dependencyName ) )
1192 + // {
1193 + // if ( DEBUG )
1194 + // {
1195 + // debug( "Ignoring " + project + " dependency: " + dependencyName + " (already on classpath: " + classpathFile + ")" );
1196 + // }
1197 + // project.remove( "dependencies", dependency );
1198 + // break;
1199 + // }
1200 + // }
1201 + // }
1202 + //
1203 + // if ( TRACE )
1204 + // {
1205 + // trace( "scar", "Project: " + project + "\n" + project );
1206 + // }
1207 + //
1208 + // return project;
1209 + }
1210 +
1211 + protected boolean mBuilt = false;
1212 + protected boolean mSources = false;
1213 + protected List<Project> mDependantProjects = new ArrayList<Project>();
1214 + }