Source for java.awt.MediaTracker

   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:             else
 638:               prev = e;
 639:             e = e.next;
 640:           }
 641:       }
 642:   }
 643: 
 644:   /**
 645:    * Removes an image with the specified ID from this MediaTracker.
 646:    *
 647:    * @param image the image to be removed
 648:    */
 649:   public void removeImage(Image image, int id)
 650:   {
 651:     synchronized (this)
 652:       {
 653:         MediaEntry e = head;
 654:         MediaEntry prev = null;
 655:         while (e != null)
 656:           {
 657:             if (e.id == id && e.image == image)
 658:               {
 659:                 if (prev == null)
 660:                   head = e.next;
 661:                 else
 662:                   prev.next = e.next;
 663:               }
 664:             else
 665:               prev = e;
 666:             e = e.next;
 667:           }
 668:       }
 669:   }
 670: 
 671:   /**
 672:    * Removes an image with the specified ID and scale from this MediaTracker.
 673:    *
 674:    * @param image the image to be removed
 675:    */
 676:   public void removeImage(Image image, int id, int width, int height)
 677:   {
 678:     synchronized (this)
 679:       {
 680:         MediaEntry e = head;
 681:         MediaEntry prev = null;
 682:         while (e != null)
 683:           {
 684:             if (e.id == id && e.image == image
 685:                 && e.width == width && e.height == height)
 686:               {
 687:                 if (prev == null)
 688:                   head = e.next;
 689:                 else
 690:                   prev.next = e.next;
 691:               }
 692:             else
 693:               prev = e;
 694:             e = e.next;
 695:           }
 696:       }
 697:   }
 698: }