Frames | No Frames |
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: }