KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > google > gwt > user > rebind > ui > ImageBundleBuilder


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

16 package com.google.gwt.user.rebind.ui;
17
18 import com.google.gwt.core.ext.GeneratorContext;
19 import com.google.gwt.core.ext.TreeLogger;
20 import com.google.gwt.core.ext.UnableToCompleteException;
21 import com.google.gwt.dev.util.Util;
22
23 import java.awt.Graphics2D JavaDoc;
24 import java.awt.image.BufferedImage JavaDoc;
25 import java.io.BufferedInputStream JavaDoc;
26 import java.io.IOException JavaDoc;
27 import java.io.InputStream JavaDoc;
28 import java.io.OutputStream JavaDoc;
29 import java.net.URL JavaDoc;
30 import java.security.MessageDigest JavaDoc;
31 import java.security.NoSuchAlgorithmException JavaDoc;
32 import java.util.ArrayList JavaDoc;
33 import java.util.HashMap JavaDoc;
34 import java.util.Iterator JavaDoc;
35 import java.util.List JavaDoc;
36 import java.util.Map JavaDoc;
37
38 import javax.imageio.ImageIO JavaDoc;
39
40 /**
41  * Accumulates state for the bundled image.
42  */

43 class ImageBundleBuilder {
44
45   /**
46    * The rectangle at which the original image is placed into the composite
47    * image.
48    */

49   public static class ImageRect {
50
51     public final int height;
52     public final BufferedImage JavaDoc image;
53     public int left;
54     public final int width;
55
56     public ImageRect(BufferedImage JavaDoc image) {
57       this.image = image;
58       this.width = image.getWidth();
59       this.height = image.getHeight();
60     }
61   }
62
63   private final Map JavaDoc imageNameToImageRectMap = new HashMap JavaDoc();
64   private final MessageDigest JavaDoc md5;
65   private final List JavaDoc orderedImageRects = new ArrayList JavaDoc();
66
67   public ImageBundleBuilder() {
68     try {
69       md5 = MessageDigest.getInstance("MD5");
70     } catch (NoSuchAlgorithmException JavaDoc e) {
71       throw new RuntimeException JavaDoc("Error initializing MD5", e);
72     }
73   }
74
75   /**
76    * Assimilates the image associated with a particular image method into the
77    * master composite. If the method names an image that has already been
78    * assimilated, the existing image rectangle is reused.
79    *
80    * @param logger a hierarchical logger which logs to the hosted console
81    * @param imageName the name of an image that can be found on the classpath
82    * @throws UnableToCompleteException if the image with name
83    * <code>imageName</code> cannot be added to the master composite image
84    */

85   public void assimilate(TreeLogger logger, String JavaDoc imageName)
86       throws UnableToCompleteException {
87
88     /*
89      * Decide whether or not we need to add to the composite image. Either way,
90      * we associated it with the rectangle of the specified image as it exists
91      * within the composite image. Note that the coordinates of the rectangle
92      * aren't computed until the composite is written.
93      */

94     ImageRect rect = getMapping(imageName);
95     if (rect == null) {
96       // Assimilate the image into the composite.
97
rect = addImage(logger, imageName);
98
99       // Map the URL to its image so that even if the same URL is used more than
100
// once, we only include the referenced image once in the bundled image.
101
putMapping(imageName, rect);
102     }
103   }
104
105   public ImageRect getMapping(String JavaDoc imageName) {
106     return (ImageRect) imageNameToImageRectMap.get(imageName);
107   }
108
109   public String JavaDoc writeBundledImage(TreeLogger logger, GeneratorContext context)
110       throws UnableToCompleteException {
111
112     // Determine how big the composited image should be by taking the
113
// sum of the widths and the max of the heights.
114
int nextLeft = 0;
115     int maxHeight = 0;
116     for (Iterator JavaDoc iter = orderedImageRects.iterator(); iter.hasNext();) {
117       ImageRect imageRect = (ImageRect) iter.next();
118       imageRect.left = nextLeft;
119       nextLeft += imageRect.width;
120       if (imageRect.height > maxHeight) {
121         maxHeight = imageRect.height;
122       }
123     }
124
125     // Create the bundled image.
126
BufferedImage JavaDoc bundledImage = new BufferedImage JavaDoc(nextLeft, maxHeight,
127         BufferedImage.TYPE_INT_ARGB_PRE);
128     Graphics2D JavaDoc g2d = bundledImage.createGraphics();
129
130     for (Iterator JavaDoc iter = orderedImageRects.iterator(); iter.hasNext();) {
131       ImageRect imageRect = (ImageRect) iter.next();
132       g2d.drawImage(imageRect.image, imageRect.left, 0, null);
133     }
134     g2d.dispose();
135
136     // Compute the strong name as the hex version of the hash.
137
byte[] hash = md5.digest();
138     char[] strongName = new char[2 * hash.length];
139     int j = 0;
140     for (int i = 0; i < hash.length; i++) {
141       strongName[j++] = Util.HEX_CHARS[(hash[i] & 0xF0) >> 4];
142       strongName[j++] = Util.HEX_CHARS[hash[i] & 0x0F];
143     }
144
145     // Only PNG is supported right now, but still we introduce a variable to
146
// anticipate an update when the best output file type is inferred.
147
String JavaDoc bundleFileType = "png";
148
149     // Compute the file name. The '.cache' part indicates that it can be
150
// permanently cached.
151
String JavaDoc bundleFileName = new String JavaDoc(strongName) + ".cache." + bundleFileType;
152
153     OutputStream JavaDoc outStream = context.tryCreateResource(logger, bundleFileName);
154
155     if (outStream != null) {
156       try {
157         // Write the image bytes to the pending stream.
158
if (!ImageIO.write(bundledImage, bundleFileType, outStream)) {
159           logger.log(TreeLogger.ERROR, "Unsupported output file type", null);
160           throw new UnableToCompleteException();
161         }
162
163         // Commit the stream.
164
context.commitResource(logger, outStream);
165
166       } catch (IOException JavaDoc e) {
167         logger.log(TreeLogger.ERROR, "Failed while writing", e);
168         throw new UnableToCompleteException();
169       }
170     } else {
171       logger.log(
172           TreeLogger.TRACE,
173           "Generated image bundle file already exists; no need to rewrite it.",
174           null);
175     }
176
177     return bundleFileName;
178   }
179
180   private ImageRect addImage(TreeLogger logger, String JavaDoc imageName)
181       throws UnableToCompleteException {
182
183     logger = logger.branch(TreeLogger.TRACE,
184         "Adding image '" + imageName + "'", null);
185
186     // Fetch the image.
187
BufferedImage JavaDoc image = null;
188     try {
189       // Could turn this lookup logic into an externally-supplied policy for
190
// increased generality.
191
URL JavaDoc imageUrl = getClass().getClassLoader().getResource(imageName);
192       if (imageUrl == null) {
193         // This should never happen, because this check is done right after
194
// the image name is retrieved from the metadata or the method name.
195
// If there is a failure in obtaining the resource, it will happen
196
// before this point.
197
logger.log(
198             TreeLogger.ERROR,
199             "Resource not found on classpath (is the name specified as Class.getResource() would expect?)",
200             null);
201         throw new UnableToCompleteException();
202       }
203
204       // Assimilate this file's bytes into the MD5.
205
InputStream JavaDoc is = imageUrl.openStream();
206       BufferedInputStream JavaDoc bis = new BufferedInputStream JavaDoc(is);
207       byte imgByte;
208       while ((imgByte = (byte) bis.read()) != -1) {
209         md5.update(imgByte);
210       }
211       is.close();
212
213       // Load the image from the URL instead of the stream (with the assumption
214
// that having the URL provides a tiny bit more context to the parser).
215
image = ImageIO.read(imageUrl);
216       if (image == null) {
217         logger.log(TreeLogger.ERROR, "Unrecognized image file format", null);
218         throw new UnableToCompleteException();
219       }
220
221     } catch (IOException JavaDoc e) {
222       logger.log(TreeLogger.ERROR, "Unable to read image resource", null);
223       throw new UnableToCompleteException();
224     }
225
226     ImageRect imageRect = new ImageRect(image);
227     orderedImageRects.add(imageRect);
228     return imageRect;
229   }
230
231   private void putMapping(String JavaDoc imageName, ImageRect rect) {
232     imageNameToImageRectMap.put(imageName, rect);
233   }
234
235 }
236
Popular Tags