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