1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
package Torello.Java;

import Torello.Java.StrFilter;

import java.util.function.Predicate;
import java.util.Iterator;

/**
 * A simple functional-interface (lambda) for filtering lists of <CODE>FileNode</CODE>.
 * 
 * <EMBED CLASS='external-html' DATA-FILE-ID=FILENODE_FILTER>
 * 
 * @see StrFilter
 */
@FunctionalInterface
public interface FileNodeFilter extends Predicate<FileNode>, java.io.Serializable
{
    /** <EMBED CLASS='external-html' DATA-FILE-ID=SVUIDFI>  */
    public static final long serialVersionUID = 1;

    /**
     * <EMBED CLASS='external-html' DATA-FILE-ID=FUNC_INTER_METH>
     *
     * @param fn Any file node
     *
     * @return A {@code TRUE} value should mean that the {@code FileNode} has passed the test
     * (will be kept / retained).  A return value of {@code FALSE} should indicate that the
     * {@code FileNode} needs to be filtered.
     */
    public boolean test(FileNode fn);

    /**
     * Standard Java-{@code Predicate}, Java Functional-Interface AND routine.
     *
     * <BR /><BR />Logically AND's <B>{@code 'this'}</B> {@code FileNodeFilter} with the filter
     * parameter <B>{@code 'other'}</B>
     *
     * @param other A 2nd {@code FileNodeFilter} with which <B>{@code 'this'}</B> filter may be
     * AND'ed.
     *
     * @return A new {@code FileNodeFilter} that is the logical-and of <B>{@code 'this'}</B> filter
     * and <B>{@code 'other'}</B>
     */
    default FileNodeFilter and(FileNodeFilter other)
    {
        // FAIL-FAST: Check that the lambda will not throw a null pointer exception prior to
        // creating the lambda.

        if (other == null) throw new NullPointerException
            ("The parameter 'other' to FileNodeFilter.and(other) was null.");

        return (FileNode f) -> this.test(f) && other.test(f);
    }

    /**
     * Standard Java-{@code Predicate}, Java Functional-Interface OR routine.
     *
     * <BR /><BR />Logically OR's <B>{@code 'this'}</B> {@code FileNodeFilter} with the filter
     * parameter <B>{@code 'other'}</B>
     *
     * @param other A 2nd {@code FileNodeFilter} with which <B>{@code 'this'}</B> filter may be
     * or'ed.
     *
     * @return A new FileNodeFilter that is the logical-or of <B>{@code 'this'}</B> filter and
     * <B>"other"</B>
     */
    default FileNodeFilter or(FileNodeFilter other)
    {
        // FAIL-FAST: Check that the lambda will not throw a null pointer exception prior to
        // creating the lambda.

        if (other == null) throw new NullPointerException
            ("The parameter 'other' to FileNodeFilter.or(other) was null..");

        return (FileNode f) -> this.test(f) || other.test(f);
    }

    /**
     * Standard Java-{@code Predicate}, Java Functional-Interface NEGATE routine.
     *
     * <BR /><BR />Generates 's a {@code FileNodeFilter} that is the "logical-not" of
     * <B>{@code 'this'}</B> filter.
     *
     * @return A new {@code FileNodeFilter} that would return {@code TRUE} whenever
     * <B>{@code 'this'}</B> would return {@code FALSE}, and vice-versa.
     */
    default FileNodeFilter negate() { return (FileNode f) -> ! this.test(f); }

    /**
     * This is similar to the java streams function {@code filter(Predicate<>)}.  Elements that do
     * not meet the criteria specified by this {@code FileNodeFilter} - <I>specifically, if an
     * element of the input-parameter {@code 'urlList'} would evaluate to {@code FALSE}</I> - then
     * that element shall be removed from the list.
     *
     * @param fileNodes An {@code Iterable} of {@code FileNode's} which the user would like
     * filtered using {@code 'this'} filter.
     *
     * @return The number of elements that were removed from parameter {@code 'fileNodes'} based on
     * the results of the {@code 'test(FileNode)'} lambda-method {@code FileNodeFilter.test()} of
     * {@code 'this'} instance.
     */
    public default int filter(Iterable<FileNode> fileNodes)
    {
        int                 removeCount = 0;
        Iterator<FileNode>  iter        = fileNodes.iterator();

        // If the filter test returns FALSE, then remove the URL from the collection.
        // Increment the removeCount Counter.

        while (iter.hasNext()) if (! test(iter.next())) { removeCount++; iter.remove(); }

        return removeCount;
    }

    /**
     * This wraps a {@code StrFilter} inside of a {@code FileNodeFilter}.  The
     * {@code String}-comparison that is performed will use the full-path-name of the
     * {@code FileNode}.
     *
     * @param sf This is a "String Predicate" that has (usually, but not required) been built by
     * one of the many {@code String}-Filter Factory-Build static-methods of
     * {@code class StrFilter}.  The Predicate's that are constructed via the build methods of
     * {@code StrFilter} call the {@code Object.toString()} method on the objects they receive for
     * testing.
     *
     * @return FileNodeFilter This will return an instance of a {@code FileNodeFilter} that will
     * test the full-path-name as a {@code String}.
     *
     * @see StrFilter
     */ 
    public static FileNodeFilter fromStrFilter(StrFilter sf)
    {
        if (sf == null) throw new NullPointerException(
            "The String-Filter Predicate Parameter 'sf' in static-factory builder method " +
            "'fromStrFilter' was passed a null value."
        );

        return (FileNode fn) -> sf.test(fn);
    }
}