Android Open Source - Android-Exif Exif Tag






From Project

Back to project page Android-Exif.

License

The source code is released under:

GNU General Public License

If you think the Android project Android-Exif listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.

Java Source Code

/*
 * Copyright (C) 2012 The Android Open Source Project
 */*  w  w  w  .  j a v  a  2s. c  om*/
 * 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 com.ai.exif;

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}.
 * <p>
 * Copy from and com.android.camera.exif
 * 
 * @author leo
 * @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;

  private static Charset US_ASCII = Charset.forName("US-ASCII");
  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;
  }

  static final int SIZE_UNDEFINED = 0;

  // 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;

  private static final SimpleDateFormat TIME_FORMAT = new SimpleDateFormat("yyyy:MM:dd kk:mm:ss");

  /**
   * 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;
  }

  // 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;
  }

  /**
   * 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];
  }

  /**
   * 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 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;
  }

  /**
   * 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;
  }

  /**
   * 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 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;
  }

  /**
   * 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)));
    }
  }

  /**
   * 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 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 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 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 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 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 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 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 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 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 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 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 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();
    }
  }

  /**
   * 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.
   * 
   * @exception 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));
  }

  /**
   * Gets the {@link #TYPE_ASCII} data.
   * 
   * @exception 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.
   * 
   * @exception 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.
   * @exception 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 checkBadComponentCount(int count) {
    if (mHasDefinedDefaultComponentCount && (mComponentCountActual != count)) {
      return true;
    }
    return false;
  }

  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 "";
    }
  }

  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";
  }

}




Java Source Code List

com.ai.exif.ByteBufferInputStream.java
com.ai.exif.CountedDataInputStream.java
com.ai.exif.ExifData.java
com.ai.exif.ExifInterface.java
com.ai.exif.ExifInvalidFormatException.java
com.ai.exif.ExifModifier.java
com.ai.exif.ExifOutputStream.java
com.ai.exif.ExifParser.java
com.ai.exif.ExifReader.java
com.ai.exif.ExifTag.java
com.ai.exif.IfdData.java
com.ai.exif.IfdId.java
com.ai.exif.JpegHeader.java
com.ai.exif.OrderedDataOutputStream.java
com.ai.exif.Rational.java