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 
26 package com.sun.imageio.plugins.gif;
27 
28 import javax.imageio.metadata.IIOInvalidTreeException;
29 import javax.imageio.metadata.IIOMetadata;
30 import javax.imageio.metadata.IIOMetadataFormatImpl;
31 import org.w3c.dom.Node;
32 
33 /**
34  * Class which adds utility DOM element attribute access methods to
35  * {@code IIOMetadata} for subclass use.
36  */
37 abstract 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
fatal(Node node, String reason)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
getStringAttribute(Node node, String name, String defaultValue, boolean required, String[] range)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
getIntAttribute(Node node, String name, int defaultValue, boolean required, boolean bounded, int min, int max)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
getFloatAttribute(Node node, String name, float defaultValue, boolean required)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
getIntAttribute(Node node, String name, boolean bounded, int min, int max)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
getFloatAttribute(Node node, String name)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
getBooleanAttribute(Node node, String name, boolean defaultValue, boolean required)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
getBooleanAttribute(Node node, String name)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
getEnumeratedAttribute(Node node, String name, String[] legalNames, int defaultValue, boolean required)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
getEnumeratedAttribute(Node node, String name, String[] legalNames)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
getAttribute(Node node, String name, String defaultValue, boolean required)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
getAttribute(Node node, String name)228     protected static String getAttribute(Node node, String name)
229       throws IIOInvalidTreeException {
230         return getAttribute(node, name, null, true);
231     }
232 
GIFMetadata(boolean standardMetadataFormatSupported, String nativeMetadataFormatName, String nativeMetadataFormatClassName, String[] extraMetadataFormatNames, String[] extraMetadataFormatClassNames)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 
mergeTree(String formatName, Node root)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 
getColorTable(Node colorTableNode, String entryNodeName, boolean lengthExpected, int expectedLength)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 
mergeNativeTree(Node root)312     protected abstract void mergeNativeTree(Node root)
313       throws IIOInvalidTreeException;
314 
mergeStandardTree(Node root)315    protected abstract void mergeStandardTree(Node root)
316       throws IIOInvalidTreeException;
317 }
318