org.apache.commons.imaging.formats.tiff.TiffField.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.commons.imaging.formats.tiff.TiffField.java

Source

/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You 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 org.apache.commons.imaging.formats.tiff;

import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.nio.charset.Charset;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;

import org.apache.commons.imaging.ImageReadException;
import org.apache.commons.imaging.common.BinaryFunctions;
import org.apache.commons.imaging.common.ByteOrder;
import org.apache.commons.imaging.formats.tiff.constants.AllTagConstants;
import org.apache.commons.imaging.formats.tiff.constants.TiffConstants;
import org.apache.commons.imaging.formats.tiff.constants.TiffDirectoryType;
import org.apache.commons.imaging.formats.tiff.constants.TiffTagConstants;
import org.apache.commons.imaging.formats.tiff.fieldtypes.FieldType;
import org.apache.commons.imaging.formats.tiff.taginfos.TagInfo;

/**
 * A TIFF field in a TIFF directory. Immutable.
 */
public class TiffField {
    private final TagInfo tagInfo;
    private final int tag;
    private final int directoryType;
    private final FieldType fieldType;
    private final long count;
    private final long offset;
    private final byte[] value;
    private final ByteOrder byteOrder;
    private final int sortHint;

    public TiffField(final int tag, final int directoryType, final FieldType fieldType, final long count,
            final long offset, final byte[] value, final ByteOrder byteOrder, final int sortHint)
            throws ImageReadException {

        this.tag = tag;
        this.directoryType = directoryType;
        this.fieldType = fieldType;
        this.count = count;
        this.offset = offset;
        this.value = value;
        this.byteOrder = byteOrder;
        this.sortHint = sortHint;

        tagInfo = getTag(directoryType, tag);
    }

    public int getDirectoryType() {
        return directoryType;
    }

    public TagInfo getTagInfo() {
        return tagInfo;
    }

    /**
     * Returns the field's tag, derived from bytes 0-1.
     * @return the tag, as an <code>int</code> in which only the lowest 2 bytes are set 
     */
    public int getTag() {
        return tag;
    }

    /**
     * Returns the field's type, derived from bytes 2-3.
     * @return the field's type, as a {@code FieldType} object.
     */
    public FieldType getFieldType() {
        return fieldType;
    }

    /**
     * Returns the field's count, derived from bytes 4-7.
     * @return the count
     */
    public long getCount() {
        return count;
    }

    /**
     * Returns the TIFF field's offset/value field, derived from bytes 8-11.
     * @return the field's offset in a <code>long</code> of 4 packed bytes,
     * or its inlined value <= 4 bytes long encoded in the field's byte order.
     */
    public int getOffset() {
        return (int) offset;
    }

    /**
     * Returns the field's byte order.
     * @return the byte order
     */
    public ByteOrder getByteOrder() {
        return byteOrder;
    }

    public int getSortHint() {
        return sortHint;
    }

    /**
     * Indicates whether the field's value is inlined into the offset field.
     * @return true if the value is inlined
     */
    public boolean isLocalValue() {
        return (count * fieldType.getSize()) <= TiffConstants.TIFF_ENTRY_MAX_VALUE_LENGTH;
    }

    /**
     * The length of the field's value.
     * @return the length, in bytes.
     */
    public int getBytesLength() {
        return (int) count * fieldType.getSize();
    }

    /**
     * Returns a copy of the raw value of the field. 
     * @return the value of the field, in the byte order of the field.
     */
    public byte[] getByteArrayValue() {
        return BinaryFunctions.head(value, getBytesLength());
    }

    public final class OversizeValueElement extends TiffElement {
        public OversizeValueElement(final int offset, final int length) {
            super(offset, length);
        }

        @Override
        public String getElementDescription(final boolean verbose) {
            if (verbose) {
                return null;
            }

            return "OversizeValueElement, tag: " + getTagInfo().name + ", fieldType: " + getFieldType().getName();
        }
    }

    public TiffElement getOversizeValueElement() {
        if (isLocalValue()) {
            return null;
        }

        return new OversizeValueElement(getOffset(), value.length);
    }

    private static TagInfo getTag(final int directoryType, final int tag, final List<TagInfo> possibleMatches) {
        // Please keep this method in sync with TiffImageMetadata's findField()

        if (possibleMatches.size() < 1) {
            return null;
        }
        // else if (possibleMatches.size() == 1)
        // {
        // TagInfo tagInfo = (TagInfo) possibleMatches.get(0);
        // return tagInfo;
        // }

        // first search for exact match.
        for (int i = 0; i < possibleMatches.size(); i++) {
            final TagInfo tagInfo = possibleMatches.get(i);
            if (tagInfo.directoryType == TiffDirectoryType.EXIF_DIRECTORY_UNKNOWN) {
                // pass
                continue;
            } else if (directoryType == tagInfo.directoryType.directoryType) {
                return tagInfo;
            }
        }

        // accept an inexact match.
        for (int i = 0; i < possibleMatches.size(); i++) {
            final TagInfo tagInfo = possibleMatches.get(i);

            if (tagInfo.directoryType == TiffDirectoryType.EXIF_DIRECTORY_UNKNOWN) {
                // pass
                continue;
            } else if (directoryType >= 0 && tagInfo.directoryType.isImageDirectory()) {
                return tagInfo;
            } else if (directoryType < 0 && !tagInfo.directoryType.isImageDirectory()) {
                return tagInfo;
            }
        }

        // accept a wildcard match.
        for (int i = 0; i < possibleMatches.size(); i++) {
            final TagInfo tagInfo = possibleMatches.get(i);

            if (tagInfo.directoryType == TiffDirectoryType.EXIF_DIRECTORY_UNKNOWN) {
                return tagInfo;
            }
        }

        // // accept a very rough match.
        // for (int i = 0; i < possibleMatches.size(); i++)
        // {
        // TagInfo tagInfo = (TagInfo) possibleMatches.get(i);
        // if (tagInfo.exifDirectory == EXIF_DIRECTORY_UNKNOWN)
        // return tagInfo;
        // else if (directoryType == DIRECTORY_TYPE_EXIF
        // && tagInfo.exifDirectory == EXIF_DIRECTORY_EXIF_IFD)
        // return tagInfo;
        // else if (directoryType == DIRECTORY_TYPE_INTEROPERABILITY
        // && tagInfo.exifDirectory == EXIF_DIRECTORY_INTEROP_IFD)
        // return tagInfo;
        // else if (directoryType == DIRECTORY_TYPE_GPS
        // && tagInfo.exifDirectory == EXIF_DIRECTORY_GPS)
        // return tagInfo;
        // else if (directoryType == DIRECTORY_TYPE_MAKER_NOTES
        // && tagInfo.exifDirectory == EXIF_DIRECTORY_MAKER_NOTES)
        // return tagInfo;
        // else if (directoryType >= 0
        // && tagInfo.exifDirectory.isImageDirectory())
        // return tagInfo;
        // else if (directoryType < 0
        // && !tagInfo.exifDirectory.isImageDirectory())
        // return tagInfo;
        // }

        return TiffTagConstants.TIFF_TAG_UNKNOWN;

        // if (true)
        // throw new Error("Why didn't this algorithm work?");
        //
        // {
        // TagInfo tagInfo = (TagInfo) possibleMatches.get(0);
        // return tagInfo;
        // }

        // Object key = new Integer(tag);
        //
        // if (directoryType == DIRECTORY_TYPE_EXIF
        // || directoryType == DIRECTORY_TYPE_INTEROPERABILITY)
        // {
        // if (EXIF_TAG_MAP.containsKey(key))
        // return (TagInfo) EXIF_TAG_MAP.get(key);
        // }
        // else if (directoryType == DIRECTORY_TYPE_GPS)
        // {
        // if (GPS_TAG_MAP.containsKey(key))
        // return (TagInfo) GPS_TAG_MAP.get(key);
        // }
        // else
        // {
        // if (TIFF_TAG_MAP.containsKey(key))
        // return (TagInfo) TIFF_TAG_MAP.get(key);
        // }
        //
        // if (ALL_TAG_MAP.containsKey(key))
        // return (TagInfo) ALL_TAG_MAP.get(key);

        // public static final int DIRECTORY_TYPE_EXIF = -2;
        // // public static final int DIRECTORY_TYPE_SUB = 5;
        // public static final int DIRECTORY_TYPE_GPS = -3;
        // public static final int DIRECTORY_TYPE_INTEROPERABILITY = -4;
        //
        // private static final Map GPS_TAG_MAP = makeTagMap(ALL_GPS_TAGS,
        // false);
        // private static final Map TIFF_TAG_MAP = makeTagMap(ALL_TIFF_TAGS,
        // false);
        // private static final Map EXIF_TAG_MAP = makeTagMap(ALL_EXIF_TAGS,
        // false);
        // private static final Map ALL_TAG_MAP = makeTagMap(ALL_TAGS, true);
        //
        // for (int i = 0; i < ALL_TAGS.length; i++)
        // {
        // TagInfo2 tag = ALL_TAGS[i];
        // if (tag.tag == value)
        // return tag;
        // }

        // return TIFF_TAG_UNKNOWN;
    }

    private static TagInfo getTag(final int directoryType, final int tag) {
        final List<TagInfo> possibleMatches = ALL_TAG_MAP.get(tag);

        if (null == possibleMatches) {
            return TiffTagConstants.TIFF_TAG_UNKNOWN;
        }

        final TagInfo result = getTag(directoryType, tag, possibleMatches);
        return result;
    }

    public String getValueDescription() {
        try {
            return getValueDescription(getValue());
        } catch (final ImageReadException e) {
            return "Invalid value: " + e.getMessage();
        }
    }

    private String getValueDescription(final Object o) {
        if (o == null) {
            return null;
        }

        if (o instanceof Number) {
            return o.toString();
        } else if (o instanceof String) {
            return "'" + o.toString().trim() + "'";
        } else if (o instanceof Date) {
            final DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ");
            return df.format((Date) o);
        } else if (o instanceof Object[]) {
            final Object objects[] = (Object[]) o;
            final StringBuilder result = new StringBuilder();

            for (int i = 0; i < objects.length; i++) {
                final Object object = objects[i];

                if (i > 50) {
                    result.append("... (" + objects.length + ")");
                    break;
                }
                if (i > 0) {
                    result.append(", ");
                }
                result.append("" + object);
            }
            return result.toString();
            // } else if (o instanceof Number[])
            // {
            // Number numbers[] = (Number[]) o;
            // StringBuffer result = new StringBuffer();
            //
            // for (int i = 0; i < numbers.length; i++)
            // {
            // Number number = numbers[i];
            //
            // if (i > 0)
            // result.append(", ");
            // result.append("" + number);
            // }
            // return result.toString();
            // }
        } else if (o instanceof short[]) {
            final short values[] = (short[]) o;
            final StringBuffer result = new StringBuffer();

            for (int i = 0; i < values.length; i++) {
                final short value = values[i];

                if (i > 50) {
                    result.append("... (" + values.length + ")");
                    break;
                }
                if (i > 0) {
                    result.append(", ");
                }
                result.append("" + value);
            }
            return result.toString();
        } else if (o instanceof int[]) {
            final int values[] = (int[]) o;
            final StringBuffer result = new StringBuffer();

            for (int i = 0; i < values.length; i++) {
                final int value = values[i];

                if (i > 50) {
                    result.append("... (" + values.length + ")");
                    break;
                }
                if (i > 0) {
                    result.append(", ");
                }
                result.append("" + value);
            }
            return result.toString();
        } else if (o instanceof long[]) {
            final long values[] = (long[]) o;
            final StringBuffer result = new StringBuffer();

            for (int i = 0; i < values.length; i++) {
                final long value = values[i];

                if (i > 50) {
                    result.append("... (" + values.length + ")");
                    break;
                }
                if (i > 0) {
                    result.append(", ");
                }
                result.append("" + value);
            }
            return result.toString();
        } else if (o instanceof double[]) {
            final double values[] = (double[]) o;
            final StringBuffer result = new StringBuffer();

            for (int i = 0; i < values.length; i++) {
                final double value = values[i];

                if (i > 50) {
                    result.append("... (" + values.length + ")");
                    break;
                }
                if (i > 0) {
                    result.append(", ");
                }
                result.append("" + value);
            }
            return result.toString();
        } else if (o instanceof byte[]) {
            final byte values[] = (byte[]) o;
            final StringBuffer result = new StringBuffer();

            for (int i = 0; i < values.length; i++) {
                final byte value = values[i];

                if (i > 50) {
                    result.append("... (" + values.length + ")");
                    break;
                }
                if (i > 0) {
                    result.append(", ");
                }
                result.append("" + value);
            }
            return result.toString();
        } else if (o instanceof char[]) {
            final char values[] = (char[]) o;
            final StringBuffer result = new StringBuffer();

            for (int i = 0; i < values.length; i++) {
                final char value = values[i];

                if (i > 50) {
                    result.append("... (" + values.length + ")");
                    break;
                }
                if (i > 0) {
                    result.append(", ");
                }
                result.append("" + value);
            }
            return result.toString();
        } else if (o instanceof float[]) {
            final float values[] = (float[]) o;
            final StringBuffer result = new StringBuffer();

            for (int i = 0; i < values.length; i++) {
                final float value = values[i];

                if (i > 50) {
                    result.append("... (" + values.length + ")");
                    break;
                }
                if (i > 0) {
                    result.append(", ");
                }
                result.append("" + value);
            }
            return result.toString();
        }
        // else if (o instanceof short[])
        // {
        // short numbers[] = (short[]) o;
        // StringBuffer result = new StringBuffer();
        //
        // for (int i = 0; i < numbers.length; i++)
        // {
        // short number = numbers[i];
        //
        // if (i > 0)
        // result.append(", ");
        // result.append("" + number);
        // }
        // return result.toString();
        // }

        return "Unknown: " + o.getClass().getName();
    }

    public void dump() {
        final PrintWriter pw = new PrintWriter(new OutputStreamWriter(System.out, Charset.defaultCharset()));
        dump(pw);
        pw.flush();
    }

    public void dump(final PrintWriter pw) {
        dump(pw, null);
    }

    public void dump(final PrintWriter pw, final String prefix) {
        if (prefix != null) {
            pw.print(prefix + ": ");
        }

        pw.println(toString());
        pw.flush();
    }

    public String getDescriptionWithoutValue() {
        return getTag() + " (0x" + Integer.toHexString(getTag()) + ": " + getTagInfo().name + "): ";
    }

    @Override
    public String toString() {
        final StringBuilder result = new StringBuilder();

        result.append(getTag() + " (0x" + Integer.toHexString(getTag()) + ": " + getTagInfo().name + "): ");
        result.append(getValueDescription() + " (" + getCount() + " " + getFieldType().getName() + ")");

        return result.toString();
    }

    public String getTagName() {
        if (getTagInfo() == TiffTagConstants.TIFF_TAG_UNKNOWN) {
            return getTagInfo().name + " (0x" + Integer.toHexString(getTag()) + ")";
        }
        return getTagInfo().name;
    }

    public String getFieldTypeName() {
        return getFieldType().getName();
    }

    public static final String Attribute_Tag = "Tag";

    public Object getValue() throws ImageReadException {
        // System.out.print("getValue");
        return getTagInfo().getValue(this);
    }

    public String getStringValue() throws ImageReadException {
        final Object o = getValue();
        if (o == null) {
            return null;
        }
        if (!(o instanceof String)) {
            throw new ImageReadException("Expected String value(" + getTagInfo().getDescription() + "): " + o);
        }
        return (String) o;
    }

    private static final Map<Object, List<TagInfo>> makeTagMap(final List<TagInfo> tags,
            final boolean ignoreDuplicates, final String name) {
        // make sure to use the thread-safe version; this is shared state.
        final Map<Object, List<TagInfo>> map = new Hashtable<Object, List<TagInfo>>();

        for (int i = 0; i < tags.size(); i++) {
            final TagInfo tag = tags.get(i);

            List<TagInfo> tagList = map.get(tag.tag);
            if (tagList == null) {
                tagList = new ArrayList<TagInfo>();
                map.put(tag.tag, tagList);
            }
            tagList.add(tag);
        }

        return map;
    }

    private static final Map<Object, List<TagInfo>> ALL_TAG_MAP = makeTagMap(AllTagConstants.ALL_TAGS, true, "All");

    public int[] getIntArrayValue() throws ImageReadException {
        final Object o = getValue();
        // if (o == null)
        // return null;

        if (o instanceof Number) {
            return new int[] { ((Number) o).intValue() };
        } else if (o instanceof Number[]) {
            final Number numbers[] = (Number[]) o;
            final int result[] = new int[numbers.length];
            for (int i = 0; i < numbers.length; i++) {
                result[i] = numbers[i].intValue();
            }
            return result;
        } else if (o instanceof short[]) {
            final short numbers[] = (short[]) o;
            final int result[] = new int[numbers.length];
            for (int i = 0; i < numbers.length; i++) {
                result[i] = 0xffff & numbers[i];
            }
            return result;
        } else if (o instanceof int[]) {
            final int numbers[] = (int[]) o;
            final int result[] = new int[numbers.length];
            for (int i = 0; i < numbers.length; i++) {
                result[i] = numbers[i];
            }
            return result;
        }

        throw new ImageReadException("Unknown value: " + o + " for: " + getTagInfo().getDescription());
        // return null;
    }

    public double[] getDoubleArrayValue() throws ImageReadException {
        final Object o = getValue();
        // if (o == null)
        // return null;

        if (o instanceof Number) {
            return new double[] { ((Number) o).doubleValue() };
        } else if (o instanceof Number[]) {
            final Number numbers[] = (Number[]) o;
            final double result[] = new double[numbers.length];
            for (int i = 0; i < numbers.length; i++) {
                result[i] = numbers[i].doubleValue();
            }
            return result;
        } else if (o instanceof short[]) {
            final short numbers[] = (short[]) o;
            final double result[] = new double[numbers.length];
            for (int i = 0; i < numbers.length; i++) {
                result[i] = numbers[i];
            }
            return result;
        } else if (o instanceof int[]) {
            final int numbers[] = (int[]) o;
            final double result[] = new double[numbers.length];
            for (int i = 0; i < numbers.length; i++) {
                result[i] = numbers[i];
            }
            return result;
        } else if (o instanceof float[]) {
            final float numbers[] = (float[]) o;
            final double result[] = new double[numbers.length];
            for (int i = 0; i < numbers.length; i++) {
                result[i] = numbers[i];
            }
            return result;
        } else if (o instanceof double[]) {
            final double numbers[] = (double[]) o;
            final double result[] = new double[numbers.length];
            for (int i = 0; i < numbers.length; i++) {
                result[i] = numbers[i];
            }
            return result;
        }

        throw new ImageReadException("Unknown value: " + o + " for: " + getTagInfo().getDescription());
        // return null;
    }

    public int getIntValueOrArraySum() throws ImageReadException {
        final Object o = getValue();
        // if (o == null)
        // return -1;

        if (o instanceof Number) {
            return ((Number) o).intValue();
        } else if (o instanceof Number[]) {
            final Number numbers[] = (Number[]) o;
            int sum = 0;
            for (final Number number : numbers) {
                sum += number.intValue();
            }
            return sum;
        } else if (o instanceof short[]) {
            final short[] numbers = (short[]) o;
            int sum = 0;
            for (final short number : numbers) {
                sum += number;
            }
            return sum;
        } else if (o instanceof int[]) {
            final int numbers[] = (int[]) o;
            int sum = 0;
            for (final int number : numbers) {
                sum += number;
            }
            return sum;
        }

        throw new ImageReadException("Unknown value: " + o + " for: " + getTagInfo().getDescription());
        // return -1;
    }

    public int getIntValue() throws ImageReadException {
        final Object o = getValue();
        if (o == null) {
            throw new ImageReadException("Missing value: " + getTagInfo().getDescription());
        }

        return ((Number) o).intValue();
    }

    public double getDoubleValue() throws ImageReadException {
        final Object o = getValue();
        if (o == null) {
            throw new ImageReadException("Missing value: " + getTagInfo().getDescription());
        }

        return ((Number) o).doubleValue();
    }
}