001package Torello.Java.Build; 002 003import Torello.HTML.HTMLPage; // needed for a JavaDoc '@link' 004 005import Torello.Java.ReadOnly.ReadOnlyList; 006import Torello.Java.ReadOnly.ReadOnlyArrayList; 007import Torello.Java.ReadOnly.ROArrayListBuilder; 008 009import Torello.Java.StrCSV; 010import Torello.Java.StringParse; 011import Torello.Java.StrCmpr; 012 013import static Torello.Java.C.*; 014 015import java.io.File; 016import java.util.TreeSet; 017import java.util.stream.Stream; 018 019/** 020 * This User-Data class is used to describe the Java-Packages included inside of a Java Project. 021 * 022 * <BR /><BR /><B CLASS=JDDescLabel>Java-HTML JAR BuildPackage instances:</B> 023 * 024 * <BR />You may view the list of packages that are used to build the Java-HTML JAR-Library 025 * here, in the link below: 026 * 027 * <BR /><BR /><A HREF='hilite-files/MyPackages.java.html'>MyPackages.java</A> 028 */ 029public class BuildPackage 030{ 031 // ******************************************************************************************** 032 // ******************************************************************************************** 033 // Static Flag-Fields 034 // ******************************************************************************************** 035 // ******************************************************************************************** 036 037 038 /** 039 * This flag may be attached to one of your {@code BuildPackage} instances to signify that the 040 * package being flagged should not ever be re-compiled when Build-Stage 1 ({@code 'javac'}) is 041 * executing - <I>unless the package's nick-name has been explicity specified at the Command 042 * Line Interface</I>. 043 * 044 * <BR /><BR />This flag can be an invaluable tool for speeding up build times be eliminating 045 * the compilation for Source-Files whose code is not changing and is not dependent on the 046 * Source-Files inside your project. 047 * 048 * <BR /><BR /><B CLASS=JDDescLabel>For Instance, Java-HTML:</B> 049 * 050 * <BR />In the Java-HTML {@code '.jar'} File, the external-imports that are included such as 051 * the Glass-Fish JSON Processor, and the Apache CLI Tools have Source-Files that do not ever 052 * need to be recompiled. Those packages are included in this {@code '.jar'} file's 053 * documentation, but when this project is built, the Compiler Build-Stage will skip the 054 * compilation of the Source-Files in those Packages. 055 */ 056 public static final byte DO_NOT_RECOMPILE = 1; 057 058 /** 059 * This flag may be attached to one of your {@code BuildPackage} instances to signify that the 060 * package being flagged should not ever be documented when Build-Stage 2 ({@code 'javadoc'}) 061 * is executing. 062 * 063 * <BR /><BR />When this flag is attached to a {@code BuildPackage}, there will simply be no 064 * documentation generated for that package. 065 * 066 * <BR /><BR /><B CLASS=JDDescLabel>For Instance, Java-HTML:</B> 067 * 068 * <BR />This Project's {@code '.jar'} File includes several classes that act as Internal 069 * Processors for the Java-Doc Upgrader Tool. Those classes are all part of a root package 070 * known as {@code JDUInternal}. 071 * 072 * <BR /><BR />Because the Source-Code in these classes serves no purpose whatsoever as a part 073 * of any kind of "External API", the methods and fields of those classes are not documented. 074 * The {@code BuildPackage} instance for the {@code JDUInternal} Package has been flagged with 075 * this flag {@code DO_NOT_DOCUMENT} 076 */ 077 public static final byte DO_NOT_DOCUMENT = DO_NOT_RECOMPILE << 1; 078 079 /** 080 * This is a special flag that may be used to signify that a particular {@code BuildPackage} 081 * instance actually refers to a tree of packages, rather than just a single package. 082 * 083 * <BR /><BR />This flag works very well with the {@link #DO_NOT_DOCUMENT} Flag. 084 * 085 * <BR /><BR /><B CLASS=JDDescLabel>For Instance, Java-HTML:</B> 086 * 087 * <BR />Again, the internal Java-Doc Upgrader Classes, rooted in package {@code JDUInternal}, 088 * actually form a tree of packages that are ultimately included in the {@code '.jar'} File. 089 */ 090 public static final byte HAS_SUB_PACKAGES = DO_NOT_DOCUMENT << 1; 091 092 /** 093 * This is a flag that can be used to boost Build-Times in a way quite similar to the 094 * {@link #DO_NOT_RECOMPILE} flag. When a {@code BuildPackage} instance is flagged with 095 * {@code QUICKER_BUILD_SKIP}, if a user is performing a "Partial Build" using one of the 096 * Partial-Build Flags ({@code -pb1} and {@code -pb2}), then that package will simply be 097 * eliminated from the Build. 098 * 099 * <BR /><BR />For packages that are not necessarily external and independent packages, but 100 * are disjoint enough such that they do not need to be recompiled or documented in most 101 * situations, then this flag can help boost Build-Speed during development. 102 * 103 * <BR /><BR /><B CLASS=JDDescLabel>For Instance, Java-HTML:</B> 104 * 105 * <BR />The Java-HTML {@code '.jar'} includes an Experimental Headless-Browser 106 * Package. The {@code BuildPackage} instance that models that package is flagged with the 107 * {@code QUICKER_BUILD_SKIP}. This means that during development, unless a Full Release 108 * Build has been invoked (Build-Switch {@code '-cb1'}), the Browser-Package is simply ignored. 109 * 110 * <BR /><BR />It is not compiled by Stage 1; it is not documented by the {@code 'javadoc'} 111 * stage, and its documentation pages are not upgraded. Because that package was generated by 112 * a Code-Generator, and is extremely lengthy and doesn't change, it doesn't need to be 113 * included in the Build-Process at all unless a Full-Release Build is being invoked. 114 * 115 * <BR /><BR />Note that the Build-Switch {@code '-NQB'} can override the elimination of 116 * packages that have been flagged in this way. 117 */ 118 public static final byte QUICKER_BUILD_SKIP = HAS_SUB_PACKAGES << 1; 119 120 /** 121 * This can be used to request that the class files for a Java Package not be included in the 122 * {@code '.jar'} File generated for that package. 123 * 124 * <BR /><BR /><B CLASS=JDDescLabel>For Instance, Java-HTML:</B> 125 * 126 * <BR />There is a proprietary, internal, Builder Package Hierarchy that is only used for 127 * improving this particular package. The classes in package {@code Torello.BuildJAR} are, 128 * therefore, flagged with the {@code 'DO_NOT_JAR'} flag. 129 * 130 * <BR /><BR />A cursory inspection of the Java-HTML {@code '.jar'} File will reveal that 131 * there is not package named {@code Torello.BuildJAR} inside the {@code '.jar'} File. This is 132 * because that package contains proprietary and internal classes that serve no purpose 133 * whatsoever outside of the Build-Process. 134 */ 135 public static final byte DO_NOT_JAR = QUICKER_BUILD_SKIP << 1; 136 137 /** 138 * Can be used to flag a package as "Under Development." Packages flagged as such will not be 139 * included in the Build unless explicity requested. 140 * 141 * <BR /><BR /><B CLASS=JDDescLabel>For Instance, Java-HTML:</B> 142 * 143 * <BR />The most recent addition to Java-HTML has been the package {@code Torello.CSS}. 144 * It is currently under development and therefore wholly eliminated from the Build except 145 * when it has been explicity requested. 146 * 147 * <BR /><BR />The elimination of packages flagged with the {@code 'EARLY_DEVELOPMENT'} flag 148 * can be overriden using the Build Command-Line Switch {@code '-IEDP'}, which simply stands 149 * for "Include Early Development Packages". 150 */ 151 public static final byte EARLY_DEVELOPMENT = DO_NOT_JAR << 1; 152 153 154 // ******************************************************************************************** 155 // ******************************************************************************************** 156 // Static Fields 157 // ******************************************************************************************** 158 // ******************************************************************************************** 159 160 161 // This can be important 162 public static final boolean DEBUGGING = false; 163 164 // This is ubiquitous in Build-Stuff 165 private static final String FS = File.separator; 166 167 // For (most) packages, which do not have "Helper Packages" 168 private static final ReadOnlyList<String> EMPTY_LIST = ReadOnlyList.of(); 169 170 171 // ******************************************************************************************** 172 // ******************************************************************************************** 173 // Instance Fields 174 // ******************************************************************************************** 175 // ******************************************************************************************** 176 177 178 /** This package's full-name. */ 179 public final String fullName; 180 181 /** This package's class-path location within the File-System. */ 182 public final String classPathLocation; 183 184 /** This package's root-directory within the File-System. */ 185 public final String pkgRootDirectory; 186 187 /** A Nick-Name that may be used when trying to request this package at the CLI */ 188 public final String nickName; 189 190 /** 191 * Indicates that this package's Source-Code Directory has an {@code 'upgrade-files/'} 192 * sub-directory. The {@code 'upgrade-files/'} directory stores myriad configurations that are 193 * utilized by the Java-Doc Upgrader Tool, which is executed in Build-Stage 3. 194 * 195 * <BR /><BR />This field is assigned as follows: 196 * 197 * <BR /><DIV CLASS=SNIP>{@code 198 * File f = new File(this.pkgRootDirectory + "upgrade-files" + FS); 199 * 200 * this.hasUpgradeFilesDir = f.exists() && f.isDirectory(); 201 * }</DIV> 202 */ 203 public final boolean hasUpgradeFilesDir; 204 205 /** 206 * Indicates that this package's Source-Code Directory has a {@code 'package-source/'} 207 * sub-directory containing {@code '.java'} Files. The {@code 'package-source/'} directory 208 * can be used to organize and categorize {@code '.java'} Files within a single Java Package 209 * so that they are not all lumped together in a single Java-Package Directory. 210 * 211 * <BR /><BR />This field is assigned as follows: 212 * 213 * <BR /><DIV CLASS=SNIP>{@code 214 * f = new File(this.pkgRootDirectory + "package-source" + FS); 215 * 216 * this.hasPackageSourceDir = f.exists() && f.isDirectory(); 217 * }</DIV> 218 */ 219 public final boolean hasPackageSourceDir; 220 221 /** 222 * This field indicates whether or not the {@link #DO_NOT_RECOMPILE} flag was assigned to 223 * {@code 'this'} instance of {@code BuildPackage}. This field's value is assigned, in this 224 * class' constructor, as below: 225 * 226 * <BR /><DIV CLASS=SNIP>{@code 227 * this.mustReCompile = (flags & DO_NOT_RECOMPILE) == 0; 228 * }</DIV> 229 * 230 * @see #DO_NOT_RECOMPILE 231 */ 232 public final boolean mustReCompile; 233 234 /** 235 * This field simply indicates whether or not the {@link #DO_NOT_DOCUMENT} was assigned to 236 * {@code 'this'} instance of {@code BuildPackage}. This field's value is assigned, in this 237 * class' constructor, as below: 238 * 239 * <BR /><DIV CLASS=SNIP>{@code 240 * this.mustDocument = (flags & DO_NOT_DOCUMENT) == 0; 241 * }</DIV> 242 * 243 * @see #DO_NOT_DOCUMENT 244 */ 245 public final boolean mustDocument; 246 247 /** 248 * This field simply indicates whether or not the {@link #DO_NOT_JAR} was assigned to 249 * {@code 'this'} instance of {@code BuildPackage}. This field's value is assigned, in this 250 * class' constructor, as below: 251 * 252 * <BR /><DIV CLASS=SNIP>{@code 253 * this.doNotJAR = (flags & DO_NOT_JAR) >= 1; 254 * }</DIV> 255 * 256 * @see #DO_NOT_JAR 257 */ 258 public final boolean doNotJAR; 259 260 /** 261 * This field simply indicates whether or not the {@link #HAS_SUB_PACKAGES} was assigned to 262 * {@code 'this'} instance of {@code BuildPackage}. This field's value is assigned, in this 263 * class' constructor, as below: 264 * 265 * <BR /><DIV CLASS=SNIP>{@code 266 * this.hasSubPackages = (flags & HAS_SUB_PACKAGES) >= 1; 267 * }</DIV> 268 * 269 * @see #HAS_SUB_PACKAGES 270 */ 271 public final boolean hasSubPackages; 272 273 /** 274 * This field simply indicates whether or not the {@link #QUICKER_BUILD_SKIP} was assigned to 275 * {@code 'this'} instance of {@code BuildPackage}. This field's value is assigned, in this 276 * class' constructor, as below: 277 * 278 * <BR /><DIV CLASS=SNIP>{@code 279 * this.skipIfQuickerBuild = (flags & QUICKER_BUILD_SKIP) >= 1; 280 * }</DIV> 281 * 282 * @see #QUICKER_BUILD_SKIP 283 */ 284 public final boolean skipIfQuickerBuild; 285 286 /** 287 * This field simply indicates whether or not the {@link #EARLY_DEVELOPMENT} was assigned to 288 * {@code 'this'} instance of {@code BuildPackage}. This field's value is assigned, in this 289 * class' constructor, as below: 290 * 291 * <BR /><DIV CLASS=SNIP>{@code 292 * this.earlyDevelopment = (flags & EARLY_DEVELOPMENT) >= 1; 293 * }</DIV> 294 * 295 * @see #EARLY_DEVELOPMENT 296 */ 297 public final boolean earlyDevelopment; 298 299 /** 300 * Helper packages may be passed as parameters to the {@code BuildPackage} constructor. All a 301 * "Helper Package" is is a sub-directory of the Package-Directory that contains additional 302 * classes that are germaine to the classes in this package. 303 * 304 * <BR /><BR />The classes in a Helper-Package will not be documented by {@code 'javadoc'}, nor 305 * will they be (obviously) be upgrader by the Stage 3 Build-Class performing the JavaDoc 306 * Upgrade. 307 * 308 * <BR /><BR />Instead, these classes will be compiled by the {@code 'javac'}, and included, 309 * quietly, in the final {@code '.jar'} File produced by this Build. This can be a great way 310 * to add simple classes that are required for an API Class to fully function, but do not 311 * actually need to be documented and included in the API themselves. 312 * 313 * <BR /><BR />A cursory inspection of the contents of the Java-HTML {@code '.jar'} 314 * Distribution should reveal, for instance the HTML Helper-Package 315 * {@code 'Torello.HTML.parse'}, which does the actual parsing for the HTML Parsing class 316 * {@link HTMLPage}. This class is fundamental and necessary to the operation of the HTML 317 * Package, but is largely useless in the API that's exported to the end user. 318 * 319 * <BR /><BR />This field's value is assigned by this class' constructor, as below: 320 * 321 * <BR /><DIV CLASS=SNIP>{@code 322 * this.helperPackages = (helperPackages == null) || (helperPackages.length == 0) 323 * ? EMPTY_LIST 324 * : new ReadOnlyArrayList( 325 * 0, 326 * (String subDirName) -> this.pkgRootDirectory + 327 * (subDirName.endsWith(FS) ? subDirName : (subDirName + FS)), 328 * helperPackages 329 * ); 330 * }</DIV> 331 */ 332 public final ReadOnlyList<String> helperPackages; 333 334 335 // ******************************************************************************************** 336 // ******************************************************************************************** 337 // Lone Constructor 338 // ******************************************************************************************** 339 // ******************************************************************************************** 340 341 342 /** 343 * Constructs an instance of this class. 344 * 345 * @param fullName The full name of the package. This package, for instance, is named 346 * {@code Torello.Java.Build}. 347 * 348 * @param classPathLocation The root class-path location 349 * 350 * @param nickName A nick-name for this package that may be passed at the Command-Line 351 * Interface when it is necessary to specify that this package be included in the Build-Proces. 352 * 353 * @param flags The <B>{@code Boolean-OR}</B> of all flags being assigned to this package. 354 * 355 * @param helperPackages A list of any and all sub-directory packages to be included in this 356 * {@code '.jar'} upon which this package depends. This list must contain {@code String's} 357 * that name actual sub-directories of the package-directory that contains the source-files 358 * for this package. 359 * 360 * @throws IllegalArgumentException This exception throws under the following circumstances: 361 * 362 * <BR /><BR /><UL CLASS=JDUL> 363 * <LI> If {@link #mustDocument} and {@link #hasSubPackages} are both, simultaneously, set</LI> 364 * <LI> If any of the "Helper Packages" do not name valid sub-directories of the primary 365 * package-directory 366 * </LI> 367 * </UL> 368 */ 369 public BuildPackage( 370 final String fullName, 371 final String classPathLocation, 372 final String nickName, 373 final int flags, 374 final String... helperPackages 375 ) 376 { 377 this.fullName = fullName; 378 379 if (StrCmpr.equalsXOR(classPathLocation, ".", "")) 380 this.classPathLocation = ""; 381 else if (! classPathLocation.endsWith(FS)) 382 this.classPathLocation = classPathLocation + FS; 383 else 384 this.classPathLocation = classPathLocation; 385 386 this.nickName = nickName; 387 this.earlyDevelopment = (flags & EARLY_DEVELOPMENT) >= 1; 388 this.skipIfQuickerBuild = (flags & QUICKER_BUILD_SKIP) >= 1; 389 this.mustReCompile = (flags & DO_NOT_RECOMPILE) == 0; // The "NOT" of the flag 390 this.mustDocument = (flags & DO_NOT_DOCUMENT) == 0; // The "NOT" of the flag 391 this.doNotJAR = (flags & DO_NOT_JAR) >= 1; 392 this.hasSubPackages = (flags & HAS_SUB_PACKAGES) >= 1; 393 this.pkgRootDirectory = classPathLocation + fullName.replace(".", FS) + FS; 394 395 this.helperPackages = (helperPackages == null) || (helperPackages.length == 0) 396 ? EMPTY_LIST 397 : new ReadOnlyArrayList<>( 398 0, 399 (String subDirName) -> this.pkgRootDirectory + 400 (subDirName.endsWith(FS) ? subDirName : (subDirName + FS)), 401 helperPackages 402 ); 403 404 File f = null; 405 406 for (String pkgDirName : this.helperPackages) 407 { 408 f = new File(pkgDirName); 409 410 if ((! f.exists()) || (! f.isDirectory())) throw new IllegalArgumentException( 411 "Helper Package [" + pkgDirName + "] specifies a directory that either doesn't " + 412 "exist or is a file, rather than a directory:\n" + f.toString() 413 ); 414 } 415 416 f = new File(this.pkgRootDirectory + "upgrade-files" + FS); 417 418 this.hasUpgradeFilesDir = f.exists() && f.isDirectory(); 419 420 f = new File(this.pkgRootDirectory + "package-source" + FS); 421 422 this.hasPackageSourceDir = f.exists() && f.isDirectory(); 423 424 if (this.mustDocument && this.hasSubPackages) throw new IllegalArgumentException 425 ("this.mustDocument AND this.hasSubPackages"); 426 } 427 428 429 // ******************************************************************************************** 430 // ******************************************************************************************** 431 // toString 432 // ******************************************************************************************** 433 // ******************************************************************************************** 434 435 436 /** 437 * Stringify an instance of this class. 438 * @return a Java {@code String} representation of this class. 439 */ 440 public String toString() 441 { 442 final int LEN = 22; 443 444 final String CPL = (classPathLocation.length() == 0) 445 ? BGREEN + "Current Working Directory" + RESET 446 : classPathLocation; 447 448 return 449 StringParse.rightSpacePad("fullName:", LEN) + fullName + '\n' + 450 StringParse.rightSpacePad("classPathLocation:", LEN) + CPL + '\n' + 451 StringParse.rightSpacePad("pkgRootDirectory:", LEN) + pkgRootDirectory + '\n' + 452 StringParse.rightSpacePad("nickName:", LEN) + nickName + '\n' + 453 StringParse.rightSpacePad("hasUpgradeFilesDir:", LEN) + hasUpgradeFilesDir + '\n' + 454 StringParse.rightSpacePad("hasPackageSourceDir:", LEN) + hasPackageSourceDir + '\n' + 455 StringParse.rightSpacePad("mustReCompile:", LEN) + mustReCompile + '\n' + 456 StringParse.rightSpacePad("mustDocument:", LEN) + mustDocument + '\n' + 457 StringParse.rightSpacePad("hasSubPackages:", LEN) + hasSubPackages + '\n' + 458 StringParse.rightSpacePad("skipIfQuickerBuild:", LEN) + skipIfQuickerBuild + '\n' + 459 StringParse.rightSpacePad("earlyDevelopment:", LEN) + earlyDevelopment + '\n' + 460 StringParse.rightSpacePad("helperPackages:", LEN) + 461 '[' + StrCSV.toCSV(helperPackages, true, true, null) + ']'; 462 } 463 464 465 // ******************************************************************************************** 466 // ******************************************************************************************** 467 // Static Helper 468 // ******************************************************************************************** 469 // ******************************************************************************************** 470 471 472 static ReadOnlyList<BuildPackage> nickNameArgVPackages 473 (BuildPackage[] allPackages, ReadOnlyList<String> packageNickNames) 474 { 475 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 476 // Print it to terminal 477 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 478 479 if (DEBUGGING) 480 { 481 System.out.print("packageNickNames: "); 482 for (String pnn : packageNickNames) System.out.print(pnn + ", "); 483 System.out.println(); 484 } 485 486 487 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 488 // Pkg Nick-Name Duplicate-Checker (typing the same name more than once - print & exit) 489 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 490 491 TreeSet<String> duplicateCheckerTS = new TreeSet<>(); 492 493 for (String nickName : packageNickNames) 494 495 if (duplicateCheckerTS.contains(nickName)) 496 { 497 System.err.println( 498 "Duplicate Package Nick-Name Provided: " + BRED + nickName + RESET + '\n' + 499 "Exiting..." 500 ); 501 502 System.exit(1); 503 } 504 505 else duplicateCheckerTS.add(nickName); 506 507 508 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 509 // Convert to a list of "BuildPackage" instances 510 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 511 512 // Keeps a list of the packages that matched a nick-name 513 ROArrayListBuilder<BuildPackage> roab = new ROArrayListBuilder<>(packageNickNames.size()); 514 515 // Keeps a list of any nick-names for which no matching package is found. This is needed 516 // to ensure that if there is one un-matched, that the loop DOES NOT BREAK, and continues 517 // to check for matches... So that when there is an unmatched nick-name, the error-message 518 // that is ultimately printed to the user - CONTAINS THE LIST OF **ALL** unmatched 519 // nick-names. 520 521 Stream.Builder<String> unrecognized = Stream.builder(); 522 523 // A flag indicating that there is at least one unmatched package 524 boolean atLeastOneMismatch = false; 525 526 TOP: 527 for (int i=0; i < packageNickNames.size(); i++) 528 { 529 for (BuildPackage pkg : allPackages) 530 531 if (pkg.nickName.equals(packageNickNames.get(i))) 532 { 533 roab.add(pkg); 534 535 if (DEBUGGING) System.out.println 536 ("argv[" + i + "]: " + packageNickNames.get(i) + " ==> " + pkg.fullName); 537 538 continue TOP; 539 } 540 541 atLeastOneMismatch = true; 542 unrecognized.accept(packageNickNames.get(i)); 543 } 544 545 if (atLeastOneMismatch) 546 { 547 System.err.println( 548 "Unrecognized Package Nick-Name(s): " + 549 "[" + 550 BGREEN + String.join(", ", unrecognized.build().toArray(String[]::new)) + RESET + 551 "]\n" + 552 "This Name could not be mapped to any of your Java Packages to Compile\n" + 553 "Exiting...\n" 554 ); 555 556 System.exit(1); 557 } 558 559 560 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 561 // DONE 562 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 563 564 return roab.build(); 565 } 566}