001: /*
002: *
003: *
004: * Copyright 1990-2007 Sun Microsystems, Inc. All Rights Reserved.
005: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
006: *
007: * This program is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU General Public License version
009: * 2 only, as published by the Free Software Foundation.
010: *
011: * This program is distributed in the hope that it will be useful, but
012: * WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * General Public License version 2 for more details (a copy is
015: * included at /legal/license.txt).
016: *
017: * You should have received a copy of the GNU General Public License
018: * version 2 along with this work; if not, write to the Free Software
019: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA
021: *
022: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
023: * Clara, CA 95054 or visit www.sun.com if you need additional
024: * information or have any questions.
025: */
026: package com.sun.perseus.model;
027:
028: import com.sun.perseus.platform.URLResolver;
029:
030: import java.util.Hashtable;
031:
032: import com.sun.perseus.util.RunnableQueue;
033: import com.sun.perseus.util.RunnableQueue.RunnableHandler;
034: import com.sun.perseus.util.RunnableQueue.RunnableQueueHandler;
035:
036: import com.sun.perseus.j2d.ImageLoaderUtil;
037: import com.sun.perseus.j2d.RasterImage;
038:
039: /**
040: * Default implementation of the <code>ImageLoader</code> interface.
041: *
042: * @version $Id: DefaultImageLoader.java,v 1.4 2006/06/29 10:47:30 ln156897 Exp $
043: */
044: public class DefaultImageLoader implements ImageLoader {
045: /**
046: * Simple hashtable used to cache image objects.
047: */
048: protected Hashtable cache = new Hashtable();
049:
050: /**
051: * RunnableQueue used to load image asynchronously
052: * @see #getImageLater
053: */
054: protected static RunnableQueue loadingQueue;
055:
056: /**
057: * ImageLoaderUtil contains helper methods which make this
058: * implementation easier.
059: */
060: protected ImageLoaderUtil loaderUtil = new ImageLoaderUtil();
061:
062: /**
063: * Use a single loading queue for the implementation.
064: */
065: static {
066: loadingQueue = RunnableQueue.createRunnableQueue(null);
067: loadingQueue.resumeExecution();
068: }
069:
070: /**
071: * Default constructor
072: */
073: public DefaultImageLoader() {
074: }
075:
076: /**
077: * Returns the image that should be used to represent
078: * an image which is loading.
079: *
080: * @return the image to use to represent a pending loading.
081: */
082: public RasterImage getLoadingImage() {
083: return loaderUtil.getLoadingImage();
084: }
085:
086: /**
087: * Returns the image that should be used to represent an
088: * image which could not be loaded.
089: *
090: * @return the image to represent broken uris or content.
091: */
092: public RasterImage getBrokenImage() {
093: return loaderUtil.getBrokenImage();
094: }
095:
096: /**
097: * Resolves the input relative and base URI into an absolute URI
098: * which can be used in subsequent calls to needsURI, getImageAndWait
099: * or getImageLater calls.
100: * @param uri the requested URI content.
101: * @param baseURI the base URI. Needed in case uri is relative.
102: * @return the resolved URI that should be requested in follow on
103: * needsURI, getImageAndWait or getImageLater calls or null if
104: * the URI cannot be resolved.
105: */
106: public String resolveURI(final String uri, final String baseURI) {
107: if (uri == null) {
108: return null;
109: }
110:
111: // Do not load base64 images as we do not want to
112: // store the base64 string in the cache, because it
113: // might be huge.
114: if (loaderUtil.isDataURI(uri)) {
115: return uri;
116: }
117:
118: try {
119: return URLResolver.resolve(baseURI, uri);
120: } catch (IllegalArgumentException iae) {
121: return null;
122: }
123: }
124:
125: /**
126: * Notifies the URILoader that the given uri will be needed.
127: *
128: * @param absoluteURI the requested URI content.
129: */
130: public void needsURI(final String absoluteURI) {
131: // Do not load base64 images as we do not want to
132: // store the base64 string in the cache, because it
133: // might be huge.
134: //
135: // Correct content should not have the same base64
136: // string duplicated. Rather, the same image element
137: // can be referenced by a use element.
138: if (!loaderUtil.isDataURI(absoluteURI)) {
139: // Check if the image is currently loaded
140: synchronized (cache) {
141: if (cache.get(absoluteURI) == null) {
142: // Start loading the image now so that it is ready when
143: // we need it for rendering.
144: loadingQueue.invokeLater(new ImageLoadRunnable(
145: absoluteURI), null);
146: }
147: }
148: }
149: }
150:
151: /**
152: * Requests the given image. This call blocks until an image is
153: * returned.
154: *
155: * @param uri the requested URI. Should be a resolved URI returned
156: * from an earlier call to <code>needsURI</code>.
157: * @return the image after it has been loaded. If the image could
158: * not be loaded, this returns the same image as returned
159: * by a call to <code>getBrokenImage</code>.
160: */
161: public RasterImage getImageAndWait(final String uri) {
162: // If we are dealing with a data URI, decode the image
163: // now. Data URIs do not go in the cache.
164: if (loaderUtil.isDataURI(uri)) {
165: return loaderUtil.getEmbededImage(uri);
166: }
167:
168: // We are dealing with a regular URI which requires IO.
169: // The image might already be in the loading queue if
170: // a call to needsURI was made.
171: synchronized (cache) {
172: RasterImage img = (RasterImage) cache.get(uri);
173: if (img != null) {
174: return img;
175: }
176: }
177:
178: // The URI has not been retrieved at all or the
179: // ImageLoadRunnable has not completed yet. We
180: // simply preempt a new ImageLoadRunnable. When that
181: // one complete, we will be sure the image is in
182: // the cache.
183: ImageLoadRunnable loader = new ImageLoadRunnable(uri);
184:
185: try {
186: loadingQueue.preemptAndWait(loader, null);
187: } catch (InterruptedException ie) {
188: // We were interrupted while waiting for the image.
189: // Return brokenImage
190: return loaderUtil.getBrokenImage();
191: }
192:
193: synchronized (cache) {
194: return (RasterImage) cache.get(uri);
195: }
196: }
197:
198: /**
199: * Requests the given image. This call returns immediately and
200: * the image is set on the input <code>ImageNode</code> when the
201: * image becomes available.
202: *
203: * @param uri the requested URI. Should be a resolved URI returned
204: * from an earlier call to <code>needsURI</code>.
205: * @param rasterImageConsumer the <code>ImageNode</code> whose image
206: * member should be set as a result of loading the
207: * image.
208: */
209: public void getImageLater(final String uri,
210: final RasterImageConsumer imageNode) {
211: // Only load later images which have not been loaded yet
212: // and which are not data URIs.
213: if (loaderUtil.isDataURI(uri)) {
214: imageNode.setImage(loaderUtil.getEmbededImage(uri), uri);
215: return;
216: }
217:
218: RasterImage img = null;
219: synchronized (cache) {
220: img = (RasterImage) cache.get(uri);
221: }
222:
223: if (img != null) {
224: imageNode.setImage(img, uri);
225: return;
226: }
227:
228: ImageLoadRunnable loader = new ImageLoadRunnable(uri, imageNode);
229: loadingQueue.invokeLater(loader, null);
230: }
231:
232: /**
233: * Determines whether this ImageLoader can handle relative uri's
234: *
235: * @return true if this ImageLoader can handle relative uri's;
236: * false otherwise.
237: */
238: public boolean allowsRelativeURI() {
239: return false;
240: }
241:
242: /**
243: * Some ImageLoader implementations may wish to wait until the end of the
244: * Document load to start retrieving resources. This method notifies
245: * the implementation that the DocumentNode completed loading successfully.
246: *
247: * @param doc the DocumentNode which just finised loading.
248: */
249: public void documentLoaded(final DocumentNode doc) {
250: // Do nothing. The DefaultImageLoader implementation loads image
251: // as soon as they are notified in needsURI calls.
252: }
253:
254: /**
255: * In cases where the ImageLoader may update the images associated to a URI,
256: * RasterImageConsumer interested in updates need to register their interest
257: * throught this method.
258: *
259: * @param absoluteURI the URI the RasterImageConsumer is interested in.
260: * @param imageNode the RasterImageConsumer interested in the URI.
261: */
262: public void addRasterImageConsumer(final String absoluteURI,
263: final RasterImageConsumer imageNode) {
264: }
265:
266: /**
267: * In cases where the ImageLoader may update the images associated to a URI,
268: * RasterImageConsumer interested in updates need to de-register their
269: * interest throught this method.
270: *
271: * @param absoluteURI the URI the RasterImageConsumer is interested in.
272: * @param imageNode the RasterImageConsumer interested in the URI.
273: */
274: public void removeRasterImageConsumer(final String absoluteURI,
275: final RasterImageConsumer imageNode) {
276: }
277:
278: /**
279: * Utility method. Used to wait until all pending load operations are
280: * complete.
281: *
282: * @throws InterruptedException if the thread is interrupted while
283: * waiting in this method call.
284: */
285: public void waitForAll() throws InterruptedException {
286: loadingQueue.invokeAndWait(new Runnable() {
287: public void run() {
288: }
289: }, null);
290: }
291:
292: // =========================================================================
293:
294: /**
295: * Simple Runnables used to load images asynchronously.
296: */
297: class ImageLoadRunnable implements Runnable {
298: /**
299: * The uri from which image content is loaded.
300: */
301: private String uri;
302:
303: /**
304: * The <code>ImageNode</code> for which image content is
305: * loaded.
306: */
307: private RasterImageConsumer node;
308:
309: /**
310: * Construct with an absolute URI
311: *
312: * @param uri the uri to load
313: */
314: public ImageLoadRunnable(final String uri) {
315: this .uri = uri;
316: }
317:
318: /**
319: * Construct with an absolute URI and a node on which
320: * the loaded image should be set.
321: *
322: * @param uri the uri to load
323: * @param node the image consumer on which the image should be
324: * set.
325: */
326: public ImageLoadRunnable(final String uri,
327: final RasterImageConsumer node) {
328: this .uri = uri;
329: this .node = node;
330: }
331:
332: /**
333: * <code>Runnable</code> implementation. Loads the image and
334: * sets the resulting image on the associated <code>ImageNode</code>
335: */
336: public void run() {
337: RasterImage img = null;
338: synchronized (cache) {
339: img = (RasterImage) cache.get(uri);
340: }
341:
342: if (img == null) {
343: // If the image was not loaded before, load it now
344: // and put it in the cache.
345: img = loaderUtil.getExternalImage(uri);
346: synchronized (cache) {
347: cache.put(uri, img);
348: }
349: }
350:
351: if (node != null) {
352: // Make sure we update the image content in the
353: // update thread, if there is one
354: if (node.getUpdateQueue() != null) {
355: ImageSetter setter = new ImageSetter(img, node, uri);
356: try {
357: node.getUpdateQueue().invokeAndWait(setter,
358: node.getRunnableHandler());
359: } catch (InterruptedException ie) {
360: // We were interrupted while setting the image.
361: // This means this image loader's loadingQueue thread
362: // has been interrupted... Not much we can do as we
363: // do not know if the setter has been invoked or not.
364: // Just end gracefully.
365: return;
366: }
367: } else {
368: node.setImage(img, uri);
369: }
370: }
371: }
372: }
373:
374: /**
375: * Simple <code>Runnable</code> implementation used to set
376: * an <code>ImageNode</code>'s image in the update thread.
377: */
378: static class ImageSetter implements Runnable {
379: /**
380: * The image to set
381: */
382: private RasterImage img;
383:
384: /**
385: * The node on which the image should be set
386: */
387: private RasterImageConsumer node;
388:
389: /**
390: * The uri from which the image was loaded.
391: */
392: private String uri;
393:
394: /**
395: * Construct with an image, an image node and uri
396: *
397: * @param img the image to set on the node
398: * @param node the image node to modify
399: * @param uri the uri that was retrieved
400: */
401: public ImageSetter(final RasterImage img,
402: final RasterImageConsumer node, final String uri) {
403: this .img = img;
404: this .node = node;
405: this .uri = uri;
406: }
407:
408: /**
409: * <code>Runnable</code> implementation. We simply set the
410: * <code>ImageNode</code> image.
411: */
412: public void run() {
413: node.setImage(img, uri);
414: }
415: }
416: }
|