001package Torello.Java;
002
003import java.util.*;
004import java.util.function.*;
005import java.util.stream.*;
006import java.io.*;
007
008import Torello.Java.Shell.C;
009import Torello.Java.Additional.*;
010
011/**
012 * <CODE>FileNode - Documentation.</CODE><BR /><BR />
013 * <EMBED CLASS="external-html" DATA-FILE-ID="FN">
014 */
015public final class FileNode
016    implements CharSequence, Comparable<FileNode>, java.io.Serializable, Cloneable
017{
018    /** <EMBED CLASS="external-html" DATA-FILE-ID="SVUID"> */
019    public static final long serialVersionUID = 1;
020
021    /**
022     * When this variable is <B>TRUE</B> debug and status information will be sent to 
023     * {@code Standard Output}
024     */
025    public static boolean VERBOSE = false;
026
027    /** The name of the file or directory is saved/stored here */
028    public final String name;
029
030    /**
031     * If {@code 'this'} class-instance represents a directory in the BASH/UNIX or MS-DOS file
032     * system, this variable will be <B>TRUE</B>.
033     */
034    public final boolean isDirectory;
035
036    /** 
037     * The parent/container {@code 'FileNode'} directory is saved here - like a <I>pointer 
038     * "tree"</I>.
039     */
040    protected FileNode parent;
041
042    /**
043     * When a tree is loaded into memory, the size of each file is saved in this variable.  It can
044     * be retrieved (and even changed).
045     */
046    public final long fileSize;
047
048    /**
049     * When a tree is loaded into memory, the file-date of the file is saved in this variable.  It
050     * can be retrieved.  If the {@code SecurityManager.checkRead(fileName)} denies read access to
051     * the file, this field will equal zero.
052     *
053     * <BR /><BR /><B><SPAN STYLE="color: red;">NOTE:</SPAN></B> This field is a Java simple-type
054     * {@code 'long'} which represents the time the file was last modified, measured in milliseconds
055     * since the epoch {@code (00:00:00 GMT, January 1, 1970)}
056     */
057    public final long lastModified;
058
059    /**
060     * This variable remains null for all instances of this class which represent 'files' on the
061     * underlying operating-system, rather than 'directories.'  On the other hand, if
062     * {@code 'this'} instance of {@code FileNode} represents an MS-DOS, Unix, or Apple OS
063     * directory, then field {@code Vector<FileNode> children} will be instantiated and hold all
064     * files and sub-directories which are found.
065     */
066    protected final Vector<FileNode> children;
067
068
069    // ********************************************************************************************
070    // ********************************************************************************************
071    // FileNode Constructors
072    // ********************************************************************************************
073    // ********************************************************************************************
074
075    /**
076     * This constructor builds a {@code FileNode} object - which <I>must be a directory
077     * {@code FileNode}  instance</I> and may not be a file {@code FileNode} instance.
078     *
079     * <BR /><BR /><B>SPECIFICALLY:</B> The node that is instantiated will have a
080     * {@code final int fileSize} field value of {@code '0'} and a
081     * {@code final boolean isDirectory} value of <B>TRUE</B>.
082     *
083     * <BR /><BR /><B>NOTE:</B> File-name validity checks are not performed here, no validity
084     * checks are performed because this constructor has protected access, and the calls to it are
085     * generated here in this class.   Furthermore, the values sent to these parameters are all
086     * retrieved from getter-calls to {@code class java.io.File}, and likely are not invalid.
087     *
088     * @param name The name of {@code 'this' FileNode}
089     *
090     * @param parent This is the parent or "container-directory" of {@code 'this' FileNode}.  If a
091     * {@code FileNode} that is not a directory itself is passed as the parent, then an exception
092     * will be thrown.
093     *
094     * @param lastModified This must be a {@code long} value indicating when the file was last
095     * modified - according to the operating-system.  This value may be {@code '0'}, and if so, it
096     * indicates that this information was either not available, or a read of the value was not
097     * allowed by the operating-system security manager.
098     *
099     * <DIV CLASS="COMPLETE">{@code
100     * this.name            = name;  
101     * this.parent          = parent;
102     * this.isDirectory     = true;
103     * this.fileSize        = 0;
104     * this.lastModified    = lastModified;
105     * this.children        = new Vector<>();
106     * }</DIV>
107     */
108    protected FileNode(String name, FileNode parent, long lastModified)
109    {
110        this.name           = name;  
111        this.parent         = parent;
112        this.isDirectory    = true;
113        this.fileSize       = 0;
114        this.lastModified   = lastModified;
115        this.children       = new Vector<>();
116    }
117
118    /**
119     * This constructor builds a {@code FileNode} object which <I>must be a file {@code FileNode}
120     * instance</I> - and may not be a directory {@code FileNode} instance.
121     *
122     * <BR /><BR /><B>SPECIFICALLY:</B> The node that is instantiated will have a {@code final 
123     * boolean isDirectory} value of <B>FALSE</B>.
124     * <BR /><BR /><B>NOTE:</B> File-name validity checks are not performed here, no validity
125     * checks are performed because this constructor has protected access, and the calls to it are
126     * generated here in this class.   Furthermore, the values sent to these parameters are all'
127     * retrieved from getter-calls to {@code class java.io.File}, and likely are not invalid.
128     *
129     * @param name The name of {@code 'this' FileNode}
130     *
131     * @param parent This is the parent or "container-directory" of {@code 'this' FileNode}.
132     * If a {@code FileNode} that is not a directory itself is passed as the parent, then an
133     * exception will be thrown.
134     *
135     * @param fileSize The size of this file - in bytes.
136     *
137     * @param lastModified This must be a long value indicating when the file was last modified -
138     * according to the operating-system.  This value may be {@code '0'}, and if so, it indicates
139     * that this information was either not available, or a read of the value was not allowed by
140     * the operating-system security manager.
141     *
142     * <DIV CLASS="COMPLETE">{@code
143     * this.name            = name;  
144     * this.parent          = parent;
145     * this.isDirectory     = false;
146     * this.fileSize        = fileSize;
147     * this.lastModified    = lastModified;
148     * this.children        = null;
149     * }</DIV>
150     */
151    protected FileNode(String name, FileNode parent, long fileSize, long lastModified)
152    {
153        this.name           = name;  
154        this.parent         = parent;
155        this.isDirectory    = false;
156        this.fileSize       = fileSize;
157        this.lastModified   = lastModified;
158        this.children       = null;
159    }
160
161    /**
162     * This constructor builds a "root" {@code FileNode} instance.
163     * @param name The name of {@code 'this' FileNode}
164     */
165    protected FileNode(String name)
166    {
167        if (name.contains("\n")) throw new IllegalArgumentException(
168            "File & Directory names may not contain newlines:\n" + name
169        );
170
171        if (name.endsWith(File.separator) || name.endsWith("\'"))
172            name = name.substring(0, name.length() - 1);
173
174        long lastModified   = 0;
175
176        try
177        {
178            File f = new File(name);
179
180            if (! f.isDirectory()) throw new IllegalArgumentException(
181                "You have attempted to create a new Root Directory - but the filename passed " +
182                "isn't valid: " + name
183            );
184
185            lastModified = f.lastModified();
186        }
187        catch (SecurityException se)
188        { 
189            throw new IllegalArgumentException(
190                "You have attempted to create a new Root FileNode instance - but a Security Exception " +
191                "is preventing this.  See this exception's Throwable.getCause() for more details.", se
192            );
193        }
194
195        this.name           = name;  
196        this.parent         = null;
197        this.isDirectory    = true;
198        this.fileSize       = 0;
199        this.lastModified   = lastModified;
200        this.children       = new Vector<>();
201    }
202
203    /**
204     * This is the <B>"Factory Method"</B> for this class.  The {@code String name} parameter is
205     * intended to be the root directory-name from which the tree is intended to be built.
206     *
207     * <BR /><BR /><B>NOTE:</B> Once a "root node" for the tree has been built, calling the
208     * {@code loadTree(...)} methods is simple, and the only way to populate the tree of UNIX or
209     * MS-DOS file-names.
210     *
211     * <DIV CLASS="EXAMPLE">{@code
212     * FileNode fn = FileNode.createRoot("etc/MyDataFiles/user123/").loadTree();
213     * }</DIV>
214     *
215     * @return An instance of this class from which a {@code FileNode} tree may be instantiated.
216     */
217    public static FileNode createRoot(String name)
218    { return new FileNode(name); }
219
220
221    // ********************************************************************************************
222    // ********************************************************************************************
223    // retrieve operations
224    // ********************************************************************************************
225    // ********************************************************************************************
226
227    /**
228     * Convenience Method.  Invokes {@link #pollDir(String, boolean)}
229     * <BR />Does <B>NOT</B> ignore case.
230     */
231    public FileNode pollDir(String dirName) { return pollDir(dirName, false); }
232
233    /**
234     * Retrieves the sub-directory {@code FileNode} instance named by parameter {@code 'dirName'}
235     * if there is a {@code FileNode} that is a <I>direct descendant</I> of {@code 'this'} instance
236     * of {@code FileNode}.
237     * 
238     * <BR /><BR /><B>Differing:</B> Different than method {@link #dir(String, boolean)}, this
239     * method will "extract" the directory from the in-memory file-tree.  If a directory is 
240     * found matching the requested name and case requirements, when it is returned, it's parent
241     * {@code FileNode} pointer instance shall be null, and the parent will not contain a link
242     * to the directory in it's list of child-nodes.
243     * 
244     * @param dirName This is the name of any directory.
245     * 
246     * <BR /><BR /><B STYLE="color: red">IMPORTANT:</B> This must be the <I><B>name-only</I></B>
247     * leaving out all parent-directory or drive-letter text.
248     * 
249     * <BR /><BR /><B STYLE="color: red">FURTHERMORE:</B> The forward slash ({@code '/'}) or the
250     * back-slash ({@code '\'}) character that sometimes is appended to a directory-name
251     * <B><I>may not</I></B> be included in this name (unless a forward-slash or back-slash is
252     * a part of the name of the directory).
253     * 
254     * <BR /><BR /><B STYLE="color: red">FINALLY:</B> When this directory is extracted, none
255     * of the child pointers contained by this directory-instance of {@code FileNode} will be
256     * modified.  In essence, the entire sub-tree - <I>starting at the directory that was 
257     * specified</I> - will be extracted from the parent-tree.  Any / all contents of the
258     * sub-tree shall be in the same state as they were prior to the extraction.
259     * 
260     * @param ignoreCase For some files and directories, on some operating systems (Microsoft
261     * Windows, for instance) file-system name case is not considered relevant when matching
262     * directory names.  If this parameter is passed <B>TRUE</B>, then name comparisons will use
263     * a case-insensitive comparison mechanism.
264     * 
265     * @return The child {@code FileNode} (sub-directory) of {@code 'this'} directory whose name
266     * matches the name provided by parameter {@code 'dirName'}.  It's {@code 'parent'} field
267     * will be null, and the parent {@code FileNode} instance will not have a pointer to the
268     * instance that is returned.
269     * 
270     * <BR /><BR />If no matching directory is found, then this method shall return null.
271     * 
272     * @throws DirExpectedException If {@code 'this'} instance of {@code FileNode} is a file,
273     * not a directory, then this exception shall throw.  Only directories can contain other
274     * instances of {@code FileNode}.
275     * 
276     * @see #dir(String, boolean)
277     * @see #children
278     */
279    public FileNode pollDir(String dirName, boolean ignoreCase)
280    {
281        FileNode ret = dir(dirName, ignoreCase);
282
283        if (ret != null)
284        {
285            children.remove(ret);
286            ret.parent = null;
287        }
288
289        return ret;
290    }
291
292    /** 
293     * Convenience Method.  Invokes {@link #dir(String, boolean)}.
294     * <BR />Does <B>NOT</B> ignore case.
295     */
296    public FileNode dir(String dirName) { return dir(dirName, false); }
297
298    /**
299     * Retrieves the sub-directory {@code FileNode} instance named by parameter {@code 'dirName'}
300     * if there is a {@code FileNode} that is a <I>direct descendant</I> of {@code 'this'} instance
301     * of {@code FileNode}.
302     * 
303     * @param dirName This is the name of any directory.
304     * 
305     * <BR /><BR /><B STYLE="color: red">IMPORTANT:</B> This must be the <I><B>name-only</I></B>
306     * leaving out all parent-directory or drive-letter text.
307     * 
308     * <BR /><BR /><B STYLE="color: red">FURTHERMORE:</B> The forward slash ({@code '/'}) or the
309     * back-slash ({@code '\'}) character that sometimes is appended to a directory-name
310     * <B><I>may not</I></B> be included in this name (unless a forward-slash or back-slash is
311     * a part of the name of the directory).
312     * 
313     * @param ignoreCase For some files and directories, on some operating systems (Microsoft
314     * Windows, for instance) file-system name case is not considered relevant when matching
315     * directory names.  If this parameter is passed <B>TRUE</B>, then name comparisons will use
316     * a case-insensitive comparison mechanism.
317     * 
318     * @return The child {@code FileNode} (sub-directory) of this directory whose name matches
319     * the name provided by parameter {@code 'dirName'}.
320     * 
321     * <BR /><BR />If no matching directory is found, then this method shall return null.
322     * 
323     * @throws DirExpectedException If {@code 'this'} instance of {@code FileNode} is a file,
324     * not a directory, then this exception shall throw.  Only directories can contain other
325     * instances of {@code FileNode}.
326     * 
327     * @see #children
328     * @see #isDirectory
329     * @see #name
330     */
331    public FileNode dir(String dirName, boolean ignoreCase)
332    {
333        // Only directories may contain other instances of FileNode
334        DirExpectedException.check(this);
335
336        // We are looking for a directory named 'dirName'
337        //
338        // IMPORTANT: The outer squiqgly-braces are MANDATORY.  Without them, there is 
339        //            "deceptive indentation," because the 'else' is paired with the second-if,
340        //            not the first!
341        if (ignoreCase)
342        {
343            for (FileNode fn : children)
344                if (fn.isDirectory && fn.name.equalsIgnoreCase(dirName)) return fn;
345        }
346        else
347        {
348            for (FileNode fn2 : children)
349                if (fn2.isDirectory && fn2.name.equals(dirName)) return fn2;
350        }
351
352        // Not found, return null.
353        return null;
354    }
355
356    /** 
357     * Convenience Method.  Invokes {@link #pollFile(String, boolean)}.
358     * <BR />Does <B>NOT</B> ignore case.
359     */
360    public FileNode pollFile(String fileName) { return pollFile(fileName, false); }
361
362    /**
363     * Retrieves a {@code FileNode} instance named by parameter {@code 'fileName'} if there is
364     * a {@code FileNode} that is a <I>direct descendant</I> of {@code 'this'} instance
365     * of {@code FileNode}, <B><I>and</I></B> that instance is a file (not a directory) whose
366     * name matches {@code 'fileName'}.
367     * 
368     * <BR /><BR /><B>Differing:</B> Different than method {@link #file(String, boolean)}, this
369     * method will "extract" the file from the in-memory file-tree.  If a file is 
370     * found matching the requested name and case requirements, when it is returned, it's parent
371     * {@code FileNode} pointer instance shall be null, and the parent will not contain a link
372     * to the returned {@code FileNode} in it's list of child-nodes.
373     * 
374     * @param fileName This is the name of any file.
375     * 
376     * <BR /><BR /><B STYLE="color: red">IMPORTANT:</B> This must be the <I><B>name-only</I></B>
377     * leaving out all parent-directory or drive-letter text.
378     * 
379     * @param ignoreCase For some files and directories, on some operating systems (Microsoft
380     * Windows, for instance) file-system name case is not considered relevant when matching
381     * file-names.  If this parameter is passed <B>TRUE</B>, then name comparisons will use
382     * a case-insensitive comparison mechanism.
383     * 
384     * @return The child {@code FileNode} of {@code 'this'} directory whose name matches the
385     * name provided by parameter {@code 'fileName'}.  It's {@code 'parent'} field
386     * will be null, and the parent {@code FileNode} instance will not have a pointer to the
387     * instance that is returned.
388     * 
389     * <BR /><BR />If no matching file is found, then this method shall return null.
390     * 
391     * @throws DirExpectedException If {@code 'this'} instance of {@code FileNode} is a file,
392     * not a directory, then this exception shall throw.  Only directories can contain other
393     * instances of {@code FileNode}.
394     * 
395     * @see #file(String, boolean)
396     */
397    public FileNode pollFile(String fileName, boolean ignoreCase)
398    {
399        FileNode ret = file(fileName, ignoreCase);
400
401        if (ret != null)
402        {
403            children.remove(ret);
404            ret.parent = null;
405        }
406
407        return ret;
408    }
409
410    /** 
411     * Convenience Method.  Invokes {@link #file(String, boolean)}.
412     * <BR />Does <B>NOT</B> ignore case.
413     */
414    public FileNode file(String fileName) { return file(fileName, false); }
415
416    /**
417     * Retrieves a {@code FileNode} named by parameter {@code 'fileName'} if there is a
418     * {@code FileNode} instance that is a <I>direct descendant</I> of {@code 'this' FileNode}
419     * that is, itself, a file and not a directory.
420     * 
421     * @param fileName This is the name of any file.
422     * 
423     * <BR /><BR /><B STYLE="color: red">IMPORTANT:</B> This must be the <I><B>name-only</I></B>
424     * leaving out all parent-directory or drive-letter text.
425     * 
426     * @param ignoreCase For some files and directories, on some operating systems (Microsoft
427     * Windows, for instance) file-name case is not considered relevant when matching file
428     * names.  If this parameter is passed <B>TRUE</B>, then file-name comparisons will use
429     * a case-insensitive comparison mechanism.
430     * 
431     * @return An instance of {@code FileNode} that is a <I>direct-descendant</I> of
432     * {@code 'this'} directory - and whose name matches the name provided by parameter
433     * {@code 'fileName'}.
434     * 
435     * <BR /><BR />If no matching file is found, then this method shall return null.
436     * 
437     * @throws DirExpectedException If {@code 'this'} instance of {@code FileNode} is a file,
438     * not a directory, then this exception shall throw.  Only directories can contain other
439     * instances of {@code FileNode}.
440     * 
441     * @see #children
442     * @see #isDirectory
443     * @see #name
444     */
445    public FileNode file(String fileName, boolean ignoreCase)
446    {
447        // Only directories may contain other instances of FileNode
448        DirExpectedException.check(this);
449
450        // We are looking for a file named 'fileName'
451        //
452        // IMPORTANT: The outer squiqly-braces are MANDATORY.  Without them, there is 
453        //            "deceptive indentation," because the 'else' is paired with the second-if,
454        //            not the first!
455        if (ignoreCase)
456        {
457            for (FileNode fn : children)
458                if ((! fn.isDirectory) && fn.name.equalsIgnoreCase(fileName)) return fn;
459        }
460        else
461        {
462            for (FileNode fn2 : children)
463                if ((! fn2.isDirectory) && fn2.name.equals(fileName)) return fn2;
464        }
465
466        // Not found, return null.
467        return null;
468    }
469
470    // ********************************************************************************************
471    // ********************************************************************************************
472    // These methods satisfy the Cloneable, Comparable, CharSequence Interfaces
473    // ********************************************************************************************
474    // ********************************************************************************************
475
476    /**
477     * This satisfies Java's "hash-code" method requirement.  This can facilitate saving instances
478     * of this class into tables, maps, lists, etc.
479     *
480     * @return A hash-code to be used by a hash-algorithm with likely few crashes.  Note that the
481     * hash from Java's {@code java.lang.String} is simply reused.
482     */
483    public int hashCode() { return toString().hashCode(); }
484
485    /*
486     * Java's {@code public boolean equals(Object o)} requirements.
487     *
488     * <BR /><BR /><B><SPAN STYLE="color: red;">FINAL METHOD:</B></SPAN> This method is 
489     * <B>{@code final}</B>, and cannot be modified by sub-classes.
490     *
491     * @param o This may be any {@code java.lang.Object}, but only ones of {@code 'this'} type
492     * whose internal-values are identical will cause this method to return <B>TRUE</B>.
493     *
494     * @return <B>TRUE</B> If {@code 'this'} instance' internal-fields are equal to the
495     * internal-fields another {@code FileNode} instance.
496     * 
497     * <BR /><BR /><B><SPAN STYLE='color: red;">DEEP-EQUALS:</B></SPAN> Due to how Java's
498     * {@code class Vector} has implemented it's {@code Vector.equals(other)} method - which is
499     * how the child tree-branches of a directory {@code FileNode} stores it's directory
500     * branches - this method <I>does, indeed, perform a 'Deep Equals'</I>.
501     *
502     * @see FileNode#name
503     * @see FileNode#parent
504     * @see FileNode#isDirectory
505     * @see FileNode#children
506     */
507    public final boolean equals(Object o)
508    {
509        FileNode other;
510        return (this == o) || (
511                (o != null)
512            &&  (this.getClass().equals(o.getClass()))
513            &&  ((other = (FileNode) o).name.equals(this.name))
514            &&  (this.parent        == other.parent)        // NOTE: This is a "Reference Comparison"
515            &&  (this.isDirectory   == other.isDirectory)
516            &&  (this.fileSize      == other.fileSize)
517            &&  (this.lastModified  == other.lastModified)
518            &&  this.name.equals(other.name)
519            &&  (((this.children == null) && (other.children == null))
520                || 
521                (this.children.equals(other.children)))
522        );
523    }
524
525    /**
526     * Java's {@code interface Cloneable} requirements.  This instantiates a new {@code FileNode}
527     * with identical fields.  The field {@code Vector<FileNode> 'children'} shall be cloned too.
528     *
529     * @return A new {@code FileNode} whose internal fields are identical to this one.
530     *
531     * <BR /><BR /><B><SPAN STYLE="color: red;">IMPORTANT (DEEP-CLONE) NOTE:</SPAN></B> This
532     * <B>does not</B> perform a deep-tree-traversal clone.  Instead, {@code 'this'} instance is
533     * merely copied, and it's child nodes have references inserted into the internal list of
534     * child-nodes.
535     *
536     * @see FileNode#name
537     * @see FileNode#parent
538     * @see FileNode#isDirectory
539     */
540    public FileNode clone()
541    {
542        if (this.isDirectory)
543        {
544            FileNode ret = new FileNode(this.name, this.parent, this.lastModified);
545            ret.children.addAll(this.children);
546            return ret;
547        }
548        else return new FileNode(this.name, this.parent, this.fileSize, this.lastModified);
549    }
550
551    /**
552     * Java's {@code interface Comparable<T>} requirements.  This does a very simple comparison 
553     * using the results to a call of method {@code public String getFullPathname()}
554     *
555     * @param fn Any other {@code FileNode} to be compared to {@code 'this' FileNode}.  The file
556     * or directories {@code getFullPathName()} is used to perform a "String" comparison.
557     *
558     * @return An integer that fulfils Java's {@code interface Comparable<T> public boolean 
559     * compareTo(T t)} method requirements.
560     *
561     * @see #getFullPathName()
562     */
563    public final int compareTo(FileNode fn)
564    { return this.getFullPathName().compareTo(fn.getFullPathName()); }
565
566    /**
567     * This is an "alternative Comparitor" that can be used for sorting instances of this class.
568     * It should work with the {@code Collections.sort(List, Comparator)} method in the standard 
569     * JDK package {@code java.util.*;}
570     *
571     * <BR /><BR /><B>NOTE:</B> This version utilizes the standard JDK
572     * {@code String.compareToIgnoreCase(String)} method.
573     *
574     * @see #getFullPathName()
575     */
576    public static final Comparator<FileNode> comp2 =
577        (FileNode fn1, FileNode fn2) -> fn1.getFullPathName().compareToIgnoreCase(fn2.getFullPathName());
578
579    /**
580     * Converts {@code 'this' FileNode} to a {@code String}.
581     *
582     * @return The complete-full path-name of this file or directory.
583     */
584    public String toString() { return this.getFullPathName(); }
585
586    /**
587     * Returns the {@code char} value at the specified index of the results to a call of method
588     * {@code public String getFullPathname()}.  An index ranges from {@code zero} to
589     * {@code length() - 1}. The first {@code char} value of the sequence is at index {@code zero},
590     * the next at index {@code one}, and so on, as for array indexing.
591     *
592     * <BR /><BR /><B>NOTE:</B> If the {@code char} value specified by the index is a surrogate,
593     * the surrogate value is returned.
594     *
595     * <BR /><BR /><B><SPAN STYLE="color: red;">FINAL METHOD:</B></SPAN> This method is final, and
596     * cannot be modified by sub-classes.
597     * 
598     * @param index The index of the {@code char} value to be returned
599     *
600     * @return The specified {@code char} value
601     *
602     * @see #getFullPathName()
603     */
604    public final char charAt(int index) { return this.getFullPathName().charAt(index); }
605
606    /**
607     * Returns the length of the {@code String} returned by {@code public String getFullPathname()}
608     * The length is the number of 16-bit characters in the sequence.
609     *
610     * <BR /><BR /><B><SPAN STYLE="color: red;">FINAL METHOD:</B></SPAN> This method is final, and
611     * cannot be modified by sub-classes.
612     *
613     * @return the number of characters in the "Full Path Name" for {@code 'this'} file or\
614     * directory.
615     *
616     * @see #getFullPathName()
617     */
618    public final int length() { return this.getFullPathName().length(); }
619
620    /**
621     * Returns a {@code java.lang.CharSequence} that is a subsequence of the results to a call of
622     * method {@code public String getFullPathname()}
623     *
624     * <BR /><BR /> The subsequence starts with the {@code char} value at the specified index and 
625     * ends with the {@code char} value at index {@code 'end - 1'}.  The length (in characters) of
626     * the returned sequence is {@code 'end - start'},  so in the case where
627     * {@code 'start == end'} then an empty sequence is returned.
628     *
629     * <BR /><BR /><B><SPAN STYLE="color: red;">FINAL METHOD:</B></SPAN> This method is
630     * {@code final}, and cannot be modified by sub-classes.
631     *
632     * @param start The start index, inclusive
633     *
634     * @param end The end index, exclusive
635     *
636     * @return The specified subsequence
637     *
638     * @see #getFullPathName()
639     */
640    public final CharSequence subSequence(int start, int end)
641    { return this.getFullPathName().substring(start, end); }
642
643
644    // ********************************************************************************************
645    // Basic Methods
646    // ********************************************************************************************
647
648    /**
649     * This returns the name of the file, but leaves off the "extension"
650     * @return Returns the name <I>without the file-extension</I>
651     * @throws FileExpectedException Since only files may have extensions, if {@code 'this'}
652     * instance of {@code FileNode} is a directory, the {@code FileExpectedException} will throw.
653     */
654    public String nameNoExt()
655    {
656        FileExpectedException.check(this);  // Directories do not have extensions
657
658        int pos = name.lastIndexOf('.');
659
660        if (pos == -1) return name;
661
662        return name.substring(0, pos);
663    }
664
665    /**
666     * This returns the extension of the file.  If this file does not have an extension,
667     * then null shall be returned.
668     * @param includeTheDot if the user would like to have the {@code '.'} included in the
669     * return {@code String}, then <B>TRUE</B> should be passed to this parameter.  
670     * @return Returns this file's extension
671     * @throws FileExpectedException Since only files may have extensions, if {@code 'this'}
672     * instance of {@code FileNode} is a directory, the {@code FileExpectedException} will throw.
673     */
674    public String ext(boolean includeTheDot)
675    {
676        FileExpectedException.check(this);  // Directories do not have extensions
677
678        int pos = name.lastIndexOf('.');
679
680        if (pos == -1) return null;
681
682        return includeTheDot ? name.substring(pos) : name.substring(pos+1);        
683    }
684
685    /**
686     * Invokes the input {@code java.util.function.Consumer<FileNode>} on each element in the
687     * tree.  Note that if {@code 'this'} instance of {@code FileNode} is a file, not a directory,
688     * then the passed {@code Consumer} shall only be invoked using {@code 'this'} instance.
689     * @param c This is any java {@code Consumer<FileNode>}
690     */
691    public void forEach(Consumer<FileNode> c)
692    {
693        c.accept(this);
694        children.forEach((FileNode fn) -> fn.forEach(c));
695    }
696
697    // ********************************************************************************************
698    // Deep-Tree Traversal
699    // ********************************************************************************************
700
701    /**
702     * Whereas the standard Java {@code clone()} method in this class returns a new, cloned,
703     * instance of {@code FileNode}, if {@code 'this'} instance of {@code FileNode} is a directory,
704     * the tree-branch represented by {@code 'this' FileNode} instance would not be copied by an
705     * invocation of {@code 'clone'}.  However, if using this method, {@code 'deepClone'}, on a
706     * directory-{@code FileNode} instance, <B><I>the entire tree-branch represented by
707     * {@code 'this' FileNode} instance is copied.</I></B>.
708     * 
709     * <BR /><BR /><B>NOTE:</B>The method's {@code clone()} and {@code deepClone()} shall return
710     * identical results when used on an instance of {@code FileNode} that represents a file, not
711     * a directory (<I>and, therefore, does not have any tree-branch information associated with
712     * it.</I>).
713     * 
714     * @return a <B>"Deep Clone"</B> of {@code 'this' FileNode} instance.  If {@code 'this'}
715     * instance of {@code FileNode} represents a file, not a directory, the results of this method
716     * shall be identical to the results of an invocation of the standard {@code 'clone()'} method.
717     * If {@code 'this' FileNode} represents an operation-system directory (not a file), then 
718     * each and every child of this tree-branch shall also be copied / cloned by this method.
719     */
720    public FileNode deepClone()
721    {
722        if (this.isDirectory)
723        {
724            FileNode ret = new FileNode(this.name, this.parent, this.lastModified);
725            for (FileNode child : children) ret.children.add(child.deepClone());
726            return ret;
727        }
728        else return this.clone();
729    }
730
731    /*
732     ******************************************************************
733     * JAVA'S Vector.equals ALREADY DOES A DEEP EQUALS ON IT'S CONTENTS
734     ******************************************************************
735     *
736     * Performs a complete tree traversal to ensure that {@code 'this'} instance of
737     * {@code FileNode} represents precisely the same files from the file-system as input
738     * parameter {@code 'other'}
739     *
740     * @param other Another instance of {@code FileNode}
741     *
742     * @return <B>TRUE</B> if and only if every file incorporated by {@code 'this'} instance
743     * has a one-to-one mapping with an identical tree {@code 'other'}.
744     * 
745     * <BR /><BR /><B><SPAN STYLE='color: red;'>NOTE:</B></SPAN> If {@code 'this'} instance
746     * {@code '.isDirectory'} value is <B>FALSE</B> (because {@code 'this'} instance of 
747     * {@code FileNode} represents a file, not a directory), then this method shall simply
748     * return the results of an invocation to the standard {@code equals(other)} method.  If
749     * {@code 'this'} is a file (not a directory), then there are no tree-branch nodes to
750     * test or traverse.
751
752    public boolean deepEquals(FileNode other)
753    {
754        if (! this.isDirectory)     return this.equals(other);
755        if (! this.equals(other))   return false;
756
757        for (FileNode child : children)
758        {
759            int pos;
760
761            if ((pos = other.children.indexOf(child)) == -1) return false;
762
763            FileNode otherChild;
764
765            if ((otherChild = other.children.elementAt(pos)).isDirectory)
766            { if (! otherChild.deepEquals(child))   return false; }
767            else 
768            { if (! otherChild.equals(child))       return false; }
769        }
770
771        return true;
772    }
773
774    public static void main(String[] argv)
775    {
776        FileNode fn = FileNode.createRoot("Torello").loadTree();
777        FileNode fn2 = FileNode.createRoot("Torello/Java").loadTree();
778        FileNode copy = fn.deepClone();
779
780        System.out.println("fn.deepEquals(copy):\t" + fn.deepEquals(copy));
781        System.out.println("fn.deepEquals(fn2):\t" + fn.deepEquals(fn2));
782    }
783    */
784
785    // ********************************************************************************************
786    // ********************************************************************************************
787    // These methods actually load the contents of the MS-DOS or UNIX file-system into this tree-data-structure
788    // ********************************************************************************************
789    // ********************************************************************************************
790
791    /**
792     * This method will fill out the tree with parameter {@code int maxTreeDepth} set to
793     * {@code '-1'}, meaning the entire directory-tree, <I>no matter how large</I>, will be
794     * visited.  Also, the parameters {@code directoryFilter} and {@code fileFilter} are simply
795     * set to null - and another method named {@code loadTree(...)} is invoked.
796     *
797     * @return a reference to {@code 'this' FileNode}, for convenience only.
798     *
799     * @see #loadTree(int, FilenameFilter, FileFilter)
800     */
801    public FileNode loadTree() { return loadTree(-1, null, null); }
802
803    /**
804     * This is a simplified loading method to load the contents of a directory into a 
805     * {@code FileNode} tree.  There are three primary scenarios to consider when invoking this
806     * method, based on the values provided to the two input parameters {@code 'includeFiles'}
807     * and {@code 'includeDirectories'}.  
808     * 
809     * <BR /><BR />One of these three cases is trivial, if <B>TRUE</B> has been passed to both of
810     * the inputs, then this method's loading-behavior shall be identical to invoking
811     * {@link #loadTree()} with zero parameters - all files in all sub-directories will be loaded.
812     * 
813     * <BR /><BR /><B>CASE 1:</B> If <B>TRUE</B> has been passed to {@code 'includeDirectories'},
814     * but not to the other parameter, then <I>only the directory-tree</I> (without any of the
815     * files it contains) will be loaded into the {@code FileNode} tree.
816     * 
817     * <BR /><BR /><B>CASE 2:</B> If <B>TRUE</B> has been passed to {@code 'includeFiles'},
818     * but not to {@code 'includeDirectories'}, then this method will not traverse any branches
819     * of the directory-tree rooted at {@code 'this' FileNode}, but rather will only import the
820     * file-contents of {@code 'this'} directory into the tree.  Computer jargon specialists would
821     * call this a 'level-1' tree-traversal.
822     * 
823     * <BR /><BR /><B>FINALLY:</B> if <B>FALSE</B> has been provided to both of these parameters,
824     * then an {@code IllegalArgumentException} will throw, because there is no action for this
825     * method to take.
826     *
827     * @param includeFiles If the user would like to have file names imported into this
828     * data-structure, set this boolean value to <B>TRUE</B>.  If this parameter is <B>FALSE</B>,
829     * then files will not be imported into {@code 'this' FileNode}.
830     *
831     * @param includeDirectories If the user would like to have directory names imported into this
832     * data-structure, set this boolean value to <B>TRUE</B>.  If this parameter is <B>FALSE</B>,
833     * then directories will not be imported into {@code 'this' FileNode}.  Only file names in
834     * the current ('working') directory represented by {@code 'this' FileNode} may be imported.
835     * 
836     * <BR /><BR /><B>NOTE:</B> If this parameter is <B>FALSE</B>, subsequent branches of the 
837     * directory-tree will not be traversed.  This is because files must be contained by a
838     * directory - and since directories are being excluded as per this parameter, therefore any
839     * files in subdirectories cannot be stored in this tree
840     *
841     * @return a reference to {@code 'this' FileNode}, for convenience only.
842     *
843     * @throws DirExpectedException This method will only execute if the instance of
844     * {@code 'this'} is actually a directory.  Files on the file-system are leaves, not branches -
845     * so they do not have contents to load.
846     *
847     * @throws IllegalArgumentException If both of the boolean parameters are set to <B>FALSE</B>,
848     * then this exception shall be thrown because the method-invocation would not be making an
849     * actual request to do any importing / loading.
850     *
851     * @see #loadTree(int, FilenameFilter, FileFilter)
852     */
853    public FileNode loadTree(boolean includeFiles, boolean includeDirectories)
854    {
855        if ((! includeFiles) && (! includeDirectories)) throw new IllegalArgumentException(
856            "loadTree(boolean, boolean) has been invoked with both search criteria booleans set to FALSE.  " +
857            "This means that there is nothing for the method to do."
858        );
859
860        return loadTree
861            (-1, (File dir, String name) -> includeFiles, (File file) -> includeDirectories);
862    }
863
864    /**
865     * This is a simplified loading method to load just the directory tree of a root
866     * {@code 'FileNode'} into memory.  Files on the file-system that reside in this directory-tree
867     * will be ignored - <I>meaning their file-names will not be imported into this
868     * {@code 'FileNode'} tree instance.</I>
869     *
870     * @param maxTreeDepth This value should be set to the maximum number of levels deep to search
871     * when traversing the tree.
872     *
873     * <BR /><BR /><SPAN STYLE="color: red;"><B>NOTE:</B></SPAN> Simply use any negative number
874     * to always visit the tree to its maximum depth.  If this value is set to zero, then an
875     * exception shall throw.
876     *
877     * @param directoryFilter This is an interface from the {@code java.io .*} library as well.
878     * It can be used here to filter <B><I>which directories</I></B> are visited by the recursion
879     * algorithm (it ignores files).  If a directory fails the {@code "boolean test"} method here,
880     * that branch of the tree will not be included in this object.
881     * 
882     * <BR /><BR />The {@code public interface java.io.FileFilter} has a method signature of:
883     *
884     * <DIV CLASS="METHODSIGNATURE">{@code
885     * public boolean test(File file)
886     * }</DIV>
887     *
888     * <BR />Where:
889     * <BR /><BR /><UL CLASS="JDUL">
890     * <LI>{@code File file} parameter represents an actual UNIX (or MS-DOS) file.</LI>
891     * </UL>
892     *
893     * <BR /><BR /><SPAN STYLE="color: red;"><B>NOTE:</B></SPAN> If this filter parameter is null,
894     * the parameter is ignored - <I>and all sub-directories are visited.</I>
895     *
896     * <BR /><BR /><B>ALSO:</B> The filter used should return <B>FALSE</B> if the passed directory
897     * should be skipped. The filter must return <B>TRUE</B> if the directory must be included,
898     * or 'kept', (and subsequently visited and loaded into the tree).
899     *
900     * @throws DirExpectedException This method will only execute if the instance of {@code 'this'}
901     * is actually a directory.  Files on the file-system are leaves, not branches - so they do not
902     * have contents to load.
903
904     * @throws IllegalArgumentException If the value of {@code 'maxTreeDepth'} is set to
905     * {@code zero}, then this exception shall be thrown because the method-invocation would not
906     * be making an actual request to do anything.
907     */
908    public FileNode loadTreeJustDirs(int maxTreeDepth, FileFilter directoryFilter)
909    { 
910        if (maxTreeDepth == 0) throw new IllegalArgumentException(
911            "loadTreeJustDirs(int, FileFilter) has been invoked with the maxTreeDepth Parameter " +
912            "set to zero.  This means that there is nothing for the method to do."
913        );
914
915        // Setting the return value of the filter-predicate to 'false' for the fileFilter parameter.
916        return loadTree(maxTreeDepth, (File dir, String name) -> false, directoryFilter);
917    }
918
919    /**
920     * This populates {@code 'this'} {@code FileNode} tree with the contents of the file-system
921     * directory represented by {@code 'this'}.
922     * 
923     * <BR /><BR /><B>IMPORTANT:</B> This method can only be invoked on an instance of 
924     * {@code 'FileNode'} that represents a directory on the UNIX or MS-DOS file-system.  An 
925     * exception shall throw if this method is invoked on an instance of a file.
926     *
927     * @param maxTreeDepth <I><B>This variable should be set to -1 (or any negative number) if the
928     * entire tree is required.</B></I>  For all positive {@code int maxTreeDepth}, the recursive
929     * algorithm will search through the file-system tree, and add nodes to the child-directories
930     * as it encounters them, recursively.  When {@code maxTreeDepth} has been reached, the
931     * recursion ends.
932     *
933     * <BR /><BR /><B>NOTE:</B> When parameter {@code maxTreeDepth} is passed a value of
934     * {@code '1'}, the directory represented by {@code 'this' FileNode} will be the only
935     * directory searched for files and directories.  When {@code maxTreeDepth} is passed
936     * {@code '2'}, {@code 'this'} directory and each sub-directory of {@code 'this'} shall be
937     * searched, <I>and so on and so forth, recursively.</I>
938     * 
939     * @param fileFilter This is an interface from the {@code java.io.*} library.  It can be used
940     * here to filter <B><I>which files</I></B> (not directories) are loaded into the tree.
941     *
942     * <BR /><BR />The {@code public interface java.io.FilenameFilter} has a method with this
943     * signature:
944     *
945     * <DIV CLASS="METHODSIGNATURE">{@code
946     * public boolean test(File dir, String name)
947     * }</DIV>
948     *
949     * <BR />Where:
950     * <BR /><BR /><UL CLASS="JDUL">
951     * <LI>The {@code File dir} parameter represents the containing/parent directory.
952     * </LI>
953     * <LI>The {@code String name} parameter represents the name of the file <I>(not including the
954     *     full path).</I>
955     * </LI>
956     * </UL>
957     * 
958     * <BR /><SPAN STYLE="color: red;"><B>NOTE:</B></SPAN> If this {@code filter} parameter is
959     * null, the parameter is ignored - <I>and all files shall be included in the result.</I>
960     *
961     * <BR /><BR /><B>ALSO:</B> The {@code filter} used should return <B>FALSE</B> if the passed
962     * file should be skipped. The filter must return <B>TRUE</B> if the file must be included,
963     * or 'kept', (and subsequently loaded into the tree).
964     *
965     * @param directoryFilter This is an interface from the {@code java.io.*} library as well.
966     * It can be used here to filter <B><I>which directories</I></B> are visited by the recursion
967     * algorithm (it ignores files).  If a directory fails the {@code "boolean test"} method here,
968     * that branch of the tree will not be included in this object.
969     * 
970     * <BR /><BR />The {@code public interface java.io.FileFilter} has a method signature of:
971     *
972     * <DIV CLASS="METHODSIGNATURE">{@code
973     * public boolean test(File file)
974     * }</DIV>
975     *
976     * <BR />Where:
977     * <BR /><BR /><UL CLASS="JDUL">
978     * <LI>{@code File file} parameter represents an actual UNIX (or MS-DOS) file.</LI>
979     * </UL>
980     *
981     * <BR /><BR /><SPAN STYLE="color: red;"><B>NOTE:</B></SPAN> If this filter parameter is null,
982     * the parameter is ignored - <I>and all sub-directories  are visited.</I>
983     *
984     * <BR /><BR /><B>ALSO:</B> The filter used should return <B>FALSE</B> if the passed directory
985     * should be skipped. The filter must return <B>TRUE</B> if the directory must be included,
986     * or 'kept', (and subsequently visited and loaded into the tree).
987     *
988     * @return a reference to {@code 'this' FileNode}, for convenience only.
989     *
990     * @throws DirExpectedException This method will only execute if the instance of {@code 'this'}
991     * is actually a directory.  Files on the file-system are leaves, not branches - so they do not
992     * have contents to load.
993     *
994     * @throws IllegalArgumentException If the value of {@code 'maxTreeDepth'} is set to 
995     * {@code zero}, then this exception shall be thrown because the method-invocation would not
996     * be making an actual request to do anything.
997     *
998     * @see #loadTree()
999     * @see DirExpectedException#check(FileNode)
1000     */
1001    public FileNode loadTree(int maxTreeDepth, FilenameFilter fileFilter, FileFilter directoryFilter)
1002    {
1003        DirExpectedException.check(this);
1004
1005        if (maxTreeDepth == 0) throw new IllegalArgumentException(
1006            "loadTree(int, FilenameFilter, FileFilter) has been invoked with the maxTreeDepth " + 
1007            "(integer) parameter set to zero.  This means that there is nothing for the method to do."
1008        );
1009
1010        loadTreeINTERNAL(maxTreeDepth, fileFilter, directoryFilter);
1011
1012        return this;
1013    }
1014
1015    private void loadTreeINTERNAL(int maxTreeDepth, FilenameFilter fileFilter, FileFilter directoryFilter)
1016    {
1017        File f = getJavaIOFile();
1018        if (VERBOSE) System.out.println(f.getPath());
1019
1020        // TRICKY! Added: 2019.05.16 - if we are "re-loading" the tree this line is imperative
1021        this.children.removeAllElements(); 
1022
1023        File[] subFilesAndDirs = f.listFiles();
1024
1025        for (File sub : subFilesAndDirs)
1026
1027            if (sub.isDirectory())
1028            {
1029                if (VERBOSE) System.out.println("TESTING DIR: " + sub.getAbsolutePath());
1030
1031                if (directoryFilter != null) if (! directoryFilter.accept(sub)) continue;
1032
1033                long lastModified = 0;
1034                try { lastModified = sub.lastModified(); } catch (SecurityException se) { }
1035
1036                FileNode newSubDir = new FileNode(sub.getName(), this, lastModified);
1037                children.addElement(newSubDir);
1038
1039                if (VERBOSE) System.out.println("ADDED DIR: " + newSubDir.getFullPathName());
1040
1041                // (maxTreeDepth != 0) DO NOT USE --maxTreeDepth, this line is inside a loop.
1042                // Negative maxTreeDepth ==> entire Tree.  Positive maxTreeDepth ==> number of branches deep.
1043                if (maxTreeDepth != 0)
1044                    newSubDir.loadTreeINTERNAL(maxTreeDepth - 1, fileFilter, directoryFilter);
1045
1046            } else { /* sub is a file, not a directory */
1047
1048                // if (VERBOSE) System.out.println("TESTING FILE: " + "[" + sub.getAbsolutePath() + "]\t[" + sub.getName() + "]");
1049
1050                if (fileFilter != null)
1051                    if (! fileFilter.accept(sub.getParentFile(), sub.getName()))
1052                        continue;
1053
1054                long lastModified = 0;
1055                try { lastModified = sub.lastModified(); } catch (SecurityException se) { }
1056
1057                children.addElement(new FileNode(sub.getName(), this, sub.length(), lastModified));
1058
1059                if (VERBOSE) System.out.println("ADDED FILE: " + sub.getPath() + "\t\t[" + sub.length() + "]");
1060            }
1061    }
1062
1063
1064    // ********************************************************************************************
1065    // ********************************************************************************************
1066    // Returns information about the contents of the "children Vector<FileNode>"
1067    // ********************************************************************************************
1068    // ********************************************************************************************
1069
1070
1071    /**
1072     * This returns the number of children-nodes in {@code 'this'} instance of {@code FileNode}.
1073     *
1074     * <BR /><BR /><B>NOTE:</B> This is not a 'recursive' method, and that means the integer 
1075     * returned by this method is only a count of the number of <B><I>direct-descendants</I></B> of
1076     * {@code 'this' FileNode}.
1077     *
1078     * @see #numDirChildren()
1079     * @see #numFileChildren()
1080     * @see #children
1081     *
1082     * @throws DirExpectedException If {@code 'this'} instance of {@code FileNode} is not a
1083     * directory, but rather a file, then this exception is thrown.  (Files <I>may not</I> have
1084     * child-nodes, only directories may have them).
1085     */
1086    public int numChildren() { DirExpectedException.check(this); return children.size(); }
1087
1088    /**
1089     * This returns the exact number of children-nodes that are <B>directories</B> of
1090     * {@code 'this' FileNode}.
1091     *
1092     * <BR /><BR /><B>NOTE:</B> This is not a 'recursive' method, and that means the integer 
1093     * returned by this method is only a count of the number of <B><I>direct-descendants</I></B> of
1094     * {@code 'this' FileNode}.
1095     *
1096     * @see #numFileChildren()
1097     * @see #numChildren()
1098     * @see #children
1099     *
1100     * @throws DirExpectedException If {@code 'this'} instance of {@code FileNode} is not a
1101     * directory, but rather a file, then this exception is thrown.  (Files <I>may not</I> have
1102     * child-nodes, only directories may have them).
1103     */
1104    public int numDirChildren()
1105    {
1106        DirExpectedException.check(this);
1107
1108        int dirCount = 0;
1109
1110        for (int i=0; i < children.size(); i++) if (children.elementAt(i).isDirectory) dirCount++;
1111
1112        return dirCount;
1113    }
1114
1115    /**
1116     * This returns the exact number of children-nodes that are <B>files</B> of
1117     * {@code 'this' FileNode}.
1118     *
1119     * <BR /><BR /><B>NOTE:</B> This is not a 'recursive' method, and that means the integer 
1120     * returned by this method is only a count of the number of <B><I>direct-descendants</I></B> of
1121     * {@code 'this' FileNode}.
1122     *
1123     * @see #numDirChildren()
1124     * @see #numChildren()
1125     * @see #isDirectory
1126     * @see #children
1127     *
1128     * @throws DirExpectedException If {@code 'this'} instance of {@code FileNode} is not a
1129     * directory, but rather a file, then this exception is thrown.  (Files <I>may not</I> have
1130     * child-nodes, only directories may have them).
1131     */
1132    public int numFileChildren()
1133    {
1134        DirExpectedException.check(this);
1135
1136        int fileCount = 0;
1137
1138        for (int i=0; i < children.size(); i++) if (! children.elementAt(i).isDirectory) fileCount++;
1139
1140        return fileCount;
1141    }
1142
1143
1144    // ********************************************************************************************
1145    // ********************************************************************************************
1146    // Print Tree - self explanatory
1147    // ********************************************************************************************
1148    // ********************************************************************************************
1149
1150
1151    /** 
1152     * Convenience Method.  Passes {@code System.out} to {@code Appendable}, and nulls.  
1153     * <BR />Invokes: {@link #printTree(Appendable, boolean, FileNodeFilter, FileNodeFilter)}.
1154     * <BR />Catches: {@code Appendable's IOException} 
1155     */
1156    public void printTree()
1157    { try { printTree(System.out, false, null, null); } catch (IOException e) { e.printStackTrace(); } }
1158
1159    /** 
1160     * Convenience Method.
1161     * <BR />Passes 'null' to {@code Appendable} parameter (uses {@code System.out})  
1162     * <BR />Invokes: {@link #printTree(Appendable, boolean, FileNodeFilter, FileNodeFilter)}.
1163     * <BR />Catches: {@code Appendable's IOException} 
1164     */
1165    public void printTreeNOIOE(boolean showSizes, FileNodeFilter fileTest, FileNodeFilter directoryTest)
1166    { try { printTree(null, showSizes, fileTest, directoryTest); } catch (IOException ioe) { } }
1167
1168    /**
1169     * This will print the directory tree to the {@code java.lang.Appendable} passed as a parameter.  
1170     * Specific test {@code Predicate's} may be sent to this method to identify which branches of the
1171     * File/Directory tree should be printed.
1172     *
1173     * @param a If this is null, then {@code System.out} is used.  If it is not null, then
1174     * information is printed to this Java {@code java.lang.Appendable}.
1175     * 
1176     * <EMBED CLASS="external-html" DATA-FILE-ID="APPENDABLE">
1177     *
1178     * @param showSizes If this is true, then "file-size" information will also be printed with the
1179     * file.
1180     *
1181     * @param fileTest If this is null, then it is ignored, and all <B>files</B> in the
1182     * {@code FileNode} Directory Tree pass (are accepted).  If this parameter is not null, then
1183     * each {@code FileNode} that is not a directory is run through the {@code Predicate's}
1184     * {@code public boolean test(FileNode fn)} method.  If the test returns <B>FALSE</B>, then
1185     * this file is not printed to the output.
1186     *
1187     * @param directoryTest If this is null, then it is ignored, and all <B>directories</B> in
1188     * the {@code FileNode} Directory Tree pass (are accepted).  If this parameter is not null,
1189     * then each {@code FileNode} that is a directory is run through the Predicate's test method,
1190     * and any directories that fail this {@code Predicate's test()} method (when
1191     * {@code directoryTest.test(dir)} returns <B>FALSE</B>), that directory will not be printed to
1192     * the output.
1193     *
1194     * @throws IOException Java's {@code interface Appendable} mandates that the unchecked Java
1195     * {@code IOException} must be caught when using this interface.
1196     *
1197     * @see #printTree()
1198     * @see #getDirContentsFiles()
1199     * @see #getDirContentsDirs()
1200     * @see #fileSize
1201     * @see #getFullPathName
1202     */
1203    public void printTree
1204        (Appendable a, boolean showSizes, FileNodeFilter fileTest, FileNodeFilter directoryTest)
1205        throws IOException
1206    {
1207        if (a == null) a = System.out;
1208
1209        for (FileNode file : getDirContentsFiles(fileTest))
1210            a.append((showSizes ? (file.fileSize + ",\t") : "") + file.getFullPathName() + '\n');
1211
1212        for (FileNode dir : getDirContentsDirs(directoryTest))
1213        {
1214            a.append(dir.getFullPathName() + '\n');
1215            dir.printTree(a, showSizes, fileTest, directoryTest);
1216        }
1217    }
1218
1219
1220    // ********************************************************************************************
1221    // ********************************************************************************************
1222    // These check the size of a directory's contents.  The perform the sums using recursion
1223    // ********************************************************************************************
1224    // ********************************************************************************************
1225
1226
1227    /** Convenience Method.  Invokes {@link #getDirContentsSize(FileNodeFilter)} */
1228    public long getDirContentsSize()
1229    { return getDirContentsSize(null); }
1230
1231    /**
1232     * This sums the file-sizes of each file <B>in the current directory, not sub-directories</B>
1233     * that pass the requirements of the {@code Predicate<FileNode>} here.  If
1234     * {@code p.test(fileNode)} fails, then the size of a {@code FileNode} is not counted in the
1235     * total sum.
1236     *
1237     * <BR /><BR /><B>NOTE:</B> This only retrieves the contents of {@code 'this'} directory - and
1238     * does not expand the leaf-node directories when scanning for files or sub-directories sizes!
1239     *
1240     * @param fileTest Any java {@code Predicate} that satisfies the requirement of having a:
1241     * {@code public boolean test(FileNode f); } method.   the {@code interface 'FileNodeFilter'}
1242     * will also work, if using Java 7.
1243     *
1244     * <BR /><BR />This is used to "test" whether to include the files in a directory'
1245     * {@code fileSize} in the summed return value.  When <B>TRUE</B> is returned by the 
1246     * {@code Predicate test(...)} method, a file's size will be included in the sum-total
1247     * directory-size.  When the {@code Predicate test(...)} method returns <B>FALSE</B>, the
1248     * tested file's size will be ignored and not included in the total.
1249     *
1250     * <BR /><BR />This may be null, and if it is, it is ignored.  This means that file-sizes for
1251     * all files in the directory will count towards the total-size returned by this method.
1252     *
1253     * @return The sum of file-sizes for each file which passes the {@code Predicate} test in this
1254     * directory.
1255     *
1256     * @throws DirExpectedException If {@code 'this'} instance of {@code FileNode} is not a
1257     * directory, but rather a file, then this exception is thrown.  (Files <I>may not</I> have
1258     * child-nodes, only directories).
1259     *
1260     * @see #fileSize
1261     * @see #children
1262     * @see #isDirectory
1263     */
1264    public long getDirContentsSize(FileNodeFilter fileTest)
1265    {
1266        DirExpectedException.check(this);
1267
1268        long size=0;           
1269
1270        for (FileNode f : children)
1271            if (! f.isDirectory)
1272                if ((fileTest == null) || fileTest.test(f))
1273                    size += f.fileSize;
1274
1275        return size;        
1276    }
1277
1278    /** Convenience Method.  Invokes {@link #getDirTotalContentsSize(FileNodeFilter, FileNodeFilter)} */
1279    public long getDirTotalContentsSize()
1280    { return getDirTotalContentsSize(null, null); }
1281
1282    /**
1283     * This sums the file contents in the current directory - and all sub-directories as well.
1284     * Only files that pass the {@code Predicate 'fileTest'} parameter are counted.  Furthermore,
1285     * only directories that pass the {@code Predicate 'directoryTest'} will be traversed and
1286     * inspected.
1287     *
1288     * @param fileTest Any {@code java.util.function.Predicate} or lambda-expressions that
1289     * satisfies the requirement of having a: {@code public boolean test(FileNode f); } method.
1290     * Implementing the {@code interface 'FileNodeFilter'} will also work, if using Java 7.
1291     *
1292     * <BR /><BR />This is used to "test" whether to include the {@code fileSize} for a specific
1293     * file in a directory in the summed return value.  When <B>TRUE</B> is returned by the
1294     * {@code Predicate 'test'}  method, a file's size will be included in the sum-total
1295     * directory-size.  When the {@code Predicate 'test'} method returns <B>FALSE</B>, the tested
1296     * file's size will be ignored, and not included in the total.
1297     *
1298     * <BR /><BR />This may be null, and if it is, it is ignored.  This means that file-sizes for
1299     * all files in the directory will count towards the total-size returned by this method.
1300     *
1301     * @param directoryTest java predicate that satisfies the requirement of having a:
1302     * {@code public boolean test(FileNode f); } method.  The {@code interface 'FileNodeFilter'}
1303     * will also work, if using Java 7.
1304     *
1305     * <BR /><BR />This is used to test directories, rather than files, for inclusion in the total
1306     * file-size returned by this method.  When <B>TRUE</B> is returned by the {@code filter's
1307     * 'test'} method, then that directory shall be traversed, inspected, and its contents
1308     * shall have their {@code fileSize's} included in the computed result.
1309     *
1310     * <BR /><BR />This parameter may be null, and if it is, it is ignored.  This would mean that
1311     * all sub-directories shall be traversed when computing the total directory size.
1312     *
1313     * @return The sum of all file-sizes for each file in this directory that pass
1314     * {@code 'fileTest'}, and all sub-dir's that pass the {@code 'directoryTest'}.
1315     *
1316     * @throws DirExpectedException If {@code 'this'} instance of {@code FileNode} is not a 
1317     * directory, but rather a file, then this exception is thrown.  (Files <I>may not</I> have
1318     * child-nodes, only directories).
1319     (
1320     * @see #fileSize
1321     * @see #children
1322     * @see #getDirTotalContentsSize()
1323     */
1324    public long getDirTotalContentsSize(FileNodeFilter fileTest, FileNodeFilter directoryTest)
1325    {
1326        DirExpectedException.check(this);
1327
1328        long size=0;
1329
1330        for (FileNode f : children)
1331
1332            if (f.isDirectory)
1333            {
1334                if ((directoryTest == null) || directoryTest.test(f))
1335                    size += f.getDirTotalContentsSize(fileTest, directoryTest);
1336            }
1337            else
1338            {
1339                if ((fileTest == null) || fileTest.test(f))
1340                    size += f.fileSize;
1341            }                 
1342
1343        return size;
1344    }
1345
1346
1347    // ********************************************************************************************
1348    // ********************************************************************************************
1349    // These count files and sub-directories
1350    // ********************************************************************************************
1351    // ********************************************************************************************
1352
1353
1354    /** Convenience Method.  Invokes {@link #count(FileNodeFilter, FileNodeFilter)} */
1355    public int count() { return count(null, null); }
1356
1357    /**
1358     * Performs a count on the total number of files and directories contained by {@code 'this'}
1359     * directory.  This method is recursive, and traverses both {@code 'this'} directory, and all
1360     * sub-directories when calculating the return-value.
1361     *
1362     * @param fileFilter This allows a user to eliminate certain files from the total count.
1363     * 
1364     * <BR /><BR />The filter provided should be a {@code Predicate<FileNode>} that returns
1365     * <B>TRUE</B> if the file <I>should be counted</I>, and <B>FALSE</B> if the file <I>should
1366     * <B>not</B></I> be counted.
1367     *
1368     * <BR /><BR />This parameter may be {@code 'null'}, and if it is, it will be ignored.  In
1369     * such cases, all files will be included in the total count.
1370     * 
1371     * @param directoryFilter This allows a user to skip branches of the directory-tree when
1372     * performing the count.
1373     * 
1374     * <BR /><BR />The filter provided should be a {@code Predicate<FileNode>} that returns
1375     * <B>TRUE</B> if the sub-directory <I>should be entered</I> (and counted), and <B>FALSE</B>
1376     * if the sub-directory tree-branch <I>should be skipped</I> completely.
1377     *
1378     * <EMBED CLASS='external-html' DATA-FILE-ID=FNCOUNTDF>
1379     *
1380     * @return A total count of all files and sub-directories contained by {@code 'this'} instance
1381     * of {@code FileNode} - less the files and directory-tree branches that were excluded by the
1382     * filters that may or may not have been passed to this method.
1383     *
1384     * @throws DirExpectedException If the user has attempted to perform a count on a
1385     * {@code FileNode} that is a 'file' rather than a 'directory'.
1386     */
1387    public int count(FileNodeFilter fileFilter, FileNodeFilter directoryFilter)
1388    {
1389        DirExpectedException.check(this);
1390        // This was moved to an "INTERNAL" method to avoid invoking the above exception check
1391        // every time this (recursive) code encounters a directory.
1392        return countINTERNAL(fileFilter, directoryFilter);
1393    }
1394
1395    private int countINTERNAL(FileNodeFilter fileFilter, FileNodeFilter directoryFilter)
1396    {
1397        int count = 0;
1398
1399        for (FileNode fn : children)
1400            if (fn.isDirectory)
1401            {
1402                if ((directoryFilter == null) || directoryFilter.test(fn))
1403                    count += 1 /* 'this' adds 1 */ + fn.countINTERNAL(fileFilter, directoryFilter);
1404            }
1405            else // fn is a file, not a dir.
1406                if ((fileFilter == null) || fileFilter.test(fn))
1407                    count++;
1408
1409        return count;
1410    }
1411
1412    /** Convenience Method.  Invokes {@link #countJustFiles(FileNodeFilter, FileNodeFilter)} */
1413    public int countJustFiles() { return countJustFiles(null, null); }
1414
1415    /**
1416     * Performs a count on the total number of <I><B>files only</I></B> (does not count sub
1417     * directories) contained by {@code 'this'} directory.  This method is recursive, and traverses
1418     * both {@code 'this'} directory, and all sub-directories when calculating the return-value.
1419     *
1420     * @param fileFilter This allows a user to eliminate certain files from the total count.
1421     * 
1422     * <BR /><BR />The filter provided should be a {@code Predicate<FileNode>} that returns
1423     * <B>TRUE</B> if the file <I>should be counted</I>, and <B>FALSE</B> if the file <I>should
1424     * <B>not</B></I> be counted.
1425     *
1426     * <BR /><BR />This parameter may be {@code 'null'}, and if it is, it will be ignored.  In
1427     * such cases, all files will be included in the total count.
1428     * 
1429     * @param directoryFilter This allows a user to skip branches of the directory-tree when
1430     * performing the count.
1431     * 
1432     * <BR /><BR />The filter provided should be a {@code Predicate<FileNode>} that returns
1433     * <B>TRUE</B> if the sub-directory <I>should be entered</I> (the directory itself will not
1434     * contribute to the count).  When this filter returns <B>FALSE</B> the sub-directory
1435     * tree-branch <I>will be skipped</I> completely, and any files in those sub-directories will
1436     * not contribute to the total file-count.
1437     *
1438     * <EMBED CLASS='external-html' DATA-FILE-ID=FNCOUNTDF>
1439     *
1440     * @return A total count of all files (excluding sub-directories) contained by {@code 'this'}
1441     * instance of {@code FileNode} - less the files that reside in directory-tree branches that
1442     * were excluded by the {@code 'directoryFilter'} parameter <B><I>and</I></B> less the files
1443     * that were excluded by {@code 'fileFilter'}.
1444     *
1445     * @throws DirExpectedException If the user has attempted to perform a count on a
1446     * {@code FileNode} that is a 'file' rather than a 'directory'.
1447     */
1448    public int countJustFiles(FileNodeFilter fileFilter, FileNodeFilter directoryFilter)
1449    {
1450        DirExpectedException.check(this);
1451        // This was moved to an "INTERNAL" method to avoid invoking the above exception check
1452        // every time this (recursive) code encounters a directory.
1453        return countJustFilesINTERNAL(fileFilter, directoryFilter);
1454    }
1455
1456    private int countJustFilesINTERNAL
1457        (FileNodeFilter fileFilter, FileNodeFilter directoryFilter)
1458    {
1459        int count = 0;
1460
1461        for (FileNode fn : children)
1462            if (fn.isDirectory)
1463            {
1464                if ((directoryFilter == null) || directoryFilter.test(fn))
1465                    count += fn.countJustFilesINTERNAL(fileFilter, directoryFilter);
1466            }
1467            else // fn is a file, not a dir.
1468                if ((fileFilter == null) || fileFilter.test(fn))
1469                    count++;
1470
1471        return count;
1472    }
1473
1474    /** Convenience Method.  Invokes {@link #countJustDirs(FileNodeFilter)} */
1475    public int countJustDirs() { return countJustDirs(null); }
1476
1477    /**
1478     * Performs a count on the total number of sub-directories contained by {@code 'this'}
1479     * directory.  This method is recursive, and traverses all sub-directories when calculating
1480     * the return-value.
1481     *
1482     * @param directoryFilter This allows a user to skip branches of the directory-tree when
1483     * performing the count.
1484     * 
1485     * <BR /><BR />The filter provided should be a {@code Predicate<FileNode>} that returns
1486     * <B>TRUE</B> if the sub-directory <I>should be entered</I> and <B>FALSE</B> if sub-directory
1487     * tree-branch <I>should be skipped</I> completely.
1488     *
1489     * <EMBED CLASS='external-html' DATA-FILE-ID=FNCOUNTDF>
1490     *
1491     * @return A total count of all sub-directories contained by {@code 'this'}
1492     * instance of {@code FileNode} - less the sub-directories that reside in directory-tree
1493     * branches that were excluded by the {@code 'directoryFilter'} parameter.
1494     *
1495     * @throws DirExpectedException If the user has attempted to perform a count on a
1496     * {@code FileNode} that is a 'file' rather than a 'directory'.
1497     */
1498    public int countJustDirs(FileNodeFilter directoryFilter)
1499    {
1500        DirExpectedException.check(this);
1501        // This was moved to an "INTERNAL" method to avoid invoking the above exception check
1502        // every time this (recursive) code encounters a directory.
1503        return countJustDirsINTERNAL(directoryFilter);
1504    }
1505
1506    private int countJustDirsINTERNAL
1507        (FileNodeFilter directoryFilter)
1508    {
1509        int count = 0;
1510
1511        if (directoryFilter == null)
1512            for (FileNode fn1 : children)
1513                if (fn1.isDirectory)
1514                    count += 1 /* 'this' adds 1 */ + fn1.countJustDirsINTERNAL(directoryFilter);
1515        else
1516            for (FileNode fn2 : children)
1517                if (fn2.isDirectory)
1518                    if (directoryFilter.test(fn2))
1519                        count += 1 /* 'this' adds 1 */ + fn2.countJustDirsINTERNAL(directoryFilter);
1520
1521        return count;
1522    }
1523
1524
1525    // ********************************************************************************************
1526    // ********************************************************************************************
1527    // ALL - a single level in the file-tree.
1528    // ********************************************************************************************
1529    // ********************************************************************************************
1530
1531
1532    /**
1533     * Convenience Method.
1534     * <BR />Automatically Selects: {@link RetTypeChoice#VECTOR}
1535     * <BR />Invokes: {@link #getDirContents(VarList, FileNodeFilter)}
1536     */
1537    public Vector<FileNode> getDirContents()
1538    { return getDirContents(RetTypeChoice.VECTOR, null); }
1539
1540    /**
1541     * Convenience Method.
1542     * <BR />Accepts: {@link RetTypeChoice}
1543     * <BR />Invokes: {@link #getDirContents(VarList, FileNodeFilter)}
1544     */
1545    public <T> T getDirContents(VarList<T, FileNode> listChoice)
1546    { return getDirContents(listChoice, null); }
1547
1548    /**
1549     * Convenience Method.
1550     * <BR />Automatically Selects: {@link RetTypeChoice#VECTOR}
1551     * <BR />Accepts: {@link FileNodeFilter}
1552     * <BR />Invokes: {@link #getDirContents(VarList, FileNodeFilter)}
1553     */
1554    public Vector<FileNode> getDirContents(FileNodeFilter filter)
1555    { return getDirContents(RetTypeChoice.VECTOR, filter); }
1556
1557    /**
1558     * This method returns a {@code Vector} that contains the contents of a directory.
1559     *
1560     * <BR /><BR /><B>IMPORTANT:</B> This method may only be invoked on {@code FileNode} instances
1561     * which represent directories on the file-system.  If this method is invoked on a file, an
1562     * exception shall throw.   This is because files on the file-system do not have 'contents' in
1563     * the sense of 'holding' or 'having' other files or sub-directories.
1564     *
1565     * <BR /><BR /><B><SPAN STYLE="color: red;">TREE TRAVERSAL NOTE:</B></SPAN> This {@code Vector}
1566     * is not recursively generated, and does not traverse the entire directory tree.  The search
1567     * logic only visits the files &amp; directories contained within the directory on which
1568     * {@code 'this'} instance is invoked.  To retrieve a {@code Vector} of the entire tree
1569     * (or a sub-tree), instead use a version of {@code 'flatten(...)'}.
1570     *
1571     * @param listChoice <EMBED CLASS="external-html" DATA-FILE-ID="FNRTCP">
1572     * 
1573     * @param filter When this parameter is used, any files or directories that do not pass the
1574     * {@code filter's 'test'} method shall not be included in the return {@code Vector}.
1575     * Specifically, the {@code filter} that is passed should return <B>TRUE</B> when a file or
1576     * directory needs to be included in the {@code Vector}.  When the {@code filter} returns
1577     * <B>FALSE</B> after testing a file or directory, the {@code Vector} that is returned by this
1578     * method shall exclude that file or directory.
1579     *
1580     * <BR /><BR />If this parameter is null, it will be ignored, and every {@code FileNode}
1581     * contained by the directory will be included in the return {@code Vector}.
1582     * 
1583     * @return A {@code Vector} that contains the files inside the current directory.  
1584     *
1585     * <EMBED CLASS="external-html" DATA-FILE-ID="FNRTCRET">
1586     *
1587     * @throws DirExpectedException If {@code 'this'} instance of {@code FileNode} is not a
1588     * directory, but a file, then this exception is thrown.  (Files <I>may not</I> have
1589     * child-nodes, only directories).
1590     * 
1591     * @see FileNodeFilter
1592     *
1593     * @see #children
1594     */
1595    public <T> T getDirContents(VarList<T, FileNode> listChoice, FileNodeFilter filter)
1596    {
1597        DirExpectedException.check(this);
1598
1599        VarList<T, FileNode> ret = listChoice.create();
1600
1601        children.forEach
1602            ((FileNode fn) -> { if ((filter == null) || filter.test(fn)) ret.insert(fn); });
1603
1604        return ret.retrieve();
1605    }
1606
1607
1608    // ********************************************************************************************
1609    // ********************************************************************************************
1610    // DIRECTORIES - a single level in the file-tree.
1611    // ********************************************************************************************
1612    // ********************************************************************************************
1613
1614
1615    /**
1616     * Convenience Method.
1617     * <BR />Automatically Selects: {@link RetTypeChoice#VECTOR}
1618     * <BR />Invokes: {@link #getDirContentsDirs(VarList, FileNodeFilter)}
1619     */
1620    public Vector<FileNode> getDirContentsDirs()
1621    { return getDirContentsDirs(RetTypeChoice.VECTOR, null); }
1622
1623    /**
1624     * Convenience Method.
1625     * <BR />Accepts: {@link RetTypeChoice}
1626     * <BR />Invokes: {@link #getDirContentsDirs(VarList, FileNodeFilter)}
1627     */
1628    public <T> T getDirContentsDirs(VarList<T, FileNode> listChoice)
1629    { return getDirContentsDirs(listChoice, null); }
1630
1631    /**
1632     * Convenience Method.
1633     * <BR />Automatically Selects: {@link RetTypeChoice#VECTOR}
1634     * <BR />Accepts: {@link FileNodeFilter}
1635     * <BR />Invokes: {@link #getDirContentsDirs(VarList, FileNodeFilter)}
1636     */
1637    public Vector<FileNode> getDirContentsDirs(FileNodeFilter filter)
1638    { return getDirContentsDirs(RetTypeChoice.VECTOR, filter); }
1639
1640    /**
1641     * This method is almost identical to the method {@code getDirContents(filter)}.  This will
1642     * 'logical-and' a {@code filter} which requires that only {@code FileNode} instances <I>which
1643     * are directories</I> can be included in the {@code Vector}.  {@code FileNode} instances which
1644     * are files, will not be contained by the {@code Vector} that is generated.
1645     *
1646     * <BR /><BR /><B>IMPORTANT:</B> This method may only be invoked on {@code FileNode}
1647     * instances which represent directories on the file-system.  If this method is invoked on a
1648     * file, an exception shall throw.   This is because files on the file-system do not have
1649     * 'contents' in the sense of 'holding' or 'having' other files or sub-directories.
1650     *
1651     * <BR /><BR /><B><SPAN STYLE="color: red;">TREE TRAVERSAL NOTE:</B></SPAN> This
1652     * {@code Vector} is not recursively generated, and does not traverse the entire directory
1653     * tree.   The search logic only visits the files &amp; directories contained within the
1654     * directory on which {@code 'this'} instance is invoked.  To retrieve a {@code Vector} of the
1655     * entire tree (or a sub-tree), instead use a version of {@code 'flatten(...)'}.
1656     *
1657     * @param listChoice <EMBED CLASS="external-html" DATA-FILE-ID="FNRTCP">
1658     * 
1659     * @param filter When this parameter is used, any directories that do not pass the 
1660     * {@code filter's 'test'} method shall not be included in the return {@code Vector}.
1661     * Specifically, the {@code filter} that is passed should return <B>TRUE</B> when a directory
1662     * needs to be included in the {@code Vector}.  When the {@code filter} returns <B>FALSE</B>
1663     * after testing a directory, the {@code Vector} that is returned by this method shall skip
1664     * that directory.
1665     *
1666     * <BR /><BR />If this parameter is null, it will be ignored, and every sub-directory
1667     * {@code FileNode} contained by this directory instance will be included in the return
1668     * {@code Vector}.
1669     *
1670     * @return A {@code Vector} that contains the sub-directories inside the current directory.
1671     *
1672     * <EMBED CLASS="external-html" DATA-FILE-ID="FNRTCRET">
1673     *
1674     * @throws DirExpectedException If {@code 'this'} instance of {@code FileNode} is not a
1675     * directory, but a file, then this exception is thrown.  (Files <I>may not</I> have
1676     * child-nodes, only directories).
1677     *
1678     * @see FileNodeFilter
1679     * @see #isDirectory
1680     */
1681    public <T> T getDirContentsDirs(VarList<T, FileNode> listChoice, FileNodeFilter filter)
1682    { 
1683        FileNodeFilter dirFilter = (FileNode fn) -> fn.isDirectory;
1684        return getDirContents(listChoice, (filter != null) ? filter.and(dirFilter) : dirFilter);
1685    }
1686
1687
1688    // ********************************************************************************************
1689    // ********************************************************************************************
1690    // FILES - a single level in the file-tree.
1691    // ********************************************************************************************
1692    // ********************************************************************************************
1693
1694
1695    /**
1696     * Convenience Method.
1697     * <BR />Automatically Selects: {@link RetTypeChoice#VECTOR}
1698     * <BR />Invokes: {@link #getDirContentsFiles(VarList, FileNodeFilter)}
1699     * */
1700    public Vector<FileNode> getDirContentsFiles()
1701    { return getDirContentsFiles(RetTypeChoice.VECTOR, null); }
1702
1703    /**
1704     * Convenience Method.
1705     * <BR />Accepts: {@link RetTypeChoice}
1706     * <BR />Invokes: {@link #getDirContentsFiles(VarList, FileNodeFilter)}
1707     * */
1708    public <T> T getDirContentsFiles(VarList<T, FileNode> listChoice)
1709    { return getDirContentsFiles(listChoice, null); }
1710
1711    /**
1712     * Convenience Method.
1713     * <BR />Automatically Selects: {@link RetTypeChoice#VECTOR}
1714     * <BR />Accepts: {@link FileNodeFilter}
1715     * <BR />Invokes: {@link #getDirContentsFiles(VarList, FileNodeFilter)}
1716     * */
1717    public Vector<FileNode> getDirContentsFiles(FileNodeFilter filter)
1718    { return getDirContentsFiles(RetTypeChoice.VECTOR, filter); }
1719
1720    /**
1721     * This method is almost identical to the method {@code getDirContents(filter)}.  This will
1722     * 'logical-and' a {@code filter} which requires that only {@code FileNode} instances <I>which
1723     * are files</I> can be included in the return {@code Vector}.  {@code FileNode} instances
1724     * which are directories, will not be contained by the {@code Vector} that is generated.
1725     *
1726     * <BR /><BR /><B>IMPORTANT:</B> This method may only be invoked on {@code FileNode} instances
1727     * which represent directories on the file-system.  If this method is invoked on a file, an
1728     * exception shall throw.   This is because files on the file-system do not have 'contents' in
1729     * the sense of 'holding' or 'having' other files or sub-directories.
1730     *
1731     * <BR /><BR /><B><SPAN STYLE="color: red;">TREE TRAVERSAL NOTE:</B></SPAN> This {@code Vector}
1732     * is not recursively generated, and does not traverse the entire directory tree.  The search
1733     * logic only visits the files &amp; directories contained within the directory on which
1734     * {@code 'this'} instance is invoked.  To retrieve a {@code Vector} of the entire tree (or a
1735     * sub-tree), instead use a version of {@code 'flatten(...)'}.
1736     *
1737     * @param listChoice <EMBED CLASS="external-html" DATA-FILE-ID="FNRTCP">
1738     * 
1739     * @param filter When this parameter is used, any files that do not pass the {@code filter's
1740     * 'test'} method shall not be included in the return {@code Vector}.  Specifically, the
1741     * {@code filter} that is passed should return <B>TRUE</B> when a file needs to be included in
1742     * the {@code Vector}.  When the {@code filter} returns <B>FALSE</B> after testing a file, the
1743     * {@code Vector} that is returned by this method shall skip that file.
1744     *
1745     * <BR /><BR />If this parameter is null, it will be ignored, and every instance of
1746     * {@code FileNode} that is a file and is contained by this directory instance will be included
1747     * in the return {@code Vector}.
1748     *
1749     * @return A {@code Vector} that contains the files inside the current directory.
1750     *
1751     * <EMBED CLASS="external-html" DATA-FILE-ID="FNRTCRET">
1752     *
1753     * @throws DirExpectedException If {@code 'this'} instance of {@code FileNode} is not a
1754     * directory, but a file, then this exception is thrown.  (Files <I>may not</I> have
1755     * child-nodes, only directories).
1756     *
1757     * @see FileNodeFilter
1758     * @see #isDirectory
1759     */
1760    public <T> T getDirContentsFiles(VarList<T, FileNode> listChoice, FileNodeFilter filter)
1761    { 
1762        FileNodeFilter fileFilter = (FileNode fn) -> ! fn.isDirectory;
1763
1764        return getDirContents
1765            (listChoice, (filter != null) ? filter.and(fileFilter) : fileFilter);
1766    }
1767
1768    
1769    // ********************************************************************************************
1770    // ********************************************************************************************
1771    // "Flatten" routines - Just-Directories & Just-Files
1772    // ********************************************************************************************
1773    // ********************************************************************************************
1774
1775
1776    /** 
1777     * Convenience Method.
1778     * <BR />Automatically Selects: {@link RetTypeChoice#VECTOR}
1779     * <BR />Invokes: {@link #flatten(VarList, int, FileNodeFilter, boolean, FileNodeFilter, boolean)}
1780     * <BR />Parameter: {@code includeDirectoriesInResult} set <B>TRUE</B>
1781     * <BR />Parameter: {@code includeFilesInResult} set <B>FALSE</B>
1782     */
1783    public Vector<FileNode> flattenJustDirs()
1784    { return flatten(RetTypeChoice.VECTOR, -1, null, false, null, true); }
1785
1786    /** 
1787     * Convenience Method.
1788     * <BR />Accepts: {@link RetTypeChoice}
1789     * <BR />Invokes: {@link #flatten(VarList, int, FileNodeFilter, boolean, FileNodeFilter, boolean)} 
1790     * <BR />Parameter: {@code includeDirectoriesInResult} set <B>TRUE</B>
1791     * <BR />Parameter: {@code includeFilesInResult} set <B>FALSE</B>
1792     */
1793    public <T> T flattenJustDirs(VarList<T, FileNode> listChoice)
1794    { return flatten(listChoice, -1, null, false, null, true); }
1795
1796    /**
1797     * Convenience Method.
1798     * <BR />Automatically Selects: {@link RetTypeChoice#VECTOR}
1799     * <BR />Invokes: {@link #flatten(VarList, int, FileNodeFilter, boolean, FileNodeFilter, boolean)}
1800     * <BR />Parameter: {@code includeDirectoriesInResult} set <B>TRUE</B>
1801     * <BR />Parameter: {@code includeFilesInResult} set <B>FALSE</B>
1802     */
1803    public Vector<FileNode> flattenJustDirs(int maxTreeDepth, FileNodeFilter directoryFilter)
1804    { return flatten(RetTypeChoice.VECTOR, maxTreeDepth, null, false, directoryFilter, true); }
1805
1806    /**
1807     * Convenience Method.
1808     * <BR />Accepts: {@link RetTypeChoice}
1809     * <BR />Invokes: {@link #flatten(VarList, int, FileNodeFilter, boolean, FileNodeFilter, boolean)}
1810     * <BR />Parameter: {@code includeDirectoriesInResult} set <B>TRUE</B>
1811     * <BR />Parameter: {@code includeFilesInResult} set <B>FALSE</B>
1812     */
1813    public <T> T flattenJustDirs(VarList<T, FileNode> listChoice, int maxTreeDepth, FileNodeFilter directoryFilter)
1814    { return flatten(listChoice, maxTreeDepth, null, false, directoryFilter, true); }
1815
1816    /**
1817     * Convenience Method.
1818     * <BR />Automatically Selects: {@link RetTypeChoice#VECTOR}
1819     * <BR />Invokes: {@link #flatten(VarList, int, FileNodeFilter, boolean, FileNodeFilter, boolean)}
1820     * <BR />Parameter: {@code includeDirectoriesInResult} set <B>FALSE</B>
1821     * <BR />Parameter: {@code includeFilesInResult} set <B>TRUE</B>
1822     */
1823    public Vector<FileNode> flattenJustFiles()
1824    { return flatten(RetTypeChoice.VECTOR, -1, null, true, null, false); }
1825
1826    /**
1827     * Convenience Method.
1828     * <BR />Accepts: {@link RetTypeChoice} 
1829     * <BR />Invokes: {@link #flatten(VarList, int, FileNodeFilter, boolean, FileNodeFilter, boolean)}
1830     * <BR />Parameter: {@code includeDirectoriesInResult} set <B>FALSE</B>
1831     * <BR />Parameter: {@code includeFilesInResult} set <B>TRUE</B>
1832     */
1833    public <T> T flattenJustFiles(VarList<T, FileNode> listChoice)
1834    { return flatten(listChoice, -1, null, true, null, false); }
1835
1836    /**
1837     * Convenience Method.
1838     * <BR />Automatically Selects: {@link RetTypeChoice#VECTOR}
1839     * <BR />Invokes: {@link #flatten(VarList, int, FileNodeFilter, boolean, FileNodeFilter, boolean)}
1840     * <BR />Parameter: {@code includeDirectoriesInResult} set <B>FALSE</B>
1841     * <BR />Parameter: {@code includeFilesInResult} set <B>TRUE</B>
1842     */
1843    public Vector<FileNode> flattenJustFiles(int maxTreeDepth, FileNodeFilter fileFilter)
1844    { return flatten(RetTypeChoice.VECTOR, maxTreeDepth, fileFilter, true, null, false); }
1845
1846    /**
1847     * Convenience Method.
1848     * <BR />Accepts: {@link RetTypeChoice}.
1849     * <BR />Invokes: {@link #flatten(VarList, int, FileNodeFilter, boolean, FileNodeFilter, boolean)}
1850     * <BR />Parameter: {@code includeDirectoriesInResult} set <B>FALSE</B>
1851     * <BR />Parameter: {@code includeFilesInResult} set <B>TRUE</B>
1852     */
1853    public <T> T flattenJustFiles(VarList<T, FileNode> listChoice, int maxTreeDepth, FileNodeFilter fileFilter)
1854    { return flatten(listChoice, maxTreeDepth, fileFilter, true, null, false); }
1855
1856
1857    // ********************************************************************************************
1858    // ********************************************************************************************
1859    // Core Flatten Routines
1860    // ********************************************************************************************
1861    // ********************************************************************************************
1862
1863    /**
1864     * Convenience Method.
1865     * <BR />Automatically Selects: {@link RetTypeChoice#VECTOR}
1866     * <BR />Invokes: {@link #flatten(VarList, int, FileNodeFilter, boolean, FileNodeFilter, boolean)}
1867     * <BR />Parameters {@code includeFilesInResult, includeDirectoriesInResult} both set <B>TRUE</B>
1868     */
1869    public Vector<FileNode> flatten()
1870    { return flatten(RetTypeChoice.VECTOR, -1, null, true, null, true); }
1871
1872    /**
1873     * Convenience Method.
1874     * <BR />Accepts: {@link RetTypeChoice} 
1875     * <BR />Invokes: {@link #flatten(VarList, int, FileNodeFilter, boolean, FileNodeFilter, boolean)}
1876     * <BR />Parameters {@code includeFilesInResult, includeDirectoriesInResult} both set <B>TRUE</B>
1877     */
1878    public <T> T flatten(VarList<T, FileNode> listChoice)
1879    { return flatten(listChoice, -1, null, true, null, true); }
1880
1881    /**
1882     * Convenience Method.
1883     * <BR />Automatically Selects: {@link RetTypeChoice#VECTOR}
1884     * <BR />Invokes: {@link #flatten(VarList, int, FileNodeFilter, boolean, FileNodeFilter, boolean)}
1885     */
1886    public Vector<FileNode> flatten(
1887            int maxTreeDepth,
1888            FileNodeFilter fileFilter,      boolean includeFilesInResult, 
1889            FileNodeFilter directoryFilter, boolean includeDirectoriesInResult)
1890    {
1891        return flatten(RetTypeChoice.VECTOR, maxTreeDepth,
1892            fileFilter, includeFilesInResult,
1893            directoryFilter, includeDirectoriesInResult
1894        );
1895    }
1896
1897    /**
1898     * This flattens the {@code FileNode} tree into a simple java {@code Vector<FileNode>}.
1899     * Primarily, if the tree was pruned during loading in a way that has changed, or the tree was
1900     * not pruned at all - then this method is for you! :)  Any one of these parameters may be null
1901     * or negative, and they will be ignored completely.  Make sure to use <B>TRUE</B> for the two
1902     * boolean values if you do not want to lose the file-system type out of your result set.
1903     * Review &amp; read the parameter explanations below, closely, to see what the specifiers do.
1904     * 
1905     * <BR /><BR /><B>GENERALLY:</B> <I><SPAN STYLE="color: red;">The concept of "flatten" is
1906     * identical to the concept of "retrieve" or "search"</SPAN></I> (because all perform 'copying'
1907     * a set of {@code filter}-matches into a list).  If one wishes to scour/search a
1908     * {@code FileNode} tree, and obtain individual nodes of the tree and save them in a list, it
1909     * is easily done by simply writing the appropriate file &amp; directory filter {@code public
1910     * boolean test(FileNode fn)} methods (or using Java "lambda" expressions instead of actual
1911     * methods) - <I><B>and then invoking a version of {@code flatten(...)}</I></B>.
1912     *
1913     * <BR /><BR />
1914     * If you would like to "flatten" the entire tree into a {@code Vector}, leave the
1915     * {@code filter} parameters blank by passing them null.
1916     *
1917     * @param listChoice <EMBED CLASS="external-html" DATA-FILE-ID="FNRTCP">
1918     * 
1919     * @param maxTreeDepth This value should be set to the maximum number of levels deep to 
1920     * <I>recursively</I> search when traversing the traversing the file-system / operating-system
1921     * directory-tree.
1922     *
1923     * <BR /><BR /><SPAN STYLE="color: red;"><B>NOTE:</B></SPAN> Simply use any negative number to
1924     * always visit the tree to its maximum depth.
1925     *
1926     * @param fileFilter This is a Java 8 "accept" {@code interface java.util.function.Predicate}.
1927     * Implementing the {@code 'test(FileNode)'} method, allows one to pick &amp; choose which
1928     * files will be visited as the tree is recursively traversed.  Use a lambda-expression, if
1929     * needed or for convenience.
1930     *
1931     * <BR /><BR /><B>NOTE:</B> This parameter may be null, and if so - <I>all files</I> are
1932     * presumed to pass the {@code filter test}. 
1933     *
1934     * <BR /><BR /><B><SPAN STYLE="color: red;">JAVA STREAM'S</SPAN></B> The behavior of this
1935     * {@code filter}-logic is identical to the Java 8+ Streams-Method
1936     * {@code 'filter(Predicate<...>)'}.  Specifically, when the {@code filter} returns a <B>TRUE</B>
1937     * value for a particular {@code FileNode}, that {@code FileNode} shall be retained, or 'kept',
1938     * in the returned result-set.  When the {@code filter} returns <B>FALSE</B> for a
1939     * {@code FileNode}, that file or directory will be removed from the result-set.
1940     *
1941     * @param includeFilesInResult If this parameter is <B>TRUE</B>, then files will be included in
1942     * the resulting {@code Vector}.
1943     *
1944     * <BR /><SPAN STYLE="color: red;"><B>NOTE:</B></SPAN> If this parameter is <B>FALSE</B>, this
1945     * value will "over-ride" any results that may be produced from the {@code public boolean
1946     * fileFilter.test(this)} method.
1947     *
1948     * @param directoryFilter This is also a Java 8 "accept"  {@code interface
1949     * java.util.function.Predicate}.  Implementing the {@code 'test(FileNode)'} method, allows
1950     * one to pick &amp; choose which directories will be visited as the tree is recursively
1951     * traversed.  Use a lambda-expression, if needed or for convenience.
1952     *
1953     * <BR /><BR /><B><SPAN STYLE="color: red;">JAVA STREAM'S</SPAN></B> The behavior of this
1954     * {@code filter}-logic is identical to the Java 8+ Streams-Method
1955     * {@code 'filter(Predicate<...>)'.}  Specifically, when the {@code filter} returns a
1956     * <B>TRUE</B> value for a particular {@code FileNode}, that {@code FileNode} shall be
1957     * retained, or 'kept', in the returned result-set.  When the {@code filter} returns
1958     * <B>FALSE</B> for a {@code FileNode}, that file or directory will be removed from the
1959     * result-set.
1960     *
1961     * <BR /><BR /><B>NOTE:</B> This parameter may be null, and if so - <I>all directories</I>
1962     * will be traversed.
1963     *
1964     * <BR /><BR /><SPAN STYLE="color: red;"><B>ALSO:</B></SPAN> There is no way to differentiate
1965     * which directories are traversed and which directories are included in the result set - they
1966     * are either traversed or not.  Whether the particular {@code FileNode} (which is a directory,
1967     * not a file) is included in the result set, is determined by:
1968     *
1969     * <BR /><BR /><UL CLASS="JDUL">
1970     * <LI>Whether that {@code FileNode} has passed (been accepted by) the {@code public boolean
1971     * directoryFilter.test(this)} method.
1972     * </LI>
1973     * <LI>And also whether/if the {@code boolean includeDirectoriesInResult} boolean-flag has been
1974     * set to <B>TRUE</B>.
1975     * </LI>
1976     * </UL>
1977     *
1978     * @param includeDirectoriesInResult If this parameter is <B>TRUE</B>, then directories will be
1979     * included in the resulting {@code Vector}.
1980     *
1981     * <BR /><BR /><SPAN STYLE="color: red;"><B>NOTE:</B></SPAN> If this parameter is <B>FALSE</B>,
1982     * this value will "over-ride" any results that may be produced from the {@code public boolean
1983     * directoryFilter.test(this)} method.
1984     *
1985     * @return A flattened version of this tree.
1986     *
1987     * <EMBED CLASS="external-html" DATA-FILE-ID="FNRTCRET">
1988     *
1989     * @throws DirExpectedException If {@code 'this'} instance of {@code FileNode} is not a
1990     * directory, but a file, then this exception is thrown.  (Files <I>may not</I> have 
1991     * child-nodes, only directories).
1992     *
1993     * @throws IllegalArgumentException If the value of {@code 'maxTreeDepth'} is set to
1994     * {@code zero}, then this exception shall be thrown because the method-invocation would not be
1995     * making an actual request to do anything.
1996     *
1997     * <BR /><BR />This exception shall <I><B>*also* be throw if</I></B> both of the boolean
1998     * parameters are set to <B>FALSE</B>, for the same reason being that the method-invocation
1999     * would not be making a request.
2000     */
2001    public <T> T flatten(
2002            VarList<T, FileNode> listChoice,
2003            int maxTreeDepth,
2004            FileNodeFilter fileFilter,      boolean includeFilesInResult, 
2005            FileNodeFilter directoryFilter, boolean includeDirectoriesInResult)
2006    {
2007        DirExpectedException.check(this);
2008
2009        if (maxTreeDepth == 0) throw new IllegalArgumentException(
2010            "flatten(int, FileNodeFilter, boolean, directoryFilter, boolean) has been invoked " +
2011            "with the maxTreeDepth (integer) parameter set to zero.  This means that there is " +
2012            "nothing for the method to do."
2013        );
2014
2015        if ((! includeFilesInResult) && (! includeDirectoriesInResult)) throw new IllegalArgumentException(
2016            "flatten(int, FileNodeFilter, boolean, directoryFilter, boolean) has been invoked " +
2017            "with both of the two boolean search criteria values set to FALSE.  This means that " +
2018            "there is nothing for the method to do."
2019        );
2020
2021        // This allows us to permit the user to decide what type of Container and what type of
2022        // Object inside the container is returned.
2023        VarList<T, FileNode> ret = listChoice.create();
2024
2025        // 'this' directory needs to be included (unless filtering directories)
2026        if (includeDirectoriesInResult && ((directoryFilter == null) || directoryFilter.test(this)))
2027            ret.insert(this);
2028
2029        // Call the general-purpose flatten method.
2030        flattenINTERNAL(
2031            this, ret, fileFilter, includeFilesInResult, directoryFilter,
2032            includeDirectoriesInResult, 0, maxTreeDepth
2033        );
2034
2035        // retrieve the Container specified by the user.
2036        return ret.retrieve();
2037    }
2038
2039    private static void flattenINTERNAL(
2040            FileNode cur,                   VarList<?, FileNode> ret,
2041            FileNodeFilter fileFilter,      boolean includeFilesInResult, 
2042            FileNodeFilter directoryFilter, boolean includeDirectoriesInResult,
2043            int curTreeDepth,               int maxTreeDepth
2044        )
2045    {
2046        if ((maxTreeDepth >= 0) && (curTreeDepth > maxTreeDepth)) return;
2047 
2048        if (VERBOSE) System.out.println(cur.name);
2049
2050        for (FileNode fn : cur.children)
2051        {
2052            if (fn.isDirectory)
2053            { if (includeDirectoriesInResult    && ((directoryFilter == null)   || directoryFilter.test(fn))) ret.insert(fn); }
2054
2055            else /* fn.isDirectory is FALSE, and therefore this is a file! */
2056            { if (includeFilesInResult          && ((fileFilter == null)        || fileFilter.test(fn)))      ret.insert(fn); }
2057        }
2058
2059        for (FileNode fn : cur.children)
2060             if (fn.isDirectory && ((directoryFilter == null) || directoryFilter.test(fn)))
2061                flattenINTERNAL(
2062                    fn, ret, fileFilter, includeFilesInResult, directoryFilter,
2063                    includeDirectoriesInResult, curTreeDepth+1, maxTreeDepth
2064                );
2065    }
2066
2067
2068    // ********************************************************************************************
2069    // ********************************************************************************************
2070    // Prune Tree
2071    // ********************************************************************************************
2072    // ********************************************************************************************
2073
2074
2075    /** 
2076     * Convenience Method.
2077     * <BR />Invokes: {@link #pruneTree(FileNodeFilter, boolean)}.
2078     * <BR />Returns: {@code 'this'} rather than {@code 'int'}
2079     */
2080    public FileNode prune(FileNodeFilter fileFilter, boolean nullThePointers)
2081    { this.pruneTree(fileFilter, nullThePointers); return this; }
2082
2083    /**
2084     * This removes instances of {@code FileNode} that meet these conditions:
2085     *
2086     * <BR /><BR /><OL CLASS="JDOL">
2087     * <LI>Are file instances, not directories. Specifically: {@code public final boolean
2088     *     isDirectory == false;}<BR />
2089     * </LI>
2090     * <LI>Do not pass the {@code 'fileFilter.test(...)'} method.  If the test method returns
2091     *    <B>FALSE</B>, the file shall be removed from the containing directory's {@code private 
2092     *    final Vector<FileNode> children} file-list.
2093     * </LI>
2094     * </OL>
2095     *
2096     * <BR /><BR /><B><SPAN STYLE="color: red">RECURSION:</B></SPAN> This method shall skip
2097     * through, 'traverse', the entire {@code FileNode} tree and prune all 'file-leaves' that do
2098     * not meet the criteria specified by the {@code 'fileFilter'} parameter.
2099     *
2100     * @param fileFilter This is the test used to filter out files from the directory-tree that
2101     * begins at {@code 'this'} instance.  Returning <B>FALSE</B> shall eliminate the file from its
2102     * containing parent, and when this filter returns <B>TRUE</B> that file shall remain.
2103     *
2104     * @param nullThePointers The primary use of this boolean is to remind users that this
2105     * data-structure {@code class FileNode} is actually a tree that maintains pointers in both
2106     * directions - upwards and downwards.  Generally, trees have the potential to make programming
2107     * an order of magnitude more complicated.  Fortunately, because this data-structure merely
2108     * represents the file-system, <I><B>and because</I></B> the data-structure itself (READ:
2109     * {@code 'this'} tree) does not have much use for being modified itself...  The fact that
2110     * {@code FileNode} is a two-way, bi-directional tree rarely seems important.  The most useful
2111     * methods are those that "flatten" the tree, and then process the data in the files listed.
2112     *
2113     * <BR /><BR /><B>POINT:</B> When this parameter is set to <B>TRUE</B>, all parent pointers
2114     * shall be nulled, and this can make garbage-collection easier.
2115     *
2116     * @return The number of files that were removed.
2117     *
2118     * @throws DirExpectedException If {@code 'this'} instance of {@code FileNode} does not
2119     * represent a 'directory' on the file-system, then this exception shall throw.
2120     *
2121     * @see #prune(FileNodeFilter, boolean)
2122     * @see #isDirectory
2123     * @see #parent
2124     */
2125    public int pruneTree(FileNodeFilter fileFilter, boolean nullThePointers)
2126    {
2127        DirExpectedException.check(this);
2128
2129        Iterator<FileNode>  iter        = this.children.iterator();
2130        int                 removeCount = 0;
2131
2132        while (iter.hasNext())
2133        {
2134            FileNode fn = iter.next();
2135
2136            /*
2137            DEBUGGING, KEEP HERE.
2138            System.out.print
2139                ("Testing: fn.name: " + fn.name + "\tfn.getParentDir().name: " + fn.getParentDir().name);
2140            */
2141
2142            if (! fn.isDirectory)
2143
2144                if (! fileFilter.test(fn))
2145                // NOTE: This only filters 'files' (leaf-nodes) out of the tree.  This 'tree-prune'
2146                // operation does not have any bearing on 'directory-nodes' (branch-nodes) in the tree.
2147                {
2148                    if (nullThePointers) fn.parent = null;
2149                        // These types of lines can help the Java Garbage-Collector.
2150                        // They also prevent the user from ever utilizing this object reference again.
2151
2152                    // System.out.println("\tRemoving...");
2153
2154                    iter.remove();
2155                        // This iterator is one generated by class 'Vector<FileNode>', and its remove()
2156                        // operation, therefore, is fully-supported.  This removes FileNode fn from 'this'
2157                        // private, final field 'private Vector<FileNode> children'
2158
2159                    removeCount++;
2160                    continue;
2161                }
2162
2163            // Keep Here, for Testing
2164            // System.out.println("\tKeeping...");
2165
2166            if (fn.isDirectory) removeCount += fn.pruneTree(fileFilter, nullThePointers);
2167        }
2168
2169        return removeCount;
2170    }
2171
2172
2173    // ********************************************************************************************
2174    // ********************************************************************************************
2175    // Simple stuff
2176    // ********************************************************************************************
2177    // ********************************************************************************************
2178
2179
2180    /**
2181     * Returns the parent of {@code 'this' FileNode}
2182     *
2183     * @return {@code this.parent}
2184     *
2185     * <BR /><BR /><B>NOTE</B> If this is a "root node" or "root directory" then null will be
2186     * returned here.
2187     *
2188     * @see #parent
2189     */
2190    public FileNode getParentDir() { return parent; }
2191
2192    /**
2193     * Move's a file or directory from "inside" or "within" the contents of the current/parent
2194     * directory, and into a new/destination/parent directory.  If the {@code destinationDir} is
2195     * not actually a directory, then an exception is thrown.  If this is already a child/member
2196     * of the {@code destinationDir}, then an exception is also thrown.
2197     *
2198     * <BR /><BR /><B>NOTE:</B> This method <I>does not modify</I> the underlying UNIX or MS-DOS
2199     * file-system - just the {@code FileNode} Tree  representation in Java Memory!   (No UNIX,
2200     * Apple, MS-DOS, etc. files are actually moved by this method)
2201     *
2202     * @param destinationDir the destination directory
2203     *
2204     * @throws java.util.InputMismatchException
2205     * @throws DirExpectedException If {@code 'destinationDir'} is not a directory, but a file,
2206     * then this exception is thrown.  (Files <I>may not</I> contain child-nodes, only directories)
2207     *
2208     * @see #parent
2209     * @see #name
2210     * @see #children
2211     */
2212    public void move(FileNode destinationDir)
2213    {
2214        DirExpectedException.check(destinationDir);
2215
2216        if (this.parent == destinationDir) throw new java.util.InputMismatchException(
2217            "[" + name + "] - is already a member of the target directory " +
2218            "[" + destinationDir.name + "]"
2219        );
2220
2221        parent = destinationDir;
2222
2223        destinationDir.children.addElement(this);
2224    }
2225
2226    /**
2227     * This deletes a file from the {@code FileNode} tree.  It's only operation is to remove
2228     * {@code 'this'} from the parent-node's {@code Vector<FileNode> children} node-list!  For
2229     * Java garbage collection purposes, it also empties (calls
2230     * {@code children.removeAllElements()}) on the children {@code Vector} - if there is one
2231     * (a.k.a) if {@code 'this' FileNode} represents a directory, not a file!
2232     *
2233     * <BR /><BR /><B>NOTE:</B> This method <I>does not modify</I> the underlying UNIX or MS-DOS
2234     * file-system - just the {@code FileNode}-Tree representation in Java Memory!  (No UNIX,
2235     * Apple, MS-DOS, etc. files are actually deleted by this method)
2236     *
2237     * @see #parent
2238     * @see #children
2239     * @see #isDirectory
2240     */
2241    public void del()
2242    {
2243        parent.children.removeElement(this);
2244        if (isDirectory) children.removeAllElements();
2245        parent = null;
2246    }
2247
2248
2249    /**
2250     * This visits the {@code '.name'} field for {@code 'this' FileNode}, as well as all parent
2251     * instances of {@code 'this' FileNode}, and concatenates those {@code String's}.
2252     * @return the full, available path name for {@code 'this' FileNode} as a {@code String}
2253     * @see #parent
2254     * @see #isDirectory
2255     * @see #name
2256     * @see #getFullPathName()
2257     */
2258    public String getFullPathName()
2259    {
2260        if (parent == null)
2261            return name + (isDirectory ? File.separatorChar : "");
2262        else
2263            return parent.getFullPathName() + name + (isDirectory ? File.separatorChar : "");
2264    }
2265
2266    /**
2267     * Returns the as much of the "Full Path Name" of the file referenced by {@code 'this'}
2268     * filename as is possible for this particular {@code FileNode}.
2269     * 
2270     * <BR /><BR /><B>NOTE:</B> If this file or directory does not have a parent, then the empty
2271     * (zero-length) {@code String} will be returned.  Usually, unless certain tree modification
2272     * operations have been performed, only a <I><B>root-node</B></I> {@code FileNode} will have a
2273     * 'null' parent.
2274     *
2275     * @return the full, available path name to {@code 'this' FileNode} - leaving out the actual
2276     * name of this file.
2277     *
2278     * <BR /><BR /><B>SPECIFICALLY:</B>
2279     * <BR /><BR /><UL CLASS="JDUL">
2280     * <LI>for a file such as {@code 'directory-1/subdirectory-2/filename.txt'}</LI>
2281     * <LI>{@code 'directory-1/subdirectory-2/'} would be returned</LI>
2282     * </UL>
2283     *
2284     * @see #parent
2285     * @see #getFullPathName()
2286     */
2287    public String getParentPathName()
2288    { if (parent == null) return ""; else return parent.getFullPathName(); }
2289
2290    /**
2291     * Gets the {@code java.io.File} version of a file.  The java class for files has quite a bit
2292     * of interactive stuff for a file system - including checking for {@code 'r/w/x'} permissions.
2293     * This can be useful.
2294     *
2295     * @return Gets the {@code java.io.File} instance of {@code 'this' FileNode}
2296     *
2297     * @see #getFullPathName()
2298     */
2299    public File getJavaIOFile() { return new File(getFullPathName()); }
2300
2301    /**
2302     * This presumes that {@code 'this'} instance of {@code FileNode} is not a directory, but
2303     * rather a file.  If it is not a file, then an exception shall throw.  This method also
2304     * requires that {@code 'this'} file represents a <B>text-file</B> that may be loaded into a
2305     * {@code String}.
2306     * 
2307     * <BR /><BR />This method will load the contents of {@code 'this'} file into a 
2308     * {@code java.lang.String} and then pass that {@code String} (along with {@code 'this'
2309     * FileNode} to the method {@code 'apply(FileNode, String'} provided by the
2310     * {@code BiFunction<FileNode, String>} input-parameter {@code 'f'}.
2311     *
2312     * <BR /><BR />This is the type of method that could easily be used in conjunction with Java's
2313     * {@code java.util.stream.Stream} A.P.I.  If the {@code 'f'} parameter were to include a
2314     * regular-expression for modifying the contents of a file, then by using Java-Streams a 
2315     * programmer could easily write a UNIX {@code 'SED'} or {@code 'AWK'} like script with just a
2316     * couple lines of Java.
2317     *
2318     * <BR /><BR />Presuming that an appropriate {@code 'myFunction'} were written, any one of the
2319     * following invocations could perform a UNIX {@code 'SED'} or {@code 'AWK'} type of routine on
2320     * a suite of text-files, <I><B>instantly</I></B>.
2321     *
2322     * <BR /><BR /><UL CLASS="JDUL">
2323     * <LI>{@code Stream<FileNode>.forEach(fileNode -> fileNode.apply(myFunction, myIOEHandler));}</LI>
2324     * <LI>{@code Vector<FileNode>.forEach(fileNode -> fileNode.apply(myFunction, myIOEHandler));}</LI>
2325     * <LI>{@code Stream<FileNode>.map(fileNode -> fileNode.apply(...));}</LI>
2326     * </UL>
2327     *
2328     * @param f This is the java {@code FunctionalInterface 'BiFunction'}  As a 
2329     * {@code functional-interface}, it has a method named {@code 'apply'} and this method
2330     * {@code 'apply'} receives two parameters itself: 
2331     *
2332     * <BR /><BR /><OL CLASS="JDOL">
2333     * <LI>The first Parameter shall be {@code 'this'} instance of {@code 'FileNode'}</LI>
2334     * <LI>The second Parameter shall be the file-contents on the file-system of
2335     *     {@code 'this' FileNode} - passed as a {@code java.lang.String}.</LI>
2336     * </OL>
2337     *
2338     * <BR /><B><SPAN STYLE="color: red;">EXPECTED RETURN:</B></SPAN> The
2339     * {@code functional-interface} that is passed to parameter {@code 'f'} should provide a
2340     * return-value that is a "new, updated {@code String}" that shall be used to "replace the
2341     * file-contents" of this instance of {@code FileNode}.
2342     *
2343     * <BR /><BR /><B><SPAN STYLE="color: red;">WRITE-NOTICE:</B></SPAN> This operation will
2344     * <B><I>OVER-WRITE</I></B> the current contents of a file on the file-system.  Also, this
2345     * operation treats the file as if it contained text.  Data-Files containing binary-data may
2346     * not be used with this method.  This is the <I><B>only method in class {@code 'FileNode'} that
2347     * actually will modify the underlying file-system.</I></B>
2348     *
2349     * @param ioeh This is an instance of {@code functional-interface class 'IOExceptionHandler'}.
2350     * It receives an instance of an {@code IOException}, and the programmer may insert any type
2351     * of code he wants to see happen when an {@code IOException} is thrown.  The 'added-value' of
2352     * including this handler is that when batches of {@code apply's} are performed on a
2353     * {@code FileNode}-tree, one file causing an exception throw does not have to halt the entire
2354     * batch-process.
2355     *
2356     * <BR /><BR /><B>NOTE:</B> This parameter may be null, and if it is, it shall be ignored.  It
2357     * is only invoked if it is not null, and if an exception occurs when either reading or writing
2358     * the file to/from the file-system.
2359     *
2360     * @return This method returns <B>TRUE</B> if there were no I/O faults when either reading or
2361     * writing the file.
2362     *
2363     * @throws FileExpectedException If {@code 'destinationDir'} is not a file, but a directory,
2364     * then this exception is thrown.
2365     *
2366     * @see #getFullPathName()
2367     * @see FileRW#loadFileToString(String)
2368     * @see FileRW#writeFile(CharSequence, String)
2369     * @see IOExceptionHandler#accept(FileNode, IOException)
2370     */
2371    public boolean apply(BiFunction<FileNode, String, String> f, IOExceptionHandler ioeh)
2372    {
2373        // This method can only be used with 'file' FileNode's.
2374        // FileNode's that are 'directories' do not have "text-contents" or "file-contents"
2375        FileExpectedException.check(this);
2376
2377        try
2378        {
2379            // Read 'this' file into a String
2380            String fileName     = this.toString();
2381            String fileContents = FileRW.loadFileToString(fileName);
2382
2383            // Send the contents of 'this' file to the BiFunction parameter 'f'
2384            fileContents = f.apply(this, fileContents);
2385
2386            // Re-write the new file back to the old location.
2387            FileRW.writeFile(fileContents, fileName);
2388
2389            return true;
2390        }
2391        catch (IOException e)
2392        { 
2393            // if an I/O exception did occur, send the information to the
2394            // I/O exception handler provided by the user (if and only if the
2395            // actually provided a non-null exception handler)
2396            if (ioeh != null) ioeh.accept(this, e);
2397
2398            return false;
2399        }
2400    }
2401
2402
2403    /**
2404     * This presumes that {@code 'this'} instance of {@code FileNode} is not a directory, but
2405     * rather a file.  If it is not a file, then an exception shall throw.  This method also
2406     * requires that {@code 'this'} file represents a <B>text-file</B> that may be loaded into a
2407     * {@code String}.
2408     * 
2409     * <BR /><BR />This method will load the contents of {@code 'this'} file into a 
2410     * {@code java.lang.String} and then pass that {@code String} (along with {@code 'this'
2411     * FileNode} to the method {@code 'accept(FileNode, String'} provided by the
2412     * {@code BiConsumer<FileNode, String>} input-parameter {@code 'c'}.
2413     *
2414     * @param c This is the java {@code FunctionalInterface 'BiConsumer'}.  As a
2415     * {@code functional-interface}, it has a method named {@code 'accept'} and this method
2416     * {@code 'accept'} receives two parameters itself: 
2417     *
2418     * <BR /><BR /><OL CLASS="JDOL">
2419     * <LI>The first Parameter shall be {@code 'this'} instance of {@code 'FileNode'}</LI>
2420     * <LI>The second Parameter shall be the file-contents on the file-system of
2421     *     {@code 'this' FileNode} - passed as a {@code java.lang.String}.</LI>
2422     * </OL>
2423     * 
2424     * @param ioeh This an is instance of {@code FunctionalInterface 'IOExceptionHandler'}.  It
2425     * receives an instance of an {@code IOException}, and the programmer may insert any type of
2426     * code he wants to see happen when an {@code IOException} is thrown.  The 'added-value' of
2427     * including this handler is that when batches of {@code accept's} are performed one a 
2428     * {@code FileNode}-tree, one file causing an exception throw does not have to halt the entire
2429     * batch-process.
2430     *
2431     * <BR /><BR /><B>NOTE:</B> This parameter may be null, and if it is, it shall be ignored.
2432     * It is only invoked if it is not null, and if an exception occurs when either reading or
2433     * writing the file to/from the file-system.
2434     *
2435     * @throws FileExpectedException If {@code 'destinationDir'} is not a file, but a directory,
2436     * then this exception is thrown.
2437     *
2438     * @see FileRW#loadFileToString(String)
2439     * @see #getFullPathName()
2440     * @see IOExceptionHandler#accept(FileNode, IOException)
2441     */
2442    public void accept(BiConsumer<FileNode, String> c, IOExceptionHandler ioeh)
2443    {
2444        // This method can only be used with 'file' FileNode's.
2445        // FileNode's that are 'directories' do not have "text-contents" or "file-contents"
2446        FileExpectedException.check(this);
2447
2448        try
2449            { c.accept(this, FileRW.loadFileToString(getFullPathName())); }
2450
2451        catch (IOException ioe)
2452
2453            // if an I/O exception did occur, send the information to the
2454            // I/O exception handler provided by the user (if and only if the
2455            // actually provided a non-null exception handler)
2456
2457            { if (ioeh != null) ioeh.accept(this, ioe); }
2458    }
2459
2460    /**
2461     * This presumes that {@code 'this'} instance of {@code FileNode} is not a directory, but
2462     * rather a file.  If it is not a file, then an exception shall throw.  This method also
2463     * requires that {@code 'this'} file represents a <B>text-file</B> that may be loaded into a
2464     * {@code String}.
2465     * 
2466     * <BR /><BR />This method will load the contents of {@code 'this'} file into a 
2467     * {@code java.lang.String} and then pass that {@code String} (along with {@code 'this'
2468     * FileNode} to the method {@code 'ask(FileNode, String'} provided by the
2469     * {@code BiPredicate<FileNode, String>} input-parameter {@code 'p'}.
2470     *
2471     * <BR /><BR />This is the type of method that could easily be used in conjunction with a
2472     * {@code java.util.stream.Stream} or a {@code java.util.Vector}.
2473     *
2474     * <BR /><BR /><UL CLASS="JDUL">
2475     * <LI>{@code Stream<FileNode>.filter(fileNode -> fileNode.ask(myFilterPred, myIOEHandler));}</LI>
2476     * <LI>{@code Vector<FileNode>.removeIf(fileNode -> fileNode.ask(myFilterPred, myIOEHandler));}</LI>
2477     * </UL>
2478     *
2479     * @param p This is the java {@code FunctionalInterface 'BiPredicate'}.  As a
2480     * {@code functional-interface}, it has a method named {@code 'test'} and this method
2481     * {@code 'test'} receives two parameters itself: 
2482     *
2483     * <BR /><BR /><OL CLASS="JDOL">
2484     * <LI>The first parameter shall be {@code 'this'} instance of {@code 'FileNode'}</LI>
2485     * <LI>The second parameter shall be the file-contents on the file-system of
2486     *     {@code 'this' FileNode} - passed as a {@code java.lang.String}.</LI>
2487     * </OL>
2488     *
2489     * <BR /><BR /><B>IMPORTANT:</B> The {@code functional-interface} that is passed to parameter
2490     * {@code 'p'} should provide a return {@code boolean}-value that is to act as a pass/fail
2491     * filter criteria.  It is important to note that the Java {@code Vector} method
2492     * {@code Vector.removeIf(Predicate)} and the Java method {@code Stream.filter(Predicate)} 
2493     * will produce <B><I>exactly opposite outputs</I></B> based on the same filter logic.
2494     * 
2495     * <BR /><BR />To explain further, when {@code Vector.removeIf(Predicate)} is used, the
2496     * predicate should return <B>TRUE</B> to indicate that the {@code FileNode} needs to be
2497     * eliminated not retained.  When {@code Stream.filter(Predicate)} is used, <B>TRUE</B> should
2498     * indicate that the {@code FileNode} should be retained not eliminated.
2499     *
2500     * @param ioeh This is an instance of functional-interface class, {@code IOExceptionHandler}.
2501     * It receives an instance of an {@code IOException}, and the programmer may insert any type of
2502     * code he wants to see happen when an {@code IOException} is thrown.  The 'added-value' of
2503     * including this handler is that when batches of {@code ask's} are performed on a 
2504     * {@code FileNode}-tree, one file causing an exception throw does not have to halt the entire
2505     * batch-process.
2506     *
2507     * <BR /><BR /><B>NOTE:</B> This parameter may be null, and if it is, it shall be ignored.  It
2508     * is only invoked if it is not null, and if an exception occurs when either reading or writing
2509     * the file to/from the file-system.
2510     *
2511     * @return This method returns <B>TRUE</B> if there were no I/O faults when either reading or
2512     * writing the file.
2513     *
2514     * @throws FileExpectedException If {@code 'destinationDir'} is not a file, but a directory,
2515     * then this exception is thrown.
2516     *
2517     * @see #getFullPathName()
2518     * @see FileRW#loadFileToString(String)
2519     * @see IOExceptionHandler#accept(FileNode, IOException)
2520     */
2521    public boolean ask(BiPredicate<FileNode, String> p, IOExceptionHandler ioeh)
2522    {
2523        // This method can only be used with 'file' FileNode's.
2524        // FileNode's that are 'directories' do not have "text-contents" or "file-contents"
2525        FileExpectedException.check(this);
2526
2527        try
2528            { return p.test(this, FileRW.loadFileToString(getFullPathName())); }
2529        catch (IOException ioe)
2530            { if (ioeh != null) ioeh.accept(this, ioe); return false; }
2531    }
2532
2533    /**
2534     * There are not any "Tree Structures" present in the HTML Search, Update, and Scrape Packages.
2535     * In the Java Packages, the {@code class 'FileNode'} is the lone source of "Tree Structures."
2536     * The Java Garbage Collector sometimes seems to work in mysterious ways.  The external-library
2537     * "Java-Parser" is a library that is only used for the Java-Doc Upgrader Tool.  Java Parser is
2538     * not present in anything related to HTML Search, Scrape or Update.  The Garbage Collector has
2539     * had some trouble clearing out the AST Trees that are generated by the Java-Parser import when
2540     * using the Java-Doc Upgrader Tool.  A Circular Tree is probably impossible to clean.  The
2541     * Garbage Collector for memory references has worked wonderfully well with 100% of
2542     * vectorized-html pages that have been generated <I>although not so much for the "Java Parser
2543     * Bridge" routines</I>.
2544     *
2545     * <BR /><BR />This method will 'null-ify' all references (pointers) in a
2546     * {@code 'FileNode'}-Tree.  The {@code FileNode}-Tree can be a great asset or tool during the
2547     * development process when looking through file-contents and trying to modify them - <I>or
2548     * just find files with certain characteristics.</I>
2549     *
2550     * @see #getDirContents()
2551     * @see #getDirContentsDirs()
2552     * @see #parent
2553     * @see #children
2554     */
2555    public void NULL_THE_TREE()
2556    {
2557        Iterator<FileNode> iter = getDirContents(RetTypeChoice.ITERATOR);
2558        while (iter.hasNext()) iter.next().parent = null;
2559
2560        iter = getDirContentsDirs(RetTypeChoice.ITERATOR);
2561        while (iter.hasNext()) iter.next().NULL_THE_TREE();
2562
2563        children.clear();
2564    }
2565
2566    /**
2567     * <CODE>FileNode RetTypeChoice - Documentation.</CODE>
2568     * <EMBED CLASS="external-html" DATA-FILE-ID="FNRTC">
2569     */
2570    public static class RetTypeChoice
2571    {
2572        /** {@code Comparator} for sorting instances of {@code FileNode} by their {@code 'lastModified'} field. */
2573        protected static final Comparator<FileNode> BY_DATE         = Comparator.comparingLong((FileNode fn) -> fn.lastModified);
2574        /** {@code Comparator} for sorting instances of {@code FileNode} by their {@code 'fileSize'} field. */
2575        protected static final Comparator<FileNode> BY_FILE_SIZE    = Comparator.comparingLong((FileNode fn) -> fn.fileSize);
2576        /** {@code Comparator} for sorting instances of {@code FileNode} by the results of method {@code FileNode.getFullPathName()} */
2577        protected static final Comparator<FileNode> BY_FULLPATH     = (FileNode fn1, FileNode fn2) -> fn1.getFullPathName().compareTo(fn2.getFullPathName());
2578        /** {@code Comparator} for sorting instances of {@code FileNode} by their {@code 'name'} field. */
2579        protected static final Comparator<FileNode> BY_FILENAME     = (FileNode fn1, FileNode fn2) -> fn1.name.compareTo(fn2.name);
2580
2581        private static final VarListBuilder<FileNode> vlb = new VarListBuilder<>(FileNode.class);
2582
2583        /** Asks a method to return {@code Vector<FileNode>} */
2584        public static final VarList<Vector<FileNode>,           FileNode>   VECTOR          = vlb.VECTOR;
2585        /** Asks a method to return {@code ArrayList<FileNode>} */
2586        public static final VarList<ArrayList<FileNode>,        FileNode>   ARRAYLIST       = vlb.ARRAYLIST;
2587        /** Asks a method to return {@code Stream<FileNode>} */
2588        public static final VarList<Stream<FileNode>,           FileNode>   STREAM          = vlb.STREAM;
2589        /** Asks a method to return {@code Stream.Builder<FileNode>} */
2590        public static final VarList<Stream.Builder<FileNode>,   FileNode>   STREAM_BUILDER  = vlb.STREAM_BUILDER;
2591        /** Asks a method to return {@code FileNode[]} */
2592        public static final VarList<FileNode[],                 FileNode>   ARRAY           = vlb.ARRAY;
2593        /** Asks a method to return {@code Iterator<FileNode>} */
2594        public static final VarList<Iterator<FileNode>,         FileNode>   ITERATOR        = vlb.ITERATOR;
2595        /** Asks a method to return {@code HashSet<FileNode>} */
2596        public static final VarList<HashSet<FileNode>,          FileNode>   HASHSET         = vlb.HASHSET;
2597        /** Asks a method to return {@code LinkedList<FileNode>} */
2598        public static final VarList<LinkedList<FileNode>,       FileNode>   LINKEDLIST      = vlb.LINKEDLIST;
2599        /** Asks a method to return {@code Stack<FileNode>} */
2600        public static final VarList<Stack<FileNode>,            FileNode>   STACK           = vlb.STACK;
2601
2602        private static final VarListBuilderWithSort<FileNode> vlbws1 = new VarListBuilderWithSort<>
2603            (BY_DATE, FileNode.class);
2604
2605        /** Asks a method to return {@code Vector<FileNode>}, sorted by {@link FileNode#lastModified} */
2606        public static final VarList<Vector<FileNode>,           FileNode>   SORTED_BY_DATE_VECTOR           = vlbws1.VECTOR;
2607        /** Asks a method to return {@code ArrayList<FileNode>}, sorted by {@link FileNode#lastModified} */
2608        public static final VarList<ArrayList<FileNode>,        FileNode>   SORTED_BY_DATE_ARRAYLIST        = vlbws1.ARRAYLIST;
2609        /** Asks a method to return {@code Stream<FileNode>}, sorted by {@link FileNode#lastModified} */
2610        public static final VarList<Stream<FileNode>,           FileNode>   SORTED_BY_DATE_STREAM           = vlbws1.STREAM;
2611        /** Asks a method to return {@code Stream.Builder<FileNode>}, sorted by {@link FileNode#lastModified} */
2612        public static final VarList<Stream.Builder<FileNode>,   FileNode>   SORTED_BY_DATE_STREAM_BUILDER   = vlbws1.STREAM_BUILDER;
2613        /** Asks a method to return {@code FileNode[]}, sorted by {@link FileNode#lastModified} */
2614        public static final VarList<FileNode[],                 FileNode>   SORTED_BY_DATE_ARRAY            = vlbws1.ARRAY;
2615        /** Asks a method to return {@code Iterator<FileNode>}, sorted by {@link FileNode#lastModified} */
2616        public static final VarList<Iterator<FileNode>,         FileNode>   SORTED_BY_DATE_ITERATOR         = vlbws1.ITERATOR;
2617        /** Asks a method to return {@code TreeSet<FileNode>}, sorted by {@link FileNode#lastModified} */
2618        public static final VarList<TreeSet<FileNode>,          FileNode>   SORTED_BY_DATE_TREESET          = vlbws1.TREESET;
2619        /** Asks a method to return {@code LinkedList<FileNode>}, sorted by {@link FileNode#lastModified} */
2620        public static final VarList<LinkedList<FileNode>,       FileNode>   SORTED_BY_DATE_LINKEDLIST       = vlbws1.LINKEDLIST;
2621        /** Asks a method to return {@code Stack<FileNode>}, sorted by {@link FileNode#lastModified} */
2622        public static final VarList<Stack<FileNode>,            FileNode>   SORTED_BY_DATE_STACK            = vlbws1.STACK;
2623
2624        private static final VarListBuilderWithSort<FileNode> vlbws2 = new VarListBuilderWithSort<>
2625            (BY_FILE_SIZE, FileNode.class);
2626
2627        /** Asks a method to return {@code Vector<FileNode>}, sorted by {@link FileNode#fileSize} */
2628        public static final VarList<Vector<FileNode>,           FileNode>   SORTED_BY_SIZE_VECTOR           = vlbws2.VECTOR;
2629        /** Asks a method to return {@code ArrayList<FileNode>}, sorted by {@link FileNode#fileSize} */
2630        public static final VarList<ArrayList<FileNode>,        FileNode>   SORTED_BY_SIZE_ARRAYLIST        = vlbws2.ARRAYLIST;
2631        /** Asks a method to return {@code Stream<FileNode>}, sorted by {@link FileNode#fileSize} */
2632        public static final VarList<Stream<FileNode>,           FileNode>   SORTED_BY_SIZE_STREAM           = vlbws2.STREAM;
2633        /** Asks a method to return {@code Stream.Builder<FileNode>}, sorted by {@link FileNode#fileSize} */
2634        public static final VarList<Stream.Builder<FileNode>,   FileNode>   SORTED_BY_SIZE_STREAM_BUILDER   = vlbws2.STREAM_BUILDER;
2635        /** Asks a method to return {@code FileNode[]}, sorted by {@link FileNode#fileSize} */
2636        public static final VarList<FileNode[],                 FileNode>   SORTED_BY_SIZE_ARRAY            = vlbws2.ARRAY;
2637        /** Asks a method to return {@code Iterator<FileNode>}, sorted by {@link FileNode#fileSize} */
2638        public static final VarList<Iterator<FileNode>,         FileNode>   SORTED_BY_SIZE_ITERATOR         = vlbws2.ITERATOR;
2639        /** Asks a method to return {@code TreeSet<FileNode>}, sorted by {@link FileNode#fileSize} */
2640        public static final VarList<TreeSet<FileNode>,          FileNode>   SORTED_BY_SIZE_TREESET          = vlbws2.TREESET;
2641        /** Asks a method to return {@code LinkedList<FileNode>}, sorted by {@link FileNode#fileSize} */
2642        public static final VarList<LinkedList<FileNode>,       FileNode>   SORTED_BY_SIZE_LINKEDLIST       = vlbws2.LINKEDLIST;
2643        /** Asks a method to return {@code Stack<FileNode>}, sorted by {@link FileNode#fileSize} */
2644        public static final VarList<Stack<FileNode>,            FileNode>   SORTED_BY_SIZE_STACK            = vlbws2.STACK;
2645
2646        private static final VarListBuilderWithSort<FileNode> vlbws3 = new VarListBuilderWithSort<>
2647            (BY_FULLPATH, FileNode.class);
2648
2649        /** Asks a method to return {@code Vector<FileNode>}, sorted by {@link FileNode#getFullPathName()} */
2650        public static final VarList<Vector<FileNode>,           FileNode>   SORTED_BY_FULLPATH_VECTOR           = vlbws3.VECTOR;
2651        /** Asks a method to return {@code ArrayList<FileNode>}, sorted by {@link FileNode#getFullPathName()} */
2652        public static final VarList<ArrayList<FileNode>,        FileNode>   SORTED_BY_FULLPATH_ARRAYLIST        = vlbws3.ARRAYLIST;
2653        /** Asks a method to return {@code Stream<FileNode>}, sorted by {@link FileNode#getFullPathName()} */
2654        public static final VarList<Stream<FileNode>,           FileNode>   SORTED_BY_FULLPATH_STREAM           = vlbws3.STREAM;
2655        /** Asks a method to return {@code Stream.Builder<FileNode>}, sorted by {@link FileNode#getFullPathName()} */
2656        public static final VarList<Stream.Builder<FileNode>,   FileNode>   SORTED_BY_FULLPATH_STREAM_BUILDER   = vlbws3.STREAM_BUILDER;
2657        /** Asks a method to return {@code FileNode[]}, sorted by {@link FileNode#getFullPathName()} */
2658        public static final VarList<FileNode[],                 FileNode>   SORTED_BY_FULLPATH_ARRAY            = vlbws3.ARRAY;
2659        /** Asks a method to return {@code Iterator<FileNode>}, sorted by {@link FileNode#getFullPathName()} */
2660        public static final VarList<Iterator<FileNode>,         FileNode>   SORTED_BY_FULLPATH_ITERATOR         = vlbws3.ITERATOR;
2661        /** Asks a method to return {@code TreeSet<FileNode>}, sorted by {@link FileNode#getFullPathName()} */
2662        public static final VarList<TreeSet<FileNode>,          FileNode>   SORTED_BY_FULLPATH_TREESET          = vlbws3.TREESET;
2663        /** Asks a method to return {@code LinkedList<FileNode>}, sorted by {@link FileNode#getFullPathName()} */
2664        public static final VarList<LinkedList<FileNode>,       FileNode>   SORTED_BY_FULLPATH_LINKEDLIST       = vlbws3.LINKEDLIST;
2665        /** Asks a method to return {@code Stack<FileNode>}, sorted by {@link FileNode#getFullPathName()} */
2666        public static final VarList<Stack<FileNode>,            FileNode>   SORTED_BY_FULLPATH_STACK            = vlbws3.STACK;
2667
2668        private static final VarListBuilderWithSort<FileNode> vlbws4 = new VarListBuilderWithSort<>
2669            (BY_FILENAME, FileNode.class);
2670
2671        /** Asks a method to return {@code Vector<FileNode>}, sorted by {@link FileNode#name} */
2672        public static final VarList<Vector<FileNode>,           FileNode>   SORTED_BY_FILENAME_VECTOR           = vlbws4.VECTOR;
2673        /** Asks a method to return {@code ArrayList<FileNode>}, sorted by {@link FileNode#name} */
2674        public static final VarList<ArrayList<FileNode>,        FileNode>   SORTED_BY_FILENAME_ARRAYLIST        = vlbws4.ARRAYLIST;
2675        /** Asks a method to return {@code Stream<FileNode>}, sorted by {@link FileNode#name} */
2676        public static final VarList<Stream<FileNode>,           FileNode>   SORTED_BY_FILENAME_STREAM           = vlbws4.STREAM;
2677        /** Asks a method to return {@code Stream.Builder<FileNode>}, sorted by {@link FileNode#name} */
2678        public static final VarList<Stream.Builder<FileNode>,   FileNode>   SORTED_BY_FILENAME_STREAM_BUILDER   = vlbws4.STREAM_BUILDER;
2679        /** Asks a method to return {@code FileNode[]}, sorted by {@link FileNode#name} */
2680        public static final VarList<FileNode[],                 FileNode>   SORTED_BY_FILENAME_ARRAY            = vlbws4.ARRAY;
2681        /** Asks a method to return {@code Iterator<FileNode>}, sorted by {@link FileNode#name} */
2682        public static final VarList<Iterator<FileNode>,         FileNode>   SORTED_BY_FILENAME_ITERATOR         = vlbws4.ITERATOR;
2683        /** Asks a method to return {@code TreeSet<FileNode>}, sorted by {@link FileNode#name} */
2684        public static final VarList<TreeSet<FileNode>,          FileNode>   SORTED_BY_FILENAME_TREESET          = vlbws4.TREESET;
2685        /** Asks a method to return {@code LinkedList<FileNode>}, sorted by {@link FileNode#name} */
2686        public static final VarList<LinkedList<FileNode>,       FileNode>   SORTED_BY_FILENAME_LINKEDLIST       = vlbws4.LINKEDLIST;
2687        /** Asks a method to return {@code Stack<FileNode>}, sorted by {@link FileNode#name} */
2688        public static final VarList<Stack<FileNode>,            FileNode>   SORTED_BY_FILENAME_STACK            = vlbws4.STACK;
2689
2690        private static final VarListBuilderWithApply<FileNode, String> vlbwa1 = new VarListBuilderWithApply<>
2691            ((FileNode fn) -> fn.name, String.class);
2692
2693        /** Asks a method to return {@code Vector<String>} where {@code String} is retrieved from {@link FileNode#name} */
2694        public static final VarList<Vector<String>,         FileNode>   FILENAME_VECTOR         = vlbwa1.VECTOR;
2695        /** Asks a method to return {@code ArrayList<String>} where {@code String} is retrieved from {@link FileNode#name} */
2696        public static final VarList<ArrayList<String>,      FileNode>   FILENAME_ARRAYLIST      = vlbwa1.ARRAYLIST;
2697        /** Asks a method to return {@code Stream<String>} where {@code String} is retrieved from {@link FileNode#name} */
2698        public static final VarList<Stream<String>,         FileNode>   FILENAME_STREAM         = vlbwa1.STREAM;
2699        /** Asks a method to return {@code Stream.Builder<String>} where {@code String} is retrieved from {@link FileNode#name} */
2700        public static final VarList<Stream.Builder<String>, FileNode>   FILENAME_STREAM_BUILDER = vlbwa1.STREAM_BUILDER;
2701        /** Asks a method to return {@code String[]} where {@code String} is retrieved from {@link FileNode#name} */
2702        public static final VarList<String[],               FileNode>   FILENAME_ARRAY          = vlbwa1.ARRAY;
2703        /** Asks a method to return {@code Iterator<String>} where {@code String} is retrieved from {@link FileNode#name} */
2704        public static final VarList<Iterator<String>,       FileNode>   FILENAME_ITERATOR       = vlbwa1.ITERATOR;
2705        /** Asks a method to return {@code HashSet<String>} where {@code String} is retrieved from {@link FileNode#name} */
2706        public static final VarList<HashSet<String>,        FileNode>   FILENAME_HASHSET        = vlbwa1.HASHSET;
2707        /** Asks a method to return {@code LinkedList<String>} where {@code String} is retrieved from {@link FileNode#name} */
2708        public static final VarList<LinkedList<String>,     FileNode>   FILENAME_LINKEDLIST     = vlbwa1.LINKEDLIST;
2709        /** Asks a method to return {@code Stack<String>} where {@code String} is retrieved from {@link FileNode#name} */
2710        public static final VarList<Stack<String>,          FileNode>   FILENAME_STACK          = vlbwa1.STACK;
2711
2712        private static final VarListBuilderWithApply<FileNode, String> vlbwa2 = new VarListBuilderWithApply<>
2713            ((FileNode fn) -> fn.getFullPathName(), String.class);
2714
2715        /** Asks a method to return {@code Vector<String>} where {@code String} is retrieved from {@link FileNode#getFullPathName()} */
2716        public static final VarList<Vector<String>,         FileNode>   FULLPATH_VECTOR         = vlbwa2.VECTOR;
2717        /** Asks a method to return {@code ArrayList<String>} where {@code String} is retrieved from {@link FileNode#getFullPathName()} */
2718        public static final VarList<ArrayList<String>,      FileNode>   FULLPATH_ARRAYLIST      = vlbwa2.ARRAYLIST;
2719        /** Asks a method to return {@code Stream<String>} where {@code String} is retrieved from {@link FileNode#getFullPathName()} */
2720        public static final VarList<Stream<String>,         FileNode>   FULLPATH_STREAM         = vlbwa2.STREAM;
2721        /** Asks a method to return {@code Stream.Builder<String>} where {@code String} is retrieved from {@link FileNode#getFullPathName()} */
2722        public static final VarList<Stream.Builder<String>, FileNode>   FULLPATH_STREAM_BUILDER = vlbwa2.STREAM_BUILDER;
2723        /** Asks a method to return {@code String[]} where {@code String} is retrieved from {@link FileNode#getFullPathName()} */
2724        public static final VarList<String[],               FileNode>   FULLPATH_ARRAY          = vlbwa2.ARRAY;
2725        /** Asks a method to return {@code Iterator<String>} where {@code String} is retrieved from {@link FileNode#getFullPathName()} */
2726        public static final VarList<Iterator<String>,       FileNode>   FULLPATH_ITERATOR       = vlbwa2.ITERATOR;
2727        /** Asks a method to return {@code HashSet<String>} where {@code String} is retrieved from {@link FileNode#getFullPathName()} */
2728        public static final VarList<HashSet<String>,        FileNode>   FULLPATH_HASHSET        = vlbwa2.HASHSET;
2729        /** Asks a method to return {@code LinkedList<String>} where {@code String} is retrieved from {@link FileNode#getFullPathName()} */
2730        public static final VarList<LinkedList<String>,     FileNode>   FULLPATH_LINKEDLIST     = vlbwa2.LINKEDLIST;
2731        /** Asks a method to return {@code Stack<String>} where {@code String} is retrieved from {@link FileNode#getFullPathName()} */
2732        public static final VarList<Stack<String>,          FileNode>   FULLPATH_STACK          = vlbwa2.STACK;
2733
2734        private static final VarListBuilderWithApplyAndSort<FileNode, String> vlbwaas1 = new VarListBuilderWithApplyAndSort<>
2735            ((FileNode fn) -> fn.name, String::compareTo, String.class);
2736
2737        /** Asks a method to return a <I><B>sorted</B></I> {@code Vector<String>} where {@code String} derives from {@link FileNode#name} */
2738        public static final VarList<Vector<String>,         FileNode>   SORTED_FILENAME_VECTOR              = vlbwaas1.VECTOR;
2739        /** Asks a method to return a <I><B>sorted</B></I> {@code ArrayList<String>} where {@code String} derives from {@link FileNode#name} */
2740        public static final VarList<ArrayList<String>,      FileNode>   SORTED_FILENAME_ARRAYLIST           = vlbwaas1.ARRAYLIST;
2741        /** Asks a method to return a <I><B>sorted</B></I> {@code Stream<String>} where {@code String} derives from {@link FileNode#name} */
2742        public static final VarList<Stream<String>,         FileNode>   SORTED_FILENAME_STREAM              = vlbwaas1.STREAM;
2743        /** Asks a method to return a <I><B>sorted</B></I> {@code Stream.Builder<String>} where {@code String} derives from {@link FileNode#name} */
2744        public static final VarList<Stream.Builder<String>, FileNode>   SORTED_FILENAME_STREAM_BUILDER   = vlbwaas1.STREAM_BUILDER;
2745        /** Asks a method to return a <I><B>sorted</B></I> {@code String[]} where {@code String} derives from {@link FileNode#name} */
2746        public static final VarList<String[],               FileNode>   SORTED_FILENAME_ARRAY               = vlbwaas1.ARRAY;
2747        /** Asks a method to return a <I><B>sorted</B></I> {@code Iterator<String>} where {@code String} derives from {@link FileNode#name} */
2748        public static final VarList<Iterator<String>,       FileNode>   SORTED_FILENAME_ITERATOR            = vlbwaas1.ITERATOR;
2749        /** Asks a method to return a <I><B>sorted</B></I> {@code TreeSet<String>} where {@code String} derives from {@link FileNode#name} */
2750        public static final VarList<TreeSet<String>,        FileNode>   SORTED_FILENAME_TREESET             = vlbwaas1.TREESET;
2751        /** Asks a method to return a <I><B>sorted</B></I> {@code LinkedList<String>} where {@code String} derives from {@link FileNode#name} */
2752        public static final VarList<LinkedList<String>,     FileNode>   SORTED_FILENAME_LINKEDLIST          = vlbwaas1.LINKEDLIST;
2753        /** Asks a method to return a <I><B>sorted</B></I> {@code Stack<String>} where {@code String} derives from {@link FileNode#name} */
2754        public static final VarList<Stack<String>,          FileNode>   SORTED_FILENAME_STACK               = vlbwaas1.STACK;
2755
2756        private static final VarListBuilderWithApplyAndSort<FileNode, String> vlbwaas2 = new VarListBuilderWithApplyAndSort<>
2757            ((FileNode fn) -> fn.getFullPathName(), String::compareTo, String.class);
2758
2759        /** Asks a method to return a <I><B>sorted</B></I> {@code Vector<String>} where {@code String} derives from {@link FileNode#getFullPathName()} */
2760        public static final VarList<Vector<String>,         FileNode>   SORTED_FULLPATH_VECTOR          = vlbwaas2.VECTOR;
2761        /** Asks a method to return a <I><B>sorted</B></I> {@code ArrayList<String>} where {@code String} derives from {@link FileNode#getFullPathName()} */
2762        public static final VarList<ArrayList<String>,      FileNode>   SORTED_FULLPATH_ARRAYLIST       = vlbwaas2.ARRAYLIST;
2763        /** Asks a method to return a <I><B>sorted</B></I> {@code Stream<String>} where {@code String} derives from {@link FileNode#getFullPathName()} */
2764        public static final VarList<Stream<String>,         FileNode>   SORTED_FULLPATH_STREAM          = vlbwaas2.STREAM;
2765        /** Asks a method to return a <I><B>sorted</B></I> {@code Stream.Builder<String>} where {@code String} derives from {@link FileNode#getFullPathName()} */
2766        public static final VarList<Stream.Builder<String>, FileNode>   SORTED_FULLPATH_STREAM_BUILDER  = vlbwaas2.STREAM_BUILDER;
2767        /** Asks a method to return a <I><B>sorted</B></I> {@code String[]} where {@code String} derives from {@link FileNode#getFullPathName()} */
2768        public static final VarList<String[],               FileNode>   SORTED_FULLPATH_ARRAY           = vlbwaas2.ARRAY;
2769        /** Asks a method to return a <I><B>sorted</B></I> {@code Iterator<String>} where {@code String} derives from {@link FileNode#getFullPathName()} */
2770        public static final VarList<Iterator<String>,       FileNode>   SORTED_FULLPATH_ITERATOR        = vlbwaas2.ITERATOR;
2771        /** Asks a method to return a <I><B>sorted</B></I> {@code TreeSet<String>} where {@code String} derives from {@link FileNode#getFullPathName()} */
2772        public static final VarList<TreeSet<String>,        FileNode>   SORTED_FULLPATH_TREESET         = vlbwaas2.TREESET;
2773        /** Asks a method to return a <I><B>sorted</B></I> {@code LinkedList<String>} where {@code String} derives from {@link FileNode#getFullPathName()} */
2774        public static final VarList<LinkedList<String>,     FileNode>   SORTED_FULLPATH_LINKEDLIST      = vlbwaas2.LINKEDLIST;
2775        /** Asks a method to return a <I><B>sorted</B></I> {@code Stack<String>} where {@code String} derives from {@link FileNode#getFullPathName()} */
2776        public static final VarList<Stack<String>,          FileNode>   SORTED_FULLPATH_STACK           = vlbwaas2.STACK;
2777
2778        private static final VarListBuilderWithSortAndApply<FileNode, String> vlbwsaa1 = new VarListBuilderWithSortAndApply<>
2779            (BY_DATE, (FileNode fn) -> fn.name, String.class);
2780
2781        /** Asks a method to return a <I><B>sorted</B></I> {@code Vector<String>} where {@code String} derives from {@link FileNode#name}.  Contents are sorted by {@link FileNode#lastModified} */
2782        public static final VarList<Vector<String>,         FileNode>   SORTED_BY_DATE_FILENAME_VECTOR          = vlbwsaa1.VECTOR;
2783        /** Asks a method to return a <I><B>sorted</B></I> {@code ArrayList<String>} where {@code String} derives from {@link FileNode#name}.  Contents are sorted by {@link FileNode#lastModified} */
2784        public static final VarList<ArrayList<String>,      FileNode>   SORTED_BY_DATE_FILENAME_ARRAYLIST       = vlbwsaa1.ARRAYLIST;
2785        /** Asks a method to return a <I><B>sorted</B></I> {@code Stream<String>} where {@code String} derives from {@link FileNode#name}.  Contents are sorted by {@link FileNode#lastModified} */
2786        public static final VarList<Stream<String>,         FileNode>   SORTED_BY_DATE_FILENAME_STREAM          = vlbwsaa1.STREAM;
2787        /** Asks a method to return a <I><B>sorted</B></I> {@code Stream.Builder<String>} where {@code String} derives from {@link FileNode#name}.  Contents are sorted by {@link FileNode#lastModified} */
2788        public static final VarList<Stream.Builder<String>, FileNode>   SORTED_BY_DATE_FILENAME_STREAM_BUILDER  = vlbwsaa1.STREAM_BUILDER;
2789        /** Asks a method to return a <I><B>sorted</B></I> {@code String[]} where {@code String} derives from {@link FileNode#name}.  Contents are sorted by {@link FileNode#lastModified} */
2790        public static final VarList<String[],               FileNode>   SORTED_BY_DATE_FILENAME_ARRAY           = vlbwsaa1.ARRAY;
2791        /** Asks a method to return a <I><B>sorted</B></I> {@code Iterator<String>} where {@code String} derives from {@link FileNode#name}.  Contents are sorted by {@link FileNode#lastModified} */
2792        public static final VarList<Iterator<String>,       FileNode>   SORTED_BY_DATE_FILENAME_ITERATOR        = vlbwsaa1.ITERATOR;
2793        /** Asks a method to return a <I><B>sorted</B></I> {@code LinkedList<String>} where {@code String} derives from {@link FileNode#name}.  Contents are sorted by {@link FileNode#lastModified} */
2794        public static final VarList<LinkedList<String>,     FileNode>   SORTED_BY_DATE_FILENAME_LINKEDLIST      = vlbwsaa1.LINKEDLIST;
2795        /** Asks a method to return a <I><B>sorted</B></I> {@code Stack<String>} where {@code String} derives from {@link FileNode#name}.  Contents are sorted by {@link FileNode#lastModified} */
2796        public static final VarList<Stack<String>,          FileNode>   SORTED_BY_DATE_FILENAME_STACK           = vlbwsaa1.STACK;
2797
2798        private static final VarListBuilderWithSortAndApply<FileNode, String> vlbwsaa2 = new VarListBuilderWithSortAndApply<>
2799            (BY_DATE, (FileNode fn) -> fn.getFullPathName(), String.class);
2800
2801        /** Asks a method to return a <I><B>sorted</B></I> {@code Vector<String>} where {@code String} derives from {@link FileNode#getFullPathName()}.  Contents are sorted by {@link FileNode#lastModified} */
2802        public static final VarList<Vector<String>,         FileNode>   SORTED_BY_DATE_FULLPATH_VECTOR          = vlbwsaa2.VECTOR;
2803        /** Asks a method to return a <I><B>sorted</B></I> {@code ArrayList<String>} where {@code String} derives from {@link FileNode#getFullPathName()}.  Contents are sorted by {@link FileNode#lastModified} */
2804        public static final VarList<ArrayList<String>,      FileNode>   SORTED_BY_DATE_FULLPATH_ARRAYLIST       = vlbwsaa2.ARRAYLIST;
2805        /** Asks a method to return a <I><B>sorted</B></I> {@code Stream<String>} where {@code String} derives from {@link FileNode#getFullPathName()}.  Contents are sorted by {@link FileNode#lastModified} */
2806        public static final VarList<Stream<String>,         FileNode>   SORTED_BY_DATE_FULLPATH_STREAM          = vlbwsaa2.STREAM;
2807        /** Asks a method to return a <I><B>sorted</B></I> {@code Stream.Builder<String>} where {@code String} derives from {@link FileNode#getFullPathName()}.  Contents are sorted by {@link FileNode#lastModified} */
2808        public static final VarList<Stream.Builder<String>, FileNode>   SORTED_BY_DATE_FULLPATH_STREAM_BUILDER  = vlbwsaa2.STREAM_BUILDER;
2809        /** Asks a method to return a <I><B>sorted</B></I> {@code String[]} where {@code String} derives from {@link FileNode#getFullPathName()}.  Contents are sorted by {@link FileNode#lastModified} */
2810        public static final VarList<String[],               FileNode>   SORTED_BY_DATE_FULLPATH_ARRAY           = vlbwsaa2.ARRAY;
2811        /** Asks a method to return a <I><B>sorted</B></I> {@code Iterator<String>} where {@code String} derives from {@link FileNode#getFullPathName()}.  Contents are sorted by {@link FileNode#lastModified} */
2812        public static final VarList<Iterator<String>,       FileNode>   SORTED_BY_DATE_FULLPATH_ITERATOR        = vlbwsaa2.ITERATOR;
2813        /** Asks a method to return a <I><B>sorted</B></I> {@code LinkedList<String>} where {@code String} derives from {@link FileNode#getFullPathName()}.  Contents are sorted by {@link FileNode#lastModified} */
2814        public static final VarList<LinkedList<String>,     FileNode>   SORTED_BY_DATE_FULLPATH_LINKEDLIST      = vlbwsaa2.LINKEDLIST;
2815        /** Asks a method to return a <I><B>sorted</B></I> {@code Stack<String>} where {@code String} derives from {@link FileNode#getFullPathName()}.  Contents are sorted by {@link FileNode#lastModified} */
2816        public static final VarList<Stack<String>,          FileNode>   SORTED_BY_DATE_FULLPATH_STACK           = vlbwsaa2.STACK;
2817
2818        private static final VarListBuilderWithSortAndApply<FileNode, String> vlbwsaa3 = new VarListBuilderWithSortAndApply<>
2819            (BY_FILE_SIZE, (FileNode fn) -> fn.name, String.class);
2820
2821        /** Asks a method to return a <I><B>sorted</B></I> {@code Vector<String>} where {@code String} derives from {@link FileNode#name}.  Contents are sorted by {@link FileNode#fileSize} */
2822        public static final VarList<Vector<String>,         FileNode>   SORTED_BY_SIZE_FILENAME_VECTOR          = vlbwsaa3.VECTOR;
2823        /** Asks a method to return a <I><B>sorted</B></I> {@code ArrayList<String>} where {@code String} derives from {@link FileNode#name}.  Contents are sorted by {@link FileNode#fileSize} */
2824        public static final VarList<ArrayList<String>,      FileNode>   SORTED_BY_SIZE_FILENAME_ARRAYLIST       = vlbwsaa3.ARRAYLIST;
2825        /** Asks a method to return a <I><B>sorted</B></I> {@code Stream<String>} where {@code String} derives from {@link FileNode#name}.  Contents are sorted by {@link FileNode#fileSize} */
2826        public static final VarList<Stream<String>,         FileNode>   SORTED_BY_SIZE_FILENAME_STREAM          = vlbwsaa3.STREAM;
2827        /** Asks a method to return a <I><B>sorted</B></I> {@code Stream.Builder<String>} where {@code String} derives from {@link FileNode#name}.  Contents are sorted by {@link FileNode#fileSize} */
2828        public static final VarList<Stream.Builder<String>, FileNode>   SORTED_BY_SIZE_FILENAME_STREAM_BUILDER  = vlbwsaa3.STREAM_BUILDER;
2829        /** Asks a method to return a <I><B>sorted</B></I> {@code String[]} where {@code String} derives from {@link FileNode#name}.  Contents are sorted by {@link FileNode#fileSize} */
2830        public static final VarList<String[],               FileNode>   SORTED_BY_SIZE_FILENAME_ARRAY           = vlbwsaa3.ARRAY;
2831        /** Asks a method to return a <I><B>sorted</B></I> {@code Iterator<String>} where {@code String} derives from {@link FileNode#name}.  Contents are sorted by {@link FileNode#fileSize} */
2832        public static final VarList<Iterator<String>,       FileNode>   SORTED_BY_SIZE_FILENAME_ITERATOR        = vlbwsaa3.ITERATOR;
2833        /** Asks a method to return a <I><B>sorted</B></I> {@code LinkedList<String>} where {@code String} derives from {@link FileNode#name}.  Contents are sorted by {@link FileNode#fileSize} */
2834        public static final VarList<LinkedList<String>,     FileNode>   SORTED_BY_SIZE_FILENAME_LINKEDLIST      = vlbwsaa3.LINKEDLIST;
2835        /** Asks a method to return a <I><B>sorted</B></I> {@code Stack<String>} where {@code String} derives from {@link FileNode#name}.  Contents are sorted by {@link FileNode#fileSize} */
2836        public static final VarList<Stack<String>,          FileNode>   SORTED_BY_SIZE_FILENAME_STACK           = vlbwsaa3.STACK;
2837
2838        private static final VarListBuilderWithSortAndApply<FileNode, String> vlbwsaa4 = new VarListBuilderWithSortAndApply<>
2839            (BY_FILE_SIZE, (FileNode fn) -> fn.getFullPathName(), String.class);
2840
2841        /** Asks a method to return a <I><B>sorted</B></I> {@code Vector<String>} where {@code String} derives from {@link FileNode#getFullPathName()}.  Contents are sorted by {@link FileNode#fileSize} */
2842        public static final VarList<Vector<String>,         FileNode>   SORTED_BY_SIZE_FULLPATH_VECTOR          = vlbwsaa4.VECTOR;
2843        /** Asks a method to return a <I><B>sorted</B></I> {@code ArrayList<String>} where {@code String} derives from {@link FileNode#getFullPathName()}.  Contents are sorted by {@link FileNode#fileSize} */
2844        public static final VarList<ArrayList<String>,      FileNode>   SORTED_BY_SIZE_FULLPATH_ARRAYLIST       = vlbwsaa4.ARRAYLIST;
2845        /** Asks a method to return a <I><B>sorted</B></I> {@code Stream<String>} where {@code String} derives from {@link FileNode#getFullPathName()}.  Contents are sorted by {@link FileNode#fileSize} */
2846        public static final VarList<Stream<String>,         FileNode>   SORTED_BY_SIZE_FULLPATH_STREAM          = vlbwsaa4.STREAM;
2847        /** Asks a method to return a <I><B>sorted</B></I> {@code Stream.Builder<String>} where {@code String} derives from {@link FileNode#getFullPathName()}.  Contents are sorted by {@link FileNode#fileSize} */
2848        public static final VarList<Stream.Builder<String>, FileNode>   SORTED_BY_SIZE_FULLPATH_STREAM_BUILDER  = vlbwsaa4.STREAM_BUILDER;
2849        /** Asks a method to return a <I><B>sorted</B></I> {@code String<String>} where {@code String} derives from {@link FileNode#getFullPathName()}.  Contents are sorted by {@link FileNode#fileSize} */
2850        public static final VarList<String[],               FileNode>   SORTED_BY_SIZE_FULLPATH_ARRAY           = vlbwsaa4.ARRAY;
2851        /** Asks a method to return a <I><B>sorted</B></I> {@code Iterator<String>} where {@code String} derives from {@link FileNode#getFullPathName()}.  Contents are sorted by {@link FileNode#fileSize} */
2852        public static final VarList<Iterator<String>,       FileNode>   SORTED_BY_SIZE_FULLPATH_ITERATOR        = vlbwsaa4.ITERATOR;
2853        /** Asks a method to return a <I><B>sorted</B></I> {@code LinkedList<String>} where {@code String} derives from {@link FileNode#getFullPathName()}.  Contents are sorted by {@link FileNode#fileSize} */
2854        public static final VarList<LinkedList<String>,     FileNode>   SORTED_BY_SIZE_FULLPATH_LINKEDLIST      = vlbwsaa4.LINKEDLIST;
2855        /** Asks a method to return a <I><B>sorted</B></I> {@code Stack<String>} where {@code String} derives from {@link FileNode#getFullPathName()}.  Contents are sorted by {@link FileNode#fileSize} */
2856        public static final VarList<Stack<String>,          FileNode>   SORTED_BY_SIZE_FULLPATH_STACK           = vlbwsaa4.STACK;
2857    }
2858
2859}