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
010public class JarInclude
011{
012    // This is a Package-Visible Inner-Class.  It is only usable inside Torello.Java.Build
013    // Instances of this class are included in the ReadOnlyList returned by method
014    // getAllDesriptors.  This is also a Package-Visible method
015
016    static class Descriptor
017    {
018        final String workingDirectory, subDirectory;
019        final boolean traverseTree;
020        final FileNodeFilter fileFilter, dirFilter;
021
022        Descriptor(
023            String          workingDirectory,
024            String          subDirectory,
025            boolean         traverseTree,
026            FileNodeFilter  fileFilter,
027            FileNodeFilter  dirFilter
028        )
029        {
030            this.workingDirectory   = workingDirectory;
031            this.subDirectory       = subDirectory;
032            this.traverseTree       = traverseTree;
033            this.fileFilter         = fileFilter;
034            this.dirFilter          = dirFilter;
035        }
036
037        public String toString() { return workingDirectory + subDirectory; }
038    }
039
040    /** Build an instance of this class */
041    public JarInclude() { }
042
043    private final ROVectorBuilder<JarInclude.Descriptor> descriptors =
044        new ROVectorBuilder<>();
045
046    // This is only invoked by class Builder
047    ReadOnlyList<JarInclude.Descriptor> getAllDesriptors() { return descriptors.build(); }
048
049    private static final String M1 = "Parameter '";
050    private static final String M2 = "' was passed null, but this is not allowed";
051
052    /**
053     * Convenience Method.
054     * <BR />Adds a Jar-Include Directive that <B>DOES NOT</B> recurse the directory-tree
055     * <BR />Invokes: {@link #add(String, String, boolean, FileNodeFilter, FileNodeFilter)}
056     */
057    public JarInclude add(
058        String          workingDirectory,
059        String          subDirectory,
060        FileNodeFilter  fileFilter
061    )
062    { return add(workingDirectory, subDirectory, false, fileFilter, null); }
063
064    /**
065     * Inserts a request for files to be included in the Tar-Jar Build Stage (Stage 4).
066     * 
067     * @param workingDirectory When files are added to a {@code '.jar'}-File, the "Working
068     * Directory" part of the File-System Path <B>is not included</B> in the name of the files that
069     * are inserted.
070     * 
071     * @param subDirectory The "Sub-Directory" part of the File-System Path <B>is included</B> into
072     * the names of any and all files that are inserted in the {@code '.jar'}.
073     * 
074     * @param traverseTree Indicates whether the the {@code String}-Parameter
075     * {@code 'subDirectory'} should be interpreted as a directory-name - <I>or as an entire tree
076     * branch</I> whose own sub-directories should be traversed by the file-scanner.
077     * 
078     * @param fileFilter A filter / "chooser" / specifier for deciding which files residing on the
079     * File-System inside {@code 'subDirectory'} (or {@code 'subDirectory'}, and its own
080     * sub-directories - in the case that {@code 'traverseTree'} was passed {@code TRUE}), are to
081     * be included in the {@code '.jar'}.
082     * 
083     * <BR /><BR />This filter must return {@code TRUE} if a file this filter is testing
084     * <B><I>should</I></B> be inserted into the {@code '.jar'}, and {@code FALSE}, if the file
085     * <B><I>should not</I></B> be.
086     * 
087     * <BR /><BR />This parameter may be passed null, and if it is it will be quietly ignored.
088     * When this filter is null, all files that reside within {@code 'subDirectory'} will be 
089     * inserted into the {@code '.jar'}-File.
090     * 
091     * <BR /><BR />If this parameter were passed null, and {@code 'traverseTree'} were passed
092     * {@code TRUE}, then all files inside of {@code 'subDirectory'} would be inserted into the
093     * {@code '.jar'} - <I>and furthermore, all files in all sub-directories of
094     * {@code 'subDirectory'} would also be inserted</I>.
095     * 
096     * @param dirFilter This filter can only be employed if {@code 'traverseTree'} has been passed
097     * {@code TRUE}.
098     * 
099     * <BR /><BR />When {@code 'traverseTree'} is {@code TRUE} as the directory tree rooted at
100     * {@code workingDirectory/subDirectory/} is traversed, each sub-directory that is encountered
101     * will be passed to this filter.  When this test is performed, the filter should return 
102     * {@code TRUE} to indicate that it would like a particular sub-directory searched, and 
103     * {@code FALSE} to indicate that it must be skipped.
104     * 
105     * <BR /><BR />This parameter may be passed null, and if it is it will be silently ignored.
106     * If this parameter is null, and {@code 'traverseTree'} is {@code TRUE}, all sub-directories
107     * of {@code workingDirectory/subDirectory/} will be entered / traversed.
108     * 
109     * <BR /><BR ><B>NOTE:</B> If this parameter is passed a non-null filter, but
110     * {@code 'traverseTree'} has been passed {@code FALSE}, then an
111     * {@code IllegalArgumentException} will throw.  Parameter {@code 'dirFilter'} has no use or
112     * application if the named directory-tree is not going to be traversed!
113     * 
114     * @return {@code 'this'} instance, for convenience and invocation-chaining.
115     * 
116     * @throws NullPointerException If either {@code 'workingDirectory'} or {@code 'subDirectory'}
117     * is passed null.
118     * 
119     * @throws IllegalArgumentException If either {@code 'workingDirectory'} or
120     * {@code 'subDirectory'} do not name real directories that actually exist on the File-System.
121     * 
122     * <BR /><BR />This exception will also throw if {@code 'traverseTree'} is passed {@code FALSE}
123     * but {@code 'dirFilter'} is non-null.
124     */
125    public JarInclude add(
126            String          workingDirectory,
127            String          subDirectory,
128            boolean         traverseTree,
129            FileNodeFilter  fileFilter,
130            FileNodeFilter  dirFilter
131        )
132    {
133        File f;
134
135        Objects.requireNonNull(workingDirectory, M1 + "workingDirectory" + M2);
136        Objects.requireNonNull(subDirectory, M1 + "subDirectory" + M2);
137
138        if (workingDirectory.length() > 0)
139        {
140            f = new File(workingDirectory);
141
142            if (! f.exists()) throw new IllegalArgumentException(
143                "The directory-name provided to parameter 'workingDirectory' does not exist on " +
144                "the File-System:\n[" + workingDirectory + ']'
145            );
146
147            if (! f.isDirectory()) throw new IllegalArgumentException(
148                "The directory-name provided to parameter 'workingDirectory' is not the name of " +
149                "an actual File-System directory:\n[" + workingDirectory + ']'
150            );
151        }
152
153        if (! workingDirectory.endsWith(File.separator))
154            if (workingDirectory.length() > 0)
155                workingDirectory = workingDirectory + File.separator;
156
157        String subDir = workingDirectory + subDirectory;
158
159        if (subDir.length() > 0)
160        {
161            f = new File(subDir);
162
163            if ((! f.exists()) || (! f.isDirectory())) throw new IllegalArgumentException(
164                "The directory-name provided to parameter 'subDirectory' does not exist on the " +
165                "File-System as a Sub-Directory of 'workingDirectory':\n" +
166                "[" + subDirectory + ']'
167            );
168        }
169
170        if (! subDirectory.endsWith(File.separator))
171            if (subDirectory.length() > 0)
172                subDirectory = subDirectory + File.separator;
173
174        if ((! traverseTree) && (dirFilter != null)) throw new IllegalArgumentException(
175            "You have passed FALSE to 'traverseTree', but a non-null filter to parameter " +
176            "'dirFilter'.  This is not allowed."
177        );
178        
179        this.descriptors.add
180            (new Descriptor(workingDirectory, subDirectory, traverseTree, fileFilter, dirFilter));
181
182        return this;
183    }
184}