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