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: import java.util.Enumeration;
032: import java.util.Vector;
033:
034: import javax.microedition.m2g.ExternalResourceHandler;
035:
036: import com.sun.perseus.j2d.ImageLoaderUtil;
037: import com.sun.perseus.j2d.RasterImage;
038:
039: /**
040: * JSR 226 implementation of the <code>ImageLoader</code> interface.
041: *
042: * @author <a href="mailto:kevin.wong@sun.com">Kevin Wong</a>
043: * @version $Id: SVGImageLoader.java,v 1.14 2006/06/29 10:47:34 ln156897 Exp $
044: */
045: public class SVGImageLoader extends DefaultImageLoader {
046: /**
047: * Constant used to indicate that an image has been requested to the
048: * ExternalResourceHandler but that the handler has not delivered the
049: * result yet.
050: */
051: protected final static String IMAGE_REQUESTED = "Image Requested.";
052:
053: /**
054: * Handles any external resource referenced in the image document.
055: */
056: protected ExternalResourceHandler handler;
057:
058: /**
059: * The SVGImage associated to this SVGImageLoader
060: */
061: protected SVGImageImpl svgImage;
062:
063: /**
064: * Simple hashtable used to track ImageNode objects.
065: */
066: protected Hashtable rasterImageConsumerTable = new Hashtable();
067:
068: /**
069: * ImageLoaderUtil contains helper methods which make this
070: * implementation easier.
071: */
072: protected ImageLoaderUtil loaderUtil = new ImageLoaderUtil();
073:
074: /**
075: * Set to true once the associated DocumentNode has fully loaded.
076: */
077: protected boolean documentLoaded;
078:
079: /**
080: * Keeps track of pending absoluteURI requests.
081: */
082: protected Vector pendingNeedsURI = new Vector();
083:
084: /**
085: * Constructor
086: *
087: * @param svgImage the associated SVGImage, should not be null.
088: * @param handler the ExternalResourceHandler which will get the images
089: * data.
090: */
091: public SVGImageLoader(SVGImageImpl svgImage,
092: ExternalResourceHandler handler) {
093: if (svgImage == null || handler == null) {
094: throw new NullPointerException();
095: }
096:
097: this .svgImage = svgImage;
098: this .handler = handler;
099: }
100:
101: /**
102: * Notifies the URILoader that the given uri will be needed.
103: *
104: * @param absoluteURI the requested URI content.
105: */
106: public void needsURI(final String absoluteURI) {
107: // SVGImageLoader waits until the document has loaded before actually
108: // requesting any image from the ResourceHandler.
109: if (documentLoaded) {
110: needsURIDocumentLoaded(absoluteURI);
111: } else {
112: if (!loaderUtil.isDataURI(absoluteURI)
113: && !pendingNeedsURI.contains(absoluteURI)) {
114: // Cache the request for future use (see documentLoaded method)
115: pendingNeedsURI.addElement(absoluteURI);
116: }
117: }
118: }
119:
120: /**
121: * In SVGImageLoader, we wait until the document has been loaded before
122: * acting on required raster images.
123: *
124: * @param absoluteURI the requested URI content.
125: */
126: protected void needsURIDocumentLoaded(final String absoluteURI) {
127: // Do not load base64 images as we do not want to
128: // store the base64 string in the cache, because it
129: // might be huge.
130: //
131: // Correct content should not have the same base64
132: // string duplicated. Rather, the same image element
133: // can be referenced by a use element.
134: if (!loaderUtil.isDataURI(absoluteURI)) {
135: boolean isRequested = true;
136: synchronized (cache) {
137: // First check if the image is already available (implies that
138: // images has been requested and obtained successfully)
139: Object imgObj = cache.get(absoluteURI);
140: if (null == imgObj) {
141: // The image has not been requested yet
142: isRequested = false;
143: addToCache(absoluteURI, IMAGE_REQUESTED);
144: } else if (IMAGE_REQUESTED != imgObj) {
145: // The image has been requested and retrieved
146: setRasterImageConsumerImage(absoluteURI,
147: (RasterImage) imgObj);
148: return;
149: }
150: }
151:
152: // requestResource() called outside of synchronized block
153: // so that the cache is not unnecessarily locked. handler is an
154: // external implementation and the behavior is unpredictable.
155: if (!isRequested) {
156: System.err.println("handler.requestResource: "
157: + absoluteURI);
158: handler.requestResource(svgImage, absoluteURI);
159: }
160: }
161: }
162:
163: /**
164: * Requests the given image. This call blocks until an image is
165: * returned.
166: *
167: * @param uri the requested URI.
168: * @return the loaded image or the same image as returned by
169: * a <code>getBrokenImage</code> call if the image could
170: * not be loaded.
171: */
172: public RasterImage getImageAndWait(final String uri) {
173: // If we are dealing with a data URI, decode the image
174: // now. Data URIs do not go in the cache.
175: if (loaderUtil.isDataURI(uri)) {
176: return loaderUtil.getEmbededImage(uri);
177: }
178:
179: Object img;
180:
181: // We are dealing with a regular URI which requires IO.
182: // The image might already be in the loading queue from
183: // a call in needsURI.
184: synchronized (cache) {
185: img = cache.get(uri);
186: }
187:
188: if ((img != null) && (img != IMAGE_REQUESTED)) {
189: return (RasterImage) img;
190: }
191:
192: // Make the sure the image is requested
193: if (img == null) {
194: needsURI(uri);
195: }
196:
197: // The URI has not been retrieved yet...
198: img = ((SVGImageImpl) svgImage).waitOnRequestCompleted(uri);
199:
200: return (RasterImage) img;
201: }
202:
203: /**
204: * Requests the given image. This call returns immediately and
205: * the image is set on the input <code>ImageNode</code> when the
206: * image becomes available.
207: *
208: * @param uri the requested URI.
209: * @param rasterImageConsumer the <code>RasterImageConsumer</code> whose
210: * image member should be set as a result of loading the image.
211: */
212: public void getImageLater(final String uri,
213: final RasterImageConsumer rasterImageConsumer) {
214: // Only load later images which have not been loaded yet
215: // and which are not data URIs.
216: if (loaderUtil.isDataURI(uri)) {
217: rasterImageConsumer.setImage(loaderUtil
218: .getEmbededImage(uri), uri);
219: return;
220: }
221:
222: Object img = null;
223: synchronized (cache) {
224: img = cache.get(uri);
225: }
226:
227: if ((img != null) && (img != IMAGE_REQUESTED)) {
228: rasterImageConsumer.setImage((RasterImage) img, uri);
229: return;
230: }
231:
232: // Save ImageNode associated with the uri for use with SVGImage's
233: // requestCompleted().
234: addRasterImageConsumer(uri, rasterImageConsumer);
235: }
236:
237: /**
238: * Determines whether this ImageLoader can handle relative uri's
239: *
240: * @return true if this ImageLoader can handle relative uri's;
241: * false otherwise.
242: */
243: public boolean allowsRelativeURI() {
244: return true;
245: }
246:
247: /**
248: * Some ImageLoader implementations may wish to wait until the end of the
249: * Document load to start retrieving resources. This method notifies
250: * the implementation that the DocumentNode completed loading successfully.
251: *
252: * @param doc the DocumentNode which just finised loading.
253: */
254: public void documentLoaded(final DocumentNode doc) {
255: // Place a needsURIDocumentLoaded call for each entry in the
256: // pendingNeedsURI vector. See the needsURI method.
257: documentLoaded = true;
258: int n = pendingNeedsURI.size();
259: for (int i = 0; i < n; i++) {
260: needsURIDocumentLoaded((String) pendingNeedsURI
261: .elementAt(i));
262: }
263:
264: // Empty pending requests
265: pendingNeedsURI.removeAllElements();
266: }
267:
268: /**
269: * In cases where the ImageLoader may update the images associated to a URI,
270: * RasterImageConsumer interested in updates need to register their interest
271: * throught this method.
272: *
273: * @param absoluteURI the URI the RasterImageConsumer is interested in.
274: * @param imageNode the RasterImageConsumer interested in the URI.
275: */
276: public void addRasterImageConsumer(final String absoluteURI,
277: final RasterImageConsumer imageNode) {
278: if (absoluteURI == null) {
279: return;
280: }
281:
282: synchronized (rasterImageConsumerTable) {
283: Vector v = (Vector) rasterImageConsumerTable
284: .get(absoluteURI);
285: if (v == null) {
286: v = new Vector(1);
287: v.addElement(imageNode);
288: rasterImageConsumerTable.put(absoluteURI, v);
289: } else {
290: if (!v.contains(imageNode)) {
291: v.addElement(imageNode);
292: }
293: }
294: }
295: }
296:
297: /**
298: * In cases where the ImageLoader may update the images associated to a URI,
299: * RasterImageConsumer interested in updates need to de-register their
300: * interest throught this method.
301: *
302: * @param absoluteURI the URI the RasterImageConsumer is interested in.
303: * @param imageNode the RasterImageConsumer interested in the URI.
304: */
305: public void removeRasterImageConsumer(final String absoluteURI,
306: final RasterImageConsumer imageNode) {
307: if (absoluteURI != null) {
308: synchronized (rasterImageConsumerTable) {
309: Vector v = (Vector) rasterImageConsumerTable
310: .get(absoluteURI);
311: if (v != null) {
312: v.removeElement(imageNode);
313: }
314: }
315: }
316: }
317:
318: /**
319: * Returns the Image that was previously loaded.
320: *
321: * @param uri the key for the Image cache.
322: * @return the Image associated with the key.
323: */
324: public RasterImage getImageFromCache(final String uri) {
325: Object img;
326: synchronized (cache) {
327: img = cache.get(uri);
328: if (IMAGE_REQUESTED == img) {
329: img = null;
330: }
331: }
332: return (RasterImage) img;
333: }
334:
335: /**
336: * Adds image to the Image cache.
337: *
338: * @param uri the key for the Image cache.
339: * @param image the Image to store.
340: */
341: void addToCache(final String uri, final Object image) {
342: synchronized (cache) {
343: cache.put(uri, image);
344: }
345: }
346:
347: /**
348: * Implementation helper.
349: *
350: * @param uri the uri identifying the image resource.
351: * @param image the RasterImage to send to the consumers.
352: */
353: void setRasterImageConsumerImage(final String uri,
354: final RasterImage image) {
355: ((DocumentNode) svgImage.getDocument())
356: .safeInvokeAndWait(new Runnable() {
357: public void run() {
358: synchronized (rasterImageConsumerTable) {
359: Vector v = (Vector) rasterImageConsumerTable
360: .get(uri);
361: if (v != null) {
362: int n = v.size();
363: for (int i = 0; i < n; i++) {
364: final RasterImageConsumer in = (RasterImageConsumer) v
365: .elementAt(i);
366: in.setImage(image, uri);
367: }
368: }
369: }
370: }
371: });
372: }
373:
374: }
|