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