1/*
2 * Copyright (c) 2005, 2016, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25package com.sun.imageio.plugins.tiff;
26
27import java.util.Arrays;
28import java.util.List;
29import javax.imageio.metadata.IIOMetadataNode;
30import org.w3c.dom.Node;
31import javax.imageio.plugins.tiff.TIFFDirectory;
32import javax.imageio.plugins.tiff.TIFFField;
33import javax.imageio.plugins.tiff.TIFFTag;
34import javax.imageio.plugins.tiff.TIFFTagSet;
35
36/**
37 * The {@code Node} representation of a {@code TIFFField}
38 * wherein the child node is procedural rather than buffered.
39 */
40public class TIFFFieldNode extends IIOMetadataNode {
41    private static boolean isIFD(TIFFField f) {
42        int type = f.getType();
43        return f.hasDirectory() &&
44            (type == TIFFTag.TIFF_LONG || type == TIFFTag.TIFF_IFD_POINTER);
45    }
46
47    private static String getNodeName(TIFFField f) {
48        return isIFD(f) ? "TIFFIFD" : "TIFFField";
49    }
50
51    private boolean isIFD;
52
53    private Boolean isInitialized = Boolean.FALSE;
54
55    private TIFFField field;
56
57    public TIFFFieldNode(TIFFField field) {
58        super(getNodeName(field));
59
60        isIFD = isIFD(field);
61
62        this.field = field;
63
64        TIFFTag tag = field.getTag();
65        int tagNumber = tag.getNumber();
66        String tagName = tag.getName();
67
68        if(isIFD) {
69            if(tagNumber != 0) {
70                setAttribute("parentTagNumber", Integer.toString(tagNumber));
71            }
72            if(tagName != null) {
73                setAttribute("parentTagName", tagName);
74            }
75
76            TIFFDirectory dir = field.hasDirectory() ?
77                field.getDirectory() : (TIFFDirectory)field.getData();
78            TIFFTagSet[] tagSets = dir.getTagSets();
79            if(tagSets != null) {
80                StringBuilder tagSetNames = new StringBuilder();
81                for(int i = 0; i < tagSets.length; i++) {
82                    tagSetNames.append(tagSets[i].getClass().getName());
83                    if(i != tagSets.length - 1) {
84                        tagSetNames.append(",");
85                    }
86                }
87                setAttribute("tagSets", tagSetNames.toString());
88            }
89        } else {
90            setAttribute("number", Integer.toString(tagNumber));
91            setAttribute("name", tagName);
92        }
93    }
94
95    private synchronized void initialize() {
96        if(isInitialized) return;
97
98        if(isIFD) {
99            TIFFDirectory dir = field.hasDirectory() ?
100                field.getDirectory() : (TIFFDirectory)field.getData();
101            TIFFField[] fields = dir.getTIFFFields();
102            if(fields != null) {
103                TIFFTagSet[] tagSets = dir.getTagSets();
104                List<TIFFTagSet> tagSetList = Arrays.asList(tagSets);
105                int numFields = fields.length;
106                for(int i = 0; i < numFields; i++) {
107                    TIFFField f = fields[i];
108                    int tagNumber = f.getTagNumber();
109                    TIFFTag tag = TIFFIFD.getTag(tagNumber, tagSetList);
110
111                    Node node = f.getAsNativeNode();
112
113                    if (node != null) {
114                        appendChild(node);
115                    }
116                }
117            }
118        } else {
119            IIOMetadataNode child;
120            int count = field.getCount();
121            if (field.getType() == TIFFTag.TIFF_UNDEFINED) {
122                child = new IIOMetadataNode("TIFFUndefined");
123
124                byte[] data = field.getAsBytes();
125                StringBuffer sb = new StringBuffer();
126                for (int i = 0; i < count; i++) {
127                    sb.append(Integer.toString(data[i] & 0xff));
128                    if (i < count - 1) {
129                        sb.append(",");
130                    }
131                }
132                child.setAttribute("value", sb.toString());
133            } else {
134                child = new IIOMetadataNode("TIFF" +
135                                            TIFFField.getTypeName(field.getType()) +
136                                            "s");
137
138                TIFFTag tag = field.getTag();
139
140                for (int i = 0; i < count; i++) {
141                    IIOMetadataNode cchild =
142                        new IIOMetadataNode("TIFF" +
143                                            TIFFField.getTypeName(field.getType()));
144
145                    cchild.setAttribute("value", field.getValueAsString(i));
146                    if (tag.hasValueNames() && field.isIntegral()) {
147                        int value = field.getAsInt(i);
148                        String name = tag.getValueName(value);
149                        if (name != null) {
150                            cchild.setAttribute("description", name);
151                        }
152                    }
153
154                    child.appendChild(cchild);
155                }
156            }
157            appendChild(child);
158        }
159
160        isInitialized = Boolean.TRUE;
161    }
162
163    // Need to override this method to avoid a stack overflow exception
164    // which will occur if super.appendChild is called from initialize().
165    public Node appendChild(Node newChild) {
166        if (newChild == null) {
167            throw new NullPointerException("newChild == null!");
168        }
169
170        return super.insertBefore(newChild, null);
171    }
172
173    // Override all methods which refer to child nodes.
174
175    public boolean hasChildNodes() {
176        initialize();
177        return super.hasChildNodes();
178    }
179
180    public int getLength() {
181        initialize();
182        return super.getLength();
183    }
184
185    public Node getFirstChild() {
186        initialize();
187        return super.getFirstChild();
188    }
189
190    public Node getLastChild() {
191        initialize();
192        return super.getLastChild();
193    }
194
195    public Node getPreviousSibling() {
196        initialize();
197        return super.getPreviousSibling();
198    }
199
200    public Node getNextSibling() {
201        initialize();
202        return super.getNextSibling();
203    }
204
205    public Node insertBefore(Node newChild,
206                             Node refChild) {
207        initialize();
208        return super.insertBefore(newChild, refChild);
209    }
210
211    public Node replaceChild(Node newChild,
212                             Node oldChild) {
213        initialize();
214        return super.replaceChild(newChild, oldChild);
215    }
216
217    public Node removeChild(Node oldChild) {
218        initialize();
219        return super.removeChild(oldChild);
220    }
221
222    public Node cloneNode(boolean deep) {
223        initialize();
224        return super.cloneNode(deep);
225    }
226}
227