home *** CD-ROM | disk | FTP | other *** search
/ DOS/V Power Report 1998 February / VPR9802A.ISO / APP_DEMO / VC / MAIN.BIN / MediaTracker.java < prev    next >
Text File  |  1997-10-27  |  23KB  |  805 lines

  1. /*
  2.  * @(#)MediaTracker.java    1.25 97/03/13
  3.  * 
  4.  * Copyright (c) 1995, 1996 Sun Microsystems, Inc. All Rights Reserved.
  5.  * 
  6.  * This software is the confidential and proprietary information of Sun
  7.  * Microsystems, Inc. ("Confidential Information").  You shall not
  8.  * disclose such Confidential Information and shall use it only in
  9.  * accordance with the terms of the license agreement you entered into
  10.  * with Sun.
  11.  * 
  12.  * SUN MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF THE
  13.  * SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
  14.  * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
  15.  * PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR ANY DAMAGES
  16.  * SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING
  17.  * THIS SOFTWARE OR ITS DERIVATIVES.
  18.  * 
  19.  * CopyrightVersion 1.1_beta
  20.  * 
  21.  */
  22.  
  23. package java.awt;
  24.  
  25. import java.awt.Component;
  26. import java.awt.Image;
  27. import java.awt.Graphics;
  28. import java.awt.image.ImageObserver;
  29.  
  30. /**
  31.  * A utility class to track the status of a number of media objects.
  32.  * Media objects could include images as well as audio clips, though
  33.  * currently only images are supported.  To use it, simply create an
  34.  * instance and then call addImage() for each image to be tracked.
  35.  * Each image can be assigned a unique ID for indentification purposes.
  36.  * The IDs control the priority order in which the images are fetched
  37.  * as well as identifying unique subsets of the images that can be
  38.  * waited on independently.  Here is an example:
  39.  * <pre>
  40.  *
  41.  * import java.applet.Applet;
  42.  * import java.awt.Color;
  43.  * import java.awt.Image;
  44.  * import java.awt.Graphics;
  45.  * import java.awt.MediaTracker;
  46.  *
  47.  * public class ImageBlaster extends Applet implements Runnable {
  48.  *    MediaTracker tracker;
  49.  *    Image bg;
  50.  *    Image anim[] = new Image[5];
  51.  *    int index;
  52.  *    Thread animator;
  53.  *
  54.  *    // Get the images for the background (id == 0) and the animation
  55.  *    // frames (id == 1) and add them to the MediaTracker
  56.  *    public void init() {
  57.  *        tracker = new MediaTracker(this);
  58.  *        bg = getImage(getDocumentBase(), "images/background.gif");
  59.  *        tracker.addImage(bg, 0);
  60.  *        for (int i = 0; i < 5; i++) {
  61.  *        anim[i] = getImage(getDocumentBase(), "images/anim"+i+".gif");
  62.  *        tracker.addImage(anim[i], 1);
  63.  *        }
  64.  *    }
  65.  *    // Start the animation thread.
  66.  *    public void start() {
  67.  *        animator = new Thread(this);
  68.  *        animator.start();
  69.  *    }
  70.  *    // Stop the animation thread.
  71.  *    public void stop() {
  72.  *        animator.stop();
  73.  *        animator = null;
  74.  *    }
  75.  *    // Run the animation thread.
  76.  *    // First wait for the background image to fully load and paint.
  77.  *    // Then wait for all of the animation frames to finish loading.
  78.  *    // Finally loop and increment the animation frame index.
  79.  *    public void run() {
  80.  *        try {
  81.  *        tracker.waitForID(0);
  82.  *        tracker.waitForID(1);
  83.  *        } catch (InterruptedException e) {
  84.  *        return;
  85.  *        }
  86.  *        Thread me = Thread.currentThread();
  87.  *        while (animator == me) {
  88.  *        try {
  89.  *            Thread.sleep(100);
  90.  *        } catch (InterruptedException e) {
  91.  *            break;
  92.  *        }
  93.  *        synchronized (this) {
  94.  *            index++;
  95.  *            if (index >= anim.length) {
  96.  *            index = 0;
  97.  *            }
  98.  *        }
  99.  *        repaint();
  100.  *        }
  101.  *    }
  102.  *    // The background image fills our frame so we don't need to clear
  103.  *    // the applet on repaints, just call the paint method.
  104.  *    public void update(Graphics g) {
  105.  *        paint(g);
  106.  *    }
  107.  *    // Paint a large red rectangle if there are any errors loading the
  108.  *    // images.  Otherwise always paint the background so that it appears
  109.  *    // incrementally as it is loading.  Finally, only paint the current
  110.  *    // animation frame if all of the frames (id == 1) are done loading
  111.  *    // so that we don't get partial animations.
  112.  *    public void paint(Graphics g) {
  113.  *        if ((tracker.statusAll(false) & MediaTracker.ERRORED) != 0) {
  114.  *        g.setColor(Color.red);
  115.  *        g.fillRect(0, 0, size().width, size().height);
  116.  *        return;
  117.  *        }
  118.  *        g.drawImage(bg, 0, 0, this);
  119.  *        if (tracker.statusID(1, false) == MediaTracker.COMPLETE) {
  120.  *        g.drawImage(anim[index], 10, 10, this);
  121.  *        }
  122.  *    }
  123.  * }
  124.  *
  125.  * </pre>
  126.  *
  127.  * @version     1.25, 03/13/97
  128.  * @author     Jim Graham
  129.  */
  130. public class MediaTracker implements java.io.Serializable {
  131.     Component target;
  132.     MediaEntry head;
  133.  
  134.     /*
  135.      * JDK 1.1 serialVersionUID 
  136.      */
  137.     private static final long serialVersionUID = -483174189758638095L;
  138.  
  139.     /**
  140.      * Creates a Media tracker to track images for a given Component.
  141.      * @param comp the component on which the images will eventually be drawn
  142.      */
  143.     public MediaTracker(Component comp) {
  144.     target = comp;
  145.     }
  146.  
  147.     /**
  148.      * Adds an image to the list of images being tracked.  The image
  149.      * will eventually be rendered at its default (unscaled) size.
  150.      * @param image the image to be tracked
  151.      * @param id the identifier used to later track this image
  152.      */
  153.     public void addImage(Image image, int id) {
  154.     addImage(image, id, -1, -1);
  155.     }
  156.  
  157.     /**
  158.      * Adds a scaled image to the list of images being tracked.  The
  159.      * image will eventually be rendered at the indicated size.
  160.      * @param image the image to be tracked
  161.      * @param id the identifier used to later track this image
  162.      * @param w the width that the image will be rendered at
  163.      * @param h the height that the image will be rendered at
  164.      */
  165.     public synchronized void addImage(Image image, int id, int w, int h) {
  166.     head = MediaEntry.insert(head,
  167.                  new ImageMediaEntry(this, image, id, w, h));
  168.     }
  169.  
  170.     /**
  171.      * Flag indicating some media is currently being loaded.
  172.      * @see #statusAll
  173.      * @see #statusID
  174.      */
  175.     public static final int LOADING = 1;
  176.  
  177.     /**
  178.      * Flag indicating the download of some media was aborted.
  179.      * @see #statusAll
  180.      * @see #statusID
  181.      */
  182.     public static final int ABORTED = 2;
  183.  
  184.     /**
  185.      * Flag indicating the download of some media encountered an error.
  186.      * @see #statusAll
  187.      * @see #statusID
  188.      */
  189.     public static final int ERRORED = 4;
  190.  
  191.     /**
  192.      * Flag indicating the download of media completed successfully.
  193.      * @see #statusAll
  194.      * @see #statusID
  195.      */
  196.     public static final int COMPLETE = 8;
  197.  
  198.     static final int DONE = (ABORTED | ERRORED | COMPLETE);
  199.  
  200.     /**
  201.      * Checks to see if all images have finished loading but does not start
  202.      * loading the images if they are not already loading.
  203.      * If there is an error while loading or scaling an image then that
  204.      * image is considered "complete."
  205.      * Use isErrorAny() or isErrorID() to check for errors.
  206.      * @return true if all images have finished loading, were aborted or
  207.      * encountered an error
  208.      * @see #checkAll(boolean)
  209.      * @see #checkID
  210.      * @see #isErrorAny
  211.      * @see #isErrorID
  212.      */
  213.     public boolean checkAll() {
  214.     return checkAll(false, true);
  215.     }
  216.  
  217.     /**
  218.      * Checks to see if all images have finished loading. If load is
  219.      * true, starts loading any images that are not yet being loaded.
  220.      * If there is an error while loading or scaling an image then
  221.      * that image is considered "complete."  Use isErrorAny() or
  222.      * isErrorID() to check for errors.
  223.  
  224.      * @param load start loading the images if this parameter is true
  225.      * @return true if all images have finished loading, were aborted or
  226.      * encountered an error
  227.      * @see #isErrorAny
  228.      * @see #isErrorID
  229.      * @see #checkID(int, boolean)
  230.      * @see #checkAll()
  231.      */
  232.     public boolean checkAll(boolean load) {
  233.     return checkAll(load, true);
  234.     }
  235.  
  236.     private synchronized boolean checkAll(boolean load, boolean verify) {
  237.     MediaEntry cur = head;
  238.     boolean done = true;
  239.     while (cur != null) {
  240.         if ((cur.getStatus(load, verify) & DONE) == 0) {
  241.         done = false;
  242.         }
  243.         cur = cur.next;
  244.     }
  245.     return done;
  246.     }
  247.  
  248.     /**
  249.      * Checks the error status of all of the images.
  250.      * @return true if any of the images had an error during loading
  251.      * @see #isErrorID
  252.      * @see #getErrorsAny
  253.      */
  254.     public synchronized boolean isErrorAny() {
  255.     MediaEntry cur = head;
  256.     while (cur != null) {
  257.         if ((cur.getStatus(false, true) & ERRORED) != 0) {
  258.         return true;
  259.         }
  260.         cur = cur.next;
  261.     }
  262.     return false;
  263.     }
  264.  
  265.     /**
  266.      * Returns a list of all media that have encountered an error.
  267.      * @return an array of media objects or null if there are no errors
  268.      * @see #isErrorAny
  269.      * @see #getErrorsID
  270.      */
  271.     public synchronized Object[] getErrorsAny() {
  272.     MediaEntry cur = head;
  273.     int numerrors = 0;
  274.     while (cur != null) {
  275.         if ((cur.getStatus(false, true) & ERRORED) != 0) {
  276.         numerrors++;
  277.         }
  278.         cur = cur.next;
  279.     }
  280.     if (numerrors == 0) {
  281.         return null;
  282.     }
  283.     Object errors[] = new Object[numerrors];
  284.     cur = head;
  285.     numerrors = 0;
  286.     while (cur != null) {
  287.         if ((cur.getStatus(false, false) & ERRORED) != 0) {
  288.         errors[numerrors++] = cur.getMedia();
  289.         }
  290.         cur = cur.next;
  291.     }
  292.     return errors;
  293.     }
  294.  
  295.     /**
  296.      * Starts loading all images. Waits until they have finished loading,
  297.      * are aborted, or it receives an error.
  298.      * If there is an error while loading or scaling an image then that
  299.      * image is considered "complete."
  300.      * Use isErrorAny() or statusAll() to check for errors.
  301.      * @exception InterruptedException 
  302.      *            Another thread has interrupted this thread. 
  303.      * @see #waitForID
  304.      * @see #waitForAll(long)
  305.      * @see #isErrorAny
  306.      * @see #isErrorID
  307.      */
  308.     public void waitForAll() throws InterruptedException {
  309.     waitForAll(0);
  310.     }
  311.  
  312.     /**
  313.      * Starts loading all images. Waits until they have finished loading,
  314.      * are aborted, it receives an error, or until the specified timeout has
  315.      * elapsed.
  316.      * If there is an error while loading or scaling an image then that
  317.      * image is considered "complete."
  318.      * Use isErrorAny() or statusAll() to check for errors.
  319.      * @param ms the length of time to wait for the loading to complete
  320.      * @return true if all images were successfully loaded
  321.      * @see #waitForID
  322.      * @see #waitForAll()
  323.      * @see #isErrorAny
  324.      * @see #isErrorID
  325.      * @exception InterruptedException 
  326.      *            Another thread has interrupted this thread. 
  327.      */
  328.     public synchronized boolean waitForAll(long ms)
  329.     throws InterruptedException
  330.     {
  331.     long end = System.currentTimeMillis() + ms;
  332.     boolean first = true;
  333.     while (true) {
  334.         int status = statusAll(first, first);
  335.         if ((status & LOADING) == 0) {
  336.         return (status == COMPLETE);
  337.         }
  338.         first = false;
  339.         long timeout;
  340.         if (ms == 0) {
  341.         timeout = 0;
  342.         } else {
  343.         timeout = end - System.currentTimeMillis();
  344.         if (timeout <= 0) {
  345.             return false;
  346.         }
  347.         }
  348.         wait(timeout);
  349.     }
  350.     }
  351.  
  352.     /**
  353.      * Returns the boolean OR of the status of all of the media being
  354.      * tracked.
  355.      * @param load specifies whether to start the media loading
  356.      * @see #statusID
  357.      * @see #LOADING
  358.      * @see #ABORTED
  359.      * @see #ERRORED
  360.      * @see #COMPLETE
  361.      */
  362.     public int statusAll(boolean load) {
  363.     return statusAll(load, true);
  364.     }
  365.  
  366.     private synchronized int statusAll(boolean load, boolean verify) {
  367.     MediaEntry cur = head;
  368.     int status = 0;
  369.     while (cur != null) {
  370.         status = status | cur.getStatus(load, verify);
  371.         cur = cur.next;
  372.     }
  373.     return status;
  374.     }
  375.  
  376.     /**
  377.      * Checks to see if all images tagged with the indicated ID have
  378.      * finished loading, but does not start loading the images if they
  379.      * are not already loading.
  380.      * If there is an error while loading or scaling an image then that
  381.      * image is considered "complete."
  382.      * Use isErrorAny() or isErrorID() to check for errors.
  383.      * @param id the identifier used to determine which images to check
  384.      * @return true if all tagged images have finished loading, were
  385.      * aborted, or an error occurred.
  386.      * @see #checkID(int, boolean)
  387.      * @see #checkAll
  388.      * @see #isErrorAny
  389.      * @see #isErrorID
  390.      */
  391.     public boolean checkID(int id) {
  392.     return checkID(id, false, true);
  393.     }
  394.  
  395.     /**
  396.      * Checks to see if all images tagged with the indicated ID have
  397.      * finished loading. If load is true, starts loading any images
  398.      * with that ID that are not yet being loaded.  If there is an
  399.      * error while loading or scaling an image then that image is
  400.      * considered "complete."  Use isErrorAny() or isErrorID() to
  401.      * check for errors.
  402.  
  403.      * @param id the identifier used to determine which images to check
  404.      * @param load start loading the images if this parameter is true
  405.      * @return true if all tagged images have finished loading, were
  406.      * aborted, or an error occurred
  407.      * @see #checkID(int)
  408.      * @see #checkAll
  409.      * @see #isErrorAny
  410.      * @see #isErrorID
  411.      */
  412.     public boolean checkID(int id, boolean load) {
  413.     return checkID(id, load, true);
  414.     }
  415.  
  416.     private synchronized boolean checkID(int id, boolean load, boolean verify)
  417.     {
  418.     MediaEntry cur = head;
  419.     boolean done = true;
  420.     while (cur != null) {
  421.         if (cur.getID() == id
  422.         && (cur.getStatus(load, verify) & DONE) == 0)
  423.         {
  424.         done = false;
  425.         }
  426.         cur = cur.next;
  427.     }
  428.     return done;
  429.     }
  430.  
  431.     /**
  432.      * Checks the error status of all of the images with the specified ID.
  433.      * @param id the identifier used to determine which images to check
  434.      * @return true if any of the tagged images had an error during loading
  435.      * @see #isErrorAny
  436.      * @see #getErrorsID
  437.      */
  438.     public synchronized boolean isErrorID(int id) {
  439.     MediaEntry cur = head;
  440.     while (cur != null) {
  441.         if (cur.getID() == id
  442.         && (cur.getStatus(false, true) & ERRORED) != 0)
  443.         {
  444.         return true;
  445.         }
  446.         cur = cur.next;
  447.     }
  448.     return false;
  449.     }
  450.  
  451.     /**
  452.      * Returns a list of media with the specified ID that have encountered
  453.      * an error.
  454.      * @param id the identifier used to determine which images to return
  455.      * @return an array of media objects or null if there are no errors
  456.      * @see #isErrorID
  457.      * @see #getErrorsAny
  458.      */
  459.     public synchronized Object[] getErrorsID(int id) {
  460.     MediaEntry cur = head;
  461.     int numerrors = 0;
  462.     while (cur != null) {
  463.         if (cur.getID() == id
  464.         && (cur.getStatus(false, true) & ERRORED) != 0)
  465.         {
  466.         numerrors++;
  467.         }
  468.         cur = cur.next;
  469.     }
  470.     if (numerrors == 0) {
  471.         return null;
  472.     }
  473.     Object errors[] = new Object[numerrors];
  474.     cur = head;
  475.     numerrors = 0;
  476.     while (cur != null) {
  477.         if (cur.getID() == id
  478.         && (cur.getStatus(false, false) & ERRORED) != 0)
  479.         {
  480.         errors[numerrors++] = cur.getMedia();
  481.         }
  482.         cur = cur.next;
  483.     }
  484.     return errors;
  485.     }
  486.  
  487.     /**
  488.      * Starts loading all images with the specified ID and waits until they
  489.      * have finished loading or receive an error.
  490.      * If there is an error while loading or scaling an image then that
  491.      * image is considered "complete."
  492.      * Use statusID() or isErrorID() to check for errors.
  493.      * @param id the identifier used to determine which images to wait for
  494.      * @see #waitForAll
  495.      * @see #waitForID(int, long)
  496.      * @see #isErrorAny
  497.      * @see #isErrorID
  498.      * @exception InterruptedException 
  499.      *            Another thread has interrupted this thread. 
  500.      */
  501.     public void waitForID(int id) throws InterruptedException {
  502.     waitForID(id, 0);
  503.     }
  504.  
  505.     /**
  506.      * Starts loading all images with the specified ID. Waits until they
  507.      * have finished loading, an error occurs, or the specified
  508.      * timeout has elapsed.
  509.      * If there is an error while loading or scaling an image then that
  510.      * image is considered "complete."
  511.      * Use statusID or isErrorID to check for errors.
  512.      * @param id the identifier used to determine which images to wait for
  513.      * @param ms the length of time to wait for the loading to complete
  514.      * @see #waitForAll
  515.      * @see #waitForID(int)
  516.      * @see #isErrorAny
  517.      * @see #isErrorID
  518.      * @exception InterruptedException 
  519.      *            Another thread has interrupted this thread. 
  520.      */
  521.     public synchronized boolean waitForID(int id, long ms)
  522.     throws InterruptedException
  523.     {
  524.     long end = System.currentTimeMillis() + ms;
  525.     boolean first = true;
  526.     while (true) {
  527.         int status = statusID(id, first, first);
  528.         if ((status & LOADING) == 0) {
  529.         return (status == COMPLETE);
  530.         }
  531.         first = false;
  532.         long timeout;
  533.         if (ms == 0) {
  534.         timeout = 0;
  535.         } else {
  536.         timeout = end - System.currentTimeMillis();
  537.         if (timeout <= 0) {
  538.             return false;
  539.         }
  540.         }
  541.         wait(timeout);
  542.     }
  543.     }
  544.  
  545.     /**
  546.      * Returns the boolean OR of the status of all of the media with
  547.      * a given ID.
  548.      * @param id the identifier used to determine which images to check
  549.      * @param load specifies whether to start the media loading
  550.      * @see #statusAll
  551.      * @see #LOADING
  552.      * @see #ABORTED
  553.      * @see #ERRORED
  554.      * @see #COMPLETE
  555.      */
  556.     public int statusID(int id, boolean load) {
  557.     return statusID(id, load, true);
  558.     }
  559.  
  560.     private synchronized int statusID(int id, boolean load, boolean verify) {
  561.     MediaEntry cur = head;
  562.     int status = 0;
  563.     while (cur != null) {
  564.         if (cur.getID() == id) {
  565.         status = status | cur.getStatus(load, verify);
  566.         }
  567.         cur = cur.next;
  568.     }
  569.     return status;
  570.     }
  571.  
  572.     /**
  573.      * Remove the specified Image from this MediaTracker.
  574.      * All instances of the image are removed regardless of scale or ID.
  575.      * @param image the image to be removed
  576.      * @see #removeImage(java.awt.Image, int id)
  577.      * @see #removeImage(java.awt.Image, int id, int width, int height)
  578.      */
  579.     public synchronized void removeImage(Image image) {
  580.     MediaEntry cur = head;
  581.     MediaEntry prev = null;
  582.     while (cur != null) {
  583.         MediaEntry next = cur.next;
  584.         if (cur.getMedia() == image) {
  585.         if (prev == null) {
  586.             head = next;
  587.         } else {
  588.             prev.next = next;
  589.         }
  590.         cur.cancel();
  591.         } else {
  592.         prev = cur;
  593.         }
  594.         cur = next;
  595.     }
  596.     notifyAll();    // Notify in case remaining images are "done".
  597.     }
  598.  
  599.     /**
  600.      * Remove the specified Image from the specified ID of this MediaTracker.
  601.      * All instances of the image being tracked under the given ID are
  602.      * removed regardless of scale.
  603.      * @param image the image to be removed
  604.      * @param id the tracking ID to remove the image from
  605.      * @see #removeImage(java.awt.Image)
  606.      * @see #removeImage(java.awt.Image, int id, int width, int height)
  607.      */
  608.     public synchronized void removeImage(Image image, int id) {
  609.     MediaEntry cur = head;
  610.     MediaEntry prev = null;
  611.     while (cur != null) {
  612.         MediaEntry next = cur.next;
  613.         if (cur.getID() == id && cur.getMedia() == image) {
  614.         if (prev == null) {
  615.             head = next;
  616.         } else {
  617.             prev.next = next;
  618.         }
  619.         cur.cancel();
  620.         } else {
  621.         prev = cur;
  622.         }
  623.         cur = next;
  624.     }
  625.     notifyAll();    // Notify in case remaining images are "done".
  626.     }
  627.  
  628.     /**
  629.      * Remove the specified image with the specified size and ID from
  630.      * this MediaTracker.
  631.      * Only the specific instance (with any duplicates) is removed.
  632.      * @param image the image to be removed
  633.      * @param id the tracking ID to remove the image from
  634.      * @param width the width to remove (-1 for unscaled)
  635.      * @param height the height to remove (-1 for unscaled)
  636.      * @see #removeImage(java.awt.Image)
  637.      * @see #removeImage(java.awt.Image, int id)
  638.      */
  639.     public synchronized void removeImage(Image image, int id,
  640.                      int width, int height) {
  641.     MediaEntry cur = head;
  642.     MediaEntry prev = null;
  643.     while (cur != null) {
  644.         MediaEntry next = cur.next;
  645.         if (cur.getID() == id && cur instanceof ImageMediaEntry
  646.         && ((ImageMediaEntry) cur).matches(image, width, height))
  647.         {
  648.         if (prev == null) {
  649.             head = next;
  650.         } else {
  651.             prev.next = next;
  652.         }
  653.         cur.cancel();
  654.         } else {
  655.         prev = cur;
  656.         }
  657.         cur = next;
  658.     }
  659.     notifyAll();    // Notify in case remaining images are "done".
  660.     }
  661.  
  662.     synchronized void setDone() {
  663.     notifyAll();
  664.     }
  665. }
  666.  
  667. abstract class MediaEntry {
  668.     MediaTracker tracker;
  669.     int ID;
  670.     MediaEntry next;
  671.  
  672.     int status;
  673.     boolean cancelled;
  674.  
  675.     /*
  676.      * JDK 1.1 serialVersionUID 
  677.      */
  678.     private static final long serialVersionUID = -2924957284304726459L;
  679.  
  680.     MediaEntry(MediaTracker mt, int id) {
  681.     tracker = mt;
  682.     ID = id;
  683.     }
  684.  
  685.     abstract Object getMedia();
  686.  
  687.     static MediaEntry insert(MediaEntry head, MediaEntry me) {
  688.     MediaEntry cur = head;
  689.     MediaEntry prev = null;
  690.     while (cur != null) {
  691.         if (cur.ID > me.ID) {
  692.         break;
  693.         }
  694.         prev = cur;
  695.         cur = cur.next;
  696.     }
  697.     me.next = cur;
  698.     if (prev == null) {
  699.         head = me;
  700.     } else {
  701.         prev.next = me;
  702.     }
  703.     return head;
  704.     }
  705.  
  706.     int getID() {
  707.     return ID;
  708.     }
  709.  
  710.     abstract void startLoad();
  711.  
  712.     void cancel() {
  713.     cancelled = true;
  714.     }
  715.  
  716.     static final int LOADING = MediaTracker.LOADING;
  717.     static final int ABORTED = MediaTracker.ABORTED;
  718.     static final int ERRORED = MediaTracker.ERRORED;
  719.     static final int COMPLETE = MediaTracker.COMPLETE;
  720.  
  721.     static final int LOADSTARTED = (LOADING | ERRORED | COMPLETE);
  722.     static final int DONE = (ABORTED | ERRORED | COMPLETE);
  723.  
  724.     synchronized int getStatus(boolean doLoad, boolean doVerify) {
  725.     if (doLoad && ((status & LOADSTARTED) == 0)) {
  726.         status = (status & ~ABORTED) | LOADING;
  727.         startLoad();
  728.     }
  729.     return status;
  730.     }
  731.  
  732.     void setStatus(int flag) {
  733.     synchronized (this) {
  734.         status = flag;
  735.     }
  736.     tracker.setDone();
  737.     }
  738. }
  739.  
  740. class ImageMediaEntry extends MediaEntry implements ImageObserver, 
  741. java.io.Serializable {
  742.     Image image;
  743.     int width;
  744.     int height;
  745.  
  746.     ImageMediaEntry(MediaTracker mt, Image img, int c, int w, int h) {
  747.     super(mt, c);
  748.     image = img;
  749.     width = w;
  750.     height = h;
  751.     }
  752.  
  753.     boolean matches(Image img, int w, int h) {
  754.     return (image == img && width == w && height == h);
  755.     }
  756.  
  757.     Object getMedia() {
  758.     return image;
  759.     }
  760.  
  761.     int getStatus(boolean doLoad, boolean doVerify) {
  762.     if (doVerify) {
  763.         int flags = tracker.target.checkImage(image, width, height, this);
  764.         int s = parseflags(flags);
  765.         if (s == 0) {
  766.         if ((status & (ERRORED | COMPLETE)) != 0) {
  767.             setStatus(ABORTED);
  768.         }
  769.         } else if (s != status) {
  770.         setStatus(s);
  771.         }
  772.     }
  773.     return super.getStatus(doLoad, doVerify);
  774.     }
  775.  
  776.     void startLoad() {
  777.     if (tracker.target.prepareImage(image, width, height, this)) {
  778.         setStatus(COMPLETE);
  779.     }
  780.     }
  781.  
  782.     int parseflags(int infoflags) {
  783.     if ((infoflags & ERROR) != 0) {
  784.         return ERRORED;
  785.     } else if ((infoflags & ABORT) != 0) {
  786.         return ABORTED;
  787.     } else if ((infoflags & (ALLBITS | FRAMEBITS)) != 0) {
  788.         return COMPLETE;
  789.     }
  790.     return 0;
  791.     }
  792.  
  793.     public boolean imageUpdate(Image img, int infoflags,
  794.                    int x, int y, int w, int h) {
  795.     if (cancelled) {
  796.         return false;
  797.     }
  798.     int s = parseflags(infoflags);
  799.     if (s != 0 && s != status) {
  800.         setStatus(s);
  801.     }
  802.     return ((status & LOADING) != 0);
  803.     }
  804. }
  805.