OSDN Git Service

Initial revision
[pf3gnuchains/gcc-fork.git] / libjava / classpath / 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., 51 Franklin Street, Fifth Floor, Boston, MA
19 02110-1301 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 #getErrorsID}) 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 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                 if (target.prepareImage(e.image, e))
245                   e.status = COMPLETE;
246                 else
247                   {
248                     e.status = LOADING;
249                     int flags = target.checkImage(e.image, e);
250                     if ((flags & ImageObserver.ABORT) != 0)
251                       e.status = ABORTED;
252                     else if ((flags & ImageObserver.ERROR) != 0)
253                       e.status = ERRORED;
254                     else if ((flags & ImageObserver.ALLBITS) != 0)
255                       e.status = COMPLETE;
256                   }
257                 boolean complete = (e.status
258                                     & (COMPLETE | ABORTED | ERRORED)) != 0;
259                 if (!complete)
260                   result = false;
261               }
262             else
263               result = false;
264           }
265         e = e.next;
266       }
267     return result;
268   }
269
270   /**
271    * Checks if any of the registered media objects has encountered an error
272    * during loading.
273    *
274    * @return <code>true</code> if at least one media object has encountered
275    *         an error during loading, <code>false</code> otherwise
276    *
277    */
278   public boolean isErrorAny()
279   {
280     MediaEntry e = head;    
281     while (e != null)
282       {
283         if ((e.status & ERRORED) != 0)
284           return true;
285         e = e.next;
286       }
287     return false;
288   }
289
290   /**
291    * Returns all media objects that have encountered errors during loading.
292    *
293    * @return an array of all media objects that have encountered errors
294    *         or <code>null</code> if there were no errors at all
295    */
296   public Object[] getErrorsAny()
297   {
298     MediaEntry e = head;
299     ArrayList result = null;
300     while (e != null)
301       {
302         if ((e.status & ERRORED) != 0)
303           {
304             if (result == null)
305               result = new ArrayList();
306             result.add(e.image);
307           }
308         e = e.next;
309       }
310     if (result == null)
311       return null;
312     else
313       return result.toArray();
314   }
315
316   /**
317    * Waits for all media objects to finish loading, either by completing
318    * successfully or by aborting or encountering an error.
319    *
320    * @throws InterruptedException if another thread interrupted the
321    *         current thread while waiting
322    */
323   public void waitForAll() throws InterruptedException
324   {
325     synchronized (this)
326     {
327       while (checkAll(true) == false)
328         wait();
329     }
330   }
331
332   /**
333    * Waits for all media objects to finish loading, either by completing
334    * successfully or by aborting or encountering an error.
335    *
336    * This method waits at most <code>ms</code> milliseconds. If the
337    * media objects have not completed loading within this timeframe, this
338    * method returns <code>false</code>, otherwise <code>true</code>.
339    *
340    * @param ms timeframe in milliseconds to wait for the media objects to
341    *        finish
342    *
343    * @return <code>true</code> if all media objects have successfully loaded
344    *         within the timeframe, <code>false</code> otherwise
345    *
346    * @throws InterruptedException if another thread interrupted the
347    *         current thread while waiting
348    */
349   public boolean waitForAll(long ms) throws InterruptedException
350   {
351     long start = System.currentTimeMillis();
352     boolean result = checkAll(true);
353     synchronized (this)
354     {
355       while (result == false)
356         {
357           wait(ms);
358           result = checkAll(true);
359           if ((System.currentTimeMillis() - start) > ms)
360             break;
361         }
362     }
363
364     return result;
365   }
366
367   /**
368    * Returns the status flags of all registered media objects ORed together.
369    * If <code>load</code> is <code>true</code> then media objects that
370    * are not already loading will be started to load.
371    *
372    * @param load if set to <code>true</code> then media objects that are
373    *        not already loading are started
374    *
375    * @return the status flags of all tracked media objects ORed together
376    */
377   public int statusAll(boolean load)
378   {
379     int result = 0;
380     MediaEntry e = head;
381     while (e != null)
382       {
383         if (load && e.status == 0)
384           {
385             if (target.prepareImage(e.image, e))
386               e.status = COMPLETE;
387             else
388               {
389                 e.status = LOADING;
390                 int flags = target.checkImage(e.image, e);
391                 if ((flags & ImageObserver.ABORT) != 0)
392                   e.status = ABORTED;
393                 else if ((flags & ImageObserver.ERROR) != 0)
394                   e.status = ERRORED;
395                 else if ((flags & ImageObserver.ALLBITS) != 0)
396                   e.status = COMPLETE;
397               }
398           }
399         result |= e.status;
400         e = e.next;
401       }
402     return result;
403   }
404
405   /**
406    * Checks if the media objects with <code>ID</code> have completed loading.
407    *
408    * @param id the ID of the media objects to check
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)
414   {
415     return checkID(id, false);
416   }
417
418   /**
419    * Checks if the media objects with <code>ID</code> have completed loading.
420    * If <code>load</code> is <code>true</code> then media objects that
421    * are not already loading will be started to load.
422    *
423    * @param id the ID of the media objects to check
424    * @param load if set to <code>true</code> then media objects that are
425    *        not already loading are started
426    *
427    * @return <code>true</code> if all media objects with <code>ID</code>
428    *         have successfully finished
429    */
430   public boolean checkID(int id, boolean load)
431   {
432     MediaEntry e = head;
433     boolean result = true;
434     
435     while (e != null)
436       {
437         if (e.id == id && ((e.status & (COMPLETE | ABORTED | ERRORED)) == 0))
438           {
439             if (load && ((e.status & LOADING) == 0))
440               {
441                 e.status = LOADING;
442                 if (target.prepareImage(e.image, e))
443                   e.status = COMPLETE;
444                 else
445                   {
446                     int flags = target.checkImage(e.image, e);
447                     if ((flags & ImageObserver.ABORT) != 0)
448                       e.status = ABORTED;
449                     else if ((flags & ImageObserver.ERROR) != 0)
450                       e.status = ERRORED;
451                     else if ((flags & ImageObserver.ALLBITS) != 0)
452                       e.status = COMPLETE;
453                   }
454                 boolean complete = (e.status
455                                     & (COMPLETE | ABORTED | ERRORED)) != 0;
456                 if (!complete)
457                   result = false;
458               }
459             else
460               result = false;
461           }
462         e = e.next;
463       }
464     return result;
465   }
466
467   /**
468    * Returns <code>true</code> if any of the media objects with <code>ID</code>
469    * have encountered errors during loading, false otherwise.
470    *
471    * @param id the ID of the media objects to check
472    *
473    * @return <code>true</code> if any of the media objects with <code>ID</code>
474    *         have encountered errors during loading, false otherwise
475    */
476   public boolean isErrorID(int id)
477   {
478     MediaEntry e = head;    
479     while (e != null)
480       {
481         if (e.id == id && ((e.status & ERRORED) != 0))
482           return true;
483         e = e.next;
484       }
485     return false;
486   }
487
488   /**
489    * Returns all media objects with the specified ID that have encountered
490    * an error.
491    *
492    * @param id the ID of the media objects to check
493    *
494    * @return an array of all media objects  with the specified ID that
495    *         have encountered an error
496    */
497   public Object[] getErrorsID(int id)
498   {
499     MediaEntry e = head;
500     ArrayList result = null;
501     while (e != null)
502       {
503         if (e.id == id && ((e.status & ERRORED) != 0))
504           {
505             if (result == null)
506               result = new ArrayList();
507             result.add(e.image);
508           }
509         e = e.next;
510       }
511     if (result == null)
512       return null;
513     else
514       return result.toArray();
515   }
516
517   /**
518    * Waits for all media objects with the specified ID to finish loading,
519    * either by completing successfully or by aborting or encountering an error.
520    *
521    * @param id the ID of the media objects to wait for
522    *
523    * @throws InterruptedException if another thread interrupted the
524    *         current thread while waiting
525    */
526   public void waitForID(int id) throws InterruptedException
527   {
528     MediaEntry e = head;
529     synchronized (this)
530     {
531       while (checkID (id, true) == false)
532         wait();
533     }
534   }
535
536   /**
537    * Waits for all media objects with the specified ID to finish loading,
538    * either by completing successfully or by aborting or encountering an error.
539    *
540    * This method waits at most <code>ms</code> milliseconds. If the
541    * media objects have not completed loading within this timeframe, this
542    * method returns <code>false</code>, otherwise <code>true</code>.
543    *
544    * @param id the ID of the media objects to wait for
545    * @param ms timeframe in milliseconds to wait for the media objects to
546    *        finish
547    *
548    * @return <code>true</code> if all media objects have successfully loaded
549    *         within the timeframe, <code>false</code> otherwise
550    *
551    * @throws InterruptedException if another thread interrupted the
552    *         current thread while waiting
553    */
554   public boolean waitForID(int id, long ms) throws InterruptedException
555   {
556     MediaEntry e = head;
557     long start = System.currentTimeMillis();
558     boolean result = checkID(id, true);
559
560     synchronized (this)
561     {
562       while (result == false)
563         {
564           wait(ms);
565           result = checkID(id, true);
566           if ((System.currentTimeMillis() - start) > ms)
567             break;
568         }
569     }
570
571     return result;
572   }
573
574   /**
575    * Returns the status flags of the media objects with the specified ID
576    * ORed together.
577    *
578    * If <code>load</code> is <code>true</code> then media objects that
579    * are not already loading will be started to load.
580    *
581    * @param load if set to <code>true</code> then media objects that are
582    *        not already loading are started
583    *
584    * @return the status flags of all tracked media objects ORed together
585    */
586   public int statusID(int id, boolean load)
587   {
588     int result = 0;
589     MediaEntry e = head;
590     while (e != null)
591       {
592         if (e.id == id)
593           {
594             if (load && e.status == 0)
595               {
596                 if (target.prepareImage(e.image, e))
597                   e.status = COMPLETE;
598                 else
599                   {
600                     e.status = LOADING;
601                     int flags = target.checkImage(e.image, e);
602                     if ((flags & ImageObserver.ABORT) != 0)
603                       e.status = ABORTED;
604                     else if ((flags & ImageObserver.ERROR) != 0)
605                       e.status = ERRORED;
606                     else if ((flags & ImageObserver.ALLBITS) != 0)
607                       e.status = COMPLETE;
608                   }
609               }
610             result |= e.status;
611           }
612         e = e.next;
613       }
614     return result;
615   }
616
617   /**
618    * Removes an image from this MediaTracker.
619    *
620    * @param image the image to be removed
621    */
622   public void removeImage(Image image)
623   {
624     synchronized (this)
625       {
626         MediaEntry e = head;
627         MediaEntry prev = null;
628         while (e != null)
629           {
630             if (e.image == image)
631               {
632                 if (prev == null)
633                   head = e.next;
634                 else
635                   prev.next = e.next;
636               }
637             prev = e;
638             e = e.next;
639           }
640       }
641   }
642
643   /**
644    * Removes an image with the specified ID from this MediaTracker.
645    *
646    * @param image the image to be removed
647    */
648   public void removeImage(Image image, int id)
649   {
650     synchronized (this)
651       {
652         MediaEntry e = head;
653         MediaEntry prev = null;
654         while (e != null)
655           {
656             if (e.id == id && e.image == image)
657               {
658                 if (prev == null)
659                   head = e.next;
660                 else
661                   prev.next = e.next;
662               }
663             else
664               prev = e;
665             e = e.next;
666           }
667       }
668   }
669
670   /**
671    * Removes an image with the specified ID and scale from this MediaTracker.
672    *
673    * @param image the image to be removed
674    */
675   public void removeImage(Image image, int id, int width, int height)
676   {
677     synchronized (this)
678       {
679         MediaEntry e = head;
680         MediaEntry prev = null;
681         while (e != null)
682           {
683             if (e.id == id && e.image == image
684                 && e.width == width && e.height == height)
685               {
686                 if (prev == null)
687                   head = e.next;
688                 else
689                   prev.next = e.next;
690               }
691             else
692               prev = e;
693             e = e.next;
694           }
695       }
696   }
697 }