1 /* 2 * Copyright (c) 2000, 2008, 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 package java.beans; 26 27 import com.sun.beans.decoder.DocumentHandler; 28 29 import java.io.Closeable; 30 import java.io.InputStream; 31 import java.io.IOException; 32 33 import org.xml.sax.InputSource; 34 import org.xml.sax.helpers.DefaultHandler; 35 36 /** 37 * The <code>XMLDecoder</code> class is used to read XML documents 38 * created using the <code>XMLEncoder</code> and is used just like 39 * the <code>ObjectInputStream</code>. For example, one can use 40 * the following fragment to read the first object defined 41 * in an XML document written by the <code>XMLEncoder</code> 42 * class: 43 * <pre> 44 * XMLDecoder d = new XMLDecoder( 45 * new BufferedInputStream( 46 * new FileInputStream("Test.xml"))); 47 * Object result = d.readObject(); 48 * d.close(); 49 * </pre> 50 * 51 *<p> 52 * For more information you might also want to check out 53 * <a 54 href="http://java.sun.com/products/jfc/tsc/articles/persistence3">Long Term Persistence of JavaBeans Components: XML Schema</a>, 55 * an article in <em>The Swing Connection.</em> 56 * @see XMLEncoder 57 * @see java.io.ObjectInputStream 58 * 59 * @since 1.4 60 * 61 * @author Philip Milne 62 */ 63 public class XMLDecoder { 64 private final DocumentHandler handler = new DocumentHandler(); 65 private final InputSource input; 66 private Object owner; 67 private Object[] array; 68 private int index; 69 70 /** 71 * Creates a new input stream for reading archives 72 * created by the <code>XMLEncoder</code> class. 73 * 74 * @param in The underlying stream. 75 * 76 * @see XMLEncoder#XMLEncoder(java.io.OutputStream) 77 */ 78 public XMLDecoder(InputStream in) { 79 this(in, null); 80 } 81 82 /** 83 * Creates a new input stream for reading archives 84 * created by the <code>XMLEncoder</code> class. 85 * 86 * @param in The underlying stream. 87 * @param owner The owner of this stream. 88 * 89 */ 90 public XMLDecoder(InputStream in, Object owner) { 91 this(in, owner, null); 92 } 93 94 /** 95 * Creates a new input stream for reading archives 96 * created by the <code>XMLEncoder</code> class. 97 * 98 * @param in the underlying stream. 99 * @param owner the owner of this stream. 100 * @param exceptionListener the exception handler for the stream; 101 * if <code>null</code> the default exception listener will be used. 102 */ 103 public XMLDecoder(InputStream in, Object owner, ExceptionListener exceptionListener) { 104 this(in, owner, exceptionListener, null); 105 } 106 107 /** 108 * Creates a new input stream for reading archives 109 * created by the <code>XMLEncoder</code> class. 110 * 111 * @param in the underlying stream. <code>null</code> may be passed without 112 * error, though the resulting XMLDecoder will be useless 113 * @param owner the owner of this stream. <code>null</code> is a legal 114 * value 115 * @param exceptionListener the exception handler for the stream, or 116 * <code>null</code> to use the default 117 * @param cl the class loader used for instantiating objects. 118 * <code>null</code> indicates that the default class loader should 119 * be used 120 * @since 1.5 121 */ 122 public XMLDecoder(InputStream in, Object owner, 123 ExceptionListener exceptionListener, ClassLoader cl) { 124 this(new InputSource(in), owner, exceptionListener, cl); 125 } 126 127 128 /** 129 * Creates a new decoder to parse XML archives 130 * created by the {@code XMLEncoder} class. 131 * If the input source {@code is} is {@code null}, 132 * no exception is thrown and no parsing is performed. 133 * This behavior is similar to behavior of other constructors 134 * that use {@code InputStream} as a parameter. 135 * 136 * @param is the input source to parse 137 * 138 * @since 1.7 139 */ 140 public XMLDecoder(InputSource is) { 141 this(is, null, null, null); 142 } 143 144 /** 145 * Creates a new decoder to parse XML archives 146 * created by the {@code XMLEncoder} class. 147 * 148 * @param is the input source to parse 149 * @param owner the owner of this decoder 150 * @param el the exception handler for the parser, 151 * or {@code null} to use the default exception handler 152 * @param cl the class loader used for instantiating objects, 153 * or {@code null} to use the default class loader 154 * 155 * @since 1.7 156 */ 157 private XMLDecoder(InputSource is, Object owner, ExceptionListener el, ClassLoader cl) { 158 this.input = is; 159 this.owner = owner; 160 setExceptionListener(el); 161 this.handler.setClassLoader(cl); 162 this.handler.setOwner(this); 163 } 164 165 /** 166 * This method closes the input stream associated 167 * with this stream. 168 */ 169 public void close() { 170 if (parsingComplete()) { 171 close(this.input.getCharacterStream()); 172 close(this.input.getByteStream()); 173 } 174 } 175 176 private void close(Closeable in) { 177 if (in != null) { 178 try { 179 in.close(); 180 } 181 catch (IOException e) { 182 getExceptionListener().exceptionThrown(e); 183 } 184 } 185 } 186 187 private boolean parsingComplete() { 188 if (this.input == null) { 189 return false; 190 } 191 if (this.array == null) { 192 this.handler.parse(this.input); 193 this.array = this.handler.getObjects(); 194 } 195 return true; 196 } 197 198 /** 199 * Sets the exception handler for this stream to <code>exceptionListener</code>. 200 * The exception handler is notified when this stream catches recoverable 201 * exceptions. 202 * 203 * @param exceptionListener The exception handler for this stream; 204 * if <code>null</code> the default exception listener will be used. 205 * 206 * @see #getExceptionListener 207 */ 208 public void setExceptionListener(ExceptionListener exceptionListener) { 209 if (exceptionListener == null) { 210 exceptionListener = Statement.defaultExceptionListener; 211 } 212 this.handler.setExceptionListener(exceptionListener); 213 } 214 215 /** 216 * Gets the exception handler for this stream. 217 * 218 * @return The exception handler for this stream. 219 * Will return the default exception listener if this has not explicitly been set. 220 * 221 * @see #setExceptionListener 222 */ 223 public ExceptionListener getExceptionListener() { 224 return this.handler.getExceptionListener(); 225 } 226 227 /** 228 * Reads the next object from the underlying input stream. 229 * 230 * @return the next object read 231 * 232 * @throws ArrayIndexOutOfBoundsException if the stream contains no objects 233 * (or no more objects) 234 * 235 * @see XMLEncoder#writeObject 236 */ 237 public Object readObject() { 238 return (parsingComplete()) 239 ? this.array[this.index++] 240 : null; 241 } 242 243 /** 244 * Sets the owner of this decoder to <code>owner</code>. 245 * 246 * @param owner The owner of this decoder. 247 * 248 * @see #getOwner 249 */ 250 public void setOwner(Object owner) { 251 this.owner = owner; 252 } 253 254 /** 255 * Gets the owner of this decoder. 256 * 257 * @return The owner of this decoder. 258 * 259 * @see #setOwner 260 */ 261 public Object getOwner() { 262 return owner; 263 } 264 265 /** 266 * Creates a new handler for SAX parser 267 * that can be used to parse embedded XML archives 268 * created by the {@code XMLEncoder} class. 269 * 270 * The {@code owner} should be used if parsed XML document contains 271 * the method call within context of the <java> element. 272 * The {@code null} value may cause illegal parsing in such case. 273 * The same problem may occur, if the {@code owner} class 274 * does not contain expected method to call. See details <a 275 * href="http://java.sun.com/products/jfc/tsc/articles/persistence3/">here</a>. 276 * 277 * @param owner the owner of the default handler 278 * that can be used as a value of <java> element 279 * @param el the exception handler for the parser, 280 * or {@code null} to use the default exception handler 281 * @param cl the class loader used for instantiating objects, 282 * or {@code null} to use the default class loader 283 * @return an instance of {@code DefaultHandler} for SAX parser 284 * 285 * @since 1.7 286 */ 287 public static DefaultHandler createHandler(Object owner, ExceptionListener el, ClassLoader cl) { 288 DocumentHandler handler = new DocumentHandler(); 289 handler.setOwner(owner); 290 handler.setExceptionListener(el); 291 handler.setClassLoader(cl); 292 return handler; 293 } 294 }