/* AImage.java
Purpose:
Description:
History:
Thu Feb 6 16:56:33 2003, Created by tomyeh
Copyright (C) 2002 Potix Corporation. All Rights Reserved.
{{IS_RIGHT
This program is distributed under LGPL Version 2.1 in the hope that
it will be useful, but WITHOUT ANY WARRANTY.
}}IS_RIGHT
*/
package org.zkoss.image;
import java.io.File;
import java.io.InputStream;
import java.io.FileInputStream;
import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.Reader;
import java.io.IOException;
import java.net.URL;
import java.util.Iterator;
import javax.swing.ImageIcon;
import javax.imageio.ImageIO;
import javax.imageio.ImageReader;
import javax.imageio.stream.ImageInputStream;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.zkoss.io.Files;
import org.zkoss.util.media.ContentTypes;
/**
* Represents an image.
* Unlike java.awt.Image and javax.swing.ImageIcon, this class holds
* the raw image data, i.e., the original format, as opaque.
*
* <p>In other words, it is used to retrieve and store the opaque data
* as polymorphic thru the {@link org.zkoss.util.media.Media} interface.
*
* <p>To encode AWT image to an instance of {@link Image},
* use {@link Images#encode}.
*
* @author tomyeh
* @see Images#encode
*/
public class AImage implements Image, java.io.Serializable {
private static final Logger log = LoggerFactory.getLogger(AImage.class);
/** The raw data. */
private byte[] _data;
/** The format name, e.g., "jpeg", "gif" and "png". */
private String _format;
/** The content type. */
private String _ctype;
/** The name (usually filename). */
private String _name;
/** The width. */
private int _width;
/** The height. */
private int _height;
/** the hash code. */
// private transient int _hashCode = 0;
/** Constructs an image with a byte array.
*/
public AImage(String name, byte[] data) throws IOException {
init(name, data);
}
/** Constructs an image with an input stream.
*
* <p>Note that this method automatically closes the input stream
* (since ZK 3.0.0).
*/
public AImage(String name, InputStream is) throws IOException {
try {
init(name, Files.readAll(is));
} finally {
is.close();
}
}
/** Constructs an image with a file name.
*/
public AImage(String filename) throws IOException {
this(new File(filename));
}
/** Constructs an image with a file.
*/
public AImage(File file) throws IOException {
this(file.getName(), new BufferedInputStream(new FileInputStream(file)));
}
/** Constructs an image with an URL.
*/
public AImage(URL url) throws IOException {
this(getName(url), url.openStream());
}
private void init(String name, byte[] data) throws IOException {
if (data == null)
throw new IllegalArgumentException("null data");
_name = name;
_data = data;
//retrieve format
String format = null;
try {
final ImageInputStream imis =
ImageIO.createImageInputStream(new ByteArrayInputStream(data));
final Iterator it = ImageIO.getImageReaders(imis);
if (it.hasNext()) {
final ImageReader rd = (ImageReader)it.next();
format = rd.getFormatName().toLowerCase(java.util.Locale.ENGLISH);
}
} catch (IOException ex) {
//not possible, but eat it and recover it later
}
if (format == null) {
_format = getFormatByName(name);
if (_format == null)
throw new IOException("Unknown image format: "+name);
log.warn("Unsupported image format: "+_format+"; its width and height are assumed to zero");
_width = _height = 0;
} else { //recognized by J2SDK
_format = format;
final ImageIcon ii = new ImageIcon(_data);
_width = ii.getIconWidth();
_height = ii.getIconHeight();
}
_ctype = getContentType(_format);
}
private static String getName(URL url) {
String name = url.getPath();
if (name != null) {
{
final int j = name.lastIndexOf(File.pathSeparatorChar);
if (j >= 0) name = name.substring(j + 1);
}
if (File.pathSeparatorChar != '/') {
final int j = name.lastIndexOf('/');
if (j >= 0) name = name.substring(j + 1);
}
}
return name;
}
private static String getContentType(String format) {
final String ctype = ContentTypes.getContentType(format);
return ctype != null ? ctype: "image/" + format;
}
private static String getFormatByName(String name) {
if (name != null) {
final int j = name.lastIndexOf('.') + 1,
k = name.lastIndexOf('/') + 1;
if (j > k && j < name.length())
return name.substring(j);
}
return null;
}
//-- Media --//
public final boolean isBinary() {
return true;
}
public final boolean inMemory() {
return true; //FUTURE: consider to support input stream
}
public byte[] getByteData() {
return _data;
}
/** Always throws IllegalStateException.
*/
public final String getStringData() {
throw new IllegalStateException("Use getByteData() instead");
}
/** An input stream on top of {@link #getByteData}.
* <p>Though harmless, the caller doesn't need to close the returned
* stream.
*/
public final InputStream getStreamData() {
return new ByteArrayInputStream(_data);
}
/** Always throws IllegalStateException.
*/
public final Reader getReaderData() {
throw new IllegalStateException("Use getStreamData() instead");
}
public final String getName() {
return _name;
}
public final String getFormat() {
return _format;
}
public final String getContentType() {
return _ctype;
}
//-- Image --//
/** Returns the width.
*/
public final int getWidth() {
return _width;
}
/** Returns the height.
*/
public final int getHeight() {
return _height;
}
/** Converts to an image icon.
*/
public final ImageIcon toImageIcon() {
return new ImageIcon(_data, _format);
}
public boolean isContentDisposition() {
return true;
}
//-- Object --//
/* 20041014: Tom Yeh: Due to performance and usability, it is no sense
to compare by content.
public int hashCode() {
if (_hashCode == 0)
_hashCode = Objects.hashCode(_data, 16);
return _hashCode;
}
public boolean equals(Object o) {
if (!(o instanceof Image))
return false;
final Image i = (Image)o;
return _width == i.getWidth() && _height == i.getHeight()
&& Objects.equals(_format, i.getFormat())
&& Objects.equals(_data, i.getByteData());
}
*/
}