OSDN Git Service

2005-05-04 Thomas Fitzsimmons <fitzsim@redhat.com>
[pf3gnuchains/gcc-fork.git] / libjava / java / awt / MediaTracker.java
1 /* MediaTracker.java -- Class used for keeping track of images
2    Copyright (C) 1999, 2002, 2004, 2005  Free Software Foundation, Inc.
3
4 This file is part of GNU Classpath.
5
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
9 any later version.
10
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING.  If not, write to the
18 Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
19 02111-1307 USA.
20
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library.  Thus, the terms and
23 conditions of the GNU General Public License cover the whole
24 combination.
25
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module.  An independent module is a module which is not derived from
33 or based on this library.  If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so.  If you do not wish to do so, delete this
36 exception statement from your version. */
37
38
39 package java.awt;
40
41 import java.awt.image.ImageObserver;
42 import java.util.ArrayList;
43
44 /**
45   * This class is used for keeping track of the status of various media
46   * objects.
47   *
48   * Media objects are tracked by assigning them an ID. It is possible
49   * to assign the same ID to mutliple objects, effectivly grouping them
50   * together. In this case the status flags ({@link #statusID}) and error flag
51   * (@link #isErrorID} and {@link #getErrorId}) are ORed together. This
52   * means that you cannot say exactly which media object has which status,
53   * at most you can say that there <em>are</em> certain media objects with
54   * some certain status.
55   * 
56   * At the moment only images are supported by this class.
57   *
58   * @author Aaron M. Renn (arenn@urbanophile.com)
59   * @author Bryce McKinlay
60   */
61 public class MediaTracker implements java.io.Serializable
62 {
63   /** Indicates that the media is still loading. */
64   public static final int LOADING = 1 << 0;
65
66   /** Indicates that the loading operation has been aborted. */
67   public static final int ABORTED = 1 << 1;
68
69   /** Indicates that an error has occured during loading of the media. */
70   public static final int ERRORED = 1 << 2;
71
72   /** Indicates that the media has been successfully and completely loaded. */
73   public static final int COMPLETE = 1 << 3;
74
75   /** The component on which the media is eventually been drawn. */
76   Component target;
77
78   /** The head of the linked list of tracked media objects. */
79   MediaEntry head;
80
81   /** Our serialVersionUID for serialization. */
82   static final long serialVersionUID = -483174189758638095L;
83
84   /**
85    * This represents a media object that is tracked by a MediaTracker.
86    * It also implements a simple linked list.
87    */
88   // FIXME: The serialized form documentation says MediaEntry is a 
89   // serializable field, but the serialized form of MediaEntry itself
90   // doesn't appear to be documented.
91   class MediaEntry implements ImageObserver
92   {
93     /** The ID of the media object. */
94     int id;
95
96     /** The media object. (only images are supported ATM). */
97     Image image;
98
99     /** The link to the next entry in the list. */
100     MediaEntry next;
101
102     /** The tracking status. */
103     int status;
104
105     /** The width of the image. */
106     int width;
107
108     /** The height of the image. */
109     int height;
110     
111     /**
112      * Receives notification from an {@link java.awt.image.ImageProducer}
113      * that more data of the image is available.
114      *
115      * @param img the image that is updated
116      * @param flags flags from the ImageProducer that indicate the status
117      *        of the loading process
118      * @param x the X coordinate of the upper left corner of the image
119      * @param y the Y coordinate of the upper left corner of the image
120      * @param width the width of the image
121      * @param height the height of the image
122      *
123      * @return <code>true</code> if more data is needed, <code>false</code>
124      *         otherwise
125      *
126      * @see {@link java.awt.image.ImageObserver}
127      */
128     public boolean imageUpdate(Image img, int flags, int x, int y, 
129                                int width, int height)
130     {
131       if ((flags & ABORT) != 0)
132         status = ABORTED;
133       else if ((flags & ERROR) != 0)
134         status = ERRORED;
135       else if ((flags & ALLBITS) != 0)
136         status = COMPLETE;
137       else
138         status = 0;
139
140       synchronized (MediaTracker.this)
141         {
142           MediaTracker.this.notifyAll();
143         }
144
145       // If status is not COMPLETE then we need more updates.
146       return ((status & (COMPLETE | ERRORED | ABORTED)) == 0);
147     }
148   }
149
150   /**
151    * Constructs a new MediaTracker for the component <code>c</code>. The
152    * component should be the component that uses the media (i.e. draws it).
153    *
154    * @param c the Component that wants to use the media
155    */
156   public MediaTracker(Component c)
157   {
158     target = c;
159   }
160
161   /**
162    * Adds an image to the tracker with the specified <code>ID</code>.
163    *
164    * @param image the image to be added
165    * @param id the ID of the tracker list to which the image is added
166    */
167   public void addImage(Image image, int id)
168   {
169     MediaEntry e = new MediaEntry();
170     e.id = id;
171     e.image = image;
172     synchronized(this)
173       {
174         e.next = head;
175         head = e;
176       }
177   }
178
179   /**
180    * Adds an image to the tracker with the specified <code>ID</code>.
181    * The image is expected to be rendered with the specified width and
182    * height.
183    *
184    * @param image the image to be added
185    * @param id the ID of the tracker list to which the image is added
186    * @param width the width of the image
187    * @param height the height of the image
188    */
189   public void addImage(Image image, int id, int width, int height)
190   {
191     MediaEntry e = new MediaEntry();
192     e.id = id;
193     e.image = image;
194     e.width = width;
195     e.height = height;
196     synchronized(this)
197       {
198         e.next = head;
199         head = e;
200       }
201   }
202
203   /**
204    * Checks if all media objects have finished loading, i.e. are
205    * {@link #COMPLETE}, {@link #ABORTED} or {@link #ERRORED}.
206    *
207    * If the media objects are not already loading, a call to this
208    * method does <em>not</em> start loading. This is equivalent to
209    * a call to <code>checkAll(false)</code>.
210    *
211    * @return if all media objects have finished loading either by beeing
212    *         complete, have been aborted or errored.
213    */
214   public boolean checkAll()
215   {
216     return checkAll(false);
217   }
218
219   /**
220    * Checks if all media objects have finished loading, i.e. are
221    * {@link #COMPLETE}, {@link #ABORTED} or {@link #ERRORED}.
222    *
223    * If the media objects are not already loading, and <code>load</code>
224    * is <code>true</code> then a call to this
225    * method starts loading the media objects.
226    *
227    * @param load if <code>true</code> this method starts loading objects
228    *        that are not already loading
229    *
230    * @return if all media objects have finished loading either by beeing
231    *         complete, have been aborted or errored.
232    */
233   public boolean checkAll(boolean load)
234   {
235     MediaEntry e = head;
236     boolean result = true;
237     
238     while (e != null)
239       {
240         if ((e.status & (COMPLETE | ERRORED | ABORTED)) == 0)
241           {
242             if (load && ((e.status & LOADING) == 0))
243               {
244                 e.status = LOADING;
245                 result = false;
246                 boolean complete = target.prepareImage(e.image, e);
247                 if (complete)
248                   {
249                     e.status = COMPLETE;
250                     result = true;
251                   }
252               }
253             else
254               result = false;
255           }
256         e = e.next;
257       }
258     return result;
259   }
260
261   /**
262    * Checks if any of the registered media objects has encountered an error
263    * during loading.
264    *
265    * @return <code>true</code> if at least one media object has encountered
266    *         an error during loading, <code>false</code> otherwise
267    *
268    */
269   public boolean isErrorAny()
270   {
271     MediaEntry e = head;    
272     while (e != null)
273       {
274         if ((e.status & ERRORED) != 0)
275           return true;
276         e = e.next;
277       }
278     return false;
279   }
280
281   /**
282    * Returns all media objects that have encountered errors during loading.
283    *
284    * @return an array of all media objects that have encountered errors
285    *         or <code>null</code> if there were no errors at all
286    */
287   public Object[] getErrorsAny()
288   {
289     MediaEntry e = head;
290     ArrayList result = null;
291     while (e != null)
292       {
293         if ((e.status & ERRORED) != 0)
294           {
295             if (result == null)
296               result = new ArrayList();
297             result.add(e.image);
298           }
299         e = e.next;
300       }
301     if (result == null)
302       return null;
303     else
304       return result.toArray();
305   }
306
307   /**
308    * Waits for all media objects to finish loading, either by completing
309    * successfully or by aborting or encountering an error.
310    *
311    * @throws InterruptedException if another thread interrupted the
312    *         current thread while waiting
313    */
314   public void waitForAll() throws InterruptedException
315   {
316     synchronized (this)
317     {
318       while (checkAll(true) == false)
319         wait();
320     }
321   }
322
323   /**
324    * Waits for all media objects to finish loading, either by completing
325    * successfully or by aborting or encountering an error.
326    *
327    * This method waits at most <code>ms</code> milliseconds. If the
328    * media objects have not completed loading within this timeframe, this
329    * method returns <code>false</code>, otherwise <code>true</code>.
330    *
331    * @param ms timeframe in milliseconds to wait for the media objects to
332    *        finish
333    *
334    * @return <code>true</code> if all media objects have successfully loaded
335    *         within the timeframe, <code>false</code> otherwise
336    *
337    * @throws InterruptedException if another thread interrupted the
338    *         current thread while waiting
339    */
340   public boolean waitForAll(long ms) throws InterruptedException
341   {
342     long start = System.currentTimeMillis();
343     boolean result = checkAll(true);
344     synchronized (this)
345     {
346       while (result == false)
347         {
348           wait(ms);
349           result = checkAll(true);
350           if ((System.currentTimeMillis() - start) > ms)
351             break;
352         }
353     }
354
355     return result;
356   }
357
358   /**
359    * Returns the status flags of all registered media objects ORed together.
360    * If <code>load</code> is <code>true</code> then media objects that
361    * are not already loading will be started to load.
362    *
363    * @param load if set to <code>true</code> then media objects that are
364    *        not already loading are started
365    *
366    * @return the status flags of all tracked media objects ORed together
367    */
368   public int statusAll(boolean load)
369   {
370     int result = 0;
371     MediaEntry e = head;
372     while (e != null)
373       {
374         if (load && e.status == 0)
375           {
376             boolean complete = target.prepareImage(e.image, e);
377             if (complete)
378               e.status = COMPLETE;
379             else
380               e.status = LOADING;
381           }
382         result |= e.status;
383         e = e.next;
384       }
385     return result;
386   }
387
388   /**
389    * Checks if the media objects with <code>ID</code> have completed loading.
390    *
391    * @param id the ID of the media objects to check
392    *
393    * @return <code>true</code> if all media objects with <code>ID</code>
394    *         have successfully finished
395    */
396   public boolean checkID(int id)
397   {
398     return checkID(id, false);
399   }
400
401   /**
402    * Checks if the media objects with <code>ID</code> have completed loading.
403    * If <code>load</code> is <code>true</code> then media objects that
404    * are not already loading will be started to load.
405    *
406    * @param id the ID of the media objects to check
407    * @param load if set to <code>true</code> then media objects that are
408    *        not already loading are started
409    *
410    * @return <code>true</code> if all media objects with <code>ID</code>
411    *         have successfully finished
412    */
413   public boolean checkID(int id, boolean load)
414   {
415     MediaEntry e = head;
416     boolean result = true;
417     
418     while (e != null)
419       {
420         if (e.id == id && ((e.status & (COMPLETE | ABORTED | ERRORED)) == 0))
421           {
422             if (load && ((e.status & LOADING) == 0))
423               {
424                 e.status = LOADING;
425                 result = false;
426                 boolean complete = target.prepareImage(e.image, e);
427                 if (complete)
428                   {
429                     e.status = COMPLETE;
430                     result = true;
431                   }
432               }
433             else
434               result = false;
435           }
436         e = e.next;
437       }
438     return result;
439   }
440
441   /**
442    * Returns <code>true</code> if any of the media objects with <code>ID</code>
443    * have encountered errors during loading, false otherwise.
444    *
445    * @param id the ID of the media objects to check
446    *
447    * @return <code>true</code> if any of the media objects with <code>ID</code>
448    *         have encountered errors during loading, false otherwise
449    */
450   public boolean isErrorID(int id)
451   {
452     MediaEntry e = head;    
453     while (e != null)
454       {
455         if (e.id == id && ((e.status & ERRORED) != 0))
456           return true;
457         e = e.next;
458       }
459     return false;
460   }
461
462   /**
463    * Returns all media objects with the specified ID that have encountered
464    * an error.
465    *
466    * @param id the ID of the media objects to check
467    *
468    * @return an array of all media objects  with the specified ID that
469    *         have encountered an error
470    */
471   public Object[] getErrorsID(int id)
472   {
473     MediaEntry e = head;
474     ArrayList result = null;
475     while (e != null)
476       {
477         if (e.id == id && ((e.status & ERRORED) != 0))
478           {
479             if (result == null)
480               result = new ArrayList();
481             result.add(e.image);
482           }
483         e = e.next;
484       }
485     if (result == null)
486       return null;
487     else
488       return result.toArray();
489   }
490
491   /**
492    * Waits for all media objects with the specified ID to finish loading,
493    * either by completing successfully or by aborting or encountering an error.
494    *
495    * @param id the ID of the media objects to wait for
496    *
497    * @throws InterruptedException if another thread interrupted the
498    *         current thread while waiting
499    */
500   public void waitForID(int id) throws InterruptedException
501   {
502     MediaEntry e = head;
503     synchronized (this)
504     {
505       while (checkID (id, true) == false)
506         wait();
507     }
508   }
509
510   /**
511    * Waits for all media objects with the specified ID to finish loading,
512    * either by completing successfully or by aborting or encountering an error.
513    *
514    * This method waits at most <code>ms</code> milliseconds. If the
515    * media objects have not completed loading within this timeframe, this
516    * method returns <code>false</code>, otherwise <code>true</code>.
517    *
518    * @param id the ID of the media objects to wait for
519    * @param ms timeframe in milliseconds to wait for the media objects to
520    *        finish
521    *
522    * @return <code>true</code> if all media objects have successfully loaded
523    *         within the timeframe, <code>false</code> otherwise
524    *
525    * @throws InterruptedException if another thread interrupted the
526    *         current thread while waiting
527    */
528   public boolean waitForID(int id, long ms) throws InterruptedException
529   {
530     MediaEntry e = head;
531     long start = System.currentTimeMillis();
532     boolean result = checkID(id, true);
533
534     synchronized (this)
535     {
536       while (result == false)
537         {
538           wait(ms);
539           result = checkID(id, true);
540           if ((System.currentTimeMillis() - start) > ms)
541             break;
542         }
543     }
544
545     return result;
546   }
547
548   /**
549    * Returns the status flags of the media objects with the specified ID
550    * ORed together.
551    *
552    * If <code>load</code> is <code>true</code> then media objects that
553    * are not already loading will be started to load.
554    *
555    * @param load if set to <code>true</code> then media objects that are
556    *        not already loading are started
557    *
558    * @return the status flags of all tracked media objects ORed together
559    */
560   public int statusID(int id, boolean load)
561   {
562     int result = 0;
563     MediaEntry e = head;
564     while (e != null)
565       {
566         if (e.id == id)
567           {
568             if (load && e.status == 0)
569               {
570                 boolean complete = target.prepareImage(e.image, e);
571                 if (complete)
572                   e.status = COMPLETE;
573                 else
574                   e.status = LOADING;
575               }
576             result |= e.status;
577           }
578         e = e.next;
579       }
580     return result;
581   }
582
583   /**
584    * Removes an image from this MediaTracker.
585    *
586    * @param image the image to be removed
587    */
588   public void removeImage(Image image)
589   {
590     synchronized (this)
591       {
592         MediaEntry e = head;
593         MediaEntry prev = null;
594         while (e != null)
595           {
596             if (e.image == image)
597               {
598                 if (prev == null)
599                   head = e.next;
600                 else
601                   prev.next = e.next;
602               }
603             prev = e;
604             e = e.next;
605           }
606       }
607   }
608
609   /**
610    * Removes an image with the specified ID from this MediaTracker.
611    *
612    * @param image the image to be removed
613    */
614   public void removeImage(Image image, int id)
615   {
616     synchronized (this)
617       {
618         MediaEntry e = head;
619         MediaEntry prev = null;
620         while (e != null)
621           {
622             if (e.id == id && e.image == image)
623               {
624                 if (prev == null)
625                   head = e.next;
626                 else
627                   prev.next = e.next;
628               }
629             else
630               prev = e;
631             e = e.next;
632           }
633       }
634   }
635
636   /**
637    * Removes an image with the specified ID and scale from this MediaTracker.
638    *
639    * @param image the image to be removed
640    */
641   public void removeImage(Image image, int id, int width, int height)
642   {
643     synchronized (this)
644       {
645         MediaEntry e = head;
646         MediaEntry prev = null;
647         while (e != null)
648           {
649             if (e.id == id && e.image == image
650                 && e.width == width && e.height == height)
651               {
652                 if (prev == null)
653                   head = e.next;
654                 else
655                   prev.next = e.next;
656               }
657             else
658               prev = e;
659             e = e.next;
660           }
661       }
662   }
663 }