1 /*
   2  * Copyright (c) 1995, 2007, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package java.awt;
  27 
  28 import java.awt.Component;
  29 import java.awt.Image;
  30 import java.awt.image.ImageObserver;
  31 
  32 /**
  33  * The <code>MediaTracker</code> class is a utility class to track
  34  * the status of a number of media objects. Media objects could
  35  * include audio clips as well as images, though currently only
  36  * images are supported.
  37  * <p>
  38  * To use a media tracker, create an instance of
  39  * <code>MediaTracker</code> and call its <code>addImage</code>
  40  * method for each image to be tracked. In addition, each image can
  41  * be assigned a unique identifier. This identifier controls the
  42  * priority order in which the images are fetched. It can also be used
  43  * to identify unique subsets of the images that can be waited on
  44  * independently. Images with a lower ID are loaded in preference to
  45  * those with a higher ID number.
  46  *
  47  * <p>
  48  *
  49  * Tracking an animated image
  50  * might not always be useful
  51  * due to the multi-part nature of animated image
  52  * loading and painting,
  53  * but it is supported.
  54  * <code>MediaTracker</code> treats an animated image
  55  * as completely loaded
  56  * when the first frame is completely loaded.
  57  * At that point, the <code>MediaTracker</code>
  58  * signals any waiters
  59  * that the image is completely loaded.
  60  * If no <code>ImageObserver</code>s are observing the image
  61  * when the first frame has finished loading,
  62  * the image might flush itself
  63  * to conserve resources
  64  * (see {@link Image#flush()}).
  65  *
  66  * <p>
  67  * Here is an example of using <code>MediaTracker</code>:
  68  * <p>
  69  * <hr><blockquote><pre>{@code
  70  * import java.applet.Applet;
  71  * import java.awt.Color;
  72  * import java.awt.Image;
  73  * import java.awt.Graphics;
  74  * import java.awt.MediaTracker;
  75  *
  76  * public class ImageBlaster extends Applet implements Runnable {
  77  *      MediaTracker tracker;
  78  *      Image bg;
  79  *      Image anim[] = new Image[5];
  80  *      int index;
  81  *      Thread animator;
  82  *
  83  *      // Get the images for the background (id == 0)
  84  *      // and the animation frames (id == 1)
  85  *      // and add them to the MediaTracker
  86  *      public void init() {
  87  *          tracker = new MediaTracker(this);
  88  *          bg = getImage(getDocumentBase(),
  89  *                  "images/background.gif");
  90  *          tracker.addImage(bg, 0);
  91  *          for (int i = 0; i < 5; i++) {
  92  *              anim[i] = getImage(getDocumentBase(),
  93  *                      "images/anim"+i+".gif");
  94  *              tracker.addImage(anim[i], 1);
  95  *          }
  96  *      }
  97  *
  98  *      // Start the animation thread.
  99  *      public void start() {
 100  *          animator = new Thread(this);
 101  *          animator.start();
 102  *      }
 103  *
 104  *      // Stop the animation thread.
 105  *      public void stop() {
 106  *          animator = null;
 107  *      }
 108  *
 109  *      // Run the animation thread.
 110  *      // First wait for the background image to fully load
 111  *      // and paint.  Then wait for all of the animation
 112  *      // frames to finish loading. Finally, loop and
 113  *      // increment the animation frame index.
 114  *      public void run() {
 115  *          try {
 116  *              tracker.waitForID(0);
 117  *              tracker.waitForID(1);
 118  *          } catch (InterruptedException e) {
 119  *              return;
 120  *          }
 121  *          Thread me = Thread.currentThread();
 122  *          while (animator == me) {
 123  *              try {
 124  *                  Thread.sleep(100);
 125  *              } catch (InterruptedException e) {
 126  *                  break;
 127  *              }
 128  *              synchronized (this) {
 129  *                  index++;
 130  *                  if (index >= anim.length) {
 131  *                      index = 0;
 132  *                  }
 133  *              }
 134  *              repaint();
 135  *          }
 136  *      }
 137  *
 138  *      // The background image fills the frame so we
 139  *      // don't need to clear the applet on repaints.
 140  *      // Just call the paint method.
 141  *      public void update(Graphics g) {
 142  *          paint(g);
 143  *      }
 144  *
 145  *      // Paint a large red rectangle if there are any errors
 146  *      // loading the images.  Otherwise always paint the
 147  *      // background so that it appears incrementally as it
 148  *      // is loading.  Finally, only paint the current animation
 149  *      // frame if all of the frames (id == 1) are done loading,
 150  *      // so that we don't get partial animations.
 151  *      public void paint(Graphics g) {
 152  *          if ((tracker.statusAll(false) & MediaTracker.ERRORED) != 0) {
 153  *              g.setColor(Color.red);
 154  *              g.fillRect(0, 0, size().width, size().height);
 155  *              return;
 156  *          }
 157  *          g.drawImage(bg, 0, 0, this);
 158  *          if (tracker.statusID(1, false) == MediaTracker.COMPLETE) {
 159  *              g.drawImage(anim[index], 10, 10, this);
 160  *          }
 161  *      }
 162  * }
 163  * } </pre></blockquote><hr>
 164  *
 165  * @author      Jim Graham
 166  * @since       JDK1.0
 167  */
 168 public class MediaTracker implements java.io.Serializable {
 169 
 170     /**
 171      * A given <code>Component</code> that will be
 172      * tracked by a media tracker where the image will
 173      * eventually be drawn.
 174      *
 175      * @serial
 176      * @see #MediaTracker(Component)
 177      */
 178     Component target;
 179     /**
 180      * The head of the list of <code>Images</code> that is being
 181      * tracked by the <code>MediaTracker</code>.
 182      *
 183      * @serial
 184      * @see #addImage(Image, int)
 185      * @see #removeImage(Image)
 186      */
 187     MediaEntry head;
 188 
 189     /*
 190      * JDK 1.1 serialVersionUID
 191      */
 192     private static final long serialVersionUID = -483174189758638095L;
 193 
 194     /**
 195      * Creates a media tracker to track images for a given component.
 196      * @param     comp the component on which the images
 197      *                     will eventually be drawn
 198      */
 199     public MediaTracker(Component comp) {
 200         target = comp;
 201     }
 202 
 203     /**
 204      * Adds an image to the list of images being tracked by this media
 205      * tracker. The image will eventually be rendered at its default
 206      * (unscaled) size.
 207      * @param     image   the image to be tracked
 208      * @param     id      an identifier used to track this image
 209      */
 210     public void addImage(Image image, int id) {
 211         addImage(image, id, -1, -1);
 212     }
 213 
 214     /**
 215      * Adds a scaled image to the list of images being tracked
 216      * by this media tracker. The image will eventually be
 217      * rendered at the indicated width and height.
 218      *
 219      * @param     image   the image to be tracked
 220      * @param     id   an identifier that can be used to track this image
 221      * @param     w    the width at which the image is rendered
 222      * @param     h    the height at which the image is rendered
 223      */
 224     public synchronized void addImage(Image image, int id, int w, int h) {
 225         head = MediaEntry.insert(head,
 226                                  new ImageMediaEntry(this, image, id, w, h));
 227     }
 228 
 229     /**
 230      * Flag indicating that media is currently being loaded.
 231      * @see         java.awt.MediaTracker#statusAll
 232      * @see         java.awt.MediaTracker#statusID
 233      */
 234     public static final int LOADING = 1;
 235 
 236     /**
 237      * Flag indicating that the downloading of media was aborted.
 238      * @see         java.awt.MediaTracker#statusAll
 239      * @see         java.awt.MediaTracker#statusID
 240      */
 241     public static final int ABORTED = 2;
 242 
 243     /**
 244      * Flag indicating that the downloading of media encountered
 245      * an error.
 246      * @see         java.awt.MediaTracker#statusAll
 247      * @see         java.awt.MediaTracker#statusID
 248      */
 249     public static final int ERRORED = 4;
 250 
 251     /**
 252      * Flag indicating that the downloading of media was completed
 253      * successfully.
 254      * @see         java.awt.MediaTracker#statusAll
 255      * @see         java.awt.MediaTracker#statusID
 256      */
 257     public static final int COMPLETE = 8;
 258 
 259     static final int DONE = (ABORTED | ERRORED | COMPLETE);
 260 
 261     /**
 262      * Checks to see if all images being tracked by this media tracker
 263      * have finished loading.
 264      * <p>
 265      * This method does not start loading the images if they are not
 266      * already loading.
 267      * <p>
 268      * If there is an error while loading or scaling an image, then that
 269      * image is considered to have finished loading. Use the
 270      * <code>isErrorAny</code> or <code>isErrorID</code> methods to
 271      * check for errors.
 272      * @return      <code>true</code> if all images have finished loading,
 273      *                       have been aborted, or have encountered
 274      *                       an error; <code>false</code> otherwise
 275      * @see         java.awt.MediaTracker#checkAll(boolean)
 276      * @see         java.awt.MediaTracker#checkID
 277      * @see         java.awt.MediaTracker#isErrorAny
 278      * @see         java.awt.MediaTracker#isErrorID
 279      */
 280     public boolean checkAll() {
 281         return checkAll(false, true);
 282     }
 283 
 284     /**
 285      * Checks to see if all images being tracked by this media tracker
 286      * have finished loading.
 287      * <p>
 288      * If the value of the <code>load</code> flag is <code>true</code>,
 289      * then this method starts loading any images that are not yet
 290      * being loaded.
 291      * <p>
 292      * If there is an error while loading or scaling an image, that
 293      * image is considered to have finished loading. Use the
 294      * <code>isErrorAny</code> and <code>isErrorID</code> methods to
 295      * check for errors.
 296      * @param       load   if <code>true</code>, start loading any
 297      *                       images that are not yet being loaded
 298      * @return      <code>true</code> if all images have finished loading,
 299      *                       have been aborted, or have encountered
 300      *                       an error; <code>false</code> otherwise
 301      * @see         java.awt.MediaTracker#checkID
 302      * @see         java.awt.MediaTracker#checkAll()
 303      * @see         java.awt.MediaTracker#isErrorAny()
 304      * @see         java.awt.MediaTracker#isErrorID(int)
 305      */
 306     public boolean checkAll(boolean load) {
 307         return checkAll(load, true);
 308     }
 309 
 310     private synchronized boolean checkAll(boolean load, boolean verify) {
 311         MediaEntry cur = head;
 312         boolean done = true;
 313         while (cur != null) {
 314             if ((cur.getStatus(load, verify) & DONE) == 0) {
 315                 done = false;
 316             }
 317             cur = cur.next;
 318         }
 319         return done;
 320     }
 321 
 322     /**
 323      * Checks the error status of all of the images.
 324      * @return   <code>true</code> if any of the images tracked
 325      *                  by this media tracker had an error during
 326      *                  loading; <code>false</code> otherwise
 327      * @see      java.awt.MediaTracker#isErrorID
 328      * @see      java.awt.MediaTracker#getErrorsAny
 329      */
 330     public synchronized boolean isErrorAny() {
 331         MediaEntry cur = head;
 332         while (cur != null) {
 333             if ((cur.getStatus(false, true) & ERRORED) != 0) {
 334                 return true;
 335             }
 336             cur = cur.next;
 337         }
 338         return false;
 339     }
 340 
 341     /**
 342      * Returns a list of all media that have encountered an error.
 343      * @return       an array of media objects tracked by this
 344      *                        media tracker that have encountered
 345      *                        an error, or <code>null</code> if
 346      *                        there are none with errors
 347      * @see          java.awt.MediaTracker#isErrorAny
 348      * @see          java.awt.MediaTracker#getErrorsID
 349      */
 350     public synchronized Object[] getErrorsAny() {
 351         MediaEntry cur = head;
 352         int numerrors = 0;
 353         while (cur != null) {
 354             if ((cur.getStatus(false, true) & ERRORED) != 0) {
 355                 numerrors++;
 356             }
 357             cur = cur.next;
 358         }
 359         if (numerrors == 0) {
 360             return null;
 361         }
 362         Object errors[] = new Object[numerrors];
 363         cur = head;
 364         numerrors = 0;
 365         while (cur != null) {
 366             if ((cur.getStatus(false, false) & ERRORED) != 0) {
 367                 errors[numerrors++] = cur.getMedia();
 368             }
 369             cur = cur.next;
 370         }
 371         return errors;
 372     }
 373 
 374     /**
 375      * Starts loading all images tracked by this media tracker. This
 376      * method waits until all the images being tracked have finished
 377      * loading.
 378      * <p>
 379      * If there is an error while loading or scaling an image, then that
 380      * image is considered to have finished loading. Use the
 381      * <code>isErrorAny</code> or <code>isErrorID</code> methods to
 382      * check for errors.
 383      * @see         java.awt.MediaTracker#waitForID(int)
 384      * @see         java.awt.MediaTracker#waitForAll(long)
 385      * @see         java.awt.MediaTracker#isErrorAny
 386      * @see         java.awt.MediaTracker#isErrorID
 387      * @exception   InterruptedException  if any thread has
 388      *                                     interrupted this thread
 389      */
 390     public void waitForAll() throws InterruptedException {
 391         waitForAll(0);
 392     }
 393 
 394     /**
 395      * Starts loading all images tracked by this media tracker. This
 396      * method waits until all the images being tracked have finished
 397      * loading, or until the length of time specified in milliseconds
 398      * by the <code>ms</code> argument has passed.
 399      * <p>
 400      * If there is an error while loading or scaling an image, then
 401      * that image is considered to have finished loading. Use the
 402      * <code>isErrorAny</code> or <code>isErrorID</code> methods to
 403      * check for errors.
 404      * @param       ms       the number of milliseconds to wait
 405      *                       for the loading to complete
 406      * @return      <code>true</code> if all images were successfully
 407      *                       loaded; <code>false</code> otherwise
 408      * @see         java.awt.MediaTracker#waitForID(int)
 409      * @see         java.awt.MediaTracker#waitForAll(long)
 410      * @see         java.awt.MediaTracker#isErrorAny
 411      * @see         java.awt.MediaTracker#isErrorID
 412      * @exception   InterruptedException  if any thread has
 413      *                                     interrupted this thread.
 414      */
 415     public synchronized boolean waitForAll(long ms)
 416         throws InterruptedException
 417     {
 418         long end = System.currentTimeMillis() + ms;
 419         boolean first = true;
 420         while (true) {
 421             int status = statusAll(first, first);
 422             if ((status & LOADING) == 0) {
 423                 return (status == COMPLETE);
 424             }
 425             first = false;
 426             long timeout;
 427             if (ms == 0) {
 428                 timeout = 0;
 429             } else {
 430                 timeout = end - System.currentTimeMillis();
 431                 if (timeout <= 0) {
 432                     return false;
 433                 }
 434             }
 435             wait(timeout);
 436         }
 437     }
 438 
 439     /**
 440      * Calculates and returns the bitwise inclusive <b>OR</b> of the
 441      * status of all media that are tracked by this media tracker.
 442      * <p>
 443      * Possible flags defined by the
 444      * <code>MediaTracker</code> class are <code>LOADING</code>,
 445      * <code>ABORTED</code>, <code>ERRORED</code>, and
 446      * <code>COMPLETE</code>. An image that hasn't started
 447      * loading has zero as its status.
 448      * <p>
 449      * If the value of <code>load</code> is <code>true</code>, then
 450      * this method starts loading any images that are not yet being loaded.
 451      *
 452      * @param        load   if <code>true</code>, start loading
 453      *                            any images that are not yet being loaded
 454      * @return       the bitwise inclusive <b>OR</b> of the status of
 455      *                            all of the media being tracked
 456      * @see          java.awt.MediaTracker#statusID(int, boolean)
 457      * @see          java.awt.MediaTracker#LOADING
 458      * @see          java.awt.MediaTracker#ABORTED
 459      * @see          java.awt.MediaTracker#ERRORED
 460      * @see          java.awt.MediaTracker#COMPLETE
 461      */
 462     public int statusAll(boolean load) {
 463         return statusAll(load, true);
 464     }
 465 
 466     private synchronized int statusAll(boolean load, boolean verify) {
 467         MediaEntry cur = head;
 468         int status = 0;
 469         while (cur != null) {
 470             status = status | cur.getStatus(load, verify);
 471             cur = cur.next;
 472         }
 473         return status;
 474     }
 475 
 476     /**
 477      * Checks to see if all images tracked by this media tracker that
 478      * are tagged with the specified identifier have finished loading.
 479      * <p>
 480      * This method does not start loading the images if they are not
 481      * already loading.
 482      * <p>
 483      * If there is an error while loading or scaling an image, then that
 484      * image is considered to have finished loading. Use the
 485      * <code>isErrorAny</code> or <code>isErrorID</code> methods to
 486      * check for errors.
 487      * @param       id   the identifier of the images to check
 488      * @return      <code>true</code> if all images have finished loading,
 489      *                       have been aborted, or have encountered
 490      *                       an error; <code>false</code> otherwise
 491      * @see         java.awt.MediaTracker#checkID(int, boolean)
 492      * @see         java.awt.MediaTracker#checkAll()
 493      * @see         java.awt.MediaTracker#isErrorAny()
 494      * @see         java.awt.MediaTracker#isErrorID(int)
 495      */
 496     public boolean checkID(int id) {
 497         return checkID(id, false, true);
 498     }
 499 
 500     /**
 501      * Checks to see if all images tracked by this media tracker that
 502      * are tagged with the specified identifier have finished loading.
 503      * <p>
 504      * If the value of the <code>load</code> flag is <code>true</code>,
 505      * then this method starts loading any images that are not yet
 506      * being loaded.
 507      * <p>
 508      * If there is an error while loading or scaling an image, then that
 509      * image is considered to have finished loading. Use the
 510      * <code>isErrorAny</code> or <code>isErrorID</code> methods to
 511      * check for errors.
 512      * @param       id       the identifier of the images to check
 513      * @param       load     if <code>true</code>, start loading any
 514      *                       images that are not yet being loaded
 515      * @return      <code>true</code> if all images have finished loading,
 516      *                       have been aborted, or have encountered
 517      *                       an error; <code>false</code> otherwise
 518      * @see         java.awt.MediaTracker#checkID(int, boolean)
 519      * @see         java.awt.MediaTracker#checkAll()
 520      * @see         java.awt.MediaTracker#isErrorAny()
 521      * @see         java.awt.MediaTracker#isErrorID(int)
 522      */
 523     public boolean checkID(int id, boolean load) {
 524         return checkID(id, load, true);
 525     }
 526 
 527     private synchronized boolean checkID(int id, boolean load, boolean verify)
 528     {
 529         MediaEntry cur = head;
 530         boolean done = true;
 531         while (cur != null) {
 532             if (cur.getID() == id
 533                 && (cur.getStatus(load, verify) & DONE) == 0)
 534             {
 535                 done = false;
 536             }
 537             cur = cur.next;
 538         }
 539         return done;
 540     }
 541 
 542     /**
 543      * Checks the error status of all of the images tracked by this
 544      * media tracker with the specified identifier.
 545      * @param        id   the identifier of the images to check
 546      * @return       <code>true</code> if any of the images with the
 547      *                          specified identifier had an error during
 548      *                          loading; <code>false</code> otherwise
 549      * @see          java.awt.MediaTracker#isErrorAny
 550      * @see          java.awt.MediaTracker#getErrorsID
 551      */
 552     public synchronized boolean isErrorID(int id) {
 553         MediaEntry cur = head;
 554         while (cur != null) {
 555             if (cur.getID() == id
 556                 && (cur.getStatus(false, true) & ERRORED) != 0)
 557             {
 558                 return true;
 559             }
 560             cur = cur.next;
 561         }
 562         return false;
 563     }
 564 
 565     /**
 566      * Returns a list of media with the specified ID that
 567      * have encountered an error.
 568      * @param       id   the identifier of the images to check
 569      * @return      an array of media objects tracked by this media
 570      *                       tracker with the specified identifier
 571      *                       that have encountered an error, or
 572      *                       <code>null</code> if there are none with errors
 573      * @see         java.awt.MediaTracker#isErrorID
 574      * @see         java.awt.MediaTracker#isErrorAny
 575      * @see         java.awt.MediaTracker#getErrorsAny
 576      */
 577     public synchronized Object[] getErrorsID(int id) {
 578         MediaEntry cur = head;
 579         int numerrors = 0;
 580         while (cur != null) {
 581             if (cur.getID() == id
 582                 && (cur.getStatus(false, true) & ERRORED) != 0)
 583             {
 584                 numerrors++;
 585             }
 586             cur = cur.next;
 587         }
 588         if (numerrors == 0) {
 589             return null;
 590         }
 591         Object errors[] = new Object[numerrors];
 592         cur = head;
 593         numerrors = 0;
 594         while (cur != null) {
 595             if (cur.getID() == id
 596                 && (cur.getStatus(false, false) & ERRORED) != 0)
 597             {
 598                 errors[numerrors++] = cur.getMedia();
 599             }
 600             cur = cur.next;
 601         }
 602         return errors;
 603     }
 604 
 605     /**
 606      * Starts loading all images tracked by this media tracker with the
 607      * specified identifier. This method waits until all the images with
 608      * the specified identifier have finished loading.
 609      * <p>
 610      * If there is an error while loading or scaling an image, then that
 611      * image is considered to have finished loading. Use the
 612      * <code>isErrorAny</code> and <code>isErrorID</code> methods to
 613      * check for errors.
 614      * @param         id   the identifier of the images to check
 615      * @see           java.awt.MediaTracker#waitForAll
 616      * @see           java.awt.MediaTracker#isErrorAny()
 617      * @see           java.awt.MediaTracker#isErrorID(int)
 618      * @exception     InterruptedException  if any thread has
 619      *                          interrupted this thread.
 620      */
 621     public void waitForID(int id) throws InterruptedException {
 622         waitForID(id, 0);
 623     }
 624 
 625     /**
 626      * Starts loading all images tracked by this media tracker with the
 627      * specified identifier. This method waits until all the images with
 628      * the specified identifier have finished loading, or until the
 629      * length of time specified in milliseconds by the <code>ms</code>
 630      * argument has passed.
 631      * <p>
 632      * If there is an error while loading or scaling an image, then that
 633      * image is considered to have finished loading. Use the
 634      * <code>statusID</code>, <code>isErrorID</code>, and
 635      * <code>isErrorAny</code> methods to check for errors.
 636      * @param         id   the identifier of the images to check
 637      * @param         ms   the length of time, in milliseconds, to wait
 638      *                           for the loading to complete
 639      * @see           java.awt.MediaTracker#waitForAll
 640      * @see           java.awt.MediaTracker#waitForID(int)
 641      * @see           java.awt.MediaTracker#statusID
 642      * @see           java.awt.MediaTracker#isErrorAny()
 643      * @see           java.awt.MediaTracker#isErrorID(int)
 644      * @exception     InterruptedException  if any thread has
 645      *                          interrupted this thread.
 646      */
 647     public synchronized boolean waitForID(int id, long ms)
 648         throws InterruptedException
 649     {
 650         long end = System.currentTimeMillis() + ms;
 651         boolean first = true;
 652         while (true) {
 653             int status = statusID(id, first, first);
 654             if ((status & LOADING) == 0) {
 655                 return (status == COMPLETE);
 656             }
 657             first = false;
 658             long timeout;
 659             if (ms == 0) {
 660                 timeout = 0;
 661             } else {
 662                 timeout = end - System.currentTimeMillis();
 663                 if (timeout <= 0) {
 664                     return false;
 665                 }
 666             }
 667             wait(timeout);
 668         }
 669     }
 670 
 671     /**
 672      * Calculates and returns the bitwise inclusive <b>OR</b> of the
 673      * status of all media with the specified identifier that are
 674      * tracked by this media tracker.
 675      * <p>
 676      * Possible flags defined by the
 677      * <code>MediaTracker</code> class are <code>LOADING</code>,
 678      * <code>ABORTED</code>, <code>ERRORED</code>, and
 679      * <code>COMPLETE</code>. An image that hasn't started
 680      * loading has zero as its status.
 681      * <p>
 682      * If the value of <code>load</code> is <code>true</code>, then
 683      * this method starts loading any images that are not yet being loaded.
 684      * @param        id   the identifier of the images to check
 685      * @param        load   if <code>true</code>, start loading
 686      *                            any images that are not yet being loaded
 687      * @return       the bitwise inclusive <b>OR</b> of the status of
 688      *                            all of the media with the specified
 689      *                            identifier that are being tracked
 690      * @see          java.awt.MediaTracker#statusAll(boolean)
 691      * @see          java.awt.MediaTracker#LOADING
 692      * @see          java.awt.MediaTracker#ABORTED
 693      * @see          java.awt.MediaTracker#ERRORED
 694      * @see          java.awt.MediaTracker#COMPLETE
 695      */
 696     public int statusID(int id, boolean load) {
 697         return statusID(id, load, true);
 698     }
 699 
 700     private synchronized int statusID(int id, boolean load, boolean verify) {
 701         MediaEntry cur = head;
 702         int status = 0;
 703         while (cur != null) {
 704             if (cur.getID() == id) {
 705                 status = status | cur.getStatus(load, verify);
 706             }
 707             cur = cur.next;
 708         }
 709         return status;
 710     }
 711 
 712     /**
 713      * Removes the specified image from this media tracker.
 714      * All instances of the specified image are removed,
 715      * regardless of scale or ID.
 716      * @param   image     the image to be removed
 717      * @see     java.awt.MediaTracker#removeImage(java.awt.Image, int)
 718      * @see     java.awt.MediaTracker#removeImage(java.awt.Image, int, int, int)
 719      * @since   JDK1.1
 720      */
 721     public synchronized void removeImage(Image image) {
 722         MediaEntry cur = head;
 723         MediaEntry prev = null;
 724         while (cur != null) {
 725             MediaEntry next = cur.next;
 726             if (cur.getMedia() == image) {
 727                 if (prev == null) {
 728                     head = next;
 729                 } else {
 730                     prev.next = next;
 731                 }
 732                 cur.cancel();
 733             } else {
 734                 prev = cur;
 735             }
 736             cur = next;
 737         }
 738         notifyAll();    // Notify in case remaining images are "done".
 739     }
 740 
 741     /**
 742      * Removes the specified image from the specified tracking
 743      * ID of this media tracker.
 744      * All instances of <code>Image</code> being tracked
 745      * under the specified ID are removed regardless of scale.
 746      * @param      image the image to be removed
 747      * @param      id the tracking ID from which to remove the image
 748      * @see        java.awt.MediaTracker#removeImage(java.awt.Image)
 749      * @see        java.awt.MediaTracker#removeImage(java.awt.Image, int, int, int)
 750      * @since      JDK1.1
 751      */
 752     public synchronized void removeImage(Image image, int id) {
 753         MediaEntry cur = head;
 754         MediaEntry prev = null;
 755         while (cur != null) {
 756             MediaEntry next = cur.next;
 757             if (cur.getID() == id && cur.getMedia() == image) {
 758                 if (prev == null) {
 759                     head = next;
 760                 } else {
 761                     prev.next = next;
 762                 }
 763                 cur.cancel();
 764             } else {
 765                 prev = cur;
 766             }
 767             cur = next;
 768         }
 769         notifyAll();    // Notify in case remaining images are "done".
 770     }
 771 
 772     /**
 773      * Removes the specified image with the specified
 774      * width, height, and ID from this media tracker.
 775      * Only the specified instance (with any duplicates) is removed.
 776      * @param   image the image to be removed
 777      * @param   id the tracking ID from which to remove the image
 778      * @param   width the width to remove (-1 for unscaled)
 779      * @param   height the height to remove (-1 for unscaled)
 780      * @see     java.awt.MediaTracker#removeImage(java.awt.Image)
 781      * @see     java.awt.MediaTracker#removeImage(java.awt.Image, int)
 782      * @since   JDK1.1
 783      */
 784     public synchronized void removeImage(Image image, int id,
 785                                          int width, int height) {
 786         MediaEntry cur = head;
 787         MediaEntry prev = null;
 788         while (cur != null) {
 789             MediaEntry next = cur.next;
 790             if (cur.getID() == id && cur instanceof ImageMediaEntry
 791                 && ((ImageMediaEntry) cur).matches(image, width, height))
 792             {
 793                 if (prev == null) {
 794                     head = next;
 795                 } else {
 796                     prev.next = next;
 797                 }
 798                 cur.cancel();
 799             } else {
 800                 prev = cur;
 801             }
 802             cur = next;
 803         }
 804         notifyAll();    // Notify in case remaining images are "done".
 805     }
 806 
 807     synchronized void setDone() {
 808         notifyAll();
 809     }
 810 }
 811 
 812 abstract class MediaEntry {
 813     MediaTracker tracker;
 814     int ID;
 815     MediaEntry next;
 816 
 817     int status;
 818     boolean cancelled;
 819 
 820     MediaEntry(MediaTracker mt, int id) {
 821         tracker = mt;
 822         ID = id;
 823     }
 824 
 825     abstract Object getMedia();
 826 
 827     static MediaEntry insert(MediaEntry head, MediaEntry me) {
 828         MediaEntry cur = head;
 829         MediaEntry prev = null;
 830         while (cur != null) {
 831             if (cur.ID > me.ID) {
 832                 break;
 833             }
 834             prev = cur;
 835             cur = cur.next;
 836         }
 837         me.next = cur;
 838         if (prev == null) {
 839             head = me;
 840         } else {
 841             prev.next = me;
 842         }
 843         return head;
 844     }
 845 
 846     int getID() {
 847         return ID;
 848     }
 849 
 850     abstract void startLoad();
 851 
 852     void cancel() {
 853         cancelled = true;
 854     }
 855 
 856     static final int LOADING = MediaTracker.LOADING;
 857     static final int ABORTED = MediaTracker.ABORTED;
 858     static final int ERRORED = MediaTracker.ERRORED;
 859     static final int COMPLETE = MediaTracker.COMPLETE;
 860 
 861     static final int LOADSTARTED = (LOADING | ERRORED | COMPLETE);
 862     static final int DONE = (ABORTED | ERRORED | COMPLETE);
 863 
 864     synchronized int getStatus(boolean doLoad, boolean doVerify) {
 865         if (doLoad && ((status & LOADSTARTED) == 0)) {
 866             status = (status & ~ABORTED) | LOADING;
 867             startLoad();
 868         }
 869         return status;
 870     }
 871 
 872     void setStatus(int flag) {
 873         synchronized (this) {
 874             status = flag;
 875         }
 876         tracker.setDone();
 877     }
 878 }
 879 
 880 class ImageMediaEntry extends MediaEntry implements ImageObserver,
 881 java.io.Serializable {
 882     Image image;
 883     int width;
 884     int height;
 885 
 886     /*
 887      * JDK 1.1 serialVersionUID
 888      */
 889     private static final long serialVersionUID = 4739377000350280650L;
 890 
 891     ImageMediaEntry(MediaTracker mt, Image img, int c, int w, int h) {
 892         super(mt, c);
 893         image = img;
 894         width = w;
 895         height = h;
 896     }
 897 
 898     boolean matches(Image img, int w, int h) {
 899         return (image == img && width == w && height == h);
 900     }
 901 
 902     Object getMedia() {
 903         return image;
 904     }
 905 
 906     synchronized int getStatus(boolean doLoad, boolean doVerify) {
 907         if (doVerify) {
 908             int flags = tracker.target.checkImage(image, width, height, null);
 909             int s = parseflags(flags);
 910             if (s == 0) {
 911                 if ((status & (ERRORED | COMPLETE)) != 0) {
 912                     setStatus(ABORTED);
 913                 }
 914             } else if (s != status) {
 915                 setStatus(s);
 916             }
 917         }
 918         return super.getStatus(doLoad, doVerify);
 919     }
 920 
 921     void startLoad() {
 922         if (tracker.target.prepareImage(image, width, height, this)) {
 923             setStatus(COMPLETE);
 924         }
 925     }
 926 
 927     int parseflags(int infoflags) {
 928         if ((infoflags & ERROR) != 0) {
 929             return ERRORED;
 930         } else if ((infoflags & ABORT) != 0) {
 931             return ABORTED;
 932         } else if ((infoflags & (ALLBITS | FRAMEBITS)) != 0) {
 933             return COMPLETE;
 934         }
 935         return 0;
 936     }
 937 
 938     public boolean imageUpdate(Image img, int infoflags,
 939                                int x, int y, int w, int h) {
 940         if (cancelled) {
 941             return false;
 942         }
 943         int s = parseflags(infoflags);
 944         if (s != 0 && s != status) {
 945             setStatus(s);
 946         }
 947         return ((status & LOADING) != 0);
 948     }
 949 }