001package Torello.Java.Build; 002 003import Torello.Java.FileNodeFilter; 004import Torello.Java.ReadOnly.ReadOnlyList; 005import Torello.Java.ReadOnly.ROVectorBuilder; 006 007import java.io.File; 008import java.util.Objects; 009 010/** 011 * This is a very light-weight class, and has as its primary-operation / primary-feature the 012 * ability to allow a user to request that additional files be automatically inserted into the 013 * {@code '.jar'}-File that's generated by this Build-Tool. There are several "goals" that are 014 * acheived during a Build using this Tool, one of which includes creating a {@code '.jar'} file 015 * containing the Java-Packages that have been specified to the class {@link Config}. 016 * 017 * <BR /><BR />Specifying a list of Java-Packages (using the Configuration-Class 018 * {@link BuildPackage}) guarantees that all of the required {@code '.class'}-Files are properly 019 * inserted into your {@code '.jar'}. Often, however, there may be other files that need to be 020 * inserted into the {@code 'META-INF/'} directory inside that {@code '.jar'}-File in order for 021 * services and API's to work properly. By instantiating an instance of class {@code JarInclude}, 022 * one may specify a list of as many non-class-file related files that also have to be inserted 023 * into your {@code '.jar'}-File during the build. 024 * 025 * <BR /><BR />The Build-Tool that you see here ({@link Torello.Java.Build}) is, indeed, the Tool 026 * used to generate the Documentation, Tar's and Jar's for the Java-HTML Jar-Library. This is 027 * in fact a "Self-Building Build Tool". The Source-Code Snippet included below will hopefully 028 * clarify and specify exactly how the class {@code 'JarInclude'} is used to insert additional 029 * files into a {@code '.jar'} so that exported API's properly work. 030 * 031 * <BR /><BR />Here is a method from the (non-public, non-visible) build source for Java-HTML: 032 * 033 * <DIV CLASS=EXAMPLE>{@code 034 * public static JarInclude getJarIncludes() 035 * { 036 * // RTF: Return-True-Filter 037 * final FileNodeFilter RTF = (FileNode fn) -> true; 038 * 039 * return new JarInclude() 040 * // The first one contains the list of annotation processors. The file is named: 041 * // Torello/BuildJAR/IncludeFiles/META-INF/services/javax.annotation.processing.Processor 042 * 043 * .add("Torello/BuildJAR/IncludeFiles/", "META-INF/", true, RTF, RTF) 044 * 045 * // This one is provided by the Glass-Fish JSON Provider. It is named: 046 * // Torello/etc/External/META-INF/services/javax.json.spi.JsonProvider 047 * 048 * .add("Torello/etc/External/", "META-INF/", true, RTF, RTF); 049 * } 050 * }</DIV> 051 * 052 * <BR />The above files will be automatically added into the Archive that's created in the Stage 4 053 * Build-Process / Build-Step. The first file listed helps the Java-Doc Upgrader's Annotaton 054 * Processors to work properly. The second file listed is needed to ensure that the Glass-Fish 055 * JSON-Processor included in this {@code '.jar'}-File works. 056 * 057 * <BR /><BR />And that, by the way, is all folks! That is all {@code 'JarInclude'} does. If 058 * you have such files which need to be inserted, then: 059 * 060 * <BR /><BR /><UL CLASS=JDUL> 061 * 062 * <LI> Create an instance of {@code 'JarInclude'} using this class' constructor. 063 * <BR /><BR /></LI> 064 * 065 * <LI> Register your instance of {@code 'JarInclude'} with the Configuration-Class {@link Config}. 066 * You may do this, simply, by assigning the instance / reference of this class to the 067 * {@code 'Config'} class Configuration-Field, which is simply named: 068 * {@link Config#jarIncludes}. 069 * <BR /><BR /></LI> 070 * 071 * <LI> When you obtain a {@link BuilderRecord} instance from {@link Config}, it wil automatically 072 * add your files as soon as your invoke {@link RunBuild#run(BuilderRecord)}. 073 * </LI> 074 * 075 * </UL> 076 * 077 * @see Config#jarIncludes 078 * @see Config#createBuilder(String[]) 079 * @see RunBuild#run(BuilderRecord) 080 */ 081public class JarInclude 082{ 083 // This is a Package-Visible Inner-Class. It is only usable inside Torello.Java.Build 084 // Instances of this class are included in the ReadOnlyList returned by method 085 // getAllDesriptors. This is also a Package-Visible method 086 087 static class Descriptor 088 { 089 final String workingDirectory, subDirectory; 090 final boolean traverseTree; 091 final FileNodeFilter fileFilter, dirFilter; 092 093 Descriptor( 094 String workingDirectory, 095 String subDirectory, 096 boolean traverseTree, 097 FileNodeFilter fileFilter, 098 FileNodeFilter dirFilter 099 ) 100 { 101 this.workingDirectory = workingDirectory; 102 this.subDirectory = subDirectory; 103 this.traverseTree = traverseTree; 104 this.fileFilter = fileFilter; 105 this.dirFilter = dirFilter; 106 } 107 108 public String toString() { return workingDirectory + subDirectory; } 109 } 110 111 /** Build an instance of this class */ 112 public JarInclude() { } 113 114 private final ROVectorBuilder<JarInclude.Descriptor> descriptors = 115 new ROVectorBuilder<>(); 116 117 // This is only invoked by class BuilderRecord 118 ReadOnlyList<JarInclude.Descriptor> getAllDesriptors() { return descriptors.build(); } 119 120 private static final String M1 = "Parameter '"; 121 private static final String M2 = "' was passed null, but this is not allowed"; 122 123 /** 124 * Convenience Method. 125 * <BR />Adds a Jar-Include Directive that <B>DOES NOT</B> recurse the directory-tree 126 * <BR />Invokes: {@link #add(String, String, boolean, FileNodeFilter, FileNodeFilter)} 127 */ 128 public JarInclude add( 129 String workingDirectory, 130 String subDirectory, 131 FileNodeFilter fileFilter 132 ) 133 { return add(workingDirectory, subDirectory, false, fileFilter, null); } 134 135 /** 136 * Inserts a request for files to be included in the Tar-Jar Build Stage (Stage 4). 137 * 138 * @param workingDirectory When files are added to a {@code '.jar'}-File, the "Working 139 * Directory" part of the File-System Path <B>is not included</B> in the name of the files that 140 * are inserted. 141 * 142 * @param subDirectory The "Sub-Directory" part of the File-System Path <B>is included</B> into 143 * the names of any and all files that are inserted in the {@code '.jar'}. 144 * 145 * @param traverseTree Indicates whether the the {@code String}-Parameter 146 * {@code 'subDirectory'} should be interpreted as a directory-name - <I>or as an entire tree 147 * branch</I> whose own sub-directories should be traversed by the file-scanner. 148 * 149 * @param fileFilter A filter / "chooser" / specifier for deciding which files residing on the 150 * File-System inside {@code 'subDirectory'} (or {@code 'subDirectory'}, and its own 151 * sub-directories - in the case that {@code 'traverseTree'} was passed {@code TRUE}), are to 152 * be included in the {@code '.jar'}. 153 * 154 * <BR /><BR />This filter must return {@code TRUE} if a file this filter is testing 155 * <B><I>should</I></B> be inserted into the {@code '.jar'}, and {@code FALSE}, if the file 156 * <B><I>should not</I></B> be. 157 * 158 * <BR /><BR />This parameter may be passed null, and if it is it will be quietly ignored. 159 * When this filter is null, all files that reside within {@code 'subDirectory'} will be 160 * inserted into the {@code '.jar'}-File. 161 * 162 * <BR /><BR />If this parameter were passed null, and {@code 'traverseTree'} were passed 163 * {@code TRUE}, then all files inside of {@code 'subDirectory'} would be inserted into the 164 * {@code '.jar'} - <I>and furthermore, all files in all sub-directories of 165 * {@code 'subDirectory'} would also be inserted</I>. 166 * 167 * @param dirFilter This filter can only be employed if {@code 'traverseTree'} has been passed 168 * {@code TRUE}. 169 * 170 * <BR /><BR />When {@code 'traverseTree'} is {@code TRUE} as the directory tree rooted at 171 * {@code workingDirectory/subDirectory/} is traversed, each sub-directory that is encountered 172 * will be passed to this filter. When this test is performed, the filter should return 173 * {@code TRUE} to indicate that it would like a particular sub-directory searched, and 174 * {@code FALSE} to indicate that it must be skipped. 175 * 176 * <BR /><BR />This parameter may be passed null, and if it is it will be silently ignored. 177 * If this parameter is null, and {@code 'traverseTree'} is {@code TRUE}, all sub-directories 178 * of {@code workingDirectory/subDirectory/} will be entered / traversed. 179 * 180 * <BR /><BR ><B>NOTE:</B> If this parameter is passed a non-null filter, but 181 * {@code 'traverseTree'} has been passed {@code FALSE}, then an 182 * {@code IllegalArgumentException} will throw. Parameter {@code 'dirFilter'} has no use or 183 * application if the named directory-tree is not going to be traversed! 184 * 185 * @return {@code 'this'} instance, for convenience and invocation-chaining. 186 * 187 * @throws NullPointerException If either {@code 'workingDirectory'} or {@code 'subDirectory'} 188 * is passed null. 189 * 190 * @throws IllegalArgumentException If either {@code 'workingDirectory'} or 191 * {@code 'subDirectory'} do not name real directories that actually exist on the File-System. 192 * 193 * <BR /><BR />This exception will also throw if {@code 'traverseTree'} is passed {@code FALSE} 194 * but {@code 'dirFilter'} is non-null. 195 */ 196 public JarInclude add( 197 String workingDirectory, 198 String subDirectory, 199 boolean traverseTree, 200 FileNodeFilter fileFilter, 201 FileNodeFilter dirFilter 202 ) 203 { 204 File f; 205 206 Objects.requireNonNull(workingDirectory, M1 + "workingDirectory" + M2); 207 Objects.requireNonNull(subDirectory, M1 + "subDirectory" + M2); 208 209 if (workingDirectory.length() > 0) 210 { 211 f = new File(workingDirectory); 212 213 if (! f.exists()) throw new IllegalArgumentException( 214 "The directory-name provided to parameter 'workingDirectory' does not exist on " + 215 "the File-System:\n[" + workingDirectory + ']' 216 ); 217 218 if (! f.isDirectory()) throw new IllegalArgumentException( 219 "The directory-name provided to parameter 'workingDirectory' is not the name of " + 220 "an actual File-System directory:\n[" + workingDirectory + ']' 221 ); 222 } 223 224 if (! workingDirectory.endsWith(File.separator)) 225 if (workingDirectory.length() > 0) 226 workingDirectory = workingDirectory + File.separator; 227 228 String subDir = workingDirectory + subDirectory; 229 230 if (subDir.length() > 0) 231 { 232 f = new File(subDir); 233 234 if ((! f.exists()) || (! f.isDirectory())) throw new IllegalArgumentException( 235 "The directory-name provided to parameter 'subDirectory' does not exist on the " + 236 "File-System as a Sub-Directory of 'workingDirectory':\n" + 237 "[" + subDirectory + ']' 238 ); 239 } 240 241 if (! subDirectory.endsWith(File.separator)) 242 if (subDirectory.length() > 0) 243 subDirectory = subDirectory + File.separator; 244 245 if ((! traverseTree) && (dirFilter != null)) throw new IllegalArgumentException( 246 "You have passed FALSE to 'traverseTree', but a non-null filter to parameter " + 247 "'dirFilter'. This is not allowed." 248 ); 249 250 this.descriptors.add 251 (new Descriptor(workingDirectory, subDirectory, traverseTree, fileFilter, dirFilter)); 252 253 return this; 254 } 255 256}