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 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 }