All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.sun.xml.io.XmlInStream Maven / Gradle / Ivy

The newest version!
/*
 * @(#)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.
 * (This and other reasons may make it undesirable to use the
 * java.io.Object{In,Out}put interfaces, which seem to
 * assume such things will be done.  Notice the deprecated methods.)
 *
 * 

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. * *

The implementation's use of a new JDK 1.2 feature, the class * java.net.URLClassLoader, is currently disabled. When * enabled, the <STREAM ARCHIVE=...> 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 XmlOutStream. 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. NOTE: * This can return strings much longer than the approximately * 64Kbyte limit imposed by the java.io.DataInputStream * implementation in wide use. As the only primitive for parsing * arbitrary text data, such a restriction is unreasonable. * *

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). */ 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 "); } } 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"); // base64 text ((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 read (buf, 0, buf.length). */ 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 "); } } 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; } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy