Package com.sun.xml.io

Source Code of com.sun.xml.io.XmlInStream

/*
* @(#)XmlInStream.java  1.20 99/02/05
*
* Copyright (c) 1998 Sun Microsystems, Inc. All Rights Reserved.
*
* This software is the confidential and proprietary information of Sun
* Microsystems, Inc. ("Confidential Information").  You shall not
* disclose such Confidential Information and shall use it only in
* accordance with the terms of the license agreement you entered into
* with Sun.
*
* SUN MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF THE
* SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
* IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
* PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR ANY DAMAGES
* SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING
* THIS SOFTWARE OR ITS DERIVATIVES.
*/

package com.sun.xml.io;

import java.beans.*;
import java.io.*;
import java.lang.reflect.*;
import java.net.URL;
import java.net.URLConnection;
// import java.net.URLClassLoader;    // JDK 1.2 specific!
import java.util.Enumeration;
import java.util.Hashtable;

import org.w3c.dom.*;

import org.xml.sax.*;
import org.xml.sax.helpers.ParserFactory;

import com.sun.xml.parser.Resolver;
import com.sun.xml.parser.ValidatingParser;
import com.sun.xml.tree.*;


/**
* This parses XML documents following an experimental DTD for
* externalized JavaBeans and related data.  Note that the reading
* and writing of the data must be closely synchronized; it is not
* permissible to perform any kind of "type punning" through this
* interface.  What's written as an integer must be read as such,
* not as four bytes that are then recombined; and vice versa.
* <em>(This and other reasons may make it undesirable to use the
* <code>java.io.Object{In,Out}put</code> interfaces, which seem to
* assume such things will be done.  Notice the deprecated methods.)</em>
*
* <P> The initial version makes no claims to efficiency, since it
* reads the whole thing into a parse tree, then dissects it.  A
* more efficient implementation would coroutine this with the parser.
*
* <P> The implementation's use of a new JDK 1.2 feature, the class
* <code>java.net.URLClassLoader</code>, is currently disabled.  When
* enabled, the &lt;STREAM ARCHIVE=...&gt; feature permits the
* bean classes in this stream to be loaded from the specified archive.
*
* @see XmlOutStream
*
* @author David Brownell
* @version 1.20
*/
public class XmlInStream
    implements ObjectInput
{
    /**
     * This is the name of the Java resource that holds the DTD
     * used for these "published" object streams.
     */
    private final static String resourceName = "com/sun/xml/io/stream.dtd";

    private InputStream    in;
    private Base64Decoder  decoder;
    private ClassLoader    loader;

    private ElementNode    serial;
    private NodeList    next;
    private int      index;

    private Hashtable    mapping = new Hashtable ();
   

    /**
     * Constructs a stream that may be used to read data in the format
     * written by <em>XmlOutStream</em>.  At this time it requires the
     * input stream to be a valid XML document, and reports errors if
     * that document doesn't match the structure which it supports.
     */
    public XmlInStream (InputStream in) throws IOException
    {
  try {
      Parser    parser = new ValidatingParser ();
      // Parser    parser = ParserFactory.makeParser ();
      XmlDocumentBuilder  builder = new XmlDocumentBuilder ();
      Resolver    resolver = new Resolver ();
      XmlDocument    document;

      builder.setIgnoringLexicalInfo (true);
      parser.setDocumentHandler (builder);

      resolver.registerCatalogEntry (XmlOutStream.publicId,
    resourceName, this.getClass ().getClassLoader ());
      parser.setEntityResolver (resolver);

      parser.parse (new InputSource (in));

      document = builder.getDocument ();
      serial = (ElementNode) document.getDocumentElement ();

      if (!"STREAM".equals (serial.getTagName ()))
    throw new XmlStreamException ("wrong document tag");
      loader = getClassLoader (serial.getAttribute ("ARCHIVE"));

      next = serial.getChildNodes ();
      index = 0;
      this.in = in;

  } catch (IOException e) {
      throw e;

  } catch (RuntimeException e) {
      throw e;

  } catch (SAXException e) {
      Exception  x = e.getException ();

      if (x == null)
    x = e;
if (e instanceof SAXParseException) {
    SAXParseException sex = (SAXParseException) e;
    System.out.println ("uri: " + sex.getSystemId ());
    System.out.println ("line: " + sex.getLineNumber ());
}
x.printStackTrace ();
      throw new XmlStreamException (x.getMessage ());

  } catch (Exception e) {
e.printStackTrace ();
      throw new XmlStreamException (e.getMessage ());
  }
    }

    private ClassLoader getClassLoader (String name)
    throws IOException
    {
  // System.err.println ("ARCHIVE:  " + name);

  // XXX JDK 1.2 only!
  // return new URLClassLoader (new URL [] { new URL (name) } );
  return null;
    }

    private Node advance () throws IOException
    {
  if (decoder != null)
      throw new XmlStreamException ("opaque data pending");
  return next.item (index++);
    }

    private ElementNode element () throws IOException
    {
  Node  n;
 
  //
  // Skip to the next element.
  //
  do {
      n = advance ();
      if (n == null)
    return null;
  } while (!(n instanceof ElementNode));

  return (ElementNode) n;
    }

    private ElementNode advance (String type) throws IOException
    {
  ElementNode  e = element ();

  if (e != null && !type.equals (e.getTagName ()))
      throw new XmlStreamException ("expected tag = " + type
    + " not " + e.getTagName ());
  return e;
    }

    private String value (ElementNode e) throws IOException
    {
  // This knows that all the values in this DTD are "V" attributes
  return e.getAttribute ("V");
    }

    private String value (String type) throws IOException
    {
  ElementNode  e = advance (type);

  if (e == null)
      throw new XmlStreamException ("expected '" + type + "' element");
  return value (e);
    }


    /**
     * ObjectInput method.
     * @deprecated can't meaningfully be used on structured data
     */
    public long skip (long l) throws IOException
    {
  // XXX could skip opaque data ...
  throw new XmlStreamException ("Can't skip XmlInStream data");
    }

    /**
     * DataInput method.
     * @deprecated can't meaningfully be used on structured data
     */
    public int skipBytes (int n) throws IOException
    {
  return (int) skip (n);
    }

    /**
     * ObjectInput method.
     * @deprecated can't meaningfully be used on structured data
     */
    public int available () throws IOException
    {
  // XXX could say how much opaque data was available
  return 0;
    }

    /**
     * Closes the stream, reclaiming resources.
     */
    public void close () throws IOException
    {
  serial = null;
  next = null;
  index = 0;
  mapping = null;
  in.close ();
    }

    // BOOLEAN

    /**
     * Returns a boolean value read from the input stream.
     */
    public boolean readBoolean () throws IOException
    {
  return "1".equals (value ("boolean"));
    }


    // INTEGRAL TYPES

    /**
     * Returns a byte value read from the input stream.
     */
    public int read () throws IOException
    {
  return Byte.parseByte (value ("i1"));
    }

    /**
     * Returns a byte value read from the input stream.
     */
    public byte readByte () throws IOException
    {
  return (byte) read ();
    }

    /**
     * Returns a byte value read from the input stream, with
     * no sign extension performed.
     */
    public int readUnsignedByte () throws IOException
    {
  return read ();
    }

    /**
     * Returns a short value read from the input stream.
     */
    public short readShort () throws IOException
    {
  return Short.parseShort (value ("i2"));
    }

    /**
     * Returns a short value read from the input stream, with
     * no sign extension performed.
     */
    public int readUnsignedShort () throws IOException
    {
  return 0x0ffff & readShort ();
    }

    /**
     * Returns an integer value read from the input stream.
     */
    public int readInt () throws IOException
    {
  return Integer.parseInt (value ("i4"));
    }

    /**
     * Returns a long value read from the input stream.
     */
    public long readLong () throws IOException
    {
  return Long.parseLong (value ("i8"));
    }


    // FLOATING POINT TYPES

    /**
     * Returns a floating point value read from the input stream.
     */
    public float readFloat () throws IOException
    {
  return Float.valueOf (value ("r4")).floatValue ();
    }

    /**
     * Returns a double width floating point value read from the
     * input stream.
     */
    public double readDouble () throws IOException
    {
  return Double.valueOf (value ("r8")).doubleValue ();
    }


    // CHARACTER/STRING TYPES
    /**
     * Returns a character value read from the input stream.  This is a
     * Java character (and so could be a single UNICODE surrogate), not
     * an XML character (which might need to be expressed in a pair of
     * UNICODE characters).
     */
    public char readChar () throws IOException
    {
  //
  // NOTE:  This needs to be a numeric value at least part of the
  // time, since there are chunks of UNICODE characters which are
  // disallowed (like most control characters) and single surrogate
  // characters are mishandled by at least UTF-8 readers.  The XML
  // notion of character isn't Java's notion!!
  //
  return (char) Short.parseShort (value ("c"));
    }

    /**
     * DataInput method.
     * @deprecated can't meaningfully be used on structured data,
     *  when DataOutput has no corresponding writeLine method
     */
    public String readLine () throws IOException
    {
  throw new XmlStreamException ("Can't read lines from XmlInStream");
    }

    // So, no analogue of writeBytes, writeChars ...

    /**
     * Returns a String value read from the input stream.  <b>NOTE:</b>
     * This can return strings much longer than the approximately
     * 64Kbyte limit imposed by the <code>java.io.DataInputStream</code>
     * implementation in wide use.  As the only primitive for parsing
     * arbitrary text data, such a restriction is unreasonable.
     *
     * <P><em>Another current bug:  Since Java's text model isn't the
     * same as XML's, we need to be able to escape some characters from
     * processing by XML (control characters) and UTF input streams
     * (unpaired surrogates, etc).</em>
     */
    public String readUTF () throws IOException
    {
  ElementNode  e = element ();
  Node    n;

  if ("NULL".equals (e.getTagName ()))
      return null;
  else if (!"STRING".equals (e.getTagName ()))
      throw new XmlStreamException ("expecting a string value");

  //
  // Construct the result string from #PCDATA and "c" nodes
  //
  String    retval = "";

  for (n = e.getFirstChild ();
    n != null;
    n = n.getNextSibling ()) {
      switch (n.getNodeType ()) {
        case Element.TEXT_NODE:
    retval += n.getNodeValue ();
    continue;
        case Element.ELEMENT_NODE:
    retval += (char) Short.parseShort (value ((ElementNode)n));
    continue;
        default:
    throw new XmlStreamException ("expecting text or <c>");
      }
  }
  return retval;
    }

    // OTHER

    /**
     * Reads Base64 encoded opaque binary data.  It is the caller's
     * responsibility to know how much data was written, and not to
     * attempt to read more than that amount of data.
     */
    public int read (byte buf [], int off, int len) throws IOException
    {
  int  retval = -1;

  while (retval == -1) {
      if (decoder == null) {
    Node     n = advance ("OPAQUE");

// n.b. we actually have the byte count available ...

    if (n == null)
        throw new XmlStreamException ("empty opaque element");

    // <opaque> base64 text </opaque>

    ((Element)n).normalize ();
    n = n.getFirstChild ();
    if (!(n instanceof Text))
        throw new XmlStreamException ("expected text");

    decoder = new Base64Decoder (new StringReader (n.toString ()));
      }
      retval = decoder.read (buf, off, len);
      if (decoder.isEOF ()) {
    decoder = null;
      }
  }
  return retval;
    }

    /**
     * Fills as much of the buffer as possible; shorthand
     * for <code>read (buf, 0, buf.length)</code>.
     */
    public int read (byte buf []) throws IOException
  { return read (buf, 0, buf.length); }

    /**
     * Fills the buffer.
     */
    public void readFully (byte buf []) throws IOException
    {
  readFully (buf, 0, buf.length);
    }

    /**
     * Fills the specified parts of the buffer.
     */
    public void readFully (byte buffer [], int offset, int length)
    throws IOException
    {
        int  count = 0, delta;

        while (length > 0) {
            delta = decoder.read (buffer, offset, length);
            if (delta <= 0)
                throw new XmlStreamException ("?? internal EOF ??");
            count += delta;
      offset -= delta;
      length += delta;
        }
    }

    /**
     * Reads an object or array.
     */
    public Object readObject () throws IOException
    {
  ElementNode  e = element ();

  if (e != null) {
      if ("BEAN".equals (e.getTagName ()))
    return getBean (e);
      else if  ("OBJECT".equals (e.getTagName ()))
    return mapping.get (e.getAttribute ("IDREF"));
      else if  ("NULL".equals (e.getTagName ()))
    return null;
      else if  ("ARRAY".equals (e.getTagName ()))
    return getArray (e);
// XXX accept counted OPAQUE byte array here too
  }
  throw new XmlStreamException ("needed BEAN, OBJECT, or NULL tag");
    }

    private Object getBean (ElementNode el) throws IOException
    {
  String  id = el.getAttribute ("ID");
  String  className = el.getAttribute ("CLASS");
  Class  objClass;
  Object  retval = null;

  try {
      // Instantiate object ...
      if (loader != null)
    objClass = loader.loadClass (className);
      else
    objClass = Class.forName (className);
      retval = objClass.newInstance ();

      // Store it away, in case its contents refer back...
      mapping.put (id, retval);

      // Fetch type's property info
      BeanInfo    info = Introspector.getBeanInfo (objClass);
      PropertyDescriptor  props [] = info.getPropertyDescriptors ();

      // set all reported properties
      NodeList    saved = next;
      int      lastIndex = index;

      next = el.getChildNodes ();
      index = 0;
    eachProperty:
      for (;;) {
    String    propName;
    NodeList  propValue;

    if ((el = advance ("PROPERTY")) == null)
        break;

    propName = el.getAttribute ("NAME");
    propValue = el.getChildNodes ();

    if (propValue == null)
        throw new XmlStreamException ("No value for property "
           + propName);

    for (int i = 0; i < props.length; i++) {
        if (props [i].getName ().equals (propName)) {
      setProperty (retval, propValue,
          props [i].getWriteMethod ());
      continue eachProperty;
        }
    }
    throw new XmlStreamException ("Bean type " + className
      + "has no property named " + propName);
      }
      next = saved;
      index = lastIndex;

  } catch (ClassNotFoundException e) {
      throw new XmlStreamException ("Class not found:  " + className);

  } catch (InstantiationException e) {
      throw new XmlStreamException ("Can't instantiate:  " + className);

  } catch (IllegalAccessException e) {
      throw new XmlStreamException ("Can't access:  " + className);

  } catch (IntrospectionException e) {
      throw new XmlStreamException ("Not a bean class:  " + className);

  } catch (InvocationTargetException e) {
      throw new XmlStreamException ("Can't set all properties:  "
    + className);

  }

  return retval;
    }

    private void setProperty (
  Object    bean,
  NodeList  value,
  Method    setter
    ) throws IOException, IllegalAccessException,
  IllegalArgumentException, InvocationTargetException
    {
  Object    args [] = new Object [1];

  args [0] = readValue (value);
  setter.invoke (bean, args);
    }

    private Object readValue (NodeList valueEnum)
    throws IOException
    {
  NodeList   saved = next;
  int    lastIndex = index;
  ElementNode  el;
  Object    retval;

  try {
      next = valueEnum;
      index = 0;
      el = element ();

      if ("boolean".equals (el.getTagName ())) {
    if ("1".equals (value (el)))
        retval = Boolean.TRUE;
    else
        retval = Boolean.FALSE;
    }

      else if ("i1".equals (el.getTagName ()))
    retval = new Byte (value (el));
      else if ("i2".equals (el.getTagName ()))
    retval = new Short (value (el));
      else if ("i4".equals (el.getTagName ()))
    retval = new Integer (value (el));
      else if ("i8".equals (el.getTagName ()))
    retval = new Long (value (el));

      else if ("r4".equals (el.getTagName ()))
    retval = new Float (value (el));
      else if ("r8".equals (el.getTagName ()))
    retval = new Double (value (el));

      else if ("c".equals (el.getTagName ()))
    retval = new Character ((char)Integer.parseInt (value (el)));
      else if ("STRING".equals (el.getTagName ())) {
    Node    n;
    String    value = "";

    // (#PCDATA|c)*

    for (n = el.getFirstChild ();
      n != null;
      n = n.getNextSibling ()) {
        switch (n.getNodeType ()) {
          case Element.TEXT_NODE:
      value += n.getNodeValue ();
      continue;
          case Element.ELEMENT_NODE:
      value += (char) Short.parseShort (
        value ((ElementNode)n));
      continue;
          default:
      throw new XmlStreamException ("expecting text or <c>");
        }
    }
    retval = value;
      }

      else if ("OBJECT".equals (el.getTagName ()))
    retval = mapping.get (el.getAttribute ("IDREF"));
      else if ("NULL".equals (el.getTagName ()))
    retval = null;
      else if ("BEAN".equals (el.getTagName ()))
    retval = getBean (el);
      else if ("ARRAY".equals (el.getTagName ()))
    retval = getArray (el);

// XXX OPAQUE

      else
    throw new XmlStreamException ("unrecognized tag: "
        + el.getTagName ());

      return retval;

  } finally {
      next = saved;
      index = lastIndex;
  }
    }

    private Object getArray (ElementNode el) throws IOException
    {
  String  id = el.getAttribute ("ID");
  String  className = el.getAttribute ("CLASS");
  int  length = Integer.parseInt (el.getAttribute ("LENGTH"));
  Class  elementClass;
  Object  retval = null;

  try {
      // Instantiate array ...
      if (loader != null)
    elementClass = loader.loadClass (className);
      else
    elementClass = Class.forName (className);
      retval = Array.newInstance (elementClass, length);

      // Store it away, in case its contents refer back...
      mapping.put (id, retval);

      NodeList    saved = next;
      int      lastIndex = index;

      next = el.getChildNodes ();
    eachProperty:
      for (;;) {
    int    index;
    NodeList  elementValue;

    if ((el = advance ("ELEMENT")) == null)
        break;

    index = Integer.parseInt (el.getAttribute ("INDEX"));
    elementValue = el.getChildNodes ();

    if (elementValue == null)
        throw new XmlStreamException (
      "No value for array element " + index);

    Array.set (retval, index, readValue (elementValue));
      }
      next = saved;
      index = lastIndex;

  } catch (ArrayIndexOutOfBoundsException e) {
      throw new XmlStreamException ("Array index out of bounds");

  } catch (NegativeArraySizeException e) {
      throw new XmlStreamException ("Negative array size");

  } catch (ClassNotFoundException e) {
      throw new XmlStreamException ("Class not found:  " + className);

  }

  return retval;
    }
}
TOP

Related Classes of com.sun.xml.io.XmlInStream

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.