001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.commons.imaging.formats.tiff.write;
018
019import static org.apache.commons.imaging.formats.tiff.constants.TiffConstants.TIFF_ENTRY_MAX_VALUE_LENGTH;
020
021import java.io.IOException;
022import java.nio.ByteOrder;
023import java.util.Arrays;
024
025import org.apache.commons.imaging.ImageWriteException;
026import org.apache.commons.imaging.common.BinaryOutputStream;
027import org.apache.commons.imaging.formats.tiff.fieldtypes.FieldType;
028import org.apache.commons.imaging.formats.tiff.taginfos.TagInfo;
029
030public class TiffOutputField {
031    public final int tag;
032    public final TagInfo tagInfo;
033    public final FieldType fieldType;
034    public final int count;
035    private byte[] bytes;
036    private final TiffOutputItem.Value separateValueItem;
037    private int sortHint = -1;
038    private static final String NEWLINE = System.getProperty("line.separator");
039
040    public TiffOutputField(final TagInfo tagInfo, final FieldType tagtype, final int count,
041            final byte[] bytes) {
042        this(tagInfo.tag, tagInfo, tagtype, count, bytes);
043    }
044
045    public TiffOutputField(final int tag, final TagInfo tagInfo, final FieldType fieldType,
046            final int count, final byte[] bytes) {
047        this.tag = tag;
048        this.tagInfo = tagInfo;
049        this.fieldType = fieldType;
050        this.count = count;
051        this.bytes = bytes;
052
053        if (isLocalValue()) {
054            separateValueItem = null;
055        } else {
056            final String name = "Field Separate value (" + tagInfo.getDescription()
057                    + ")";
058            separateValueItem = new TiffOutputItem.Value(name, bytes);
059        }
060    }
061
062    protected static TiffOutputField createOffsetField(final TagInfo tagInfo,
063            final ByteOrder byteOrder) throws ImageWriteException {
064        return new TiffOutputField(tagInfo, FieldType.LONG, 1,
065                FieldType.LONG.writeData(0, byteOrder));
066    }
067
068    protected void writeField(final BinaryOutputStream bos) throws IOException,
069            ImageWriteException {
070        bos.write2Bytes(tag);
071        bos.write2Bytes(fieldType.getType());
072        bos.write4Bytes(count);
073
074        if (isLocalValue()) {
075            if (separateValueItem != null) {
076                throw new ImageWriteException("Unexpected separate value item.");
077            }
078            if (bytes.length > 4) {
079                throw new ImageWriteException(
080                        "Local value has invalid length: " + bytes.length);
081            }
082
083            bos.write(bytes);
084            final int remainder = TIFF_ENTRY_MAX_VALUE_LENGTH - bytes.length;
085            for (int i = 0; i < remainder; i++) {
086                bos.write(0);
087            }
088        } else {
089            if (separateValueItem == null) {
090                throw new ImageWriteException("Missing separate value item.");
091            }
092
093            bos.write4Bytes((int) separateValueItem.getOffset());
094        }
095    }
096
097    protected TiffOutputItem getSeperateValue() {
098        return separateValueItem;
099    }
100
101    protected final boolean isLocalValue() {
102        return bytes.length <= TIFF_ENTRY_MAX_VALUE_LENGTH;
103    }
104
105    public boolean bytesEqual(final byte[] data) {
106        return Arrays.equals(bytes, data);
107    }
108
109    protected void setData(final byte[] bytes) throws ImageWriteException {
110        // if(tagInfo.isUnknown())
111        // Debug.debug("unknown tag(0x" + Integer.toHexString(tag)
112        // + ") setData", bytes);
113
114        if (this.bytes.length != bytes.length) {
115            throw new ImageWriteException("Cannot change size of value.");
116        }
117
118        // boolean wasLocalValue = isLocalValue();
119        this.bytes = bytes;
120        if (separateValueItem != null) {
121            separateValueItem.updateValue(bytes);
122        }
123        // if (isLocalValue() != wasLocalValue)
124        // throw new Error("Bug. Locality disrupted! "
125        // + tagInfo.getDescription());
126    }
127
128    @Override
129    public String toString() {
130        return toString(null);
131    }
132
133    public String toString(String prefix) {
134        if (prefix == null) {
135            prefix = "";
136        }
137        final StringBuilder result = new StringBuilder();
138
139        result.append(prefix);
140        result.append(tagInfo);
141        result.append(NEWLINE);
142
143        result.append(prefix);
144        result.append("count: ");
145        result.append(count);
146        result.append(NEWLINE);
147
148        result.append(prefix);
149        result.append(fieldType);
150        result.append(NEWLINE);
151
152        return result.toString();
153    }
154
155    public int getSortHint() {
156        return sortHint;
157    }
158
159    public void setSortHint(final int sortHint) {
160        this.sortHint = sortHint;
161    }
162}