001: /* AImage.java
002:
003: {{IS_NOTE
004: Purpose:
005:
006: Description:
007:
008: History:
009: Thu Feb 6 16:56:33 2003, Created by tomyeh
010: }}IS_NOTE
011:
012: Copyright (C) 2002 Potix Corporation. All Rights Reserved.
013:
014: {{IS_RIGHT
015: This program is distributed under GPL Version 2.0 in the hope that
016: it will be useful, but WITHOUT ANY WARRANTY.
017: }}IS_RIGHT
018: */
019: package org.zkoss.image;
020:
021: import java.io.File;
022: import java.io.InputStream;
023: import java.io.FileInputStream;
024: import java.io.ByteArrayInputStream;
025: import java.io.Reader;
026: import java.io.IOException;
027: import java.net.URL;
028: import java.util.Iterator;
029:
030: import javax.swing.ImageIcon;
031: import javax.imageio.ImageIO;
032: import javax.imageio.ImageReader;
033: import javax.imageio.stream.ImageInputStream;
034:
035: import org.zkoss.lang.Objects;
036: import org.zkoss.lang.SystemException;
037: import org.zkoss.util.logging.Log;
038: import org.zkoss.io.Files;
039: import org.zkoss.util.media.ContentTypes;
040:
041: /**
042: * Represents an image.
043: * Unlike java.awt.Image and javax.swing.ImageIcon, this class holds
044: * the raw image data, i.e., the original format, as opaque.
045: *
046: * <p>In other words, it is used to retrieve and store the opaque data
047: * as polymorphic thru the {@link org.zkoss.util.media.Media} interface.
048: *
049: * @author tomyeh
050: */
051: public class AImage implements Image, java.io.Serializable {
052: private static final Log log = Log.lookup(AImage.class);
053:
054: /** The raw data. */
055: private byte[] _data;
056: /** The format name, e.g., "jpeg", "gif" and "png". */
057: private String _format;
058: /** The content type. */
059: private String _ctype;
060: /** The name (usually filename). */
061: private String _name;
062: /** The width. */
063: private int _width;
064: /** The height. */
065: private int _height;
066:
067: /** the hash code. */
068: // private transient int _hashCode = 0;
069: public AImage(String name, byte[] data) throws IOException {
070: init(name, data);
071: }
072:
073: /** Contructs an image with an input stream.
074: *
075: * <p>Note that this method automatically closes the input stream
076: * (since ZK 3.0.0).
077: */
078: public AImage(String name, InputStream is) throws IOException {
079: try {
080: init(name, Files.readAll(is));
081: } finally {
082: is.close();
083: }
084: }
085:
086: /** Constructs an image with a file name.
087: */
088: public AImage(String filename) throws IOException {
089: this (new File(filename));
090: }
091:
092: /** Constructs an image with a file.
093: */
094: public AImage(File file) throws IOException {
095: this (file.getName(), new FileInputStream(file));
096: }
097:
098: /** Constructs an image with an URL.
099: */
100: public AImage(URL url) throws IOException {
101: this (getName(url), url.openStream());
102: }
103:
104: private void init(String name, byte[] data) throws IOException {
105: if (data == null)
106: throw new IllegalArgumentException("null data");
107: _name = name;
108: _data = data;
109:
110: //retrieve format
111: String format = null;
112: try {
113: final ImageInputStream imis = ImageIO
114: .createImageInputStream(new ByteArrayInputStream(
115: data));
116: final Iterator it = ImageIO.getImageReaders(imis);
117: if (it.hasNext()) {
118: final ImageReader rd = (ImageReader) it.next();
119: format = rd.getFormatName().toLowerCase();
120: }
121: } catch (IOException ex) {
122: //not possible, but eat it and recover it later
123: }
124:
125: if (format == null) {
126: _format = getFormatByName(name);
127: if (_format == null)
128: throw new IOException("Unknown image format: " + name);
129: log.warning("Unsupported image format: " + _format
130: + "; its width and height are assumed to zero");
131: _width = _height = 0;
132: } else { //recognized by J2SDK
133: _format = format;
134:
135: final ImageIcon ii = new ImageIcon(_data);
136: _width = ii.getIconWidth();
137: _height = ii.getIconHeight();
138: }
139: _ctype = getContentType(_format);
140: }
141:
142: private static String getName(URL url) {
143: String name = url.getPath();
144: if (name != null) {
145: {
146: final int j = name.lastIndexOf(File.pathSeparatorChar);
147: if (j >= 0)
148: name = name.substring(j + 1);
149: }
150: if (File.pathSeparatorChar != '/') {
151: final int j = name.lastIndexOf('/');
152: if (j >= 0)
153: name = name.substring(j + 1);
154: }
155: }
156: return name;
157: }
158:
159: private static String getContentType(String format) {
160: final String ctype = ContentTypes.getContentType(format);
161: return ctype != null ? ctype : "image/" + format;
162: }
163:
164: private static String getFormatByName(String name) {
165: if (name != null) {
166: final int j = name.lastIndexOf('.') + 1, k = name
167: .lastIndexOf('/') + 1;
168: if (j > k && j < name.length())
169: return name.substring(j);
170: }
171: return null;
172: }
173:
174: //-- Media --//
175: public final boolean isBinary() {
176: return true;
177: }
178:
179: public final boolean inMemory() {
180: return true; //FUTURE: consider to support input stream
181: }
182:
183: public byte[] getByteData() {
184: return _data;
185: }
186:
187: /** Always throws IllegalStateException.
188: */
189: public final String getStringData() {
190: throw new IllegalStateException("Use getByteData() instead");
191: }
192:
193: /** An input stream on top of {@link #getByteData}.
194: * <p>Though harmless, the caller doesn't need to close the returned
195: * stream.
196: */
197: public final InputStream getStreamData() {
198: return new ByteArrayInputStream(_data);
199: }
200:
201: /** Always throws IllegalStateException.
202: */
203: public final Reader getReaderData() {
204: throw new IllegalStateException("Use getStreamData() instead");
205: }
206:
207: public final String getName() {
208: return _name;
209: }
210:
211: public final String getFormat() {
212: return _format;
213: }
214:
215: public final String getContentType() {
216: return _ctype;
217: }
218:
219: //-- Image --//
220: /** Returns the width.
221: */
222: public final int getWidth() {
223: return _width;
224: }
225:
226: /** Returns the height.
227: */
228: public final int getHeight() {
229: return _height;
230: }
231:
232: /** Converts to an image icon.
233: */
234: public final ImageIcon toImageIcon() {
235: return new ImageIcon(_data, _format);
236: }
237:
238: //-- Object --//
239: /* 20041014: Tom Yeh: Due to performance and usability, it is no sense
240: to compare by content.
241: public int hashCode() {
242: if (_hashCode == 0)
243: _hashCode = Objects.hashCode(_data, 16);
244: return _hashCode;
245: }
246: public boolean equals(Object o) {
247: if (!(o instanceof Image))
248: return false;
249: final Image i = (Image)o;
250: return _width == i.getWidth() && _height == i.getHeight()
251: && Objects.equals(_format, i.getFormat())
252: && Objects.equals(_data, i.getByteData());
253: }
254: */
255: }
|