1017 lines
30 KiB
Java
1017 lines
30 KiB
Java
/*
|
|
* Copyright (C) 2012 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
package it.sephiroth.android.library.exif2;
|
|
|
|
import java.nio.charset.Charset;
|
|
import java.text.SimpleDateFormat;
|
|
import java.util.Arrays;
|
|
import java.util.Date;
|
|
|
|
/**
|
|
* This class stores information of an EXIF tag. For more information about
|
|
* defined EXIF tags, please read the Jeita EXIF 2.2 standard. Tags should be
|
|
* instantiated using {@link ExifInterface#buildTag}.
|
|
*
|
|
* @see ExifInterface
|
|
*/
|
|
public class ExifTag {
|
|
/**
|
|
* The BYTE type in the EXIF standard. An 8-bit unsigned integer.
|
|
*/
|
|
public static final short TYPE_UNSIGNED_BYTE = 1;
|
|
/**
|
|
* The ASCII type in the EXIF standard. An 8-bit byte containing one 7-bit
|
|
* ASCII code. The final byte is terminated with NULL.
|
|
*/
|
|
public static final short TYPE_ASCII = 2;
|
|
/**
|
|
* The SHORT type in the EXIF standard. A 16-bit (2-byte) unsigned integer
|
|
*/
|
|
public static final short TYPE_UNSIGNED_SHORT = 3;
|
|
/**
|
|
* The LONG type in the EXIF standard. A 32-bit (4-byte) unsigned integer
|
|
*/
|
|
public static final short TYPE_UNSIGNED_LONG = 4;
|
|
/**
|
|
* The RATIONAL type of EXIF standard. It consists of two LONGs. The first
|
|
* one is the numerator and the second one expresses the denominator.
|
|
*/
|
|
public static final short TYPE_UNSIGNED_RATIONAL = 5;
|
|
/**
|
|
* The UNDEFINED type in the EXIF standard. An 8-bit byte that can take any
|
|
* value depending on the field definition.
|
|
*/
|
|
public static final short TYPE_UNDEFINED = 7;
|
|
/**
|
|
* The SLONG type in the EXIF standard. A 32-bit (4-byte) signed integer
|
|
* (2's complement notation).
|
|
*/
|
|
public static final short TYPE_LONG = 9;
|
|
/**
|
|
* The SRATIONAL type of EXIF standard. It consists of two SLONGs. The first
|
|
* one is the numerator and the second one is the denominator.
|
|
*/
|
|
public static final short TYPE_RATIONAL = 10;
|
|
static final int SIZE_UNDEFINED = 0;
|
|
private static final int TYPE_TO_SIZE_MAP[] = new int[11];
|
|
private static final int UNSIGNED_SHORT_MAX = 65535;
|
|
private static final long UNSIGNED_LONG_MAX = 4294967295L;
|
|
private static final long LONG_MAX = Integer.MAX_VALUE;
|
|
private static final long LONG_MIN = Integer.MIN_VALUE;
|
|
|
|
static {
|
|
TYPE_TO_SIZE_MAP[TYPE_UNSIGNED_BYTE] = 1;
|
|
TYPE_TO_SIZE_MAP[TYPE_ASCII] = 1;
|
|
TYPE_TO_SIZE_MAP[TYPE_UNSIGNED_SHORT] = 2;
|
|
TYPE_TO_SIZE_MAP[TYPE_UNSIGNED_LONG] = 4;
|
|
TYPE_TO_SIZE_MAP[TYPE_UNSIGNED_RATIONAL] = 8;
|
|
TYPE_TO_SIZE_MAP[TYPE_UNDEFINED] = 1;
|
|
TYPE_TO_SIZE_MAP[TYPE_LONG] = 4;
|
|
TYPE_TO_SIZE_MAP[TYPE_RATIONAL] = 8;
|
|
}
|
|
private static final SimpleDateFormat TIME_FORMAT = new SimpleDateFormat( "yyyy:MM:dd kk:mm:ss" );
|
|
private static Charset US_ASCII = Charset.forName( "US-ASCII" );
|
|
// Exif TagId
|
|
private final short mTagId;
|
|
// Exif Tag Type
|
|
private final short mDataType;
|
|
// If tag has defined count
|
|
private boolean mHasDefinedDefaultComponentCount;
|
|
// Actual data count in tag (should be number of elements in value array)
|
|
private int mComponentCountActual;
|
|
// The ifd that this tag should be put in
|
|
private int mIfd;
|
|
// The value (array of elements of type Tag Type)
|
|
private Object mValue;
|
|
// Value offset in exif header.
|
|
private int mOffset;
|
|
|
|
// Use builtTag in ExifInterface instead of constructor.
|
|
ExifTag(
|
|
short tagId, short type, int componentCount, int ifd, boolean hasDefinedComponentCount ) {
|
|
mTagId = tagId;
|
|
mDataType = type;
|
|
mComponentCountActual = componentCount;
|
|
mHasDefinedDefaultComponentCount = hasDefinedComponentCount;
|
|
mIfd = ifd;
|
|
mValue = null;
|
|
}
|
|
|
|
/**
|
|
* Returns true if the given IFD is a valid IFD.
|
|
*/
|
|
public static boolean isValidIfd( int ifdId ) {
|
|
return ifdId == IfdId.TYPE_IFD_0 || ifdId == IfdId.TYPE_IFD_1 || ifdId == IfdId.TYPE_IFD_EXIF || ifdId == IfdId.TYPE_IFD_INTEROPERABILITY || ifdId == IfdId.TYPE_IFD_GPS;
|
|
}
|
|
|
|
/**
|
|
* Returns true if a given type is a valid tag type.
|
|
*/
|
|
public static boolean isValidType( short type ) {
|
|
return type == TYPE_UNSIGNED_BYTE || type == TYPE_ASCII ||
|
|
type == TYPE_UNSIGNED_SHORT || type == TYPE_UNSIGNED_LONG ||
|
|
type == TYPE_UNSIGNED_RATIONAL || type == TYPE_UNDEFINED ||
|
|
type == TYPE_LONG || type == TYPE_RATIONAL;
|
|
}
|
|
|
|
/**
|
|
* Returns the ID of the IFD this tag belongs to.
|
|
*
|
|
* @see IfdId#TYPE_IFD_0
|
|
* @see IfdId#TYPE_IFD_1
|
|
* @see IfdId#TYPE_IFD_EXIF
|
|
* @see IfdId#TYPE_IFD_GPS
|
|
* @see IfdId#TYPE_IFD_INTEROPERABILITY
|
|
*/
|
|
public int getIfd() {
|
|
return mIfd;
|
|
}
|
|
|
|
protected void setIfd( int ifdId ) {
|
|
mIfd = ifdId;
|
|
}
|
|
|
|
/**
|
|
* Gets the TID of this tag.
|
|
*/
|
|
public short getTagId() {
|
|
return mTagId;
|
|
}
|
|
|
|
/**
|
|
* Gets the total data size in bytes of the value of this tag.
|
|
*/
|
|
public int getDataSize() {
|
|
return getComponentCount() * getElementSize( getDataType() );
|
|
}
|
|
|
|
/**
|
|
* Gets the component count of this tag.
|
|
*/
|
|
|
|
// TODO: fix integer overflows with this
|
|
public int getComponentCount() {
|
|
return mComponentCountActual;
|
|
}
|
|
|
|
/**
|
|
* Gets the element size of the given data type in bytes.
|
|
*
|
|
* @see #TYPE_ASCII
|
|
* @see #TYPE_LONG
|
|
* @see #TYPE_RATIONAL
|
|
* @see #TYPE_UNDEFINED
|
|
* @see #TYPE_UNSIGNED_BYTE
|
|
* @see #TYPE_UNSIGNED_LONG
|
|
* @see #TYPE_UNSIGNED_RATIONAL
|
|
* @see #TYPE_UNSIGNED_SHORT
|
|
*/
|
|
public static int getElementSize( short type ) {
|
|
return TYPE_TO_SIZE_MAP[type];
|
|
}
|
|
|
|
/**
|
|
* Gets the data type of this tag
|
|
*
|
|
* @see #TYPE_ASCII
|
|
* @see #TYPE_LONG
|
|
* @see #TYPE_RATIONAL
|
|
* @see #TYPE_UNDEFINED
|
|
* @see #TYPE_UNSIGNED_BYTE
|
|
* @see #TYPE_UNSIGNED_LONG
|
|
* @see #TYPE_UNSIGNED_RATIONAL
|
|
* @see #TYPE_UNSIGNED_SHORT
|
|
*/
|
|
public short getDataType() {
|
|
return mDataType;
|
|
}
|
|
|
|
/**
|
|
* Sets the component count of this tag. Call this function before
|
|
* setValue() if the length of value does not match the component count.
|
|
*/
|
|
protected void forceSetComponentCount( int count ) {
|
|
mComponentCountActual = count;
|
|
}
|
|
|
|
/**
|
|
* Returns true if this ExifTag contains value; otherwise, this tag will
|
|
* contain an offset value that is determined when the tag is written.
|
|
*/
|
|
public boolean hasValue() {
|
|
return mValue != null;
|
|
}
|
|
|
|
/**
|
|
* Sets integer values into this tag. This method should be used for tags of
|
|
* type {@link #TYPE_UNSIGNED_SHORT}. This method will fail if:
|
|
* <ul>
|
|
* <li>The component type of this tag is not {@link #TYPE_UNSIGNED_SHORT},
|
|
* {@link #TYPE_UNSIGNED_LONG}, or {@link #TYPE_LONG}.</li>
|
|
* <li>The value overflows.</li>
|
|
* <li>The value.length does NOT match the component count in the definition
|
|
* for this tag.</li>
|
|
* </ul>
|
|
*/
|
|
public boolean setValue( int[] value ) {
|
|
if( checkBadComponentCount( value.length ) ) {
|
|
return false;
|
|
}
|
|
if( mDataType != TYPE_UNSIGNED_SHORT && mDataType != TYPE_LONG &&
|
|
mDataType != TYPE_UNSIGNED_LONG ) {
|
|
return false;
|
|
}
|
|
if( mDataType == TYPE_UNSIGNED_SHORT && checkOverflowForUnsignedShort( value ) ) {
|
|
return false;
|
|
}
|
|
else if( mDataType == TYPE_UNSIGNED_LONG && checkOverflowForUnsignedLong( value ) ) {
|
|
return false;
|
|
}
|
|
|
|
long[] data = new long[value.length];
|
|
for( int i = 0; i < value.length; i++ ) {
|
|
data[i] = value[i];
|
|
}
|
|
mValue = data;
|
|
mComponentCountActual = value.length;
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Sets integer value into this tag. This method should be used for tags of
|
|
* type {@link #TYPE_UNSIGNED_SHORT}, or {@link #TYPE_LONG}. This method
|
|
* will fail if:
|
|
* <ul>
|
|
* <li>The component type of this tag is not {@link #TYPE_UNSIGNED_SHORT},
|
|
* {@link #TYPE_UNSIGNED_LONG}, or {@link #TYPE_LONG}.</li>
|
|
* <li>The value overflows.</li>
|
|
* <li>The component count in the definition of this tag is not 1.</li>
|
|
* </ul>
|
|
*/
|
|
public boolean setValue( int value ) {
|
|
return setValue( new int[]{ value } );
|
|
}
|
|
|
|
/**
|
|
* Sets long values into this tag. This method should be used for tags of
|
|
* type {@link #TYPE_UNSIGNED_LONG}. This method will fail if:
|
|
* <ul>
|
|
* <li>The component type of this tag is not {@link #TYPE_UNSIGNED_LONG}.</li>
|
|
* <li>The value overflows.</li>
|
|
* <li>The value.length does NOT match the component count in the definition
|
|
* for this tag.</li>
|
|
* </ul>
|
|
*/
|
|
public boolean setValue( long[] value ) {
|
|
if( checkBadComponentCount( value.length ) || mDataType != TYPE_UNSIGNED_LONG ) {
|
|
return false;
|
|
}
|
|
if( checkOverflowForUnsignedLong( value ) ) {
|
|
return false;
|
|
}
|
|
mValue = value;
|
|
mComponentCountActual = value.length;
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Sets long values into this tag. This method should be used for tags of
|
|
* type {@link #TYPE_UNSIGNED_LONG}. This method will fail if:
|
|
* <ul>
|
|
* <li>The component type of this tag is not {@link #TYPE_UNSIGNED_LONG}.</li>
|
|
* <li>The value overflows.</li>
|
|
* <li>The component count in the definition for this tag is not 1.</li>
|
|
* </ul>
|
|
*/
|
|
public boolean setValue( long value ) {
|
|
return setValue( new long[]{ value } );
|
|
}
|
|
|
|
/**
|
|
* Sets Rational values into this tag. This method should be used for tags
|
|
* of type {@link #TYPE_UNSIGNED_RATIONAL}, or {@link #TYPE_RATIONAL}. This
|
|
* method will fail if:
|
|
* <ul>
|
|
* <li>The component type of this tag is not {@link #TYPE_UNSIGNED_RATIONAL}
|
|
* or {@link #TYPE_RATIONAL}.</li>
|
|
* <li>The value overflows.</li>
|
|
* <li>The value.length does NOT match the component count in the definition
|
|
* for this tag.</li>
|
|
* </ul>
|
|
*
|
|
* @see Rational
|
|
*/
|
|
public boolean setValue( Rational[] value ) {
|
|
if( checkBadComponentCount( value.length ) ) {
|
|
return false;
|
|
}
|
|
if( mDataType != TYPE_UNSIGNED_RATIONAL && mDataType != TYPE_RATIONAL ) {
|
|
return false;
|
|
}
|
|
if( mDataType == TYPE_UNSIGNED_RATIONAL && checkOverflowForUnsignedRational( value ) ) {
|
|
return false;
|
|
}
|
|
else if( mDataType == TYPE_RATIONAL && checkOverflowForRational( value ) ) {
|
|
return false;
|
|
}
|
|
|
|
mValue = value;
|
|
mComponentCountActual = value.length;
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Sets a Rational value into this tag. This method should be used for tags
|
|
* of type {@link #TYPE_UNSIGNED_RATIONAL}, or {@link #TYPE_RATIONAL}. This
|
|
* method will fail if:
|
|
* <ul>
|
|
* <li>The component type of this tag is not {@link #TYPE_UNSIGNED_RATIONAL}
|
|
* or {@link #TYPE_RATIONAL}.</li>
|
|
* <li>The value overflows.</li>
|
|
* <li>The component count in the definition for this tag is not 1.</li>
|
|
* </ul>
|
|
*
|
|
* @see Rational
|
|
*/
|
|
public boolean setValue( Rational value ) {
|
|
return setValue( new Rational[]{ value } );
|
|
}
|
|
|
|
/**
|
|
* Sets byte values into this tag. This method should be used for tags of
|
|
* type {@link #TYPE_UNSIGNED_BYTE} or {@link #TYPE_UNDEFINED}. This method
|
|
* will fail if:
|
|
* <ul>
|
|
* <li>The component type of this tag is not {@link #TYPE_UNSIGNED_BYTE} or
|
|
* {@link #TYPE_UNDEFINED} .</li>
|
|
* <li>The length does NOT match the component count in the definition for
|
|
* this tag.</li>
|
|
* </ul>
|
|
*/
|
|
public boolean setValue( byte[] value, int offset, int length ) {
|
|
if( checkBadComponentCount( length ) ) {
|
|
return false;
|
|
}
|
|
if( mDataType != TYPE_UNSIGNED_BYTE && mDataType != TYPE_UNDEFINED ) {
|
|
return false;
|
|
}
|
|
mValue = new byte[length];
|
|
System.arraycopy( value, offset, mValue, 0, length );
|
|
mComponentCountActual = length;
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Equivalent to setValue(value, 0, value.length).
|
|
*/
|
|
public boolean setValue( byte[] value ) {
|
|
return setValue( value, 0, value.length );
|
|
}
|
|
|
|
/**
|
|
* Sets byte value into this tag. This method should be used for tags of
|
|
* type {@link #TYPE_UNSIGNED_BYTE} or {@link #TYPE_UNDEFINED}. This method
|
|
* will fail if:
|
|
* <ul>
|
|
* <li>The component type of this tag is not {@link #TYPE_UNSIGNED_BYTE} or
|
|
* {@link #TYPE_UNDEFINED} .</li>
|
|
* <li>The component count in the definition for this tag is not 1.</li>
|
|
* </ul>
|
|
*/
|
|
public boolean setValue( byte value ) {
|
|
return setValue( new byte[]{ value } );
|
|
}
|
|
|
|
/**
|
|
* Sets the value for this tag using an appropriate setValue method for the
|
|
* given object. This method will fail if:
|
|
* <ul>
|
|
* <li>The corresponding setValue method for the class of the object passed
|
|
* in would fail.</li>
|
|
* <li>There is no obvious way to cast the object passed in into an EXIF tag
|
|
* type.</li>
|
|
* </ul>
|
|
*/
|
|
public boolean setValue( Object obj ) {
|
|
if( obj == null ) {
|
|
return false;
|
|
}
|
|
else if( obj instanceof Short ) {
|
|
return setValue( ( (Short) obj ).shortValue() & 0x0ffff );
|
|
}
|
|
else if( obj instanceof String ) {
|
|
return setValue( (String) obj );
|
|
}
|
|
else if( obj instanceof int[] ) {
|
|
return setValue( (int[]) obj );
|
|
}
|
|
else if( obj instanceof long[] ) {
|
|
return setValue( (long[]) obj );
|
|
}
|
|
else if( obj instanceof Rational ) {
|
|
return setValue( (Rational) obj );
|
|
}
|
|
else if( obj instanceof Rational[] ) {
|
|
return setValue( (Rational[]) obj );
|
|
}
|
|
else if( obj instanceof byte[] ) {
|
|
return setValue( (byte[]) obj );
|
|
}
|
|
else if( obj instanceof Integer ) {
|
|
return setValue( ( (Integer) obj ).intValue() );
|
|
}
|
|
else if( obj instanceof Long ) {
|
|
return setValue( ( (Long) obj ).longValue() );
|
|
}
|
|
else if( obj instanceof Byte ) {
|
|
return setValue( ( (Byte) obj ).byteValue() );
|
|
}
|
|
else if( obj instanceof Short[] ) {
|
|
// Nulls in this array are treated as zeroes.
|
|
Short[] arr = (Short[]) obj;
|
|
int[] fin = new int[arr.length];
|
|
for( int i = 0; i < arr.length; i++ ) {
|
|
fin[i] = ( arr[i] == null ) ? 0 : arr[i].shortValue() & 0x0ffff;
|
|
}
|
|
return setValue( fin );
|
|
}
|
|
else if( obj instanceof Integer[] ) {
|
|
// Nulls in this array are treated as zeroes.
|
|
Integer[] arr = (Integer[]) obj;
|
|
int[] fin = new int[arr.length];
|
|
for( int i = 0; i < arr.length; i++ ) {
|
|
fin[i] = ( arr[i] == null ) ? 0 : arr[i].intValue();
|
|
}
|
|
return setValue( fin );
|
|
}
|
|
else if( obj instanceof Long[] ) {
|
|
// Nulls in this array are treated as zeroes.
|
|
Long[] arr = (Long[]) obj;
|
|
long[] fin = new long[arr.length];
|
|
for( int i = 0; i < arr.length; i++ ) {
|
|
fin[i] = ( arr[i] == null ) ? 0 : arr[i].longValue();
|
|
}
|
|
return setValue( fin );
|
|
}
|
|
else if( obj instanceof Byte[] ) {
|
|
// Nulls in this array are treated as zeroes.
|
|
Byte[] arr = (Byte[]) obj;
|
|
byte[] fin = new byte[arr.length];
|
|
for( int i = 0; i < arr.length; i++ ) {
|
|
fin[i] = ( arr[i] == null ) ? 0 : arr[i].byteValue();
|
|
}
|
|
return setValue( fin );
|
|
}
|
|
else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Sets a timestamp to this tag. The method converts the timestamp with the
|
|
* format of "yyyy:MM:dd kk:mm:ss" and calls {@link #setValue(String)}. This
|
|
* method will fail if the data type is not {@link #TYPE_ASCII} or the
|
|
* component count of this tag is not 20 or undefined.
|
|
*
|
|
* @param time the number of milliseconds since Jan. 1, 1970 GMT
|
|
* @return true on success
|
|
*/
|
|
public boolean setTimeValue( long time ) {
|
|
// synchronized on TIME_FORMAT as SimpleDateFormat is not thread safe
|
|
synchronized( TIME_FORMAT ) {
|
|
return setValue( TIME_FORMAT.format( new Date( time ) ) );
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Sets a string value into this tag. This method should be used for tags of
|
|
* type {@link #TYPE_ASCII}. The string is converted to an ASCII string.
|
|
* Characters that cannot be converted are replaced with '?'. The length of
|
|
* the string must be equal to either (component count -1) or (component
|
|
* count). The final byte will be set to the string null terminator '\0',
|
|
* overwriting the last character in the string if the value.length is equal
|
|
* to the component count. This method will fail if:
|
|
* <ul>
|
|
* <li>The data type is not {@link #TYPE_ASCII} or {@link #TYPE_UNDEFINED}.</li>
|
|
* <li>The length of the string is not equal to (component count -1) or
|
|
* (component count) in the definition for this tag.</li>
|
|
* </ul>
|
|
*/
|
|
public boolean setValue( String value ) {
|
|
if( mDataType != TYPE_ASCII && mDataType != TYPE_UNDEFINED ) {
|
|
return false;
|
|
}
|
|
|
|
byte[] buf = value.getBytes( US_ASCII );
|
|
byte[] finalBuf = buf;
|
|
if( buf.length > 0 ) {
|
|
finalBuf = ( buf[buf.length - 1] == 0 || mDataType == TYPE_UNDEFINED ) ? buf : Arrays.copyOf( buf, buf.length + 1 );
|
|
}
|
|
else if( mDataType == TYPE_ASCII && mComponentCountActual == 1 ) {
|
|
finalBuf = new byte[]{ 0 };
|
|
}
|
|
int count = finalBuf.length;
|
|
if( checkBadComponentCount( count ) ) {
|
|
return false;
|
|
}
|
|
mComponentCountActual = count;
|
|
mValue = finalBuf;
|
|
return true;
|
|
}
|
|
|
|
private boolean checkBadComponentCount( int count ) {
|
|
if( mHasDefinedDefaultComponentCount && ( mComponentCountActual != count ) ) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Gets the value as a String. This method should be used for tags of type
|
|
* {@link #TYPE_ASCII}.
|
|
*
|
|
* @param defaultValue the String to return if the tag's value does not
|
|
* exist or cannot be converted to a String.
|
|
* @return the tag's value as a String, or the defaultValue.
|
|
*/
|
|
public String getValueAsString( String defaultValue ) {
|
|
String s = getValueAsString();
|
|
if( s == null ) {
|
|
return defaultValue;
|
|
}
|
|
return s;
|
|
}
|
|
|
|
/**
|
|
* Gets the value as a String. This method should be used for tags of type
|
|
* {@link #TYPE_ASCII}.
|
|
*
|
|
* @return the value as a String, or null if the tag's value does not exist
|
|
* or cannot be converted to a String.
|
|
*/
|
|
public String getValueAsString() {
|
|
if( mValue == null ) {
|
|
return null;
|
|
}
|
|
else if( mValue instanceof String ) {
|
|
return (String) mValue;
|
|
}
|
|
else if( mValue instanceof byte[] ) {
|
|
return new String( (byte[]) mValue, US_ASCII );
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Gets the value as a byte. If there are more than 1 bytes in this value,
|
|
* gets the first byte. This method should be used for tags of type
|
|
* {@link #TYPE_UNDEFINED} or {@link #TYPE_UNSIGNED_BYTE}.
|
|
*
|
|
* @param defaultValue the byte to return if tag's value does not exist or
|
|
* cannot be converted to a byte.
|
|
* @return the tag's value as a byte, or the defaultValue.
|
|
*/
|
|
public byte getValueAsByte( byte defaultValue ) {
|
|
byte[] b = getValueAsBytes();
|
|
if( b == null || b.length < 1 ) {
|
|
return defaultValue;
|
|
}
|
|
return b[0];
|
|
}
|
|
|
|
/**
|
|
* Gets the value as a byte array. This method should be used for tags of
|
|
* type {@link #TYPE_UNDEFINED} or {@link #TYPE_UNSIGNED_BYTE}.
|
|
*
|
|
* @return the value as a byte array, or null if the tag's value does not
|
|
* exist or cannot be converted to a byte array.
|
|
*/
|
|
public byte[] getValueAsBytes() {
|
|
if( mValue instanceof byte[] ) {
|
|
return (byte[]) mValue;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Gets the value as a Rational. If there are more than 1 Rationals in this
|
|
* value, gets the first one. This method should be used for tags of type
|
|
* {@link #TYPE_RATIONAL} or {@link #TYPE_UNSIGNED_RATIONAL}.
|
|
*
|
|
* @param defaultValue the numerator of the Rational to return if tag's
|
|
* value does not exist or cannot be converted to a Rational (the
|
|
* denominator will be 1).
|
|
* @return the tag's value as a Rational, or the defaultValue.
|
|
*/
|
|
public Rational getValueAsRational( long defaultValue ) {
|
|
Rational defaultVal = new Rational( defaultValue, 1 );
|
|
return getValueAsRational( defaultVal );
|
|
}
|
|
|
|
/**
|
|
* Gets the value as a Rational. If there are more than 1 Rationals in this
|
|
* value, gets the first one. This method should be used for tags of type
|
|
* {@link #TYPE_RATIONAL} or {@link #TYPE_UNSIGNED_RATIONAL}.
|
|
*
|
|
* @param defaultValue the Rational to return if tag's value does not exist
|
|
* or cannot be converted to a Rational.
|
|
* @return the tag's value as a Rational, or the defaultValue.
|
|
*/
|
|
public Rational getValueAsRational( Rational defaultValue ) {
|
|
Rational[] r = getValueAsRationals();
|
|
if( r == null || r.length < 1 ) {
|
|
return defaultValue;
|
|
}
|
|
return r[0];
|
|
}
|
|
|
|
/**
|
|
* Gets the value as an array of Rationals. This method should be used for
|
|
* tags of type {@link #TYPE_RATIONAL} or {@link #TYPE_UNSIGNED_RATIONAL}.
|
|
*
|
|
* @return the value as as an array of Rationals, or null if the tag's value
|
|
* does not exist or cannot be converted to an array of Rationals.
|
|
*/
|
|
public Rational[] getValueAsRationals() {
|
|
if( mValue instanceof Rational[] ) {
|
|
return (Rational[]) mValue;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Gets the value as an int. If there are more than 1 ints in this value,
|
|
* gets the first one. This method should be used for tags of type
|
|
* {@link #TYPE_UNSIGNED_SHORT}, {@link #TYPE_UNSIGNED_LONG}.
|
|
*
|
|
* @param defaultValue the int to return if tag's value does not exist or
|
|
* cannot be converted to an int.
|
|
* @return the tag's value as a int, or the defaultValue.
|
|
*/
|
|
public int getValueAsInt( int defaultValue ) {
|
|
int[] i = getValueAsInts();
|
|
if( i == null || i.length < 1 ) {
|
|
return defaultValue;
|
|
}
|
|
return i[0];
|
|
}
|
|
|
|
/**
|
|
* Gets the value as an array of ints. This method should be used for tags
|
|
* of type {@link #TYPE_UNSIGNED_SHORT}, {@link #TYPE_UNSIGNED_LONG}.
|
|
*
|
|
* @return the value as as an array of ints, or null if the tag's value does
|
|
* not exist or cannot be converted to an array of ints.
|
|
*/
|
|
public int[] getValueAsInts() {
|
|
if( mValue == null ) {
|
|
return null;
|
|
}
|
|
else if( mValue instanceof long[] ) {
|
|
long[] val = (long[]) mValue;
|
|
int[] arr = new int[val.length];
|
|
for( int i = 0; i < val.length; i++ ) {
|
|
arr[i] = (int) val[i]; // Truncates
|
|
}
|
|
return arr;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Gets the value or null if none exists. If there are more than 1 longs in
|
|
* this value, gets the first one. This method should be used for tags of
|
|
* type {@link #TYPE_UNSIGNED_LONG}.
|
|
*
|
|
* @param defaultValue the long to return if tag's value does not exist or
|
|
* cannot be converted to a long.
|
|
* @return the tag's value as a long, or the defaultValue.
|
|
*/
|
|
public long getValueAsLong( long defaultValue ) {
|
|
long[] l = getValueAsLongs();
|
|
if( l == null || l.length < 1 ) {
|
|
return defaultValue;
|
|
}
|
|
return l[0];
|
|
}
|
|
|
|
/**
|
|
* Gets the value as an array of longs. This method should be used for tags
|
|
* of type {@link #TYPE_UNSIGNED_LONG}.
|
|
*
|
|
* @return the value as as an array of longs, or null if the tag's value
|
|
* does not exist or cannot be converted to an array of longs.
|
|
*/
|
|
public long[] getValueAsLongs() {
|
|
if( mValue instanceof long[] ) {
|
|
return (long[]) mValue;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Gets the tag's value or null if none exists.
|
|
*/
|
|
public Object getValue() {
|
|
return mValue;
|
|
}
|
|
|
|
/**
|
|
* Gets a long representation of the value.
|
|
*
|
|
* @param defaultValue value to return if there is no value or value is a
|
|
* rational with a denominator of 0.
|
|
* @return the tag's value as a long, or defaultValue if no representation
|
|
* exists.
|
|
*/
|
|
public long forceGetValueAsLong( long defaultValue ) {
|
|
long[] l = getValueAsLongs();
|
|
if( l != null && l.length >= 1 ) {
|
|
return l[0];
|
|
}
|
|
byte[] b = getValueAsBytes();
|
|
if( b != null && b.length >= 1 ) {
|
|
return b[0];
|
|
}
|
|
Rational[] r = getValueAsRationals();
|
|
if( r != null && r.length >= 1 && r[0].getDenominator() != 0 ) {
|
|
return (long) r[0].toDouble();
|
|
}
|
|
return defaultValue;
|
|
}
|
|
|
|
/**
|
|
* Gets the value for type {@link #TYPE_ASCII}, {@link #TYPE_LONG},
|
|
* {@link #TYPE_UNDEFINED}, {@link #TYPE_UNSIGNED_BYTE},
|
|
* {@link #TYPE_UNSIGNED_LONG}, or {@link #TYPE_UNSIGNED_SHORT}. For
|
|
* {@link #TYPE_RATIONAL} or {@link #TYPE_UNSIGNED_RATIONAL}, call
|
|
* {@link #getRational(int)} instead.
|
|
*
|
|
* @throws IllegalArgumentException if the data type is
|
|
* {@link #TYPE_RATIONAL} or {@link #TYPE_UNSIGNED_RATIONAL}.
|
|
*/
|
|
protected long getValueAt( int index ) {
|
|
if( mValue instanceof long[] ) {
|
|
return ( (long[]) mValue )[index];
|
|
}
|
|
else if( mValue instanceof byte[] ) {
|
|
return ( (byte[]) mValue )[index];
|
|
}
|
|
throw new IllegalArgumentException( "Cannot get integer value from " + convertTypeToString( mDataType ) );
|
|
}
|
|
|
|
private static String convertTypeToString( short type ) {
|
|
switch( type ) {
|
|
case TYPE_UNSIGNED_BYTE:
|
|
return "UNSIGNED_BYTE";
|
|
case TYPE_ASCII:
|
|
return "ASCII";
|
|
case TYPE_UNSIGNED_SHORT:
|
|
return "UNSIGNED_SHORT";
|
|
case TYPE_UNSIGNED_LONG:
|
|
return "UNSIGNED_LONG";
|
|
case TYPE_UNSIGNED_RATIONAL:
|
|
return "UNSIGNED_RATIONAL";
|
|
case TYPE_UNDEFINED:
|
|
return "UNDEFINED";
|
|
case TYPE_LONG:
|
|
return "LONG";
|
|
case TYPE_RATIONAL:
|
|
return "RATIONAL";
|
|
default:
|
|
return "";
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Gets the {@link #TYPE_ASCII} data.
|
|
*
|
|
* @throws IllegalArgumentException If the type is NOT
|
|
* {@link #TYPE_ASCII}.
|
|
*/
|
|
protected String getString() {
|
|
if( mDataType != TYPE_ASCII ) {
|
|
throw new IllegalArgumentException( "Cannot get ASCII value from " + convertTypeToString( mDataType ) );
|
|
}
|
|
return new String( (byte[]) mValue, US_ASCII );
|
|
}
|
|
|
|
/*
|
|
* Get the converted ascii byte. Used by ExifOutputStream.
|
|
*/
|
|
protected byte[] getStringByte() {
|
|
return (byte[]) mValue;
|
|
}
|
|
|
|
/**
|
|
* Gets the {@link #TYPE_RATIONAL} or {@link #TYPE_UNSIGNED_RATIONAL} data.
|
|
*
|
|
* @throws IllegalArgumentException If the type is NOT
|
|
* {@link #TYPE_RATIONAL} or {@link #TYPE_UNSIGNED_RATIONAL}.
|
|
*/
|
|
protected Rational getRational( int index ) {
|
|
if( ( mDataType != TYPE_RATIONAL ) && ( mDataType != TYPE_UNSIGNED_RATIONAL ) ) {
|
|
throw new IllegalArgumentException( "Cannot get RATIONAL value from " + convertTypeToString( mDataType ) );
|
|
}
|
|
return ( (Rational[]) mValue )[index];
|
|
}
|
|
|
|
/**
|
|
* Equivalent to getBytes(buffer, 0, buffer.length).
|
|
*/
|
|
protected void getBytes( byte[] buf ) {
|
|
getBytes( buf, 0, buf.length );
|
|
}
|
|
|
|
/**
|
|
* Gets the {@link #TYPE_UNDEFINED} or {@link #TYPE_UNSIGNED_BYTE} data.
|
|
*
|
|
* @param buf the byte array in which to store the bytes read.
|
|
* @param offset the initial position in buffer to store the bytes.
|
|
* @param length the maximum number of bytes to store in buffer. If length >
|
|
* component count, only the valid bytes will be stored.
|
|
* @throws IllegalArgumentException If the type is NOT
|
|
* {@link #TYPE_UNDEFINED} or {@link #TYPE_UNSIGNED_BYTE}.
|
|
*/
|
|
protected void getBytes( byte[] buf, int offset, int length ) {
|
|
if( ( mDataType != TYPE_UNDEFINED ) && ( mDataType != TYPE_UNSIGNED_BYTE ) ) {
|
|
throw new IllegalArgumentException( "Cannot get BYTE value from " + convertTypeToString( mDataType ) );
|
|
}
|
|
System.arraycopy( mValue, 0, buf, offset, ( length > mComponentCountActual ) ? mComponentCountActual : length );
|
|
}
|
|
|
|
/**
|
|
* Gets the offset of this tag. This is only valid if this data size > 4 and
|
|
* contains an offset to the location of the actual value.
|
|
*/
|
|
protected int getOffset() {
|
|
return mOffset;
|
|
}
|
|
|
|
/**
|
|
* Sets the offset of this tag.
|
|
*/
|
|
protected void setOffset( int offset ) {
|
|
mOffset = offset;
|
|
}
|
|
|
|
protected void setHasDefinedCount( boolean d ) {
|
|
mHasDefinedDefaultComponentCount = d;
|
|
}
|
|
|
|
protected boolean hasDefinedCount() {
|
|
return mHasDefinedDefaultComponentCount;
|
|
}
|
|
|
|
private boolean checkOverflowForUnsignedShort( int[] value ) {
|
|
for( int v : value ) {
|
|
if( v > UNSIGNED_SHORT_MAX || v < 0 ) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
private boolean checkOverflowForUnsignedLong( long[] value ) {
|
|
for( long v : value ) {
|
|
if( v < 0 || v > UNSIGNED_LONG_MAX ) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
private boolean checkOverflowForUnsignedLong( int[] value ) {
|
|
for( int v : value ) {
|
|
if( v < 0 ) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
private boolean checkOverflowForUnsignedRational( Rational[] value ) {
|
|
for( Rational v : value ) {
|
|
if( v.getNumerator() < 0 || v.getDenominator() < 0 || v.getNumerator() > UNSIGNED_LONG_MAX || v.getDenominator() > UNSIGNED_LONG_MAX ) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
private boolean checkOverflowForRational( Rational[] value ) {
|
|
for( Rational v : value ) {
|
|
if( v.getNumerator() < LONG_MIN || v.getDenominator() < LONG_MIN || v.getNumerator() > LONG_MAX || v.getDenominator() > LONG_MAX ) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
@Override
|
|
public boolean equals( Object obj ) {
|
|
if( obj == null ) {
|
|
return false;
|
|
}
|
|
if( obj instanceof ExifTag ) {
|
|
ExifTag tag = (ExifTag) obj;
|
|
if( tag.mTagId != this.mTagId || tag.mComponentCountActual != this.mComponentCountActual || tag.mDataType != this.mDataType ) {
|
|
return false;
|
|
}
|
|
if( mValue != null ) {
|
|
if( tag.mValue == null ) {
|
|
return false;
|
|
}
|
|
else if( mValue instanceof long[] ) {
|
|
if( ! ( tag.mValue instanceof long[] ) ) {
|
|
return false;
|
|
}
|
|
return Arrays.equals( (long[]) mValue, (long[]) tag.mValue );
|
|
}
|
|
else if( mValue instanceof Rational[] ) {
|
|
if( ! ( tag.mValue instanceof Rational[] ) ) {
|
|
return false;
|
|
}
|
|
return Arrays.equals( (Rational[]) mValue, (Rational[]) tag.mValue );
|
|
}
|
|
else if( mValue instanceof byte[] ) {
|
|
if( ! ( tag.mValue instanceof byte[] ) ) {
|
|
return false;
|
|
}
|
|
return Arrays.equals( (byte[]) mValue, (byte[]) tag.mValue );
|
|
}
|
|
else {
|
|
return mValue.equals( tag.mValue );
|
|
}
|
|
}
|
|
else {
|
|
return tag.mValue == null;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
@Override
|
|
public String toString() {
|
|
return String.format( "tag id: %04X\n", mTagId ) + "ifd id: " + mIfd + "\ntype: " + convertTypeToString( mDataType ) + "\ncount: " + mComponentCountActual + "\noffset: " +
|
|
mOffset + "\nvalue: " + forceGetValueAsString() + "\n";
|
|
}
|
|
|
|
/**
|
|
* Gets a string representation of the value.
|
|
*/
|
|
public String forceGetValueAsString() {
|
|
if( mValue == null ) {
|
|
return "";
|
|
}
|
|
else if( mValue instanceof byte[] ) {
|
|
if( mDataType == TYPE_ASCII ) {
|
|
return new String( (byte[]) mValue, US_ASCII );
|
|
}
|
|
else {
|
|
return Arrays.toString( (byte[]) mValue );
|
|
}
|
|
}
|
|
else if( mValue instanceof long[] ) {
|
|
if( ( (long[]) mValue ).length == 1 ) {
|
|
return String.valueOf( ( (long[]) mValue )[0] );
|
|
}
|
|
else {
|
|
return Arrays.toString( (long[]) mValue );
|
|
}
|
|
}
|
|
else if( mValue instanceof Object[] ) {
|
|
if( ( (Object[]) mValue ).length == 1 ) {
|
|
Object val = ( (Object[]) mValue )[0];
|
|
if( val == null ) {
|
|
return "";
|
|
}
|
|
else {
|
|
return val.toString();
|
|
}
|
|
}
|
|
else {
|
|
return Arrays.toString( (Object[]) mValue );
|
|
}
|
|
}
|
|
else {
|
|
return mValue.toString();
|
|
}
|
|
}
|
|
|
|
}
|