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}