001 002package Torello.HTML.Tools.Images; 003 004import Torello.Java.*; 005import Torello.JavaDoc.LinkJavaSource; 006// Needed for a JavaDoc Comment {@link ...} 007import Torello.HTML.TagNode; 008 009import java.net.URL; 010import java.io.Serializable; 011import java.util.Arrays; 012 013/** 014 * After downloading all of the user's requested images, the class {@link ImageScraper} returns an 015 * instance of this class. 016 * 017 * <EMBED CLASS='external-html' DATA-FILE-ID=RESULTS> 018 * @see ImageScraper 019 */ 020@Torello.JavaDoc.JDHeaderBackgroundImg(EmbedTagFileID="IMAGE_SCRAPER_CLASS") 021public class Results implements Serializable, Cloneable 022{ 023 /** <EMBED CLASS='external-html' DATA-FILE-ID=SVUID> */ 024 public static final long serialVersionUID = 1; 025 026 027 // ******************************************************************************************** 028 // ******************************************************************************************** 029 // Public Array Fields: User may inspect these fields when an instance of 'Results' is returned 030 // ******************************************************************************************** 031 // ******************************************************************************************** 032 033 034 /** 035 * <EMBED CLASS='external-html' DATA-FILE-ID=RES_urls> 036 * <EMBED CLASS='external-html' DATA-FILE-ID=RES_PARALLEL_NOTE> 037 */ 038 public final URL[] urls; 039 040 /** 041 * <EMBED CLASS='external-html' DATA-FILE-ID=RES_b64EncodedImg> 042 * <EMBED CLASS='external-html' DATA-FILE-ID=RES_PARALLEL_NOTE> 043 */ 044 public final boolean[] b64EncodedImg; 045 046 /** 047 * <EMBED CLASS='external-html' DATA-FILE-ID=RES_skipped> 048 * <EMBED CLASS='external-html' DATA-FILE-ID=RES_PARALLEL_NOTE> 049 */ 050 public final boolean[] skipped; 051 052 /** 053 * <EMBED CLASS='external-html' DATA-FILE-ID=RES_fileNames> 054 * <EMBED CLASS='external-html' DATA-FILE-ID=RES_PARALLEL_NOTE> 055 */ 056 public final String[] fileNames; 057 058 /** 059 * <EMBED CLASS='external-html' DATA-FILE-ID=RES_saveDirectories> 060 * <EMBED CLASS='external-html' DATA-FILE-ID=RES_PARALLEL_NOTE> 061 */ 062 public final String[] saveDirectories; 063 064 /** 065 * <EMBED CLASS='external-html' DATA-FILE-ID=RES_imageFormats> 066 * <EMBED CLASS='external-html' DATA-DEFVAL=null DATA-SPEC="image-format" 067 * DATA-FILE-ID=RES_SKIPPED_NOTE> 068 * 069 * <EMBED CLASS='external-html' DATA-FILE-ID=RES_PARALLEL_NOTE> 070 */ 071 public final IF[] imageFormats; 072 073 /** 074 * <EMBED CLASS='external-html' DATA-FILE-ID=RES_exceptions> 075 * <EMBED CLASS='external-html' DATA-FILE-ID=RES_PARALLEL_NOTE> 076 */ 077 public final Exception[] exceptions; 078 079 /** 080 * <EMBED CLASS='external-html' DATA-FILE-ID=RES_sizes> 081 * <EMBED CLASS='external-html' DATA-DEFVAL="-1" DATA-SPEC=size DATA-FILE-ID=RES_SKIPPED_NOTE> 082 * <EMBED CLASS='external-html' DATA-FILE-ID=RES_PARALLEL_NOTE> 083 */ 084 public final long[] sizes; 085 086 /** 087 * <EMBED CLASS='external-html' DATA-FILE-ID=RES_widths> 088 * <EMBED CLASS='external-html' DATA-DEFVAL="-1" DATA-SPEC=width DATA-FILE-ID=RES_SKIPPED_NOTE> 089 * <EMBED CLASS='external-html' DATA-FILE-ID=RES_PARALLEL_NOTE> 090 */ 091 public final int[] widths; 092 093 /** 094 * <EMBED CLASS='external-html' DATA-FILE-ID=RES_heights> 095 * <EMBED CLASS='external-html' DATA-DEFVAL="-1" DATA-SPEC=height 096 * DATA-FILE-ID=RES_SKIPPED_NOTE> 097 * <EMBED CLASS='external-html' DATA-FILE-ID=RES_PARALLEL_NOTE> 098 */ 099 public final int[] heights; 100 101 102 // ******************************************************************************************** 103 // ******************************************************************************************** 104 // Some Package-Private Fields, Used here and by class ImageScraper 105 // ******************************************************************************************** 106 // ******************************************************************************************** 107 108 109 // next result received array position. 110 int pos = 0; 111 112 // number of successfully saved images. 113 int successCounter = 0; 114 115 116 // ******************************************************************************************** 117 // ******************************************************************************************** 118 // Package-Private Constructor 119 // ******************************************************************************************** 120 // ******************************************************************************************** 121 122 123 Results(int size) 124 { 125 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 126 // Create each of these arrays 127 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 128 129 urls = new URL[size]; 130 b64EncodedImg = new boolean[size]; 131 skipped = new boolean[size]; 132 fileNames = new String[size]; 133 saveDirectories = new String[size]; 134 imageFormats = new IF[size]; 135 exceptions = new Exception[size]; 136 sizes = new long[size]; 137 widths = new int[size]; 138 heights = new int[size]; 139 140 141 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 142 // Initialize each element of the above arrays 143 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 144 145 for (int i=0; i < size; i++) 146 { 147 urls[i] = null; 148 b64EncodedImg[i] = false; 149 skipped[i] = false; 150 fileNames[i] = null; 151 saveDirectories[i] = null; 152 imageFormats[i] = null; 153 exceptions[i] = null; 154 sizes[i] = -1; 155 widths[i] = -1; 156 heights[i] = -1; 157 } 158 } 159 160 161 // ******************************************************************************************** 162 // ******************************************************************************************** 163 // No Image Downloaded 164 // ******************************************************************************************** 165 // ******************************************************************************************** 166 167 168 // Request static-builder generated an "Exception URL" 169 // Called From: ImageScraper.loopBody(RECORD) 170 171 void tagNodeSRCError(Exception e) 172 { 173 skipped[pos] = true; 174 exceptions[pos] = e; 175 176 pos++; 177 } 178 179 180 // User-Provided "Predicate<URL> skipURL" 181 // Called From: ImageScraper.downloadImage(RECORD) 182 183 void skippedURL(URL url) 184 { 185 urls[pos] = url; 186 skipped[pos] = true; 187 188 pos++; 189 } 190 191 192 // User-Provided "boolean skipBase64EncodedImages" 193 // Called From: ImageScraper.convertB64Image(RECORD) 194 195 void skipB64() 196 { 197 b64EncodedImg[pos] = true; 198 skipped[pos] = true; 199 200 pos++; 201 } 202 203 204 // Called from many places. This method is the biggest of the Results-Reporters 205 // * Exception-thrown 206 // * 'ImageInfo' instance hasn't been constructed yet 207 208 void exceptionFail(URL url, Exception e) 209 { 210 urls[pos] = url; 211 b64EncodedImg[pos] = (url == null); 212 skipped[pos] = true; 213 exceptions[pos] = e; 214 215 pos++; 216 } 217 218 219 // ******************************************************************************************** 220 // ******************************************************************************************** 221 // "ImageInfo" instance available now: Image Successfully Downloaded and Converted to Array. 222 // ******************************************************************************************** 223 // ******************************************************************************************** 224 225 226 // There are 3 different User-Provided Lambda-Target's that might throw exceptions 227 // This is called from within "ImageScraper.RECORD.userLambdaEx(...)" 228 229 void userLambdaException(ImageInfo imageInfo, Exception e) 230 { 231 skipped[pos] = true; 232 exceptions[pos] = e; 233 234 copyImageInfo(imageInfo); 235 } 236 237 238 // The User Keep/Reject Predicate rejected this image 239 // Called From: ImageScraper.handleImageByteArray(RECORD) 240 241 void predicateReject(ImageInfo imageInfo) 242 { 243 skipped[pos] = true; 244 copyImageInfo(imageInfo); 245 } 246 247 248 // Image was written to disk somewhere, or accepted by the Request.imageReceiver 249 // Called From: ImageScraper.writeOrTransmit(RECORD) 250 251 void success(ImageInfo imageInfo, String targetDirectory) 252 { 253 // Directory where the image was saved, if called by "ImageReceiver", this will be null 254 saveDirectories[pos] = targetDirectory; 255 256 copyImageInfo(imageInfo); 257 258 // Only time this is ever incremented 259 successCounter++; 260 } 261 262 263 // ******************************************************************************************** 264 // ******************************************************************************************** 265 // SMALL HELPER 266 // ******************************************************************************************** 267 // ******************************************************************************************** 268 269 270 // Private Method, used in all 5 previous methods directly above here 271 private void copyImageInfo(ImageInfo imageInfo) 272 { 273 urls[pos] = imageInfo.url; 274 b64EncodedImg[pos] = imageInfo.isB64EncodedImage; 275 imageFormats[pos] = imageInfo.actualExtension; 276 sizes[pos] = imageInfo.imgByteArr.length; 277 widths[pos] = imageInfo.width; 278 heights[pos] = imageInfo.height; 279 fileNames[pos] = imageInfo.fileName() + '.' + imageInfo.actualExtension; 280 281 pos++; 282 } 283 284 285 // ******************************************************************************************** 286 // ******************************************************************************************** 287 // interface java.lang.Cloneable 288 // ******************************************************************************************** 289 // ******************************************************************************************** 290 291 292 /** 293 * Generates a <B STYLE='color: red;'>Deep Copy</B> of {@code 'this'} instance. This means 294 * all internal arrays are also cloned / copied 295 * 296 * @return A duplicate instance of this class, with all arrays having been copied. 297 */ 298 public Results clone() 299 { return new Results(this); } 300 301 // Private Constructor, used only for the 'clone()' method 302 private Results(Results r) 303 { 304 this.urls = r.urls.clone(); 305 this.b64EncodedImg = r.b64EncodedImg.clone(); 306 this.skipped = r.skipped.clone(); 307 this.fileNames = r.fileNames.clone(); 308 this.saveDirectories = r.saveDirectories.clone(); 309 this.imageFormats = r.imageFormats.clone(); 310 this.exceptions = r.exceptions.clone(); 311 this.sizes = r.sizes.clone(); 312 this.widths = r.widths.clone(); 313 this.heights = r.heights.clone(); 314 315 this.pos = r.pos; 316 this.successCounter = r.successCounter; 317 } 318 319 320 // ******************************************************************************************** 321 // ******************************************************************************************** 322 // java.lang.Object 323 // ******************************************************************************************** 324 // ******************************************************************************************** 325 326 327 /** 328 * Checks whether {@code 'this'} instance is equal to another instance of class 329 * {@code Results}. This method performs a <B STYLE='color: red;'>Deep Equals</B> comparison 330 * using the {@code equals(...)} method suite found in class' {@code java.util.Arrays}. 331 * 332 * @param other This may be any Java Object, but only an instance class {@code 'Results'} has 333 * any chance of being marked as <B STYLE='color: red;'>equal</B> to this instance. 334 * 335 * @return {@code TRUE} if and only if {@code 'o'} has a type that's assignable to 336 * {@code Results} - and if each of the internal arrays in this instance are equal to the 337 * arrays in parameter {@code 'o'}. 338 */ 339 @LinkJavaSource(handle="ResultsEquals") 340 public boolean equals(Object other) 341 { return ResultsEquals.equals(this, other); } 342 343 /** 344 * Returns a {@code java.lang.String} representation of {@code 'this'} instance 345 * @return A Java {@code String} containing the data inside this class. 346 */ 347 @LinkJavaSource(handle="ResultsToString") 348 public String toString() 349 { return ResultsToString.run(this); } 350 351 /** 352 * Java's hash-code requirement. The code is computed by summing the first 10 {@link #sizes} 353 * array elements. 354 * 355 * @return A hash-code that may be used when storing this node in a java sorted-collection. 356 */ 357 public int hashCode() 358 { 359 int sum = 0; 360 361 for (int i=0; (i < 10) && (i < sizes.length); i++) sum += sizes[i]; 362 363 return sum; 364 } 365}