/* | 
|
 * Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved. | 
|
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | 
|
 * | 
|
 * This code is free software; you can redistribute it and/or modify it | 
|
 * under the terms of the GNU General Public License version 2 only, as | 
|
 * published by the Free Software Foundation.  Oracle designates this | 
|
 * particular file as subject to the "Classpath" exception as provided | 
|
 * by Oracle in the LICENSE file that accompanied this code. | 
|
 * | 
|
 * This code is distributed in the hope that it will be useful, but WITHOUT | 
|
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | 
|
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License | 
|
 * version 2 for more details (a copy is included in the LICENSE file that | 
|
 * accompanied this code). | 
|
 * | 
|
 * You should have received a copy of the GNU General Public License version | 
|
 * 2 along with this work; if not, write to the Free Software Foundation, | 
|
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | 
|
 * | 
|
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | 
|
 * or visit www.oracle.com if you need additional information or have any | 
|
 * questions. | 
|
*/  | 
|
package java.beans;  | 
|
import com.sun.beans.decoder.DocumentHandler;  | 
|
import java.io.Closeable;  | 
|
import java.io.InputStream;  | 
|
import java.io.IOException;  | 
|
import java.security.AccessControlContext;  | 
|
import java.security.AccessController;  | 
|
import java.security.PrivilegedAction;  | 
|
import org.xml.sax.InputSource;  | 
|
import org.xml.sax.helpers.DefaultHandler;  | 
|
/** | 
|
 * The <code>XMLDecoder</code> class is used to read XML documents | 
|
 * created using the <code>XMLEncoder</code> and is used just like | 
|
 * the <code>ObjectInputStream</code>. For example, one can use | 
|
 * the following fragment to read the first object defined | 
|
 * in an XML document written by the <code>XMLEncoder</code> | 
|
 * class: | 
|
 * <pre> | 
|
 *       XMLDecoder d = new XMLDecoder( | 
|
 *                          new BufferedInputStream( | 
|
 *                              new FileInputStream("Test.xml"))); | 
|
 *       Object result = d.readObject(); | 
|
 *       d.close(); | 
|
 * </pre> | 
|
 * | 
|
 *<p> | 
|
 * For more information you might also want to check out | 
|
 * <a | 
|
 href="http://java.sun.com/products/jfc/tsc/articles/persistence3">Long Term Persistence of JavaBeans Components: XML Schema</a>, | 
|
 * an article in <em>The Swing Connection.</em> | 
|
 * @see XMLEncoder | 
|
 * @see java.io.ObjectInputStream | 
|
 * | 
|
 * @since 1.4 | 
|
 * | 
|
 * @author Philip Milne | 
|
*/  | 
|
public class XMLDecoder implements AutoCloseable {  | 
|
private final AccessControlContext acc = AccessController.getContext();  | 
|
private final DocumentHandler handler = new DocumentHandler();  | 
|
private final InputSource input;  | 
|
private Object owner;  | 
|
private Object[] array;  | 
|
private int index;  | 
|
    /** | 
|
     * Creates a new input stream for reading archives | 
|
     * created by the <code>XMLEncoder</code> class. | 
|
     * | 
|
     * @param in The underlying stream. | 
|
     * | 
|
     * @see XMLEncoder#XMLEncoder(java.io.OutputStream) | 
|
*/  | 
|
public XMLDecoder(InputStream in) {  | 
|
this(in, null);  | 
|
}  | 
|
    /** | 
|
     * Creates a new input stream for reading archives | 
|
     * created by the <code>XMLEncoder</code> class. | 
|
     * | 
|
     * @param in The underlying stream. | 
|
     * @param owner The owner of this stream. | 
|
     * | 
|
*/  | 
|
public XMLDecoder(InputStream in, Object owner) {  | 
|
this(in, owner, null);  | 
|
}  | 
|
    /** | 
|
     * Creates a new input stream for reading archives | 
|
     * created by the <code>XMLEncoder</code> class. | 
|
     * | 
|
     * @param in the underlying stream. | 
|
     * @param owner the owner of this stream. | 
|
     * @param exceptionListener the exception handler for the stream; | 
|
     *        if <code>null</code> the default exception listener will be used. | 
|
*/  | 
|
public XMLDecoder(InputStream in, Object owner, ExceptionListener exceptionListener) {  | 
|
this(in, owner, exceptionListener, null);  | 
|
}  | 
|
    /** | 
|
     * Creates a new input stream for reading archives | 
|
     * created by the <code>XMLEncoder</code> class. | 
|
     * | 
|
     * @param in the underlying stream.  <code>null</code> may be passed without | 
|
     *        error, though the resulting XMLDecoder will be useless | 
|
     * @param owner the owner of this stream.  <code>null</code> is a legal | 
|
     *        value | 
|
     * @param exceptionListener the exception handler for the stream, or | 
|
     *        <code>null</code> to use the default | 
|
     * @param cl the class loader used for instantiating objects. | 
|
     *        <code>null</code> indicates that the default class loader should | 
|
     *        be used | 
|
     * @since 1.5 | 
|
*/  | 
|
public XMLDecoder(InputStream in, Object owner,  | 
|
ExceptionListener exceptionListener, ClassLoader cl) {  | 
|
this(new InputSource(in), owner, exceptionListener, cl);  | 
|
}  | 
|
    /** | 
|
     * Creates a new decoder to parse XML archives | 
|
     * created by the {@code XMLEncoder} class. | 
|
     * If the input source {@code is} is {@code null}, | 
|
     * no exception is thrown and no parsing is performed. | 
|
     * This behavior is similar to behavior of other constructors | 
|
     * that use {@code InputStream} as a parameter. | 
|
     * | 
|
     * @param is  the input source to parse | 
|
     * | 
|
     * @since 1.7 | 
|
*/  | 
|
    public XMLDecoder(InputSource is) { | 
|
this(is, null, null, null);  | 
|
}  | 
|
    /** | 
|
     * Creates a new decoder to parse XML archives | 
|
     * created by the {@code XMLEncoder} class. | 
|
     * | 
|
     * @param is     the input source to parse | 
|
     * @param owner  the owner of this decoder | 
|
     * @param el     the exception handler for the parser, | 
|
     *               or {@code null} to use the default exception handler | 
|
     * @param cl     the class loader used for instantiating objects, | 
|
     *               or {@code null} to use the default class loader | 
|
     * | 
|
     * @since 1.7 | 
|
*/  | 
|
private XMLDecoder(InputSource is, Object owner, ExceptionListener el, ClassLoader cl) {  | 
|
this.input = is;  | 
|
this.owner = owner;  | 
|
setExceptionListener(el);  | 
|
this.handler.setClassLoader(cl);  | 
|
this.handler.setOwner(this);  | 
|
}  | 
|
    /** | 
|
     * This method closes the input stream associated | 
|
     * with this stream. | 
|
*/  | 
|
    public void close() { | 
|
if (parsingComplete()) {  | 
|
close(this.input.getCharacterStream());  | 
|
close(this.input.getByteStream());  | 
|
}  | 
|
}  | 
|
private void close(Closeable in) {  | 
|
if (in != null) {  | 
|
            try { | 
|
in.close();  | 
|
}  | 
|
catch (IOException e) {  | 
|
getExceptionListener().exceptionThrown(e);  | 
|
}  | 
|
}  | 
|
}  | 
|
    private boolean parsingComplete() { | 
|
        if (this.input == null) { | 
|
return false;  | 
|
}  | 
|
        if (this.array == null) { | 
|
if ((this.acc == null) && (null != System.getSecurityManager())) {  | 
|
throw new SecurityException("AccessControlContext is not set");  | 
|
}  | 
|
AccessController.doPrivileged(new PrivilegedAction<Void>() {  | 
|
public Void run() {  | 
|
XMLDecoder.this.handler.parse(XMLDecoder.this.input);  | 
|
return null;  | 
|
}  | 
|
}, this.acc);  | 
|
this.array = this.handler.getObjects();  | 
|
}  | 
|
return true;  | 
|
}  | 
|
    /** | 
|
     * Sets the exception handler for this stream to <code>exceptionListener</code>. | 
|
     * The exception handler is notified when this stream catches recoverable | 
|
     * exceptions. | 
|
     * | 
|
     * @param exceptionListener The exception handler for this stream; | 
|
     * if <code>null</code> the default exception listener will be used. | 
|
     * | 
|
     * @see #getExceptionListener | 
|
*/  | 
|
public void setExceptionListener(ExceptionListener exceptionListener) {  | 
|
if (exceptionListener == null) {  | 
|
exceptionListener = Statement.defaultExceptionListener;  | 
|
}  | 
|
this.handler.setExceptionListener(exceptionListener);  | 
|
}  | 
|
    /** | 
|
     * Gets the exception handler for this stream. | 
|
     * | 
|
     * @return The exception handler for this stream. | 
|
     *     Will return the default exception listener if this has not explicitly been set. | 
|
     * | 
|
     * @see #setExceptionListener | 
|
*/  | 
|
public ExceptionListener getExceptionListener() {  | 
|
return this.handler.getExceptionListener();  | 
|
}  | 
|
    /** | 
|
     * Reads the next object from the underlying input stream. | 
|
     * | 
|
     * @return the next object read | 
|
     * | 
|
     * @throws ArrayIndexOutOfBoundsException if the stream contains no objects | 
|
     *         (or no more objects) | 
|
     * | 
|
     * @see XMLEncoder#writeObject | 
|
*/  | 
|
public Object readObject() {  | 
|
return (parsingComplete())  | 
|
? this.array[this.index++]  | 
|
: null;  | 
|
}  | 
|
    /** | 
|
     * Sets the owner of this decoder to <code>owner</code>. | 
|
     * | 
|
     * @param owner The owner of this decoder. | 
|
     * | 
|
     * @see #getOwner | 
|
*/  | 
|
public void setOwner(Object owner) {  | 
|
this.owner = owner;  | 
|
}  | 
|
    /** | 
|
     * Gets the owner of this decoder. | 
|
     * | 
|
     * @return The owner of this decoder. | 
|
     * | 
|
     * @see #setOwner | 
|
*/  | 
|
public Object getOwner() {  | 
|
return owner;  | 
|
}  | 
|
    /** | 
|
     * Creates a new handler for SAX parser | 
|
     * that can be used to parse embedded XML archives | 
|
     * created by the {@code XMLEncoder} class. | 
|
     * | 
|
     * The {@code owner} should be used if parsed XML document contains | 
|
     * the method call within context of the <java> element. | 
|
     * The {@code null} value may cause illegal parsing in such case. | 
|
     * The same problem may occur, if the {@code owner} class | 
|
     * does not contain expected method to call. See details <a | 
|
     * href="http://java.sun.com/products/jfc/tsc/articles/persistence3/">here</a>. | 
|
     * | 
|
     * @param owner  the owner of the default handler | 
|
     *               that can be used as a value of <java> element | 
|
     * @param el     the exception handler for the parser, | 
|
     *               or {@code null} to use the default exception handler | 
|
     * @param cl     the class loader used for instantiating objects, | 
|
     *               or {@code null} to use the default class loader | 
|
     * @return an instance of {@code DefaultHandler} for SAX parser | 
|
     * | 
|
     * @since 1.7 | 
|
*/  | 
|
public static DefaultHandler createHandler(Object owner, ExceptionListener el, ClassLoader cl) {  | 
|
DocumentHandler handler = new DocumentHandler();  | 
|
handler.setOwner(owner);  | 
|
handler.setExceptionListener(el);  | 
|
handler.setClassLoader(cl);  | 
|
return handler;  | 
|
}  | 
|
}  |