GNU Classpath (0.95) | |
Frames | No Frames |
1: /* java.beans.XMLDecoder -- 2: Copyright (C) 2004, 2005, 2006 Free Software Foundation, Inc. 3: 4: This file is part of GNU Classpath. 5: 6: GNU Classpath is free software; you can redistribute it and/or modify 7: it under the terms of the GNU General Public License as published by 8: the Free Software Foundation; either version 2, or (at your option) 9: any later version. 10: 11: GNU Classpath is distributed in the hope that it will be useful, but 12: WITHOUT ANY WARRANTY; without even the implied warranty of 13: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14: General Public License for more details. 15: 16: You should have received a copy of the GNU General Public License 17: along with GNU Classpath; see the file COPYING. If not, write to the 18: Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 19: 02110-1301 USA. 20: 21: Linking this library statically or dynamically with other modules is 22: making a combined work based on this library. Thus, the terms and 23: conditions of the GNU General Public License cover the whole 24: combination. 25: 26: As a special exception, the copyright holders of this library give you 27: permission to link this library with independent modules to produce an 28: executable, regardless of the license terms of these independent 29: modules, and to copy and distribute the resulting executable under 30: terms of your choice, provided that you also meet, for each linked 31: independent module, the terms and conditions of the license of that 32: module. An independent module is a module which is not derived from 33: or based on this library. If you modify this library, you may extend 34: this exception to your version of the library, but you are not 35: obligated to do so. If you do not wish to do so, delete this 36: exception statement from your version. */ 37: 38: 39: package java.beans; 40: 41: import gnu.java.beans.DefaultExceptionListener; 42: import gnu.java.beans.decoder.PersistenceParser; 43: 44: import java.io.IOException; 45: import java.io.InputStream; 46: import java.util.Iterator; 47: import java.util.NoSuchElementException; 48: 49: /** 50: * The XMLDecoder reads XML data that is structured according to 51: * <a href="http://java.sun.com/products/jfc/tsc/articles/persistence3/javabeans.dtd">this</a> DTD 52: * and creates objects according to the content. Usually such data is generated using the 53: * {@link XMLEncoder} class. 54: * <p> 55: * An example XML document might look like this: 56: * <code> 57: * <java> 58: * <string>Hello World</string> 59: * <int>200</int> 60: * </java> 61: * </code> 62: * <p>To read the <code>String</code> and the <code>Integer</code> instance the following can be used (assume 63: * the XML data can be obtained from the InputStream):</p> 64: * <code> 65: * XMLDecoder decoder = new XMLDecoder(inputStreamContainingXMLData); 66: * String message = (String) decoder.readObject(); 67: * Integer number = (Integer) decoder.readObject(); 68: * </code> 69: * <p>Besides this basic functionality the <code>XMLDecoder</code> has some more features that might come 70: * handy in certain situations:</p> 71: * <p>An owner object can be set using the <code>setOwner</code> method which can then be accessed when 72: * decoding. This feature is only useful if the XML data is aware of the owner object. Such data may 73: * look like this (assume that the owner object is a JFrame instance):</p> 74: * <code> 75: * <java> 76: * <void method="getOwner"> 77: * <void method="setVisible"> 78: * <boolean>true<boolean> 79: * </void> 80: * </void> 81: * </java> 82: * </code> 83: * This accesses the <code>JFrame</code> and makes it visible using the <code>setVisible</code> method. 84: * <p>Please note that changing the owner <b>after</b> the having read the first object has no effect, 85: * because all object have been decoded then.</p> 86: * <p>If the <code>XMLDecoder</code> is created with no {@link ExceptionListener} instance a default one 87: * is used that prints an error message to <code>System.err</code> whenever a recoverable exception 88: * is thrown. Recovarable exceptions occur when the XML data cannot be interpreted correctly (e.g 89: * unknown classes or methods, invocation on null, ...). In general be very careful when the 90: * <code>XMLDecoder</code> provoked such exceptions because the resulting object(s) may be in an 91: * undesirable state.</p> 92: * <p>Note that changing the ExceptionListener instance after <code>readObject</code> has been called 93: * once has no effect because the decoding is completed then.</p> 94: * <p>At last one can provide a specific <code>ClassLoader</code> which is then used when <code>Class</code> 95: * objects are accessed. See {@link java.lang.Class#forName(String, boolean, ClassLoader)} for details 96: * on this.</p> 97: * <p>Note: If the <code>InputStream</code> instance given to any of the constructors is <code>null</code> 98: * the resulting <code>XMLDecoder</code> will be silently (without any exception) useless. Each call 99: * to <code>readObject</code> will return <code>null</code> and never throws an 100: * <code>ArrayIndexOutOfBoundsException</code>.</p> 101: * 102: * @author Robert Schuster 103: * @since 1.4 104: * @status updated to 1.5 105: */ 106: public class XMLDecoder 107: { 108: private Object owner; 109: 110: private ExceptionListener exceptionListener; 111: 112: private InputStream inputStream; 113: 114: private boolean isStreamClosed; 115: 116: private ClassLoader classLoader; 117: 118: private Iterator iterator; 119: 120: /** Creates a XMLDecoder instance that parses the XML data of the given input stream. 121: * Using this constructor no special ClassLoader, a default ExceptionListener 122: * and no owner object is used. 123: * 124: * @param in InputStream to read XML data from. 125: */ 126: public XMLDecoder(InputStream in) 127: { 128: this(in, null); 129: } 130: 131: /** Creates a XMLDecoder instance that parses the XML data of the given input stream. 132: * Using this constructor no special ClassLoader and a default ExceptionListener 133: * is used. 134: * 135: * @param in InputStream to read XML data from. 136: * @param owner Owner object which can be accessed and modified while parsing. 137: */ 138: public XMLDecoder(InputStream in, Object owner) 139: { 140: this(in, owner, null); 141: } 142: 143: /** Creates a XMLDecoder instance that parses the XML data of the given input stream. 144: * If the ExceptionListener argument is null a default implementation is used. 145: * 146: * @param in InputStream to read XML data from. 147: * @param owner Owner object which can be accessed and modified while parsing. 148: * @param exceptionListener ExceptionListener instance to which exception notifications are send. 149: */ 150: public XMLDecoder( 151: InputStream in, 152: Object owner, 153: ExceptionListener exceptionListener) 154: { 155: this( 156: in, 157: owner, 158: exceptionListener, 159: Thread.currentThread().getContextClassLoader()); 160: } 161: 162: /** Creates a XMLDecoder instance that parses the XML data of the given input stream. 163: * If the ExceptionListener argument is null a default implementation is used. 164: * 165: * @param in InputStream to read XML data from. 166: * @param owner Owner object which can be accessed and modified while parsing. 167: * @param exceptionListener ExceptionListener instance to which exception notifications are send. 168: * @param cl ClassLoader instance that is used for calls to <code>Class.forName(String, boolean, ClassLoader)</code> 169: * @since 1.5 170: */ 171: public XMLDecoder( 172: InputStream in, 173: Object owner, 174: ExceptionListener listener, 175: ClassLoader cl) 176: { 177: // initially here was a check for the validity of the InputStream argument but some 178: // great engineers decided that this API should silently discard this and behave rather 179: // odd: readObject will always return null ... 180: inputStream = in; 181: 182: setExceptionListener(listener); 183: 184: // validity of this object is checked in Class.forName() and therefore may be null 185: classLoader = cl; 186: 187: this.owner = owner; 188: } 189: 190: /** Closes the stream associated with this decoder. This should be done after having read all 191: * decoded objects. 192: * <p>See the description of the {@link #readObject()} for the effect caused by <code>close</code>.</p> 193: */ 194: public void close() 195: { 196: if (isStreamClosed) 197: { 198: return; 199: } 200: 201: try 202: { 203: inputStream.close(); 204: isStreamClosed = true; 205: } 206: catch (IOException e) 207: { 208: // bad style forced by original API design ... 209: } 210: } 211: 212: /** Returns the ExceptionListener instance associated with this decoder. 213: * <p>See the description of {@link XMLDecoder} class for more information on the ExceptionListener.</p> 214: * 215: * @return Current ExceptionListener of the decoder. 216: */ 217: public ExceptionListener getExceptionListener() 218: { 219: return exceptionListener; 220: } 221: 222: /** Returns the owner object of the decoder. This method is usually called 223: * from within the parsed XML data. 224: * <p>See the description of {@link XMLDecoder} class for more information on the owner object.</p> 225: * 226: * @return The owner object of this decoder. 227: */ 228: public Object getOwner() 229: { 230: return owner; 231: } 232: 233: /** Returns the next available decoded object. 234: * <p>Note that the actual decoding takes place when the method is called for the first time.</p> 235: * <p>If the <code>close</code> method was already called a <code>NoSuchElementException</code> 236: * is thrown.</p> 237: * <p>If the InputStream instance used in the constructors was <code>null</code> this method 238: * will always return <code>null</code> itself.</p> 239: * 240: * @return The next object in a sequence decoded from XML data. 241: * @throws ArrayIndexOutOfBoundsException When no more objects are available. 242: */ 243: public Object readObject() throws ArrayIndexOutOfBoundsException 244: { 245: // note: the RI does it this way ... 246: if(inputStream == null) { 247: return null; 248: } 249: 250: // note: the original API documentation says nothing on what to do 251: // when the stream was closed before readObject is called but it actually 252: // throws a NoSuchElementException - this behaviour is imitated here 253: if (isStreamClosed) 254: { 255: throw new NoSuchElementException("Cannot read any objects - XMLDecoder was already closed."); 256: } 257: 258: // creates the PersistenceParser (doing the parsing and decoding) and returns its 259: // Iterator on first invocation 260: if (iterator == null) 261: { 262: iterator = 263: new PersistenceParser( 264: inputStream, 265: exceptionListener, 266: classLoader, 267: this) 268: .iterator(); 269: } 270: 271: // note: done according to the official documentation 272: if (!iterator.hasNext()) 273: { 274: throw new ArrayIndexOutOfBoundsException("No more objects available from this XMLDecoder."); 275: } 276: 277: // returns just the next object if there was no problem 278: return iterator.next(); 279: } 280: 281: /** Sets the ExceptionListener instance to which notifications of exceptions are send 282: * while parsing the XML data. 283: * <p>See the description of {@link XMLDecoder} class for more information on the ExceptionListener.</p> 284: * 285: * @param listener 286: */ 287: public void setExceptionListener(ExceptionListener listener) 288: { 289: // uses a default implementation when null 290: if (listener == null) 291: { 292: listener = DefaultExceptionListener.INSTANCE; 293: } 294: exceptionListener = listener; 295: } 296: 297: /** Sets the owner object which can be accessed from the parsed XML data. 298: * <p>See the description of {@link XMLDecoder} class for more information on the owner object.</p> 299: * 300: * @param newOwner 301: */ 302: public void setOwner(Object newOwner) 303: { 304: owner = newOwner; 305: } 306: 307: }
GNU Classpath (0.95) |