KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > batik > svggen > ImageCacher


1 /*
2
3    Copyright 2001,2003 The Apache Software Foundation
4
5    Licensed under the Apache License, Version 2.0 (the "License");
6    you may not use this file except in compliance with the License.
7    You may obtain a copy of the License at
8
9        http://www.apache.org/licenses/LICENSE-2.0
10
11    Unless required by applicable law or agreed to in writing, software
12    distributed under the License is distributed on an "AS IS" BASIS,
13    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14    See the License for the specific language governing permissions and
15    limitations under the License.
16
17  */

18 package org.apache.batik.svggen;
19
20 import java.io.ByteArrayOutputStream JavaDoc;
21 import java.io.File JavaDoc;
22 import java.io.FileInputStream JavaDoc;
23 import java.io.FileOutputStream JavaDoc;
24 import java.io.IOException JavaDoc;
25 import java.io.OutputStream JavaDoc;
26 import java.util.Arrays JavaDoc;
27 import java.util.Hashtable JavaDoc;
28 import java.util.LinkedList JavaDoc;
29 import java.util.ListIterator JavaDoc;
30 import java.util.zip.Adler32 JavaDoc;
31 import java.util.zip.Checksum JavaDoc;
32
33 import org.w3c.dom.Document JavaDoc;
34 import org.w3c.dom.Element JavaDoc;
35
36
37 /**
38  * This class implements caching functionality for raster images.
39  *
40  * @author <a HREF="mailto:paul_evenblij@compuware.com">Paul Evenblij</a>
41  * @version $Id: ImageCacher.java,v 1.6 2004/08/18 07:14:59 vhardy Exp $
42  */

43 public abstract class ImageCacher implements SVGSyntax, ErrorConstants {
44   
45     DOMTreeManager domTreeManager = null;
46     Hashtable JavaDoc imageCache;
47     Checksum JavaDoc checkSum;
48     
49     /**
50      * Creates an ImageCacher.
51      */

52     public ImageCacher() {
53         imageCache = new Hashtable JavaDoc();
54         checkSum = new Adler32 JavaDoc();
55     }
56
57     /**
58      * Creates an ImageCacher.
59      *
60      * @param domTreeManager the DOMTreeManager for the tree this cacher works on
61      */

62     public ImageCacher(DOMTreeManager domTreeManager) {
63         this();
64         setDOMTreeManager(domTreeManager);
65     }
66
67     /**
68      * Sets the DOMTreeManager this cacher should work on.
69      *
70      * @param domTreeManager the DOMTreeManager for the tree this cacher works on
71      */

72     public void setDOMTreeManager(DOMTreeManager domTreeManager) {
73         if (domTreeManager == null){
74             throw new IllegalArgumentException JavaDoc();
75         }
76         this.domTreeManager = domTreeManager;
77     }
78
79     public DOMTreeManager getDOMTreeManager(){
80         return domTreeManager;
81     }
82
83     /**
84      * Checks if the image is already in the cache, and
85      * adds it if not. Returns a unique id for the entry.
86      *
87      * @param os the image as a byte stream
88      * @param width the width of the image
89      * @param height the height of the image
90      * @param ctx the SVGGeneratorContext
91      *
92      * @return a URI for the image
93      * @throws SVGGraphics2DIOException if an error occurs during image file i/o
94      */

95     public String JavaDoc lookup(ByteArrayOutputStream JavaDoc os,
96                          int width, int height,
97                          SVGGeneratorContext ctx)
98                              throws SVGGraphics2DIOException {
99         // We determine a checksum value for the byte data, and use it
100
// as hash key for the image. This may not be unique, so we
101
// need to check on actual byte-for-byte equality as well.
102
// The checksum will be sufficient in most cases.
103
int checksum = getChecksum(os.toByteArray());
104         Integer JavaDoc key = new Integer JavaDoc(checksum);
105         String JavaDoc href = null;
106         
107         Object JavaDoc data = getCacheableData(os);
108         
109         LinkedList JavaDoc list = (LinkedList JavaDoc) imageCache.get(key);
110         if(list == null) {
111             // Key not found: make a new key/value pair
112
list = new LinkedList JavaDoc();
113             imageCache.put(key, list);
114         } else {
115             // Key found: check if the image is already in the list
116
for(ListIterator JavaDoc i = list.listIterator(0); i.hasNext(); ) {
117                 ImageCacheEntry entry = (ImageCacheEntry) i.next();
118                 if(entry.checksum == checksum && imagesMatch(entry.src, data)) {
119                     href = entry.href;
120                     break;
121                 }
122             }
123         }
124         
125         if(href == null) {
126             // Still no hit: add our own
127
ImageCacheEntry newEntry = createEntry(checksum, data,
128                                                    width, height,
129                                                    ctx);
130             list.add(newEntry);
131             href = newEntry.href;
132         }
133  
134         return href;
135     }
136     
137     /**
138      * Returns an object which can be cached.
139      * Implementation must determine which information
140      * should actually be stored.
141      *
142      * @param os the byte stream which is to be coerced
143      */

144     abstract Object JavaDoc getCacheableData(ByteArrayOutputStream JavaDoc os);
145     
146     /**
147      * Determines if two images are equal.
148      * Interpretation of the objects referred to by
149      * <code>o1</code> and <code>o2</code> is entirely
150      * implementation-dependent.
151      *
152      * @param o1 object referring to one image
153      * @param o2 object referring to the other image
154      */

155     abstract boolean imagesMatch(Object JavaDoc o1, Object JavaDoc o2)
156                                              throws SVGGraphics2DIOException;
157
158     /**
159      * Creates a new entry for keeping in the cache.
160      *
161      * @param checksum the checksum from which the hash key is derived
162      * @param data the data to be cached
163      * @param width image width
164      * @param height image height
165      * @param ctx the SVGGeneratorContext
166      */

167     abstract ImageCacheEntry createEntry(int checksum,
168                                          Object JavaDoc data,
169                                          int width, int height,
170                                          SVGGeneratorContext ctx)
171                                              throws SVGGraphics2DIOException;
172
173     /**
174      * Calculates a checksum value for the given data.
175      */

176     int getChecksum(byte[] data) {
177         checkSum.reset();
178         checkSum.update(data, 0, data.length);
179         return (int) checkSum.getValue();
180     }
181
182     /**
183      * Instances of this class are created to keep track of the
184      * set of images processed by the ImageHandler. Each entry
185      * corresponds to one unique member of this set.
186      */

187     private class ImageCacheEntry {
188
189         /** A checksum calculated for the data cached */
190         public int checksum;
191         
192         /** An implementation-dependent object referring to the data */
193         public Object JavaDoc src;
194         
195         /** A uri identifying the data */
196         public String JavaDoc href;
197
198         /**
199          * Creates a new entry
200          */

201         public ImageCacheEntry(int checksum,
202                                Object JavaDoc src,
203                                String JavaDoc href) {
204             this.checksum = checksum;
205             this.src = src;
206             this.href = href;
207         }
208     }
209
210     /**
211      * Cache implementation for images embedded in the SVG file.
212      */

213     public static class Embedded extends ImageCacher {
214     
215         /**
216          * Sets the DOMTreeManager this cacher should work on.
217          *
218          * @param domTreeManager the DOMTreeManager for the tree this cacher works on
219          */

220         public void setDOMTreeManager(DOMTreeManager domTreeManager) {
221             // A new DOMTreeManager implies a new cache, because we cache
222
// images in the SVG tree itself
223
if(this.domTreeManager != domTreeManager) {
224                 this.domTreeManager = domTreeManager;
225                 this.imageCache = new Hashtable JavaDoc();
226             }
227         }
228
229         Object JavaDoc getCacheableData(ByteArrayOutputStream JavaDoc os) {
230             // In order to have only one instance of the image data
231
// in memory, we cache the entire xlink:href attribute value,
232
// so we can just pass a reference to the tree manager.
233
return DATA_PROTOCOL_PNG_PREFIX + os.toString();
234         }
235         
236         boolean imagesMatch(Object JavaDoc o1, Object JavaDoc o2) {
237             return o1.equals(o2);
238         }
239
240         ImageCacheEntry createEntry(int checksum, Object JavaDoc data,
241                                     int width, int height,
242                                     SVGGeneratorContext ctx) {
243
244             // Get a new unique id
245
String JavaDoc id = ctx.idGenerator.generateID(ID_PREFIX_IMAGE);
246             
247             // Add the image data reference to the <defs> section
248
addToTree(id, (String JavaDoc) data, width, height, ctx);
249
250             // Create new cache entry
251
return new ImageCacheEntry(checksum, data, SIGN_POUND + id);
252         }
253
254         /**
255          * Adds a new image element to the defs section for cached images.
256          */

257         private void addToTree(String JavaDoc id,
258                                String JavaDoc href,
259                                int width, int height,
260                                SVGGeneratorContext ctx) {
261
262             Document JavaDoc domFactory = domTreeManager.getDOMFactory();
263             // Element imageDefs = getImageDefs(domFactory, ctx);
264

265             // Create and initialize the new image element
266
Element JavaDoc imageElement = domFactory.createElementNS(SVG_NAMESPACE_URI,
267                                                               SVG_IMAGE_TAG);
268             imageElement.setAttributeNS(null, SVG_ID_ATTRIBUTE,
269                                               id);
270             imageElement.setAttributeNS(null, SVG_WIDTH_ATTRIBUTE,
271                                               Integer.toString(width));
272             imageElement.setAttributeNS(null, SVG_HEIGHT_ATTRIBUTE,
273                                               Integer.toString(height));
274             imageElement.setAttributeNS(DefaultImageHandler.XLINK_NAMESPACE_URI,
275                                               ATTR_XLINK_HREF,
276                                               href);
277             // imageDefs.appendChild(imageElement);
278
domTreeManager.addOtherDef(imageElement);
279         }
280         
281
282        /**
283          * Returns the top level defs section dedicated to cached
284          * embedded images, creating one if necessary.
285          * This one very simply creates a new defs section for each image,
286          * causing them to be spread throughout the entire SVG tree.
287          * A nicer implementation would group all imageDefs sections into
288          * one.
289          */

290         /*private Element getImageDefs(Document domFactory,
291                                      SVGGeneratorContext ctx) {
292         
293             Element imageDefs = domFactory.createElementNS(SVG_NAMESPACE_URI,
294                                                            SVG_DEFS_TAG);
295
296             String id = ctx.idGenerator.generateID(ID_PREFIX_IMAGE_DEFS);
297             imageDefs.setAttributeNS(null, SVG_ID_ATTRIBUTE, id);
298             
299             domTreeManager.appendGroup(imageDefs, null);
300
301             return imageDefs;
302             }*/

303     }
304         
305     /**
306      * Cache implementation for file-based images.
307      */

308     public static class External extends ImageCacher {
309     
310         private String JavaDoc imageDir;
311         private String JavaDoc prefix;
312         private String JavaDoc suffix;
313     
314         public External(String JavaDoc imageDir, String JavaDoc prefix, String JavaDoc suffix) {
315             super();
316             this.imageDir = imageDir;
317             this.prefix = prefix;
318             this.suffix = suffix;
319         }
320
321         Object JavaDoc getCacheableData(ByteArrayOutputStream JavaDoc os) {
322             return os;
323         }
324         
325         boolean imagesMatch(Object JavaDoc o1, Object JavaDoc o2)
326                 throws SVGGraphics2DIOException {
327             boolean match = false;
328             try {
329                 FileInputStream JavaDoc imageStream =
330                                     new FileInputStream JavaDoc((File JavaDoc) o1);
331                 int imageLen = imageStream.available();
332                 byte[] imageBytes = new byte[imageLen];
333                 byte[] candidateBytes =
334                         ((ByteArrayOutputStream JavaDoc) o2).toByteArray();
335                 
336                 int bytesRead = 0;
337                 while (bytesRead != imageLen) {
338                     bytesRead += imageStream.read
339                       (imageBytes, bytesRead, imageLen-bytesRead);
340                 }
341                             
342                 match = Arrays.equals(imageBytes, candidateBytes);
343             } catch(IOException JavaDoc e) {
344                 throw new SVGGraphics2DIOException(
345                                     ERR_READ+((File JavaDoc) o1).getName());
346             }
347             return match;
348         }
349
350         ImageCacheEntry createEntry(int checksum, Object JavaDoc data,
351                                     int width, int height,
352                                     SVGGeneratorContext ctx)
353             throws SVGGraphics2DIOException {
354         
355             // Create a new file in image directory
356
File JavaDoc imageFile = null;
357
358             try {
359                 // While the files we are generating exist, try to create
360
// another unique id.
361
while (imageFile == null) {
362                     String JavaDoc fileId = ctx.idGenerator.generateID(prefix);
363                     imageFile = new File JavaDoc(imageDir, fileId + suffix);
364                     if (imageFile.exists())
365                         imageFile = null;
366                 }
367                 
368                 // Write data to file
369
OutputStream JavaDoc outputStream = new FileOutputStream JavaDoc(imageFile);
370                 ((ByteArrayOutputStream JavaDoc) data).writeTo(outputStream);
371                 ((ByteArrayOutputStream JavaDoc) data).close();
372             } catch(IOException JavaDoc e) {
373                 throw new SVGGraphics2DIOException(ERR_WRITE+imageFile.getName());
374             }
375             
376             // Create new cache entry
377
return new ImageCacheEntry(checksum, imageFile, imageFile.getName());
378         }
379
380     }
381
382 }
383
384
385
Popular Tags