FileDocCategorySizeDatePackage
MarkerSegment.javaAPI DocJava SE 5 API6867Fri Aug 26 14:54:42 BST 2005com.sun.imageio.plugins.jpeg

MarkerSegment.java

/*
 * @(#)MarkerSegment.java	1.7 03/12/19
 *
 * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
 * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 */

package com.sun.imageio.plugins.jpeg;

import javax.imageio.metadata.IIOInvalidTreeException;
import javax.imageio.metadata.IIOMetadataNode;
import javax.imageio.stream.ImageOutputStream;

import java.io.IOException;

import org.w3c.dom.Node;
import org.w3c.dom.NamedNodeMap;

/**
 * All metadata is stored in MarkerSegments.  Marker segments
 * that we know about are stored in subclasses of this
 * basic class, which used for unrecognized APPn marker
 * segments.  XXX break out UnknownMarkerSegment as a subclass
 * and make this abstract, avoiding unused data field.
 */
class MarkerSegment implements Cloneable {
    protected static final int LENGTH_SIZE = 2; // length is 2 bytes
    int tag;      // See JPEG.java
    int length;    /* Sometimes needed by subclasses; doesn't include
                      itself.  Meaningful only if constructed from a stream */
    byte [] data = null;  // Raw segment data, used for unrecognized segments
    boolean unknown = false; // Set to true if the tag is not recognized

    /**
     * Constructor for creating <code>MarkerSegment</code>s by reading
     * from an <code>ImageInputStream</code>.
     */
    MarkerSegment(JPEGBuffer buffer) throws IOException {

        buffer.loadBuf(3);  // tag plus length
        tag = buffer.buf[buffer.bufPtr++] & 0xff;
        length = (buffer.buf[buffer.bufPtr++] & 0xff) << 8;
        length |= buffer.buf[buffer.bufPtr++] & 0xff;
        length -= 2;  // JPEG length includes itself, we don't
        buffer.bufAvail -= 3;
        // Now that we know the true length, ensure that we've got it,
        // or at least a bufferful if length is too big.
        buffer.loadBuf(length);
    }

    /**
     * Constructor used when creating segments other than by
     * reading them from a stream.  
     */
    MarkerSegment(int tag) {
        this.tag = tag;
        length = 0;
    }

    /**
     * Construct a MarkerSegment from an "unknown" DOM Node.
     */
    MarkerSegment(Node node) throws IIOInvalidTreeException {
        // The type of node should have been verified already.
        // get the attribute and assign it to the tag
        tag = getAttributeValue(node, 
                                null, 
                                "MarkerTag", 
                                0, 255, 
                                true);
        length = 0;
        // get the user object and clone it to the data
        if (node instanceof IIOMetadataNode) {
            IIOMetadataNode iioNode = (IIOMetadataNode) node;
            try {
                data = (byte []) iioNode.getUserObject();
            } catch (Exception e) {
                IIOInvalidTreeException newGuy = 
                    new IIOInvalidTreeException
                    ("Can't get User Object", node);
                newGuy.initCause(e);
                throw newGuy;
            }
        } else {
            throw new IIOInvalidTreeException
                ("Node must have User Object", node);
        }
    }

    /**
     * Deep copy of data array.
     */
    protected Object clone() {
        MarkerSegment newGuy = null;
        try {
            newGuy = (MarkerSegment) super.clone();
        } catch (CloneNotSupportedException e) {} // won't happen
        if (this.data != null) {
            newGuy.data = (byte[]) data.clone();
        }
        return newGuy;
    }

    /**
     * We have determined that we don't know the type, so load
     * the data using the length parameter.
     */
    void loadData(JPEGBuffer buffer) throws IOException {
        data = new byte[length];
        buffer.readData(data);
    }

    IIOMetadataNode getNativeNode() {
        IIOMetadataNode node = new IIOMetadataNode("unknown");
        node.setAttribute("MarkerTag", Integer.toString(tag));
        node.setUserObject(data);
        
        return node;
    }

    static int getAttributeValue(Node node, 
                                 NamedNodeMap attrs,
                                 String name,
                                 int min,
                                 int max,
                                 boolean required)
        throws IIOInvalidTreeException {
        if (attrs == null) {
            attrs = node.getAttributes();
        }
        String valueString = attrs.getNamedItem(name).getNodeValue();
        int value = -1;
        if (valueString == null) {
            if (required) {
                throw new IIOInvalidTreeException
                    (name + " attribute not found", node);
            }
        } else {
              value = Integer.parseInt(valueString);
              if ((value < min) || (value > max)) {
                  throw new IIOInvalidTreeException
                      (name + " attribute out of range", node);
              }
        }
        return value;
    }

    /**
     * Writes the marker, tag, and length.  Note that length
     * should be verified by the caller as a correct JPEG
     * length, i.e it includes itself.
     */
    void writeTag(ImageOutputStream ios) throws IOException {
        ios.write(0xff);
        ios.write(tag);
        write2bytes(ios, length);
    }

    /**
     * Writes the data for this segment to the stream in
     * valid JPEG format.
     */
    void write(ImageOutputStream ios) throws IOException {
        length = 2 + ((data != null) ? data.length : 0);
        writeTag(ios);
        if (data != null) {
            ios.write(data);
        }
    }

    static void write2bytes(ImageOutputStream ios, 
                            int value) throws IOException {
        ios.write((value >> 8) & 0xff);
        ios.write(value & 0xff);
            
    }

    void printTag(String prefix) {
        System.out.println(prefix + " marker segment - marker = 0x"
                           + Integer.toHexString(tag));
        System.out.println("length: " + length);
    }

    void print() {
        printTag("Unknown");
        if (length > 10) {
            System.out.print("First 5 bytes:");
            for (int i=0;i<5;i++) {
                System.out.print(" Ox" 
                                 + Integer.toHexString((int)data[i]));
            }
            System.out.print("\nLast 5 bytes:");
            for (int i=data.length-5;i<data.length;i++) {
                System.out.print(" Ox" 
                                 + Integer.toHexString((int)data[i]));
            }
        } else {
            System.out.print("Data:");
            for (int i=0;i<data.length;i++) {
                System.out.print(" Ox" 
                                 + Integer.toHexString((int)data[i]));
            }
        }
        System.out.println();
    }
}