1 /*
   2  * Copyright (c) 2001, 2014, 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.jpeg;
  27 
  28 import javax.imageio.metadata.IIOInvalidTreeException;
  29 import javax.imageio.metadata.IIOMetadataNode;
  30 import javax.imageio.stream.ImageOutputStream;
  31 import javax.imageio.IIOException;
  32 
  33 import java.io.IOException;
  34 
  35 import org.w3c.dom.Node;
  36 import org.w3c.dom.NamedNodeMap;
  37 
  38 /**
  39  * All metadata is stored in MarkerSegments.  Marker segments
  40  * that we know about are stored in subclasses of this
  41  * basic class, which used for unrecognized APPn marker
  42  * segments.  XXX break out UnknownMarkerSegment as a subclass
  43  * and make this abstract, avoiding unused data field.
  44  */
  45 class MarkerSegment implements Cloneable {
  46     protected static final int LENGTH_SIZE = 2; // length is 2 bytes
  47     int tag;      // See JPEG.java
  48     int length;    /* Sometimes needed by subclasses; doesn't include
  49                       itself.  Meaningful only if constructed from a stream */
  50     byte [] data = null;  // Raw segment data, used for unrecognized segments
  51     boolean unknown = false; // Set to true if the tag is not recognized
  52 
  53     /**
  54      * Constructor for creating <code>MarkerSegment</code>s by reading
  55      * from an <code>ImageInputStream</code>.
  56      */
  57     MarkerSegment(JPEGBuffer buffer) throws IOException {
  58 
  59         buffer.loadBuf(3);  // tag plus length
  60         tag = buffer.buf[buffer.bufPtr++] & 0xff;
  61         length = (buffer.buf[buffer.bufPtr++] & 0xff) << 8;
  62         length |= buffer.buf[buffer.bufPtr++] & 0xff;
  63         length -= 2;  // JPEG length includes itself, we don't
  64 
  65         if (length < 0) {
  66             throw new IIOException("Invalid segment length: " + length);
  67         }
  68         buffer.bufAvail -= 3;
  69         // Now that we know the true length, ensure that we've got it,
  70         // or at least a bufferful if length is too big.
  71         buffer.loadBuf(length);
  72     }
  73 
  74     /**
  75      * Constructor used when creating segments other than by
  76      * reading them from a stream.
  77      */
  78     MarkerSegment(int tag) {
  79         this.tag = tag;
  80         length = 0;
  81     }
  82 
  83     /**
  84      * Construct a MarkerSegment from an "unknown" DOM Node.
  85      */
  86     MarkerSegment(Node node) throws IIOInvalidTreeException {
  87         // The type of node should have been verified already.
  88         // get the attribute and assign it to the tag
  89         tag = getAttributeValue(node,
  90                                 null,
  91                                 "MarkerTag",
  92                                 0, 255,
  93                                 true);
  94         length = 0;
  95         // get the user object and clone it to the data
  96         if (node instanceof IIOMetadataNode) {
  97             IIOMetadataNode iioNode = (IIOMetadataNode) node;
  98             try {
  99                 data = (byte []) iioNode.getUserObject();
 100             } catch (Exception e) {
 101                 IIOInvalidTreeException newGuy =
 102                     new IIOInvalidTreeException
 103                     ("Can't get User Object", node);
 104                 newGuy.initCause(e);
 105                 throw newGuy;
 106             }
 107         } else {
 108             throw new IIOInvalidTreeException
 109                 ("Node must have User Object", node);
 110         }
 111     }
 112 
 113     /**
 114      * Deep copy of data array.
 115      */
 116     protected Object clone() {
 117         MarkerSegment newGuy = null;
 118         try {
 119             newGuy = (MarkerSegment) super.clone();
 120         } catch (CloneNotSupportedException e) {} // won't happen
 121         if (this.data != null) {
 122             newGuy.data = data.clone();
 123         }
 124         return newGuy;
 125     }
 126 
 127     /**
 128      * We have determined that we don't know the type, so load
 129      * the data using the length parameter.
 130      */
 131     void loadData(JPEGBuffer buffer) throws IOException {
 132         data = new byte[length];
 133         buffer.readData(data);
 134     }
 135 
 136     IIOMetadataNode getNativeNode() {
 137         IIOMetadataNode node = new IIOMetadataNode("unknown");
 138         node.setAttribute("MarkerTag", Integer.toString(tag));
 139         node.setUserObject(data);
 140 
 141         return node;
 142     }
 143 
 144     static int getAttributeValue(Node node,
 145                                  NamedNodeMap attrs,
 146                                  String name,
 147                                  int min,
 148                                  int max,
 149                                  boolean required)
 150         throws IIOInvalidTreeException {
 151         if (attrs == null) {
 152             attrs = node.getAttributes();
 153         }
 154         String valueString = attrs.getNamedItem(name).getNodeValue();
 155         int value = -1;
 156         if (valueString == null) {
 157             if (required) {
 158                 throw new IIOInvalidTreeException
 159                     (name + " attribute not found", node);
 160             }
 161         } else {
 162               value = Integer.parseInt(valueString);
 163               if ((value < min) || (value > max)) {
 164                   throw new IIOInvalidTreeException
 165                       (name + " attribute out of range", node);
 166               }
 167         }
 168         return value;
 169     }
 170 
 171     /**
 172      * Writes the marker, tag, and length.  Note that length
 173      * should be verified by the caller as a correct JPEG
 174      * length, i.e it includes itself.
 175      */
 176     void writeTag(ImageOutputStream ios) throws IOException {
 177         ios.write(0xff);
 178         ios.write(tag);
 179         write2bytes(ios, length);
 180     }
 181 
 182     /**
 183      * Writes the data for this segment to the stream in
 184      * valid JPEG format.
 185      */
 186     void write(ImageOutputStream ios) throws IOException {
 187         length = 2 + ((data != null) ? data.length : 0);
 188         writeTag(ios);
 189         if (data != null) {
 190             ios.write(data);
 191         }
 192     }
 193 
 194     static void write2bytes(ImageOutputStream ios,
 195                             int value) throws IOException {
 196         ios.write((value >> 8) & 0xff);
 197         ios.write(value & 0xff);
 198 
 199     }
 200 
 201     void printTag(String prefix) {
 202         System.out.println(prefix + " marker segment - marker = 0x"
 203                            + Integer.toHexString(tag));
 204         System.out.println("length: " + length);
 205     }
 206 
 207     void print() {
 208         printTag("Unknown");
 209         if (length > 10) {
 210             System.out.print("First 5 bytes:");
 211             for (int i=0;i<5;i++) {
 212                 System.out.print(" Ox"
 213                                  + Integer.toHexString((int)data[i]));
 214             }
 215             System.out.print("\nLast 5 bytes:");
 216             for (int i=data.length-5;i<data.length;i++) {
 217                 System.out.print(" Ox"
 218                                  + Integer.toHexString((int)data[i]));
 219             }
 220         } else {
 221             System.out.print("Data:");
 222             for (int i=0;i<data.length;i++) {
 223                 System.out.print(" Ox"
 224                                  + Integer.toHexString((int)data[i]));
 225             }
 226         }
 227         System.out.println();
 228     }
 229 }