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 }