MediaTracker.java 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699
  1. /* MediaTracker.java -- Class used for keeping track of images
  2. Copyright (C) 1999, 2002, 2004, 2005 Free Software Foundation, Inc.
  3. This file is part of GNU Classpath.
  4. GNU Classpath is free software; you can redistribute it and/or modify
  5. it under the terms of the GNU General Public License as published by
  6. the Free Software Foundation; either version 2, or (at your option)
  7. any later version.
  8. GNU Classpath is distributed in the hope that it will be useful, but
  9. WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  11. General Public License for more details.
  12. You should have received a copy of the GNU General Public License
  13. along with GNU Classpath; see the file COPYING. If not, write to the
  14. Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  15. 02110-1301 USA.
  16. Linking this library statically or dynamically with other modules is
  17. making a combined work based on this library. Thus, the terms and
  18. conditions of the GNU General Public License cover the whole
  19. combination.
  20. As a special exception, the copyright holders of this library give you
  21. permission to link this library with independent modules to produce an
  22. executable, regardless of the license terms of these independent
  23. modules, and to copy and distribute the resulting executable under
  24. terms of your choice, provided that you also meet, for each linked
  25. independent module, the terms and conditions of the license of that
  26. module. An independent module is a module which is not derived from
  27. or based on this library. If you modify this library, you may extend
  28. this exception to your version of the library, but you are not
  29. obligated to do so. If you do not wish to do so, delete this
  30. exception statement from your version. */
  31. package java.awt;
  32. import java.awt.image.ImageObserver;
  33. import java.util.ArrayList;
  34. /**
  35. * This class is used for keeping track of the status of various media
  36. * objects.
  37. *
  38. * Media objects are tracked by assigning them an ID. It is possible
  39. * to assign the same ID to mutliple objects, effectivly grouping them
  40. * together. In this case the status flags ({@link #statusID}) and error flag
  41. * (@link #isErrorID} and {@link #getErrorsID}) are ORed together. This
  42. * means that you cannot say exactly which media object has which status,
  43. * at most you can say that there <em>are</em> certain media objects with
  44. * some certain status.
  45. *
  46. * At the moment only images are supported by this class.
  47. *
  48. * @author Aaron M. Renn (arenn@urbanophile.com)
  49. * @author Bryce McKinlay
  50. */
  51. public class MediaTracker implements java.io.Serializable
  52. {
  53. /** Indicates that the media is still loading. */
  54. public static final int LOADING = 1 << 0;
  55. /** Indicates that the loading operation has been aborted. */
  56. public static final int ABORTED = 1 << 1;
  57. /** Indicates that an error has occured during loading of the media. */
  58. public static final int ERRORED = 1 << 2;
  59. /** Indicates that the media has been successfully and completely loaded. */
  60. public static final int COMPLETE = 1 << 3;
  61. /** The component on which the media is eventually been drawn. */
  62. Component target;
  63. /** The head of the linked list of tracked media objects. */
  64. MediaEntry head;
  65. /** Our serialVersionUID for serialization. */
  66. static final long serialVersionUID = -483174189758638095L;
  67. /**
  68. * This represents a media object that is tracked by a MediaTracker.
  69. * It also implements a simple linked list.
  70. */
  71. // FIXME: The serialized form documentation says MediaEntry is a
  72. // serializable field, but the serialized form of MediaEntry itself
  73. // doesn't appear to be documented.
  74. class MediaEntry implements ImageObserver
  75. {
  76. /** The ID of the media object. */
  77. int id;
  78. /** The media object. (only images are supported ATM). */
  79. Image image;
  80. /** The link to the next entry in the list. */
  81. MediaEntry next;
  82. /** The tracking status. */
  83. int status;
  84. /** The width of the image. */
  85. int width;
  86. /** The height of the image. */
  87. int height;
  88. /**
  89. * Receives notification from an {@link java.awt.image.ImageProducer}
  90. * that more data of the image is available.
  91. *
  92. * @param img the image that is updated
  93. * @param flags flags from the ImageProducer that indicate the status
  94. * of the loading process
  95. * @param x the X coordinate of the upper left corner of the image
  96. * @param y the Y coordinate of the upper left corner of the image
  97. * @param width the width of the image
  98. * @param height the height of the image
  99. *
  100. * @return <code>true</code> if more data is needed, <code>false</code>
  101. * otherwise
  102. *
  103. * @see java.awt.image.ImageObserver
  104. */
  105. public boolean imageUpdate(Image img, int flags, int x, int y,
  106. int width, int height)
  107. {
  108. if ((flags & ABORT) != 0)
  109. status = ABORTED;
  110. else if ((flags & ERROR) != 0)
  111. status = ERRORED;
  112. else if ((flags & ALLBITS) != 0)
  113. status = COMPLETE;
  114. else
  115. status = 0;
  116. synchronized (MediaTracker.this)
  117. {
  118. MediaTracker.this.notifyAll();
  119. }
  120. // If status is not COMPLETE then we need more updates.
  121. return ((status & (COMPLETE | ERRORED | ABORTED)) == 0);
  122. }
  123. }
  124. /**
  125. * Constructs a new MediaTracker for the component <code>c</code>. The
  126. * component should be the component that uses the media (i.e. draws it).
  127. *
  128. * @param c the Component that wants to use the media
  129. */
  130. public MediaTracker(Component c)
  131. {
  132. target = c;
  133. }
  134. /**
  135. * Adds an image to the tracker with the specified <code>ID</code>.
  136. *
  137. * @param image the image to be added
  138. * @param id the ID of the tracker list to which the image is added
  139. */
  140. public void addImage(Image image, int id)
  141. {
  142. MediaEntry e = new MediaEntry();
  143. e.id = id;
  144. e.image = image;
  145. synchronized(this)
  146. {
  147. e.next = head;
  148. head = e;
  149. }
  150. }
  151. /**
  152. * Adds an image to the tracker with the specified <code>ID</code>.
  153. * The image is expected to be rendered with the specified width and
  154. * height.
  155. *
  156. * @param image the image to be added
  157. * @param id the ID of the tracker list to which the image is added
  158. * @param width the width of the image
  159. * @param height the height of the image
  160. */
  161. public void addImage(Image image, int id, int width, int height)
  162. {
  163. MediaEntry e = new MediaEntry();
  164. e.id = id;
  165. e.image = image;
  166. e.width = width;
  167. e.height = height;
  168. synchronized(this)
  169. {
  170. e.next = head;
  171. head = e;
  172. }
  173. }
  174. /**
  175. * Checks if all media objects have finished loading, i.e. are
  176. * {@link #COMPLETE}, {@link #ABORTED} or {@link #ERRORED}.
  177. *
  178. * If the media objects are not already loading, a call to this
  179. * method does <em>not</em> start loading. This is equivalent to
  180. * a call to <code>checkAll(false)</code>.
  181. *
  182. * @return if all media objects have finished loading either by beeing
  183. * complete, have been aborted or errored.
  184. */
  185. public boolean checkAll()
  186. {
  187. return checkAll(false);
  188. }
  189. /**
  190. * Checks if all media objects have finished loading, i.e. are
  191. * {@link #COMPLETE}, {@link #ABORTED} or {@link #ERRORED}.
  192. *
  193. * If the media objects are not already loading, and <code>load</code>
  194. * is <code>true</code> then a call to this
  195. * method starts loading the media objects.
  196. *
  197. * @param load if <code>true</code> this method starts loading objects
  198. * that are not already loading
  199. *
  200. * @return if all media objects have finished loading either by beeing
  201. * complete, have been aborted or errored.
  202. */
  203. public boolean checkAll(boolean load)
  204. {
  205. MediaEntry e = head;
  206. boolean result = true;
  207. while (e != null)
  208. {
  209. if ((e.status & (COMPLETE | ERRORED | ABORTED)) == 0)
  210. {
  211. if (load && ((e.status & LOADING) == 0))
  212. {
  213. if (target.prepareImage(e.image, e))
  214. e.status = COMPLETE;
  215. else
  216. {
  217. e.status = LOADING;
  218. int flags = target.checkImage(e.image, e);
  219. if ((flags & ImageObserver.ABORT) != 0)
  220. e.status = ABORTED;
  221. else if ((flags & ImageObserver.ERROR) != 0)
  222. e.status = ERRORED;
  223. else if ((flags & ImageObserver.ALLBITS) != 0)
  224. e.status = COMPLETE;
  225. }
  226. boolean complete = (e.status
  227. & (COMPLETE | ABORTED | ERRORED)) != 0;
  228. if (!complete)
  229. result = false;
  230. }
  231. else
  232. result = false;
  233. }
  234. e = e.next;
  235. }
  236. return result;
  237. }
  238. /**
  239. * Checks if any of the registered media objects has encountered an error
  240. * during loading.
  241. *
  242. * @return <code>true</code> if at least one media object has encountered
  243. * an error during loading, <code>false</code> otherwise
  244. *
  245. */
  246. public boolean isErrorAny()
  247. {
  248. MediaEntry e = head;
  249. while (e != null)
  250. {
  251. if ((e.status & ERRORED) != 0)
  252. return true;
  253. e = e.next;
  254. }
  255. return false;
  256. }
  257. /**
  258. * Returns all media objects that have encountered errors during loading.
  259. *
  260. * @return an array of all media objects that have encountered errors
  261. * or <code>null</code> if there were no errors at all
  262. */
  263. public Object[] getErrorsAny()
  264. {
  265. MediaEntry e = head;
  266. ArrayList result = null;
  267. while (e != null)
  268. {
  269. if ((e.status & ERRORED) != 0)
  270. {
  271. if (result == null)
  272. result = new ArrayList();
  273. result.add(e.image);
  274. }
  275. e = e.next;
  276. }
  277. if (result == null)
  278. return null;
  279. else
  280. return result.toArray();
  281. }
  282. /**
  283. * Waits for all media objects to finish loading, either by completing
  284. * successfully or by aborting or encountering an error.
  285. *
  286. * @throws InterruptedException if another thread interrupted the
  287. * current thread while waiting
  288. */
  289. public void waitForAll() throws InterruptedException
  290. {
  291. synchronized (this)
  292. {
  293. while (checkAll(true) == false)
  294. wait();
  295. }
  296. }
  297. /**
  298. * Waits for all media objects to finish loading, either by completing
  299. * successfully or by aborting or encountering an error.
  300. *
  301. * This method waits at most <code>ms</code> milliseconds. If the
  302. * media objects have not completed loading within this timeframe, this
  303. * method returns <code>false</code>, otherwise <code>true</code>.
  304. *
  305. * @param ms timeframe in milliseconds to wait for the media objects to
  306. * finish
  307. *
  308. * @return <code>true</code> if all media objects have successfully loaded
  309. * within the timeframe, <code>false</code> otherwise
  310. *
  311. * @throws InterruptedException if another thread interrupted the
  312. * current thread while waiting
  313. */
  314. public boolean waitForAll(long ms) throws InterruptedException
  315. {
  316. long start = System.currentTimeMillis();
  317. boolean result = checkAll(true);
  318. synchronized (this)
  319. {
  320. while (result == false)
  321. {
  322. wait(ms);
  323. result = checkAll(true);
  324. if ((System.currentTimeMillis() - start) > ms)
  325. break;
  326. }
  327. }
  328. return result;
  329. }
  330. /**
  331. * Returns the status flags of all registered media objects ORed together.
  332. * If <code>load</code> is <code>true</code> then media objects that
  333. * are not already loading will be started to load.
  334. *
  335. * @param load if set to <code>true</code> then media objects that are
  336. * not already loading are started
  337. *
  338. * @return the status flags of all tracked media objects ORed together
  339. */
  340. public int statusAll(boolean load)
  341. {
  342. int result = 0;
  343. MediaEntry e = head;
  344. while (e != null)
  345. {
  346. if (load && e.status == 0)
  347. {
  348. if (target.prepareImage(e.image, e))
  349. e.status = COMPLETE;
  350. else
  351. {
  352. e.status = LOADING;
  353. int flags = target.checkImage(e.image, e);
  354. if ((flags & ImageObserver.ABORT) != 0)
  355. e.status = ABORTED;
  356. else if ((flags & ImageObserver.ERROR) != 0)
  357. e.status = ERRORED;
  358. else if ((flags & ImageObserver.ALLBITS) != 0)
  359. e.status = COMPLETE;
  360. }
  361. }
  362. result |= e.status;
  363. e = e.next;
  364. }
  365. return result;
  366. }
  367. /**
  368. * Checks if the media objects with <code>ID</code> have completed loading.
  369. *
  370. * @param id the ID of the media objects to check
  371. *
  372. * @return <code>true</code> if all media objects with <code>ID</code>
  373. * have successfully finished
  374. */
  375. public boolean checkID(int id)
  376. {
  377. return checkID(id, false);
  378. }
  379. /**
  380. * Checks if the media objects with <code>ID</code> have completed loading.
  381. * If <code>load</code> is <code>true</code> then media objects that
  382. * are not already loading will be started to load.
  383. *
  384. * @param id the ID of the media objects to check
  385. * @param load if set to <code>true</code> then media objects that are
  386. * not already loading are started
  387. *
  388. * @return <code>true</code> if all media objects with <code>ID</code>
  389. * have successfully finished
  390. */
  391. public boolean checkID(int id, boolean load)
  392. {
  393. MediaEntry e = head;
  394. boolean result = true;
  395. while (e != null)
  396. {
  397. if (e.id == id && ((e.status & (COMPLETE | ABORTED | ERRORED)) == 0))
  398. {
  399. if (load && ((e.status & LOADING) == 0))
  400. {
  401. e.status = LOADING;
  402. if (target.prepareImage(e.image, e))
  403. e.status = COMPLETE;
  404. else
  405. {
  406. int flags = target.checkImage(e.image, e);
  407. if ((flags & ImageObserver.ABORT) != 0)
  408. e.status = ABORTED;
  409. else if ((flags & ImageObserver.ERROR) != 0)
  410. e.status = ERRORED;
  411. else if ((flags & ImageObserver.ALLBITS) != 0)
  412. e.status = COMPLETE;
  413. }
  414. boolean complete = (e.status
  415. & (COMPLETE | ABORTED | ERRORED)) != 0;
  416. if (!complete)
  417. result = false;
  418. }
  419. else
  420. result = false;
  421. }
  422. e = e.next;
  423. }
  424. return result;
  425. }
  426. /**
  427. * Returns <code>true</code> if any of the media objects with <code>ID</code>
  428. * have encountered errors during loading, false otherwise.
  429. *
  430. * @param id the ID of the media objects to check
  431. *
  432. * @return <code>true</code> if any of the media objects with <code>ID</code>
  433. * have encountered errors during loading, false otherwise
  434. */
  435. public boolean isErrorID(int id)
  436. {
  437. MediaEntry e = head;
  438. while (e != null)
  439. {
  440. if (e.id == id && ((e.status & ERRORED) != 0))
  441. return true;
  442. e = e.next;
  443. }
  444. return false;
  445. }
  446. /**
  447. * Returns all media objects with the specified ID that have encountered
  448. * an error.
  449. *
  450. * @param id the ID of the media objects to check
  451. *
  452. * @return an array of all media objects with the specified ID that
  453. * have encountered an error
  454. */
  455. public Object[] getErrorsID(int id)
  456. {
  457. MediaEntry e = head;
  458. ArrayList result = null;
  459. while (e != null)
  460. {
  461. if (e.id == id && ((e.status & ERRORED) != 0))
  462. {
  463. if (result == null)
  464. result = new ArrayList();
  465. result.add(e.image);
  466. }
  467. e = e.next;
  468. }
  469. if (result == null)
  470. return null;
  471. else
  472. return result.toArray();
  473. }
  474. /**
  475. * Waits for all media objects with the specified ID to finish loading,
  476. * either by completing successfully or by aborting or encountering an error.
  477. *
  478. * @param id the ID of the media objects to wait for
  479. *
  480. * @throws InterruptedException if another thread interrupted the
  481. * current thread while waiting
  482. */
  483. public void waitForID(int id) throws InterruptedException
  484. {
  485. MediaEntry e = head;
  486. synchronized (this)
  487. {
  488. while (checkID (id, true) == false)
  489. wait();
  490. }
  491. }
  492. /**
  493. * Waits for all media objects with the specified ID to finish loading,
  494. * either by completing successfully or by aborting or encountering an error.
  495. *
  496. * This method waits at most <code>ms</code> milliseconds. If the
  497. * media objects have not completed loading within this timeframe, this
  498. * method returns <code>false</code>, otherwise <code>true</code>.
  499. *
  500. * @param id the ID of the media objects to wait for
  501. * @param ms timeframe in milliseconds to wait for the media objects to
  502. * finish
  503. *
  504. * @return <code>true</code> if all media objects have successfully loaded
  505. * within the timeframe, <code>false</code> otherwise
  506. *
  507. * @throws InterruptedException if another thread interrupted the
  508. * current thread while waiting
  509. */
  510. public boolean waitForID(int id, long ms) throws InterruptedException
  511. {
  512. MediaEntry e = head;
  513. long start = System.currentTimeMillis();
  514. boolean result = checkID(id, true);
  515. synchronized (this)
  516. {
  517. while (result == false)
  518. {
  519. wait(ms);
  520. result = checkID(id, true);
  521. if ((System.currentTimeMillis() - start) > ms)
  522. break;
  523. }
  524. }
  525. return result;
  526. }
  527. /**
  528. * Returns the status flags of the media objects with the specified ID
  529. * ORed together.
  530. *
  531. * If <code>load</code> is <code>true</code> then media objects that
  532. * are not already loading will be started to load.
  533. *
  534. * @param load if set to <code>true</code> then media objects that are
  535. * not already loading are started
  536. *
  537. * @return the status flags of all tracked media objects ORed together
  538. */
  539. public int statusID(int id, boolean load)
  540. {
  541. int result = 0;
  542. MediaEntry e = head;
  543. while (e != null)
  544. {
  545. if (e.id == id)
  546. {
  547. if (load && e.status == 0)
  548. {
  549. if (target.prepareImage(e.image, e))
  550. e.status = COMPLETE;
  551. else
  552. {
  553. e.status = LOADING;
  554. int flags = target.checkImage(e.image, e);
  555. if ((flags & ImageObserver.ABORT) != 0)
  556. e.status = ABORTED;
  557. else if ((flags & ImageObserver.ERROR) != 0)
  558. e.status = ERRORED;
  559. else if ((flags & ImageObserver.ALLBITS) != 0)
  560. e.status = COMPLETE;
  561. }
  562. }
  563. result |= e.status;
  564. }
  565. e = e.next;
  566. }
  567. return result;
  568. }
  569. /**
  570. * Removes an image from this MediaTracker.
  571. *
  572. * @param image the image to be removed
  573. */
  574. public void removeImage(Image image)
  575. {
  576. synchronized (this)
  577. {
  578. MediaEntry e = head;
  579. MediaEntry prev = null;
  580. while (e != null)
  581. {
  582. if (e.image == image)
  583. {
  584. if (prev == null)
  585. head = e.next;
  586. else
  587. prev.next = e.next;
  588. }
  589. else
  590. prev = e;
  591. e = e.next;
  592. }
  593. }
  594. }
  595. /**
  596. * Removes an image with the specified ID from this MediaTracker.
  597. *
  598. * @param image the image to be removed
  599. */
  600. public void removeImage(Image image, int id)
  601. {
  602. synchronized (this)
  603. {
  604. MediaEntry e = head;
  605. MediaEntry prev = null;
  606. while (e != null)
  607. {
  608. if (e.id == id && e.image == image)
  609. {
  610. if (prev == null)
  611. head = e.next;
  612. else
  613. prev.next = e.next;
  614. }
  615. else
  616. prev = e;
  617. e = e.next;
  618. }
  619. }
  620. }
  621. /**
  622. * Removes an image with the specified ID and scale from this MediaTracker.
  623. *
  624. * @param image the image to be removed
  625. */
  626. public void removeImage(Image image, int id, int width, int height)
  627. {
  628. synchronized (this)
  629. {
  630. MediaEntry e = head;
  631. MediaEntry prev = null;
  632. while (e != null)
  633. {
  634. if (e.id == id && e.image == image
  635. && e.width == width && e.height == height)
  636. {
  637. if (prev == null)
  638. head = e.next;
  639. else
  640. prev.next = e.next;
  641. }
  642. else
  643. prev = e;
  644. e = e.next;
  645. }
  646. }
  647. }
  648. }