/**
* JHOVE2 - Next-generation architecture for format-aware characterization
* <p>
* Copyright (c) 2010 by The Regents of the University of California. All rights reserved.
* </p>
* <p>
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* </p>
* <ul>
* <li>Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.</li>
* <li>Redistributions 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.</li>
* <li>Neither the name of the University of California/California Digital
* Library, Ithaka Harbors/Portico, or Stanford University, nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.</li>
* </ul>
* <p>
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
* </p>
*/
package org.jhove2.module.format.tiff;
import java.io.IOException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;
import org.jhove2.annotation.ReportableProperty;
import org.jhove2.annotation.ReportableProperty.PropertyType;
import org.jhove2.core.I8R;
import org.jhove2.core.JHOVE2;
import org.jhove2.core.JHOVE2Exception;
import org.jhove2.core.Message;
import org.jhove2.core.Message.Context;
import org.jhove2.core.Message.Severity;
import org.jhove2.core.format.Format;
import org.jhove2.core.format.FormatIdentification;
import org.jhove2.core.format.FormatIdentification.Confidence;
import org.jhove2.core.io.Input;
import org.jhove2.core.reportable.AbstractReportable;
import org.jhove2.core.source.ByteStreamSource;
import org.jhove2.core.source.MeasurableSource;
import org.jhove2.core.source.Source;
import org.jhove2.module.format.Validator.Validity;
import org.jhove2.module.format.tiff.type.Ascii;
import org.jhove2.module.format.tiff.type.AsciiArray;
import org.jhove2.module.format.tiff.type.Byte;
import org.jhove2.module.format.tiff.type.ByteArray;
import org.jhove2.module.format.tiff.type.Double;
import org.jhove2.module.format.tiff.type.DoubleArray;
import org.jhove2.module.format.tiff.type.FloatObject;
import org.jhove2.module.format.tiff.type.Long;
import org.jhove2.module.format.tiff.type.LongArray;
import org.jhove2.module.format.tiff.type.Rational;
import org.jhove2.module.format.tiff.type.RationalArray;
import org.jhove2.module.format.tiff.type.SByte;
import org.jhove2.module.format.tiff.type.SByteArray;
import org.jhove2.module.format.tiff.type.SLong;
import org.jhove2.module.format.tiff.type.SLongArray;
import org.jhove2.module.format.tiff.type.SShort;
import org.jhove2.module.format.tiff.type.SShortArray;
import org.jhove2.module.format.tiff.type.Short;
import org.jhove2.module.format.tiff.type.ShortArray;
import org.jhove2.module.format.tiff.type.desc.Compression;
import com.sleepycat.persist.model.Persistent;
/** TIFF IFD entry.
*
* @author mstrong
*
*/
@Persistent
public class IFDEntry
extends AbstractReportable
implements Comparable<Object>
{
/** The number of values, Count of the indicated Type */
protected long count;
/** compression in descriptive form */
protected String compression_d;
/** compression raw */
protected int compression;
/** Name of the TIFF tag */
protected String name;
/** the tag that identifies the field */
protected int tag;
/** Contains the offset to the value field */
protected long offsetOfValue;
/** the previous tag read */
public static int prevTag;
/** the field type */
protected int type;
/** the enumerated field type */
protected TiffType tiffType;
/** contains either offset of the valueOffset field or the value stored in the Valueoffset field
* used to read the value into the proper value type object */
protected long savedValueOffset;
/** offset to the IFDEntry being parsed */
protected long tagOffset;
/** Contains the value iff the value is 4 or less bytes.
* Otherwise it is the offset of value */
protected long valueOffset;
/** TIFF Version - some field types define the TIFF version */
protected int version = 4;
/** Tag Sort order error message */
private Message TagSortOrderErrorMessage;
/** Byte offset not word aligned message */
private Message ByteOffsetNotWordAlignedMessage;
/** Value Offset reference location invalid message */
private Message ValueOffsetReferenceLocationFileMessage;
/** Unknown Type Message */
private List<Message> unknownTypeMessages;
/** type mismatch message */
private Message TypeMismatchMessage;
/** unknown tag message */
private Message UnknownTagMessage;
/** invalid count value message */
private Message InvalidCountValueMessage;
private Validity isValid = Validity.Undetermined;
/** Possible TIFF values
* Each TIFF value can be of a different type so it needs to be stored in a
* separate object
*/
/** the value if the value is more than 4 btyes */
boolean isArray = false;
protected Ascii asciiValue;
protected AsciiArray asciiArrayValue;
protected Byte byteValue;
protected SByte sByteValue;
protected ByteArray byteArrayValue;
protected SByteArray sbyteArrayValue;
/** 32-bit (4-byte) unsigned integer
* @see org.jhove2.module.format.tiff.type.Long */
protected Long longValue;
protected LongArray longArrayValue;
protected SLong sLongValue;
protected SLongArray sLongArrayValue;
protected FloatObject floatValue;
protected Double doubleValue;
private DoubleArray doubleArrayValue;
protected Rational rationalValue;
protected RationalArray rationalArrayValue;
protected Rational sRationalValue;
protected RationalArray sRationalArrayValue;
protected Short shortValue;
protected ShortArray shortArrayValue;
protected SShort sShortValue;
protected SShortArray sShortArrayValue;
/* Tiff Tag definition */
protected TiffTag tagDefinition;
/** invalid date/time value message */
private Message invalidDateTimeMessage;
/** invalid date time format message */
private Message invalidDateTimeFormatMessage;
/** invalid tilewidth not multiple of 16 message */
private Message tileWidthNotMultipleOf16Message;
/** invalid tile length not multiple of 16 message */
private Message tileLengthNotMultipleOf16Message;
@ReportableProperty(order=6, value = "Entry value/offset.")
public long getValueOffset() {
return this.valueOffset;
}
public int getVersion() {
return this.version;
}
/** no arg constructor */
public IFDEntry() {
super();
this.unknownTypeMessages = new ArrayList<Message>();
}
public IFDEntry(int tag, int type, long count, long valueOffset) {
this.tag = tag;
this.tiffType = TiffType.getType(type);
this.count = count;
this.valueOffset = valueOffset;
}
/**
* parse the IFD Entry
* @param tiff2FormatMapper Factory to map tiff id to Format
* @throws IOException, JHOVE2Exception
*/
public void parse(JHOVE2 jhove2, Source source, Input input,
Tiff2FormatMapFactory tiff2FormatMapper)
throws IOException, JHOVE2Exception
{
this.isValid = Validity.True;
this.tagOffset = input.getPosition();
this.tag = input.readUnsignedShort();
if (this.tag > prevTag)
prevTag = this.tag;
else {
this.isValid = Validity.False;
Object[]messageArgs = new Object[]{tag, this.tagOffset};
this.TagSortOrderErrorMessage = (new Message(Severity.ERROR,
Context.OBJECT,
"org.jhove2.module.format.tiff.IFD.TagSortOrderErrorMessage",
messageArgs, jhove2.getConfigInfo()));
}
this.type = input.readUnsignedShort();
this.tiffType = TiffType.getType(this.type);
/* Skip over tags with unknown type; those outside of defined range. */
if (this.type < TiffType.BYTE.num()|| this.type > TiffType.IFD.num()) {
Object[]messageArgs = new Object[] {this.type, this.tagOffset };
this.unknownTypeMessages.add(new Message(Severity.WARNING,
Context.OBJECT,
"org.jhove2.module.format.tiff.IFD.UnknownTypeMessage",
messageArgs, jhove2.getConfigInfo()));
}
else {
/* set the version */
if (this.type <= TiffType.SBYTE.num() && this.type <= TiffType.IFD.num()) {
this.version = 6;
}
this.count = (int) input.readUnsignedInt();
this.savedValueOffset = input.getPosition(); // save the offset of the ValueOffset field
this.valueOffset = input.readUnsignedInt(); // read in the value stored in the ValueOffset field
long value = this.valueOffset;
if (calcValueSize(this.type, this.count) > 4) {
/* the value read is the offset to the value */
long size = ((MeasurableSource) source).getSize();
/* test that the value offset is within the file */
if (value > size) {
this.isValid = Validity.False;
Object[]messageArgs = new Object[]{tag, value, size};
this.ValueOffsetReferenceLocationFileMessage = (new Message(Severity.ERROR,
Context.OBJECT,
"org.jhove2.module.format.tiff.IFD.ValueOffsetReferenceLocationFileMessage",
messageArgs, jhove2.getConfigInfo()));
return;
}
/* test offset is word aligned */
if ((value & 1) != 0){
this.isValid = Validity.False;
Object[]messageArgs = new Object[] {value};
this.ByteOffsetNotWordAlignedMessage = (new Message(Severity.ERROR,
Context.OBJECT,
"org.jhove2.module.format.tiff.IFD.ValueByteOffsetNotWordAlignedMessage",
messageArgs, jhove2.getConfigInfo()));
return;
}
}
if (isValidTag(jhove2)) {
/* Handle tags which require unique processing of their values */
/* Parse the ICCProfile or XMP tag */
if (this.tag == TiffIFD.ICCPROFILE ||
this.tag == TiffIFD.XMP) {
ByteStreamSource bss =
jhove2.getSourceFactory().getByteStreamSource(jhove2, source,
this.valueOffset, this.count,
(this.tag == TiffIFD.ICCPROFILE) ? ".icc" : ".xml");
Format format = tiff2FormatMapper.getFormat(this.tag);
I8R identifier = format.getIdentifier();
FormatIdentification presumptiveFormat = new FormatIdentification(identifier, Confidence.PositiveSpecific);
bss.addPresumptiveFormat(presumptiveFormat);
jhove2.characterize(bss, input);
}
else {
readValues(input);
validate(jhove2, input);
}
}
/* reset the input position so that the offset is set up correctly since when you read values the
* input position gets changed from where you want to be in the IFD
* the offset of the Value field + 4 bytes will get you to the next Tag field
*/
input.setPosition(this.savedValueOffset + 4);
}
}
/**
*
* isValidTag
*
* 1) that the type matches expected type values for that tag definition
* 2) that count expected matches the count read in
*
* @param jhove2
* @return boolean
* @throws JHOVE2Exception
* @throws IOException
*/
protected boolean isValidTag(JHOVE2 jhove2) throws JHOVE2Exception, IOException {
boolean isValid = true;
/* retrieve the definition for the tag read in */
this.tagDefinition = TiffTag.getTag(this.tag);
/* validate that the type and count read in matches what is expected for this tag */
if (this.tagDefinition != null) {
this.name = this.tagDefinition.getName();
checkType(jhove2, this.tiffType, this.tagDefinition.getType());
checkCount(jhove2, this.count, this.tagDefinition.getCardinality());
}
else {
Object[]messageArgs = new Object[]{this.tag, tagOffset};
this.UnknownTagMessage = (new Message(Severity.WARNING,
Context.OBJECT,
"org.jhove2.module.format.tiff.IFDEntry.UnknownTagMessage",
messageArgs, jhove2.getConfigInfo()));
}
return isValid;
}
/**
*
* perform validations and post-processing for specific tags
*
* @param jhove2
* @throws JHOVE2Exception
* @throws IOException
*/
protected void validate(JHOVE2 jhove2, Input input) throws JHOVE2Exception, IOException {
/* Validate specific tags and set version when applicable */
/* version 5 tags */
if (this.tag == TiffIFD.ARTIST) {
if (this.version < 5) {
this.version = 5;
}
}
else if (this.tag == TiffIFD.COLORMAP) {
if (this.version < 5) {
this.version = 5;
}
}
else if (this.tag == TiffIFD.COMPRESSION) {
// get the scheme to determine version
int scheme = ((Short) this.getValue()).getValue();
if (scheme == 5 && version < 5) {
this.version = 5;
}
else if (scheme == 6 && version < 6) {
this.version = 6;
}
/* set the descriptive format for the Compression Scheme */
Compression compression =
Compression.getCompressionValue(scheme, jhove2);
if (compression != null) {
this.compression_d = compression.getDescription();
}
}
/* validate Date format is YYYY:MM:DD HH:MM:SS */
else if (this.tag == TiffIFD.DATETIME) {
if (this.version < 5) {
this.version = 5;
}
SimpleDateFormat date = new SimpleDateFormat("yyyy:MM:dd HH:mm:ss");
String dateTime = (String)((AsciiArray) this.getValue()).toString();
try {
date.parse(dateTime);
}
catch (ParseException e) {
this.isValid = Validity.False;
Object[]messageArgs = new Object[]{dateTime};
this.invalidDateTimeFormatMessage = new Message(Severity.ERROR,
Context.OBJECT,
"org.jhove2.module.format.tiff.IFDEntry.invalidDateTimeFormatMessage",
messageArgs, jhove2.getConfigInfo());
}
/* check that date is a valid date/time */
date.setLenient(false);
try {
date.parse(dateTime);
}
catch (ParseException e) {
this.isValid = Validity.False;
Object[]messageArgs = new Object[]{dateTime};
this.invalidDateTimeMessage = new Message(Severity.ERROR,
Context.OBJECT,
"org.jhove2.module.format.tiff.IFDEntry.invalidDateTimeMessage",
messageArgs, jhove2.getConfigInfo());
}
}
else if (this.tag == TiffIFD.HOSTCOMPUTER) {
if (this.version < 5) {
this.version = 5;
}
}
else if (this.tag == TiffIFD.NEWSUBFILETYPE) {
if (this.version < 5) {
this.version = 5;
}
}
else if (this.tag == TiffIFD.PREDICTOR) {
if (this.version < 5) {
this.version = 5;
}
}
else if (this.tag == TiffIFD.PRIMARY_CHROMATACITIES){
if (this.version < 5) {
this.version = 5;
}
}
else if (this.tag == TiffIFD.SOFTWARE) {
if (this.version < 5) {
this.version = 5;
}
}
else if (this.tag == TiffIFD.WHITEPOINT) {
if (this.version < 5) {
this.version = 5;
}
}
/* version 6 tags */
else if (this.tag == TiffIFD.COPYRIGHT) {
if (this.version < 6)
this.version = 6;
}
else if (this.tag == TiffIFD.DOTRANGE) {
if (this.version < 6)
this.version = 6;
}
else if (this.tag == TiffIFD.EXTRASAMPLES) {
if (this.version < 6)
this.version = 6;
}
else if (this.tag == TiffIFD.HALFTONEHINTS) {
if (this.version < 6)
this.version = 6;
}
else if (this.tag == TiffIFD.INKNAMES) {
if (this.version < 6)
this.version = 6;
}
else if (this.tag == TiffIFD.INKSET) {
if (this.version < 6)
this.version = 6;
}
else if (this.tag == TiffIFD.JPEGACTABLES) {
if (this.version < 6)
this.version = 6;
}
else if (this.tag == TiffIFD.JPEGDCTABLES) {
if (this.version < 6)
this.version = 6;
}
else if (this.tag == TiffIFD.JPEGINTERCHANGEFORMAT) {
if (this.version < 6)
this.version = 6;
}
else if (this.tag == TiffIFD.JPEGINTERCHANGEFORMATLENGTH) {
if (this.version < 6)
this.version = 6;
}
else if (this.tag == TiffIFD.JPEGLOSSLESSPREDICTORS) {
if (this.version < 6)
this.version = 6;
}
else if (this.tag == TiffIFD.JPEGPOINTTRANSFORMS) {
if (this.version < 6)
this.version = 6;
}
else if (this.tag == TiffIFD.JPEGPROC) {
if (this.version < 6)
this.version = 6;
}
else if (this.tag == TiffIFD.JPEG_RESTART_INTERVAL) {
if (this.version < 6)
this.version = 6;
}
else if (this.tag == TiffIFD.JPEG_QTABLES) {
if (this.version < 6)
this.version = 6;
}
else if (this.tag == TiffIFD.NUMBEROFINKS) {
if (this.version < 6)
this.version = 6;
}
else if (this.tag == TiffIFD.PHOTMETRIC_INTERPRETATION) {
int photometricInterpretation = ((Short) this.getValue()).getValue();
if (photometricInterpretation == 5 || //(CMYK)
photometricInterpretation == 6 || //(YCbCr)
photometricInterpretation == 8) { //(CIE L*a*b*)
if (this.version < 6)
this.version = 6;
}
}
else if (this.tag == TiffIFD.REFERENCEBLACKWHITE) {
if (this.version < 6)
this.version = 6;
}
else if (this.tag == TiffIFD.SAMPLEFORMAT) {
if (this.version < 6)
this.version = 6;
}
else if (this.tag == TiffIFD.SMINSAMPLEVALUE) {
if (this.version < 6)
this.version = 6;
}
else if (this.tag == TiffIFD.SMAXSAMPLEVALUE) {
if (this.version < 6)
this.version = 6;
}
else if (this.tag == TiffIFD.TARGETPRINTER) {
if (this.version < 6)
this.version = 6;
}
/* Validate TILELENGTH value is integral multiple of 16 */
else if (this.tag == TiffIFD.TILELENGTH ) {
if (this.version < 6)
this.version = 6;
long tileLength = ((Long) this.getValue()).getValue();
if (tileLength%16 > 0) {
this.isValid = Validity.False;
Object[]messageArgs = new Object[] {this.tag, this.valueOffset};
this.tileLengthNotMultipleOf16Message = (new Message(Severity.WARNING,
Context.OBJECT,
"org.jhove2.module.format.tiff.IFDEntry.tileLengthNotMultipleof16Message",
messageArgs, jhove2.getConfigInfo()));
return;
}
}
/* Validate TILEWIDTH value is integral multiple of 16 */
else if (this.tag == TiffIFD.TILEWIDTH ) {
if (this.version < 6)
this.version = 6;
long tileWidth = ((Long) this.getValue()).getValue();
if (tileWidth%16 > 0) {
this.isValid = Validity.False;
Object[]messageArgs = new Object[] {this.tag, this.valueOffset};
this.tileWidthNotMultipleOf16Message = (new Message(Severity.WARNING,
Context.OBJECT,
"org.jhove2.module.format.tiff.IFDEntry.tileWidthNotMultipleof16Message",
messageArgs, jhove2.getConfigInfo()));
}
}
else if (this.tag == TiffIFD.TILEBYTECOUNTS ) {
if (this.version < 6)
this.version = 6;
}
else if (this.tag == TiffIFD.TILEOFFSETS ) {
if (this.version < 6)
this.version = 6;
}
else if (this.tag == TiffIFD.TRANSFERRANGE) {
if (this.version < 6)
this.version = 6;
}
else if (this.tag == TiffIFD.YCBCRCOEFFICIENTS) {
if (this.version < 6)
this.version = 6;
}
else if (this.tag == TiffIFD.YCBCRPOSITIONING) {
if (this.version < 6)
this.version = 6;
}
else if (this.tag == TiffIFD.YCBCRSUBSAMPLING) {
if (this.version < 6)
this.version = 6;
}
}
/**
* checks that the count value read in matches the expected count value
*
* @param jhove2
* @param count
* @param expectedCount
* @throws JHOVE2Exception
*/
private void checkCount(JHOVE2 jhove2, long count, String expectedCount)
throws JHOVE2Exception
{
if (expectedCount != null) {
int expected = Integer.parseInt(expectedCount);
if (count < expected) {
this.isValid = Validity.False;
Object[]messageArgs = new Object[] {this.tag, count, expectedCount};
this.InvalidCountValueMessage = (new Message(Severity.WARNING,
Context.OBJECT,
"org.jhove2.module.format.tiff.IFDEntry.InvalidCountValueMessage",
messageArgs, jhove2.getConfigInfo()));
}
}
}
/**
* compares the type that is read in with the type that is defined for the tag
* For unsigned integers, readers accept BYTE, SHORT, LONG or IFD types
*
* @param jhove2 - JHOVE2 framework
* @param list - the list of expected types defined for this tag
* @param string - the type read in
* @throws JHOVE2Exception
*/
private void checkType(JHOVE2 jhove2, TiffType type, List<String> expectedTypes) throws JHOVE2Exception {
int typeNum = type.num();
String typeReadIn = type.name();
/* type values of 6-12 were defined in TIFF 6.0 */
if (typeNum > TiffType.SBYTE.num()) {
this.version = 6;
}
boolean typeAccepted = false;
/* Readers are supposed to accept BYTE, SHORT or LONG for any
* unsigned integer field. */
if ((type.equals(TiffType.BYTE) || (type.equals(TiffType.SHORT)) ||
(type.equals(TiffType.LONG)) || (type.equals(TiffType.IFD)))) {
for (String expectedEntry:expectedTypes){
if (expectedEntry.equalsIgnoreCase("BYTE") || expectedEntry.equalsIgnoreCase("SHORT") ||
expectedEntry.equalsIgnoreCase("LONG") || expectedEntry.equalsIgnoreCase("IFD"))
typeAccepted = true; // type is valid
}
}
// assign LONG type to tag if it can be SHORT or LONG
if (typeAccepted) {
/*
if (expectedTypes.contains("SHORT") && expectedTypes.contains("LONG")) {
this.tiffType = TiffType.LONG;
}
*/
return;
}
boolean match = false;
for (String expectedEntry:expectedTypes){
if (expectedEntry.equalsIgnoreCase(typeReadIn)) {
match = true;
}
}
if (!match) {
this.isValid = Validity.False;
Object[]messageArgs = new Object[] {typeReadIn, this.tag, expectedTypes};
this.TypeMismatchMessage = (new Message(Severity.ERROR,
Context.OBJECT,
"org.jhove2.module.format.tiff.IFDEntry.TypeMismatchMessage",
messageArgs, jhove2.getConfigInfo()));
}
}
/**
* read the value based on type
* If the size of the value is greater than 4 bytes
* read the value directly from the valueOffset field (pointed
* to by the savedValueOffset).
* Else read the value from where the valueOffset points to.
*
*
* Each value can be of a different type which is stored in a different
* object. Based on the tag's type and if the value is an array or not,
* it will be stored in the appropriate object.
*
* @param input
* @throws IOException
*/
public void readValues(Input input) throws IOException {
TiffType type = this.tiffType;
/* if the value is stored in the field, read it from the field
* otherwise use the valueOffset to go to where the value is
*/
if (calcValueSize(this.type, this.count) > 4)
input.setPosition(this.valueOffset);
else
input.setPosition(this.savedValueOffset);
if (this.count <= 1 ) {
/* store a single value */
if (type.equals(TiffType.BYTE) || type.equals(TiffType.UNDEFINED)) {
this.byteValue = new Byte(input.readUnsignedByte());
}
else if (type.equals(TiffType.ASCII)){
this.asciiValue = new Ascii();
this.asciiValue.setValue(input, this.count);
}
else if (type.equals(TiffType.SHORT)) {
this.shortValue = new Short(input.readUnsignedShort());
// store in Long type if it can be SHORT or LONG
if (this.tagDefinition != null && this.tagDefinition.getType().contains("LONG"))
this.longValue = new Long (shortValue.getValue());
}
else if (type.equals(TiffType.LONG)) {
this.longValue = new Long(input.readUnsignedInt());
}
else if (type.equals(TiffType.FLOAT)) {
this.floatValue = new FloatObject(input.readFloat());
}
else if (type.equals(TiffType.DOUBLE)) {
this.doubleValue = new Double(input.readDouble());
}
else if (type.equals(TiffType.RATIONAL)) {
long num = input.readUnsignedInt();
long denom = input.readUnsignedInt();
this.rationalValue = new Rational(num, denom);
}
else if (type.equals(TiffType.SBYTE)) {
this.sByteValue = new SByte(input.readSignedByte());
}
else if (type.equals(TiffType.SSHORT)) {
this.sShortValue = new SShort(input.readSignedShort());
}
else if (type.equals(TiffType.SLONG)) {
this.sLongValue = new SLong(input.readSignedLong());
}
else if (type.equals(TiffType.SRATIONAL)) {
long num = input.readSignedInt();
long demon = input.readSignedInt();
this.sRationalValue = new Rational(num, demon);
}
}
else {
this.isArray = true;
/* read into an array */
if (type.equals(TiffType.BYTE) || type.equals(TiffType.UNDEFINED)) {
this.byteArrayValue = new ByteArray();
this.byteArrayValue.setValue(input, this.count);
}
else if (type.equals(TiffType.ASCII)){
this.asciiArrayValue = new AsciiArray();
this.asciiArrayValue.setValue(input, this.count);
}
else if (type.equals(TiffType.SHORT)) {
this.shortArrayValue = new ShortArray();
this.shortArrayValue.setValue(input, this.count);
// store in Long type if it can be SHORT or LONG
if (this.tagDefinition != null && this.tagDefinition.getType().contains("LONG")) {
this.longArrayValue = new LongArray (shortArrayValue.getShortArrayValue());
}
}
else if (type.equals(TiffType.LONG)) {
this.longArrayValue = new LongArray();
this.longArrayValue.setValue(input, this.count);
}
else if (type.equals(TiffType.DOUBLE)) {
doubleArrayValue = new DoubleArray();
doubleArrayValue.setValue(input, this.count);
}
else if (type.equals(TiffType.RATIONAL)) {
long num = input.readUnsignedInt();
long denom = input.readUnsignedInt();
this.rationalValue = new Rational(num, denom);
}
else if (type.equals(TiffType.SBYTE)) {
this.sbyteArrayValue = new SByteArray();
this.sbyteArrayValue.setValue(input, this.count);
}
else if (type.equals(TiffType.SSHORT)) {
this.sShortArrayValue = new SShortArray();
this.sShortArrayValue.setValue(input, this.count);
}
else if (type.equals(TiffType.SLONG)) {
this.sLongArrayValue = new SLongArray();
this.sLongArrayValue.setValue(input, this.count);
}
else if (type.equals(TiffType.SRATIONAL)) {
long num = input.readSignedLong();
long demon = input.readSignedLong();
this.rationalValue = new Rational(num, demon);
}
}
}
/**
* getValue - returns the Type Object based on its' TiffType
*
* @return Object containing the value
*/
@ReportableProperty(order=7, value = "Tiff Tag Value.")
public Object getValue(){
TiffType type = this.tiffType;
if (this.isArray) {
if (type.equals(TiffType.BYTE) || (type.equals(TiffType.UNDEFINED))) {
return this.byteArrayValue;
}
else if (type.equals(TiffType.ASCII)) {
return this.asciiArrayValue;
}
else if (type.equals(TiffType.SHORT)) {
// if value can be LONG or SHORT return the value stored in Long
if (this.tagDefinition != null && this.tagDefinition.getType().contains("LONG"))
return this.longArrayValue;
else
return this.shortArrayValue;
}
else if (type.equals(TiffType.LONG)) {
return this.longArrayValue;
}
else if (type.equals(TiffType.RATIONAL)) {
return this.rationalArrayValue;
}
else if (type.equals(TiffType.SSHORT)) {
return this.sShortArrayValue;
}
else if (type.equals(TiffType.SLONG)) {
return this.sLongArrayValue;
}
else if (type.equals(TiffType.SRATIONAL)) {
return this.sRationalArrayValue;
}
}
else {
if (type.equals(TiffType.BYTE) || (type.equals(TiffType.UNDEFINED))) {
return this.byteValue;
}
else if (type.equals(TiffType.ASCII)) {
return this.asciiValue;
}
else if (type.equals(TiffType.SHORT)) {
// if value can be LONG or SHORT return the value stored in Long
if (this.tagDefinition != null && this.tagDefinition.getType().contains("LONG")) {
return this.longValue;
}
else {
return this.shortValue;
}
}
else if (type.equals(TiffType.LONG)) {
return this.longValue;
}
else if (type.equals(TiffType.RATIONAL)) {
return this.rationalValue;
}
else if (type.equals(TiffType.SBYTE)) {
return this.sByteValue;
}
else if (type.equals(TiffType.SLONG)) {
return this.sLongValue;
}
else if (type.equals(TiffType.SRATIONAL)) {
return this.sRationalValue;
}
}
return null;
}
/**
* Calculate how many bytes a given number of fields of a given
* type will require.
* @param type Field type
* @param count Field count
*/
public static long calcValueSize (int type, long count)
{
int fieldSize = 0;
fieldSize = TiffType.getType(type).size();
return count*fieldSize;
}
/**
* reset the previous tag value
*/
public static void resetPrevTag(int value) {
prevTag = value;
}
/**
* The cardinality (number of values) for this TIFF tag
* @return long
*/
@ReportableProperty (order=5, value="Entry cardinality, number of values.")
public long getCount(){
return this.count;
}
/**
* The name of the tag entry
* @return String
*/
@ReportableProperty (order=1, value = "Entry tag name.")
public String getName(){
return this.name;
}
/**
* The TIFF tag number
* @return int
*/
@ReportableProperty(order=2, value="Entry tag.")
public int getTag(){
return this.tag;
}
/**
* Get the unknown tag message
* @return the unknownTagMessage
*/
@ReportableProperty(order=9, value = "unknown tag message.")
public Message getUnknownTagMessage() {
return this.UnknownTagMessage;
}
/**
* The field type of the value for this tag
* @return TiffType
*/
@ReportableProperty(order=4, value="Tag type.")
public TiffType getTiffType(){
return this.tiffType;
}
/**
* The value read from the type field for this tag
* @return long
*/
@ReportableProperty(order=3, value="Tag type.")
public long getType(){
return this.type;
}
/**
* Get the tag sort order error message
*
* @return the tagSortOrderErrorMessage
*/
@ReportableProperty(order=7, value = "tag sort order error message.")
public Message getTagSortOrderErrorMessage() {
return this.TagSortOrderErrorMessage;
}
/**
* Get the unknown type message
*
* @return the unknownTypeMessage
*/
@ReportableProperty(order=8, value = "unknown type message.")
public List<Message> getUnknownTypeMessage() {
return this.unknownTypeMessages;
}
/**
* Get byte offset not word aligned message
*
* @return the byteOffsetNotWordAlignedMessage
*/
@ReportableProperty(order=9, value = "Byte offset not word aligned message.")
public Message getByteOffsetNotWordAlignedMessage() {
return this.ByteOffsetNotWordAlignedMessage;
}
/**
* Get the value offset reference location file message
*
* @return the valueOffsetReferenceLocationFileMessage
*/
@ReportableProperty(order=10, value = "value offset reference location file message.")
public Message getValueOffsetReferenceLocationFileMessage() {
return this.ValueOffsetReferenceLocationFileMessage;
}
/**
* @return the typeMismatchMessage
*/
@ReportableProperty(order=11, value="type mismatch message.")
public Message getTypeMismatchMessage() {
return this.TypeMismatchMessage;
}
/**
* @return the invalidCountValueMessage
*/
@ReportableProperty(order=12, value="Invalid Count Value Message.")
public Message getInvalidCountValueMessage() {
return this.InvalidCountValueMessage;
}
/**
* @return the unknownTypeMessages
*/
@ReportableProperty(order=13, value="Unknown Type Message.")
public List<Message> getUnknownTypeMessages() {
return this.unknownTypeMessages;
}
/**
* @return the invalidDateTimeMessage
*/
@ReportableProperty(order=14, value="Invalid Date Time Message.")
public Message getInvalidDateTimeMessage() {
return this.invalidDateTimeMessage;
}
/**
* @return the invalidDateTimeFormatMessage
*/
@ReportableProperty(order=15, value="Invalid Date Time Format Message.")
public Message getInvalidDateTimeFormatMessage() {
return this.invalidDateTimeFormatMessage;
}
/**
* @return the tileWidthNotMultipleOf16Message
*/
@ReportableProperty(order = 16, value = "TileWidth not multiple of 16 message.")
public Message getTileWidthNotMultipleOf16Message() {
return this.tileWidthNotMultipleOf16Message;
}
/**
* @return the tileLengthNotMultipleOf16Message
*/
@ReportableProperty(order = 17, value = "TileLength not multiple of 16 message.")
public Message getTileLengthNotMultipleOf16Message() {
return this.tileLengthNotMultipleOf16Message;
}
@ReportableProperty(order=7, value="Compression Scheme in descriptive form.",
type=PropertyType.Descriptive)
public String getCompression_descriptive() {
return this.compression_d;
}
/** Get IFDEntry validity.
* @return IFDEntry validity
*/
@ReportableProperty(order=18, value="IFDEntry validity.")
public Validity isValid() {
return this.isValid;
}
@Override
public int compareTo(Object o) {
return 0;
}
}