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 <java> 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 <java> 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 }