/*
* $RCSfile: TIFFField.java,v $
*
*
* Copyright (c) 2006 Sun Microsystems, Inc. All Rights Reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistribution of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistribution in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* Neither the name of Sun Microsystems, Inc. or the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* This software is provided "AS IS," without a warranty of any
* kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
* WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
* EXCLUDED. SUN MIDROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
* NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
* USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
* DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
* ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
* CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
* REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
* INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGES.
*
* You acknowledge that this software is not designed or intended for
* use in the design, construction, operation or maintenance of any
* nuclear facility.
*
* $Revision: 1.4 $
* $Date: 2006/04/28 01:28:49 $
* $State: Exp $
*/
package com.github.jaiimageio.plugins.tiff;
import java.util.StringTokenizer;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import com.github.jaiimageio.impl.plugins.tiff.TIFFFieldNode;
/**
* A class representing a field in a TIFF 6.0 Image File Directory.
*
* <p> A field in a TIFF Image File Directory (IFD) is defined as a
* tag number accompanied by a sequence of values of identical data type.
* TIFF 6.0 defines 12 data types; a 13th type <code>IFD</code> is
* defined in TIFF Tech Note 1 of TIFF Specification Supplement 1. These
* TIFF data types are referred to by Java constants and mapped internally
* onto Java language data types and type names as follows:
*
* <br>
* <br>
* <table border="1">
*
* <tr>
* <th>
* <b>TIFF Data Type</b>
* </th>
* <th>
* <b>Java Constant</b>
* </th>
* <th>
* <b>Java Data Type</b>
* </th>
* <th>
* <b>Java Type Name</b>
* </th>
* </tr>
*
* <tr>
* <td>
* <tt>BYTE</tt>
* </td>
* <td>
* {@link TIFFTag#TIFF_BYTE}
* </td>
* <td>
* <code>byte</code>
* </td>
* <td>
* <code>"Byte"</code>
* </td>
* </tr>
*
* <tr>
* <td>
* <tt>ASCII</tt>
* </td>
* <td>
* {@link TIFFTag#TIFF_ASCII}
* </td>
* <td>
* <code>String</code>
* </td>
* <td>
* <code>"Ascii"</code>
* </td>
* </tr>
*
* <tr>
* <td>
* <tt>SHORT</tt>
* </td>
* <td>
* {@link TIFFTag#TIFF_SHORT}
* </td>
* <td>
* <code>char</code>
* </td>
* <td>
* <code>"Short"</code>
* </td>
* </tr>
*
* <tr>
* <td>
* <tt>LONG</tt>
* </td>
* <td>
* {@link TIFFTag#TIFF_LONG}
* </td>
* <td>
* <code>long</code>
* </td>
* <td>
* <code>"Long"</code>
* </td>
* </tr>
*
* <tr>
* <td>
* <tt>RATIONAL</tt>
* </td>
* <td>
* {@link TIFFTag#TIFF_RATIONAL}
* </td>
* <td>
* <code>long[2]</code> {numerator, denominator}
* </td>
* <td>
* <code>"Rational"</code>
* </td>
* </tr>
*
* <tr>
* <td>
* <tt>SBYTE</tt>
* </td>
* <td>
* {@link TIFFTag#TIFF_SBYTE}
* </td>
* <td>
* <code>byte</code>
* </td>
* <td>
* <code>"SByte"</code>
* </td>
* </tr>
*
* <tr>
* <td>
* <tt>UNDEFINED</tt>
* </td>
* <td>
* {@link TIFFTag#TIFF_UNDEFINED}
* </td>
* <td>
* <code>byte</code>
* </td>
* <td>
* <code>"Undefined"</code>
* </td>
* </tr>
*
* <tr>
* <td>
* <tt>SSHORT</tt>
* </td>
* <td>
* {@link TIFFTag#TIFF_SSHORT}
* </td>
* <td>
* <code>short</code>
* </td>
* <td>
* <code>"SShort"</code>
* </td>
* </tr>
*
* <tr>
* <td>
* <tt>SLONG</tt>
* </td>
* <td>
* {@link TIFFTag#TIFF_SLONG}
* </td>
* <td>
* <code>int</code>
* </td>
* <td>
* <code>"SLong"</code>
* </td>
* </tr>
*
* <tr>
* <td>
* <tt>SRATIONAL</tt>
* </td>
* <td>
* {@link TIFFTag#TIFF_SRATIONAL}
* </td>
* <td>
* <code>int[2]</code> {numerator, denominator}
* </td>
* <td>
* <code>"SRational"</code>
* </td>
* </tr>
*
* <tr>
* <td>
* <tt>FLOAT</tt>
* </td>
* <td>
* {@link TIFFTag#TIFF_FLOAT}
* </td>
* <td>
* <code>float</code>
* </td>
* <td>
* <code>"Float"</code>
* </td>
* </tr>
*
* <tr>
* <td>
* <tt>DOUBLE</tt>
* </td>
* <td>
* {@link TIFFTag#TIFF_DOUBLE}
* </td>
* <td>
* <code>double</code>
* </td>
* <td>
* <code>"Double"</code>
* </td>
* </tr>
*
* <tr>
* <td>
* <tt>IFD</tt>
* </td>
* <td>
* {@link TIFFTag#TIFF_IFD_POINTER}
* </td>
* <td>
* <code>long</code>
* </td>
* <td>
* <code>"IFDPointer"</code>
* </td>
* </tr>
*
* </table>
*
* @see TIFFDirectory
* @see TIFFTag
*/
public class TIFFField implements Comparable {
private static final String[] typeNames = {
null,
"Byte", "Ascii", "Short", "Long", "Rational",
"SByte", "Undefined", "SShort", "SLong", "SRational",
"Float", "Double", "IFDPointer"
};
private static final boolean[] isIntegral = {
false,
true, false, true, true, false,
true, true, true, true, false,
false, false, false
};
/** The tag. */
private TIFFTag tag;
/** The tag number. */
private int tagNumber;
/** The tag type. */
private int type;
/** The number of data items present in the field. */
private int count;
/** The field data. */
private Object data;
/** The default constructor. */
private TIFFField() {}
private static String getAttribute(Node node, String attrName) {
NamedNodeMap attrs = node.getAttributes();
return attrs.getNamedItem(attrName).getNodeValue();
}
private static void initData(Node node,
int[] otype, int[] ocount, Object[] odata) {
int type;
int count;
Object data = null;
String typeName = node.getNodeName();
typeName = typeName.substring(4);
typeName = typeName.substring(0, typeName.length() - 1);
type = TIFFField.getTypeByName(typeName);
if (type == -1) {
throw new IllegalArgumentException("typeName = " + typeName);
}
Node child = node.getFirstChild();
count = 0;
while (child != null) {
String childTypeName = child.getNodeName().substring(4);
if (!typeName.equals(childTypeName)) {
// warning
}
++count;
child = child.getNextSibling();
}
if (count > 0) {
data = createArrayForType(type, count);
child = node.getFirstChild();
int idx = 0;
while (child != null) {
String value = getAttribute(child, "value");
String numerator, denominator;
int slashPos;
switch (type) {
case TIFFTag.TIFF_ASCII:
((String[])data)[idx] = value;
break;
case TIFFTag.TIFF_BYTE:
case TIFFTag.TIFF_SBYTE:
((byte[])data)[idx] =
(byte)Integer.parseInt(value);
break;
case TIFFTag.TIFF_SHORT:
((char[])data)[idx] =
(char)Integer.parseInt(value);
break;
case TIFFTag.TIFF_SSHORT:
((short[])data)[idx] =
(short)Integer.parseInt(value);
break;
case TIFFTag.TIFF_SLONG:
((int[])data)[idx] =
(int)Integer.parseInt(value);
break;
case TIFFTag.TIFF_LONG:
case TIFFTag.TIFF_IFD_POINTER:
((long[])data)[idx] =
(long)Long.parseLong(value);
break;
case TIFFTag.TIFF_FLOAT:
((float[])data)[idx] =
(float)Float.parseFloat(value);
break;
case TIFFTag.TIFF_DOUBLE:
((double[])data)[idx] =
(double)Double.parseDouble(value);
break;
case TIFFTag.TIFF_SRATIONAL:
slashPos = value.indexOf("/");
numerator = value.substring(0, slashPos);
denominator = value.substring(slashPos + 1);
((int[][])data)[idx] = new int[2];
((int[][])data)[idx][0] =
Integer.parseInt(numerator);
((int[][])data)[idx][1] =
Integer.parseInt(denominator);
break;
case TIFFTag.TIFF_RATIONAL:
slashPos = value.indexOf("/");
numerator = value.substring(0, slashPos);
denominator = value.substring(slashPos + 1);
((long[][])data)[idx] = new long[2];
((long[][])data)[idx][0] =
Long.parseLong(numerator);
((long[][])data)[idx][1] =
Long.parseLong(denominator);
break;
default:
// error
}
idx++;
child = child.getNextSibling();
}
}
otype[0] = type;
ocount[0] = count;
odata[0] = data;
}
/**
* Creates a <code>TIFFField</code> from a TIFF native image
* metadata node. If the value of the <tt>"tagNumber"</tt> attribute
* of the node is not found in <code>tagSet</code> then a new
* <code>TIFFTag</code> with name <code>"unknown"</code> will be
* created and assigned to the field.
*
* @param tagSet The <code>TIFFTagSet</code> to which the
* <code>TIFFTag</code> of the field belongs.
* @param node A native TIFF image metadata <code>TIFFField</code> node.
* @throws IllegalArgumentException if <code>node</code> is
* <code>null</code>.
* @throws IllegalArgumentException if the name of the node is not
* <code>"TIFFField"</code>.
*/
public static TIFFField createFromMetadataNode(TIFFTagSet tagSet,
Node node) {
if (node == null) {
throw new IllegalArgumentException("node == null!");
}
String name = node.getNodeName();
if (!name.equals("TIFFField")) {
throw new IllegalArgumentException("!name.equals(\"TIFFField\")");
}
int tagNumber = Integer.parseInt(getAttribute(node, "number"));
TIFFTag tag;
if (tagSet != null) {
tag = tagSet.getTag(tagNumber);
} else {
tag = new TIFFTag("unknown", tagNumber, 0, null);
}
int type = TIFFTag.TIFF_UNDEFINED;
int count = 0;
Object data = null;
Node child = node.getFirstChild();
if (child != null) {
String typeName = child.getNodeName();
if (typeName.equals("TIFFUndefined")) {
String values = getAttribute(child, "value");
StringTokenizer st = new StringTokenizer(values, ",");
count = st.countTokens();
byte[] bdata = new byte[count];
for (int i = 0; i < count; i++) {
bdata[i] = (byte)Integer.parseInt(st.nextToken());
}
type = TIFFTag.TIFF_UNDEFINED;
data = bdata;
} else {
int[] otype = new int[1];
int[] ocount = new int[1];
Object[] odata = new Object[1];
initData(node.getFirstChild(), otype, ocount, odata);
type = otype[0];
count = ocount[0];
data = odata[0];
}
} else {
int t = TIFFTag.MAX_DATATYPE;
while(t >= TIFFTag.MIN_DATATYPE && !tag.isDataTypeOK(t)) {
t--;
}
type = t;
}
return new TIFFField(tag, type, count, data);
}
/**
* Constructs a <code>TIFFField</code> with arbitrary data. The
* <code>type</code> parameter must be a value for which
* {@link TIFFTag#isDataTypeOK <code>tag.isDataTypeOK()</code>}
* returns <code>true</code>. The <code>data</code> parameter must
* be an array of a Java type appropriate for the type of the TIFF
* field unless {@link TIFFTag#isIFDPointer
* <code>tag.isIFDPointer()</code>} returns <code>true</code> in
* which case it must be a <code>TIFFDirectory</code> instance.
*
* <p><i>Neither the legality of <code>type</code> with respect to
* <code>tag</code> nor that or <code>data</code> with respect to
* <code>type</code> is verified by this constructor.</i> The methods
* {@link TIFFTag#isDataTypeOK <code>TIFFTag.isDataTypeOK()</code>}
* and {@link #createArrayForType <code>createArrayForType()</code>}
* should be used programmatically to ensure that subsequent errors
* such as <code>ClassCastException</code>s do not occur as a result
* of providing inconsitent parameters to this constructor.</p>
*
* <p>Note that the value (data) of the <code>TIFFField</code>
* will always be the actual field value regardless of the number of
* bytes required for that value. This is the case despite the fact
* that the TIFF <i>IFD Entry</i> corresponding to the field may
* actually contain the offset to the field's value rather than
* the value itself (the latter occurring if and only if the
* value fits into 4 bytes). In other words, the value of the
* field will already have been read from the TIFF stream. This
* subsumes the case where <code>tag.isIFDPointer()</code> returns
* <code>true</code> and the value will be a <code>TIFFDirectory</code>
* rather than an array.</p>
*
* @param tag The tag to associated with this field.
* @param type One of the <code>TIFFTag.TIFF_*</code> constants
* indicating the data type of the field as written to the TIFF stream.
* @param count The number of data values.
* @param data The actual data content of the field.
*
* @throws IllegalArgumentException if <code>tag == null</code>.
* @throws IllegalArgumentException if <code>dataType</code> is not
* one of the <code>TIFFTag.TIFF_*</code> data type constants.
* @throws IllegalArgumentException if <code>count < 0</code>.
*/
public TIFFField(TIFFTag tag, int type, int count, Object data) {
if(tag == null) {
throw new IllegalArgumentException("tag == null!");
} else if(type < TIFFTag.MIN_DATATYPE || type > TIFFTag.MAX_DATATYPE) {
throw new IllegalArgumentException("Unknown data type "+type);
} else if(count < 0) {
throw new IllegalArgumentException("count < 0!");
}
this.tag = tag;
this.tagNumber = tag.getNumber();
this.type = type;
this.count = count;
this.data = data;
}
/**
* Constructs a data array using {@link #createArrayForType
* <code>createArrayForType()</code>} and invokes
* {@link #TIFFField(TIFFTag,int,int,Object)} with the supplied
* parameters and the created array.
*
* @see #TIFFField(TIFFTag,int,int,Object)
*/
public TIFFField(TIFFTag tag, int type, int count) {
this(tag, type, count, createArrayForType(type, count));
}
/**
* Constructs a <code>TIFFField</code> with a single integral value.
* The field will have type
* {@link TIFFTag#TIFF_SHORT <code>TIFF_SHORT</code>} if
* <code>val < 65536</code> and type
* {@link TIFFTag#TIFF_LONG <code>TIFF_LONG</code>} otherwise.
* <i>It is <b>not</b> verified whether the resulting type is
* legal for <code>tag</code>.</i>
*
* @param tag The tag to associate with this field.
* @param value The value to associate with this field.
* @throws IllegalArgumentException if <code>tag == null</code>.
* @throws IllegalArgumentException if <code>value < 0</code>.
*/
public TIFFField(TIFFTag tag, int value) {
if(tag == null) {
throw new IllegalArgumentException("tag == null!");
}
if (value < 0) {
throw new IllegalArgumentException("value < 0!");
}
this.tag = tag;
this.tagNumber = tag.getNumber();
this.count = 1;
if (value < 65536) {
this.type = TIFFTag.TIFF_SHORT;
char[] cdata = new char[1];
cdata[0] = (char)value;
this.data = cdata;
} else {
this.type = TIFFTag.TIFF_LONG;
long[] ldata = new long[1];
ldata[0] = value;
this.data = ldata;
}
}
/**
* Retrieves the tag associated with this field.
*
* @return The associated <code>TIFFTag</code>.
*/
public TIFFTag getTag() {
return tag;
}
/**
* Retrieves the tag number in the range <code>[0, 65535]</code>.
*
* @return The tag number.
*/
public int getTagNumber() {
return tagNumber;
}
/**
* Returns the type of the data stored in the field. For a TIFF 6.0
* stream, the value will equal one of the <code>TIFFTag.TIFF_*</code>
* constants. For future revisions of TIFF, higher values are possible.
*
* @return The data type of the field value.
*/
public int getType() {
return type;
}
/**
* Returns the name of the supplied data type constant.
*
* @param dataType One of the <code>TIFFTag.TIFF_*</code> constants
* indicating the data type of the field as written to the TIFF stream.
* @return The type name corresponding to the supplied type constant.
* @throws IllegalArgumentException if <code>dataType</code> is not
* one of the <code>TIFFTag.TIFF_*</code> data type constants.
*/
public static String getTypeName(int dataType) {
if (dataType < TIFFTag.MIN_DATATYPE ||
dataType > TIFFTag.MAX_DATATYPE) {
throw new IllegalArgumentException("Unknown data type "+dataType);
}
return typeNames[dataType];
}
/**
* Returns the data type constant corresponding to the supplied data
* type name. If the name is unknown <code>-1</code> will be returned.
*
* @return One of the <code>TIFFTag.TIFF_*</code> constants or
* <code>-1</code> if the name is not recognized.
*/
public static int getTypeByName(String typeName) {
for (int i = TIFFTag.MIN_DATATYPE; i <= TIFFTag.MAX_DATATYPE; i++) {
if (typeName.equals(typeNames[i])) {
return i;
}
}
return -1;
}
/**
* Creates an array appropriate for the indicated data type.
*
* @param dataType One of the <code>TIFFTag.TIFF_*</code> data type
* constants.
* @param count The number of values in the array.
*
* @throws IllegalArgumentException if <code>dataType</code> is not
* one of the <code>TIFFTag.TIFF_*</code> data type constants.
* @throws IllegalArgumentException if <code>count < 0</code>.
*/
public static Object createArrayForType(int dataType, int count) {
if(count < 0) {
throw new IllegalArgumentException("count < 0!");
}
switch (dataType) {
case TIFFTag.TIFF_BYTE:
case TIFFTag.TIFF_SBYTE:
case TIFFTag.TIFF_UNDEFINED:
return new byte[count];
case TIFFTag.TIFF_ASCII:
return new String[count];
case TIFFTag.TIFF_SHORT:
return new char[count];
case TIFFTag.TIFF_LONG:
case TIFFTag.TIFF_IFD_POINTER:
return new long[count];
case TIFFTag.TIFF_RATIONAL:
return new long[count][2];
case TIFFTag.TIFF_SSHORT:
return new short[count];
case TIFFTag.TIFF_SLONG:
return new int[count];
case TIFFTag.TIFF_SRATIONAL:
return new int[count][2];
case TIFFTag.TIFF_FLOAT:
return new float[count];
case TIFFTag.TIFF_DOUBLE:
return new double[count];
default:
throw new IllegalArgumentException("Unknown data type "+dataType);
}
}
/**
* Returns the <code>TIFFField</code> as a node named either
* <tt>"TIFFField"</tt> or <tt>"TIFFIFD"</tt> as described in the
* TIFF native image metadata specification. The node will be named
* <tt>"TIFFIFD"</tt> if and only if the field's data object is an
* instance of {@link TIFFDirectory} or equivalently
* {@link TIFFTag#isIFDPointer getTag.isIFDPointer()} returns
* <code>true</code>.
*
* @return a <code>Node</code> named <tt>"TIFFField"</tt> or
* <tt>"TIFFIFD"</tt>.
*/
public Node getAsNativeNode() {
return new TIFFFieldNode(this);
}
/**
* Indicates whether the value associated with the field is of
* integral data type.
*
* @return Whether the field type is integral.
*/
public boolean isIntegral() {
return isIntegral[type];
}
/**
* Returns the number of data items present in the field. For
* <code>TIFFTag.TIFF_ASCII</code> fields, the value returned is the
* number of <code>String</code>s, not the total length of the
* data as in the file representation.
*/
public int getCount() {
return count;
}
/**
* Returns a reference to the data object associated with the field.
*
* @return The data object of the field.
*/
public Object getData() {
return data;
}
/**
* Returns the data as an uninterpreted array of
* <code>byte</code>s. The type of the field must be one of
* <code>TIFFTag.TIFF_BYTE</code>, <code>TIFF_SBYTE</code>, or
* <code>TIFF_UNDEFINED</code>.
*
* <p> For data in <code>TIFFTag.TIFF_BYTE</code> format, the application
* must take care when promoting the data to longer integral types
* to avoid sign extension.
*
* @throws ClassCastException if the field is not of type
* <code>TIFF_BYTE</code>, <code>TIFF_SBYTE</code>, or
* <code>TIFF_UNDEFINED</code>.
*/
public byte[] getAsBytes() {
return (byte[])data;
}
/**
* Returns <code>TIFFTag.TIFF_SHORT</code> data as an array of
* <code>char</code>s (unsigned 16-bit integers).
*
* @throws ClassCastException if the field is not of type
* <code>TIFF_SHORT</code>.
*/
public char[] getAsChars() {
return (char[])data;
}
/**
* Returns <code>TIFFTag.TIFF_SSHORT</code> data as an array of
* <code>short</code>s (signed 16-bit integers).
*
* @throws ClassCastException if the field is not of type
* <code>TIFF_SSHORT</code>.
*/
public short[] getAsShorts() {
return (short[])data;
}
/**
* Returns <code>TIFFTag.TIFF_SLONG</code> data as an array of
* <code>int</code>s (signed 32-bit integers).
*
* @throws ClassCastException if the field is not of type
* <code>TIFF_SHORT</code>, <code>TIFF_SSHORT</code>, or
* <code>TIFF_SLONG</code>.
*/
public int[] getAsInts() {
if (data instanceof int[]) {
return (int[])data;
} else if (data instanceof char[]){
char[] cdata = (char[])data;
int[] idata = new int[cdata.length];
for (int i = 0; i < cdata.length; i++) {
idata[i] = (int)(cdata[i] & 0xffff);
}
return idata;
} else if (data instanceof short[]){
short[] sdata = (short[])data;
int[] idata = new int[sdata.length];
for (int i = 0; i < sdata.length; i++) {
idata[i] = (int)sdata[i];
}
return idata;
} else {
throw new ClassCastException(
"Data not char[], short[], or int[]!");
}
}
/**
* Returns <code>TIFFTag.TIFF_LONG</code> or
* <code>TIFF_IFD_POINTER</code> data as an array of
* <code>long</code>s (signed 64-bit integers).
*
* @throws ClassCastException if the field is not of type
* <code>TIFF_LONG</code> or <code>TIFF_IFD_POINTER</code>.
*/
public long[] getAsLongs() {
return (long[])data;
}
/**
* Returns <code>TIFFTag.TIFF_FLOAT</code> data as an array of
* <code>float</code>s (32-bit floating-point values).
*
* @throws ClassCastException if the field is not of type
* <code>TIFF_FLOAT</code>.
*/
public float[] getAsFloats() {
return (float[])data;
}
/**
* Returns <code>TIFFTag.TIFF_DOUBLE</code> data as an array of
* <code>double</code>s (64-bit floating-point values).
*
* @throws ClassCastException if the field is not of type
* <code>TIFF_DOUBLE</code>.
*/
public double[] getAsDoubles() {
return (double[])data;
}
/**
* Returns <code>TIFFTag.TIFF_SRATIONAL</code> data as an array of
* 2-element arrays of <code>int</code>s.
*
* @throws ClassCastException if the field is not of type
* <code>TIFF_SRATIONAL</code>.
*/
public int[][] getAsSRationals() {
return (int[][])data;
}
/**
* Returns <code>TIFFTag.TIFF_RATIONAL</code> data as an array of
* 2-element arrays of <code>long</code>s.
*
* @throws ClassCastException if the field is not of type
* <code>TIFF_RATIONAL</code>.
*/
public long[][] getAsRationals() {
return (long[][])data;
}
/**
* Returns data in any format as an <code>int</code>.
*
* <p> <code>TIFFTag.TIFF_BYTE</code> values are treated as unsigned; that
* is, no sign extension will take place and the returned value
* will be in the range [0, 255]. <code>TIFF_SBYTE</code> data
* will be returned in the range [-128, 127].
*
* <p> A <code>TIFF_UNDEFINED</code> value is treated as though
* it were a <code>TIFF_BYTE</code>.
*
* <p> Data in <code>TIFF_SLONG</code>, <code>TIFF_LONG</code>,
* <code>TIFF_FLOAT</code>, <code>TIFF_DOUBLE</code> or
* <code>TIFF_IFD_POINTER</code> format are simply cast to
* <code>int</code> and may suffer from truncation.
*
* <p> Data in <code>TIFF_SRATIONAL</code> or
* <code>TIFF_RATIONAL</code> format are evaluated by dividing the
* numerator into the denominator using double-precision
* arithmetic and then casting to <code>int</code>. Loss of
* precision and truncation may occur.
*
* <p> Data in <code>TIFF_ASCII</code> format will be parsed as by
* the <code>Double.parseDouble</code> method, with the result
* case to <code>int</code>.
*/
public int getAsInt(int index) {
switch (type) {
case TIFFTag.TIFF_BYTE: case TIFFTag.TIFF_UNDEFINED:
return ((byte[])data)[index] & 0xff;
case TIFFTag.TIFF_SBYTE:
return ((byte[])data)[index];
case TIFFTag.TIFF_SHORT:
return ((char[])data)[index] & 0xffff;
case TIFFTag.TIFF_SSHORT:
return ((short[])data)[index];
case TIFFTag.TIFF_SLONG:
return ((int[])data)[index];
case TIFFTag.TIFF_LONG: case TIFFTag.TIFF_IFD_POINTER:
return (int)((long[])data)[index];
case TIFFTag.TIFF_FLOAT:
return (int)((float[])data)[index];
case TIFFTag.TIFF_DOUBLE:
return (int)((double[])data)[index];
case TIFFTag.TIFF_SRATIONAL:
int[] ivalue = getAsSRational(index);
return (int)((double)ivalue[0]/ivalue[1]);
case TIFFTag.TIFF_RATIONAL:
long[] lvalue = getAsRational(index);
return (int)((double)lvalue[0]/lvalue[1]);
case TIFFTag.TIFF_ASCII:
String s = ((String[])data)[index];
return (int)Double.parseDouble(s);
default:
throw new ClassCastException();
}
}
/**
* Returns data in any format as a <code>long</code>.
*
* <p> <code>TIFFTag.TIFF_BYTE</code> and <code>TIFF_UNDEFINED</code> data
* are treated as unsigned; that is, no sign extension will take
* place and the returned value will be in the range [0, 255].
* <code>TIFF_SBYTE</code> data will be returned in the range
* [-128, 127].
*
* <p> Data in <code>TIFF_ASCII</code> format will be parsed as by
* the <code>Double.parseDouble</code> method, with the result
* cast to <code>long</code>.
*/
public long getAsLong(int index) {
switch (type) {
case TIFFTag.TIFF_BYTE: case TIFFTag.TIFF_UNDEFINED:
return ((byte[])data)[index] & 0xff;
case TIFFTag.TIFF_SBYTE:
return ((byte[])data)[index];
case TIFFTag.TIFF_SHORT:
return ((char[])data)[index] & 0xffff;
case TIFFTag.TIFF_SSHORT:
return ((short[])data)[index];
case TIFFTag.TIFF_SLONG:
return ((int[])data)[index];
case TIFFTag.TIFF_LONG: case TIFFTag.TIFF_IFD_POINTER:
return ((long[])data)[index];
case TIFFTag.TIFF_SRATIONAL:
int[] ivalue = getAsSRational(index);
return (long)((double)ivalue[0]/ivalue[1]);
case TIFFTag.TIFF_RATIONAL:
long[] lvalue = getAsRational(index);
return (long)((double)lvalue[0]/lvalue[1]);
case TIFFTag.TIFF_ASCII:
String s = ((String[])data)[index];
return (long)Double.parseDouble(s);
default:
throw new ClassCastException();
}
}
/**
* Returns data in any format as a <code>float</code>.
*
* <p> <code>TIFFTag.TIFF_BYTE</code> and <code>TIFF_UNDEFINED</code> data
* are treated as unsigned; that is, no sign extension will take
* place and the returned value will be in the range [0, 255].
* <code>TIFF_SBYTE</code> data will be returned in the range
* [-128, 127].
*
* <p> Data in <code>TIFF_SLONG</code>, <code>TIFF_LONG</code>,
* <code>TIFF_DOUBLE</code>, or <code>TIFF_IFD_POINTER</code> format are
* simply cast to <code>float</code> and may suffer from
* truncation.
*
* <p> Data in <code>TIFF_SRATIONAL</code> or
* <code>TIFF_RATIONAL</code> format are evaluated by dividing the
* numerator into the denominator using double-precision
* arithmetic and then casting to <code>float</code>.
*
* <p> Data in <code>TIFF_ASCII</code> format will be parsed as by
* the <code>Double.parseDouble</code> method, with the result
* cast to <code>float</code>.
*/
public float getAsFloat(int index) {
switch (type) {
case TIFFTag.TIFF_BYTE: case TIFFTag.TIFF_UNDEFINED:
return ((byte[])data)[index] & 0xff;
case TIFFTag.TIFF_SBYTE:
return ((byte[])data)[index];
case TIFFTag.TIFF_SHORT:
return ((char[])data)[index] & 0xffff;
case TIFFTag.TIFF_SSHORT:
return ((short[])data)[index];
case TIFFTag.TIFF_SLONG:
return ((int[])data)[index];
case TIFFTag.TIFF_LONG: case TIFFTag.TIFF_IFD_POINTER:
return ((long[])data)[index];
case TIFFTag.TIFF_FLOAT:
return ((float[])data)[index];
case TIFFTag.TIFF_DOUBLE:
return (float)((double[])data)[index];
case TIFFTag.TIFF_SRATIONAL:
int[] ivalue = getAsSRational(index);
return (float)((double)ivalue[0]/ivalue[1]);
case TIFFTag.TIFF_RATIONAL:
long[] lvalue = getAsRational(index);
return (float)((double)lvalue[0]/lvalue[1]);
case TIFFTag.TIFF_ASCII:
String s = ((String[])data)[index];
return (float)Double.parseDouble(s);
default:
throw new ClassCastException();
}
}
/**
* Returns data in any format as a <code>double</code>.
*
* <p> <code>TIFFTag.TIFF_BYTE</code> and <code>TIFF_UNDEFINED</code> data
* are treated as unsigned; that is, no sign extension will take
* place and the returned value will be in the range [0, 255].
* <code>TIFF_SBYTE</code> data will be returned in the range
* [-128, 127].
*
* <p> Data in <code>TIFF_SRATIONAL</code> or
* <code>TIFF_RATIONAL</code> format are evaluated by dividing the
* numerator into the denominator using double-precision
* arithmetic.
*
* <p> Data in <code>TIFF_ASCII</code> format will be parsed as by
* the <code>Double.parseDouble</code> method.
*/
public double getAsDouble(int index) {
switch (type) {
case TIFFTag.TIFF_BYTE: case TIFFTag.TIFF_UNDEFINED:
return ((byte[])data)[index] & 0xff;
case TIFFTag.TIFF_SBYTE:
return ((byte[])data)[index];
case TIFFTag.TIFF_SHORT:
return ((char[])data)[index] & 0xffff;
case TIFFTag.TIFF_SSHORT:
return ((short[])data)[index];
case TIFFTag.TIFF_SLONG:
return ((int[])data)[index];
case TIFFTag.TIFF_LONG: case TIFFTag.TIFF_IFD_POINTER:
return ((long[])data)[index];
case TIFFTag.TIFF_FLOAT:
return ((float[])data)[index];
case TIFFTag.TIFF_DOUBLE:
return ((double[])data)[index];
case TIFFTag.TIFF_SRATIONAL:
int[] ivalue = getAsSRational(index);
return (double)ivalue[0]/ivalue[1];
case TIFFTag.TIFF_RATIONAL:
long[] lvalue = getAsRational(index);
return (double)lvalue[0]/lvalue[1];
case TIFFTag.TIFF_ASCII:
String s = ((String[])data)[index];
return Double.parseDouble(s);
default:
throw new ClassCastException();
}
}
/**
* Returns a <code>TIFFTag.TIFF_ASCII</code> value as a
* <code>String</code>.
*
* @throws ClassCastException if the field is not of type
* <code>TIFF_ASCII</code>.
*/
public String getAsString(int index) {
return ((String[])data)[index];
}
/**
* Returns a <code>TIFFTag.TIFF_SRATIONAL</code> data item as a
* two-element array of <code>int</code>s.
*
* @throws ClassCastException if the field is not of type
* <code>TIFF_SRATIONAL</code>.
*/
public int[] getAsSRational(int index) {
return ((int[][])data)[index];
}
/**
* Returns a TIFFTag.TIFF_RATIONAL data item as a two-element array
* of ints.
*
* @throws ClassCastException if the field is not of type
* <code>TIFF_RATIONAL</code>.
*/
public long[] getAsRational(int index) {
return ((long[][])data)[index];
}
/**
* Returns a <code>String</code> containing a human-readable
* version of the data item. Data of type
* <code>TIFFTag.TIFF_RATIONAL</code> or <code>TIFF_SRATIONAL</code> are
* represented as a pair of integers separated by a
* <code>'/'</code> character.
*
* @throws ClassCastException if the field is not of one of the
* legal field types.
*/
public String getValueAsString(int index) {
switch (type) {
case TIFFTag.TIFF_ASCII:
return ((String[])data)[index];
case TIFFTag.TIFF_BYTE: case TIFFTag.TIFF_UNDEFINED:
return Integer.toString(((byte[])data)[index] & 0xff);
case TIFFTag.TIFF_SBYTE:
return Integer.toString(((byte[])data)[index]);
case TIFFTag.TIFF_SHORT:
return Integer.toString(((char[])data)[index] & 0xffff);
case TIFFTag.TIFF_SSHORT:
return Integer.toString(((short[])data)[index]);
case TIFFTag.TIFF_SLONG:
return Integer.toString(((int[])data)[index]);
case TIFFTag.TIFF_LONG: case TIFFTag.TIFF_IFD_POINTER:
return Long.toString(((long[])data)[index]);
case TIFFTag.TIFF_FLOAT:
return Float.toString(((float[])data)[index]);
case TIFFTag.TIFF_DOUBLE:
return Double.toString(((double[])data)[index]);
case TIFFTag.TIFF_SRATIONAL:
int[] ivalue = getAsSRational(index);
String srationalString;
if(ivalue[1] != 0 && ivalue[0] % ivalue[1] == 0) {
// If the denominator is a non-zero integral divisor
// of the numerator then convert the fraction to be
// with respect to a unity denominator.
srationalString =
Integer.toString(ivalue[0] / ivalue[1]) + "/1";
} else {
// Use the values directly.
srationalString =
Integer.toString(ivalue[0]) +
"/" +
Integer.toString(ivalue[1]);
}
return srationalString;
case TIFFTag.TIFF_RATIONAL:
long[] lvalue = getAsRational(index);
String rationalString;
if(lvalue[1] != 0L && lvalue[0] % lvalue[1] == 0) {
// If the denominator is a non-zero integral divisor
// of the numerator then convert the fraction to be
// with respect to a unity denominator.
rationalString =
Long.toString(lvalue[0] / lvalue[1]) + "/1";
} else {
// Use the values directly.
rationalString =
Long.toString(lvalue[0]) +
"/" +
Long.toString(lvalue[1]);
}
return rationalString;
default:
throw new ClassCastException();
}
}
/**
* Compares this <code>TIFFField</code> with another
* <code>TIFFField</code> by comparing the tags.
*
* <p><b>Note: this class has a natural ordering that is inconsistent
* with <code>equals()</code>.</b>
*
* @throws IllegalArgumentException if the parameter is <code>null</code>.
* @throws ClassCastException if the parameter is not a
* <code>TIFFField</code>.
*/
public int compareTo(Object o) {
if (o == null) {
throw new IllegalArgumentException();
}
int oTagNumber = ((TIFFField)o).getTagNumber();
if (tagNumber < oTagNumber) {
return -1;
} else if (tagNumber > oTagNumber) {
return 1;
} else {
return 0;
}
}
}