001package Torello.HTML; 002 003import Torello.Java.StrFilter; 004import Torello.Java.StrCmpr; 005 006import java.util.function.Predicate; 007import java.util.Iterator; 008import java.net.URL; 009 010/** 011 * A simple lambda-target which extends {@code Predicate<URL>}. 012 * 013 * <EMBED CLASS='external-html' DATA-FILE-ID=URL_FILTER> 014 * 015 * @see StrFilter 016 */ 017@FunctionalInterface 018public interface URLFilter extends Predicate<URL>, java.io.Serializable 019{ 020 /** <EMBED CLASS='external-html' DATA-FILE-ID=SVUIDFI> */ 021 public static final long serialVersionUID = 1; 022 023 024 // ******************************************************************************************** 025 // ******************************************************************************************** 026 // Functional-Interface Method 027 // ******************************************************************************************** 028 // ******************************************************************************************** 029 030 031 /** 032 * <EMBED CLASS='external-html' DATA-FILE-ID=FUNC_INTER_METH> 033 * 034 * <BR /><BR />This method will receive a {@code URL.} The purpose of this method is to 035 * provide an easy means to filter certain {@code URL's} from a {@code URL}-generating list. 036 * 037 * <BR /><BR /><B><SPAN STYLE="color: red;">PRECISE NOTE:</B></SPAN> This method should return 038 * {@code FALSE} if the passed {@code URL} <I><B>should be skipped</B></I>. A return value of 039 * {@code TRUE} implies that the {@code URL} is not to be ignored or passed over, but rather 040 * 'kept.' 041 * 042 * <BR /><BR /><B>NOTE:</B> This behavior is compatible with the Java Stream's method 043 * {@code "filter(Predicate<...>)".} 044 * 045 * @param url This is a {@code URL} that will be checked against the constraints specified by 046 * {@code 'this'} filter. 047 * 048 * @return When implementing this method, returning {@code TRUE} must mean that the {@code URL} 049 * has passed the filter's test-requirements (and will subsequently be retained by whatever 050 * code is carrying out the filter operation). 051 */ 052 public boolean test(URL url); 053 054 055 // ******************************************************************************************** 056 // ******************************************************************************************** 057 // The basic-required methods, in order to make this a "Respectable Filter Class" 058 // ******************************************************************************************** 059 // ******************************************************************************************** 060 061 062 /* 063 * This is the standard-java {@code Predicate} method {@code 'and'}. If a user wants to apply 064 * two {@code URLFilters,} this method will use a lambda-expression to create a new 065 * {@code URLFilter} that "logically-AND's" the {@code 'other' Predicate} with {@code 'this' 066 * Predicate.} 067 * 068 * @param other Some other {@code URLFilter} - one that does some other test. 069 * 070 * @return A new Java-{@code Predicate} that performs both tests ({@code 'this'} and 071 * {@code 'other'}), and returns the logical-AND. 072 * 073 default URLFilter and(URLFilter other) 074 { 075 // FAIL-FAST: Check that the user-provided-parameters would not cause exceptions once 076 // the lambda-predicate is invoked. 077 if (other == null) throw new NullPointerException 078 ("The parameter 'other' to URLFilter.and(other) was null."); 079 080 return (URL url) -> this.test(url) && other.test(url); 081 } 082 083 /* 084 * This is the standard-java {@code Predicate} method {@code 'or'}. If a user wants to apply 085 * two {@code URLFilters,} this method will use a lambda-expression to create a new 086 * {@code URLFilter} that "logically-OR's" the {@code 'other' Predicate} with {@code 'this' 087 * Predicate.} 088 * 089 * @param other Some other {@code URLFilter} - one that does some other test. 090 * 091 * @return A new Java-{@code Predicate} that performs both tests ({@code 'this'} and 092 * {@code 'other'}), and returns the logical-OR. 093 * 094 default URLFilter or(URLFilter other) 095 { 096 // FAIL-FAST: Check that the user-provided-parameters would not cause exceptions once 097 // the lambda-predicate is invoked. 098 if (other == null) throw new NullPointerException 099 ("The parameter 'other' to URLFilter.or(other) was null."); 100 101 return (URL url) -> this.test(url) || other.test(url); 102 } 103 104 105 /* 106 * This is the standard-java predicate method {@code 'not'}. This method will use a 107 * lambda-expression to create a new {@code URLFilter} that is the "logical-NOT" of the 108 * original test (a.k.a. {@code 'this' Predicate}). 109 * 110 * @return A new Java-{@code Predicate} that performs negates the results of {@code 'this'} 111 * {@code Predicate.} 112 * 113 default URLFilter negate() { return (URL url) -> ! this.test(url); } 114 */ 115 116 /** 117 * This {@code URLFilter} will KEEP any Image {@code URL's} whose name ends with the standard 118 * image filenames. 119 * 120 * <BR /><BR /><B>WARNING:</B> There are occasions where an Image-{@code URL} is "handled" by 121 * a web-server internally, and the actual {@code URL} itself does not look like an image 122 * file-name at all. This has the inconvenient implication for this (factory-generated) 123 * {@code Predicate} that it might return erroneous results. An actual image file that does 124 * not end with {@code '.jpg'} or {@code '.bmp'} could be rejected, and a {@code URL} that 125 * happens to end with these {@code String's} but is not an image, might also be kept. 126 * 127 * @see StrCmpr#endsWithXOR_CI(String, String[]) 128 */ 129 public static final URLFilter imagesKEEP = (URL url) -> 130 { 131 return StrCmpr.endsWithXOR_CI 132 (url.toString().trim(), ".jpg", ".jpeg", ".gif", ".png", ".bmp"); 133 }; 134 135 /** 136 * This {@code URLFilter} will REJECT any Image {@code URL's} whose name ends with the 137 * standard image filenames. 138 * 139 * <BR /><BR /><B>WARNING:</B> There are occasions where an Image-{@code URL} is "handled" by 140 * a web-server internally, and the actual {@code URL} itself does not look like an image 141 * file-name at all. This has the inconvenient implication for this (factory-generated) 142 * {@code Predicate} that it might return erroneous results. An actual image file that does 143 * not end with {@code '.jpg'} or {@code '.bmp'} could be kept, and a {@code URL} that happens 144 * to end with these {@code String's} but is not an image, could be rejected. 145 * 146 * @see StrCmpr#endsWithNAND_CI(String, String[]) 147 */ 148 public static final URLFilter imagesREJECT = (URL url) -> 149 { 150 return StrCmpr.endsWithNAND_CI 151 (url.toString().trim(), ".jpg", ".jpeg", ".gif", ".png", ".bmp"); 152 }; 153 154 /** 155 * This is similar to the java streams function {@code filter(Predicate<>)}. Elements that do 156 * not meet the criteria specified by this (factory-generated) {@code URLFilter} - 157 * <I>specifically, if an element of the input-parameter {@code 'urlList'} would evaluate to 158 * {@code FALSE}</I> - then that element shall be removed from the list. 159 * 160 * @param urls An {@code Iterable} of {@code URL's} which the user would like filtered using 161 * {@code 'this'} filter. 162 * 163 * @return The number of elements that were removed from parameter {@code 'urls'} based on the 164 * results of the {@code URLFilter.test()} of {@code 'this'} instance. 165 */ 166 public default int filter(Iterable<URL> urls) 167 { 168 int removeCount = 0; 169 Iterator<URL> iter = urls.iterator(); 170 171 // If the filter test returns FALSE, then remove the URL from the collection. 172 // Increment the removeCount Counter. 173 174 while (iter.hasNext()) if (! test(iter.next())) { removeCount++; iter.remove(); } 175 176 return removeCount; 177 } 178 179 /** 180 * This wraps a {@code StrFilter} inside of a {@code URLFilter}. The {@code String}-comparison 181 * that is performed will use the full-path-name of the {@code URL}. 182 * 183 * <BR /><BR /><B><SPAN STYLE="color: red;">StrFilter NOTE:</SPAN></B> The class 184 * {@code 'StrFilter'} can be used in conjunction with the class-specific filters, for 185 * instance, this class {@code 'URLFilter'} 186 * 187 * @param sf This is a {@code String Predicate} that has (usually, but not required) been built 188 * by one of the many {@code String}-Filter Factory-Build static-methods of 189 * {@code class StrFilter.} The {@code Predicate's} that are constructed via the build methods 190 * of {@code StrFilter} call the standard method {@code java.lang.Object.toString()} on the 191 * objects they receive for testing. 192 * 193 * @return FileNodeFilter This will return an instance of a {@code URLFilter} that will test 194 * the {@code url} as a {@code String.} 195 * 196 * @see StrFilter 197 */ 198 public static URLFilter fromStrFilter(StrFilter sf) 199 { 200 if (sf == null) throw new NullPointerException( 201 "The String-Filter Predicate Parameter 'sf' in static-factory builder method " + 202 "'fromStrFilter' was passed a null value." 203 ); 204 205 return (URL url) -> sf.test(url); 206 } 207}