1/*
2 * Copyright (c) 2005, 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 */
25
26package com.sun.imageio.plugins.gif;
27
28import javax.imageio.metadata.IIOInvalidTreeException;
29import javax.imageio.metadata.IIOMetadata;
30import javax.imageio.metadata.IIOMetadataFormatImpl;
31import org.w3c.dom.Node;
32
33/**
34 * Class which adds utility DOM element attribute access methods to
35 * {@code IIOMetadata} for subclass use.
36 */
37abstract class GIFMetadata extends IIOMetadata {
38
39    /**
40     * Represents an undefined value of integer attributes.
41     */
42    static final int UNDEFINED_INTEGER_VALUE = -1;
43
44    //
45    // Note: These attribute methods were shamelessly lifted from
46    // com.sun.imageio.plugins.png.PNGMetadata and modified.
47    //
48
49    // Shorthand for throwing an IIOInvalidTreeException
50    protected static void fatal(Node node, String reason)
51      throws IIOInvalidTreeException {
52        throw new IIOInvalidTreeException(reason, node);
53    }
54
55    // Get an integer-valued attribute
56    protected static String getStringAttribute(Node node, String name,
57                                               String defaultValue,
58                                               boolean required,
59                                               String[] range)
60      throws IIOInvalidTreeException {
61        Node attr = node.getAttributes().getNamedItem(name);
62        if (attr == null) {
63            if (!required) {
64                return defaultValue;
65            } else {
66                fatal(node, "Required attribute " + name + " not present!");
67            }
68        }
69        String value = attr.getNodeValue();
70
71        if (range != null) {
72            if (value == null) {
73                fatal(node,
74                      "Null value for "+node.getNodeName()+
75                      " attribute "+name+"!");
76            }
77            boolean validValue = false;
78            int len = range.length;
79            for (int i = 0; i < len; i++) {
80                if (value.equals(range[i])) {
81                    validValue = true;
82                    break;
83                }
84            }
85            if (!validValue) {
86                fatal(node,
87                      "Bad value for "+node.getNodeName()+
88                      " attribute "+name+"!");
89            }
90        }
91
92        return value;
93    }
94
95
96    // Get an integer-valued attribute
97    protected static int getIntAttribute(Node node, String name,
98                                         int defaultValue, boolean required,
99                                         boolean bounded, int min, int max)
100      throws IIOInvalidTreeException {
101        String value = getStringAttribute(node, name, null, required, null);
102        if (value == null || "".equals(value)) {
103            return defaultValue;
104        }
105
106        int intValue = defaultValue;
107        try {
108            intValue = Integer.parseInt(value);
109        } catch (NumberFormatException e) {
110            fatal(node,
111                  "Bad value for "+node.getNodeName()+
112                  " attribute "+name+"!");
113        }
114        if (bounded && (intValue < min || intValue > max)) {
115            fatal(node,
116                  "Bad value for "+node.getNodeName()+
117                  " attribute "+name+"!");
118        }
119        return intValue;
120    }
121
122    // Get a float-valued attribute
123    protected static float getFloatAttribute(Node node, String name,
124                                             float defaultValue,
125                                             boolean required)
126      throws IIOInvalidTreeException {
127        String value = getStringAttribute(node, name, null, required, null);
128        if (value == null) {
129            return defaultValue;
130        }
131        return Float.parseFloat(value);
132    }
133
134    // Get a required integer-valued attribute
135    protected static int getIntAttribute(Node node, String name,
136                                         boolean bounded, int min, int max)
137      throws IIOInvalidTreeException {
138        return getIntAttribute(node, name, -1, true, bounded, min, max);
139    }
140
141    // Get a required float-valued attribute
142    protected static float getFloatAttribute(Node node, String name)
143      throws IIOInvalidTreeException {
144        return getFloatAttribute(node, name, -1.0F, true);
145    }
146
147    // Get a boolean-valued attribute
148    protected static boolean getBooleanAttribute(Node node, String name,
149                                                 boolean defaultValue,
150                                                 boolean required)
151      throws IIOInvalidTreeException {
152        Node attr = node.getAttributes().getNamedItem(name);
153        if (attr == null) {
154            if (!required) {
155                return defaultValue;
156            } else {
157                fatal(node, "Required attribute " + name + " not present!");
158            }
159        }
160        String value = attr.getNodeValue();
161        // Allow lower case booleans for backward compatibility, #5082756
162        if (value.equals("TRUE") || value.equals("true")) {
163            return true;
164        } else if (value.equals("FALSE") || value.equals("false")) {
165            return false;
166        } else {
167            fatal(node, "Attribute " + name + " must be 'TRUE' or 'FALSE'!");
168            return false;
169        }
170    }
171
172    // Get a required boolean-valued attribute
173    protected static boolean getBooleanAttribute(Node node, String name)
174      throws IIOInvalidTreeException {
175        return getBooleanAttribute(node, name, false, true);
176    }
177
178    // Get an enumerated attribute as an index into a String array
179    protected static int getEnumeratedAttribute(Node node,
180                                                String name,
181                                                String[] legalNames,
182                                                int defaultValue,
183                                                boolean required)
184      throws IIOInvalidTreeException {
185        Node attr = node.getAttributes().getNamedItem(name);
186        if (attr == null) {
187            if (!required) {
188                return defaultValue;
189            } else {
190                fatal(node, "Required attribute " + name + " not present!");
191            }
192        }
193        String value = attr.getNodeValue();
194        for (int i = 0; i < legalNames.length; i++) {
195            if(value.equals(legalNames[i])) {
196                return i;
197            }
198        }
199
200        fatal(node, "Illegal value for attribute " + name + "!");
201        return -1;
202    }
203
204    // Get a required enumerated attribute as an index into a String array
205    protected static int getEnumeratedAttribute(Node node,
206                                                String name,
207                                                String[] legalNames)
208      throws IIOInvalidTreeException {
209        return getEnumeratedAttribute(node, name, legalNames, -1, true);
210    }
211
212    // Get a String-valued attribute
213    protected static String getAttribute(Node node, String name,
214                                         String defaultValue, boolean required)
215      throws IIOInvalidTreeException {
216        Node attr = node.getAttributes().getNamedItem(name);
217        if (attr == null) {
218            if (!required) {
219                return defaultValue;
220            } else {
221                fatal(node, "Required attribute " + name + " not present!");
222            }
223        }
224        return attr.getNodeValue();
225    }
226
227    // Get a required String-valued attribute
228    protected static String getAttribute(Node node, String name)
229      throws IIOInvalidTreeException {
230        return getAttribute(node, name, null, true);
231    }
232
233    protected GIFMetadata(boolean standardMetadataFormatSupported,
234                          String nativeMetadataFormatName,
235                          String nativeMetadataFormatClassName,
236                          String[] extraMetadataFormatNames,
237                          String[] extraMetadataFormatClassNames) {
238        super(standardMetadataFormatSupported,
239              nativeMetadataFormatName,
240              nativeMetadataFormatClassName,
241              extraMetadataFormatNames,
242              extraMetadataFormatClassNames);
243    }
244
245    public void mergeTree(String formatName, Node root)
246      throws IIOInvalidTreeException {
247        if (formatName.equals(nativeMetadataFormatName)) {
248            if (root == null) {
249                throw new IllegalArgumentException("root == null!");
250            }
251            mergeNativeTree(root);
252        } else if (formatName.equals
253                  (IIOMetadataFormatImpl.standardMetadataFormatName)) {
254            if (root == null) {
255                throw new IllegalArgumentException("root == null!");
256            }
257            mergeStandardTree(root);
258        } else {
259            throw new IllegalArgumentException("Not a recognized format!");
260        }
261    }
262
263    protected byte[] getColorTable(Node colorTableNode,
264                                   String entryNodeName,
265                                   boolean lengthExpected,
266                                   int expectedLength)
267      throws IIOInvalidTreeException {
268        byte[] red = new byte[256];
269        byte[] green  = new byte[256];
270        byte[] blue = new byte[256];
271        int maxIndex = -1;
272
273        Node entry = colorTableNode.getFirstChild();
274        if (entry == null) {
275            fatal(colorTableNode, "Palette has no entries!");
276        }
277
278        while (entry != null) {
279            if (!entry.getNodeName().equals(entryNodeName)) {
280                fatal(colorTableNode,
281                      "Only a "+entryNodeName+" may be a child of a "+
282                      entry.getNodeName()+"!");
283            }
284
285            int index = getIntAttribute(entry, "index", true, 0, 255);
286            if (index > maxIndex) {
287                maxIndex = index;
288            }
289            red[index] = (byte)getIntAttribute(entry, "red", true, 0, 255);
290            green[index] = (byte)getIntAttribute(entry, "green", true, 0, 255);
291            blue[index] = (byte)getIntAttribute(entry, "blue", true, 0, 255);
292
293            entry = entry.getNextSibling();
294        }
295
296        int numEntries = maxIndex + 1;
297
298        if (lengthExpected && numEntries != expectedLength) {
299            fatal(colorTableNode, "Unexpected length for palette!");
300        }
301
302        byte[] colorTable = new byte[3*numEntries];
303        for (int i = 0, j = 0; i < numEntries; i++) {
304            colorTable[j++] = red[i];
305            colorTable[j++] = green[i];
306            colorTable[j++] = blue[i];
307        }
308
309        return colorTable;
310    }
311
312    protected abstract void mergeNativeTree(Node root)
313      throws IIOInvalidTreeException;
314
315   protected abstract void mergeStandardTree(Node root)
316      throws IIOInvalidTreeException;
317}
318