1 /* 2 * Copyright (c) 2008, 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 com.sun.beans.decoder; 26 27 import com.sun.beans.finder.ClassFinder; 28 29 import java.beans.ExceptionListener; 30 31 import java.io.IOException; 32 import java.io.StringReader; 33 34 import java.lang.ref.Reference; 35 import java.lang.ref.WeakReference; 36 37 import java.util.ArrayList; 38 import java.util.HashMap; 39 import java.util.List; 40 import java.util.Map; 41 import java.security.AccessControlContext; 42 import java.security.AccessController; 43 import java.security.PrivilegedAction; 44 45 import javax.xml.parsers.ParserConfigurationException; 46 import javax.xml.parsers.SAXParserFactory; 47 48 import org.xml.sax.Attributes; 49 import org.xml.sax.InputSource; 50 import org.xml.sax.SAXException; 51 import org.xml.sax.helpers.DefaultHandler; 52 53 import jdk.internal.misc.SharedSecrets; 54 55 /** 56 * The main class to parse JavaBeans XML archive. 57 * 58 * @since 1.7 59 * 60 * @author Sergey A. Malenkov 61 * 62 * @see ElementHandler 63 */ 64 public final class DocumentHandler extends DefaultHandler { 65 private final AccessControlContext acc = AccessController.getContext(); 66 private final Map<String, Class<? extends ElementHandler>> handlers = new HashMap<>(); 67 private final Map<String, Object> environment = new HashMap<>(); 68 private final List<Object> objects = new ArrayList<>(); 69 70 private Reference<ClassLoader> loader; 71 private ExceptionListener listener; 72 private Object owner; 73 74 private ElementHandler handler; 75 76 /** 77 * Creates new instance of document handler. 78 */ 79 public DocumentHandler() { 80 setElementHandler("java", JavaElementHandler.class); // NON-NLS: the element name 81 setElementHandler("null", NullElementHandler.class); // NON-NLS: the element name 82 setElementHandler("array", ArrayElementHandler.class); // NON-NLS: the element name 83 setElementHandler("class", ClassElementHandler.class); // NON-NLS: the element name 84 setElementHandler("string", StringElementHandler.class); // NON-NLS: the element name 85 setElementHandler("object", ObjectElementHandler.class); // NON-NLS: the element name 86 87 setElementHandler("void", VoidElementHandler.class); // NON-NLS: the element name 88 setElementHandler("char", CharElementHandler.class); // NON-NLS: the element name 89 setElementHandler("byte", ByteElementHandler.class); // NON-NLS: the element name 90 setElementHandler("short", ShortElementHandler.class); // NON-NLS: the element name 91 setElementHandler("int", IntElementHandler.class); // NON-NLS: the element name 92 setElementHandler("long", LongElementHandler.class); // NON-NLS: the element name 93 setElementHandler("float", FloatElementHandler.class); // NON-NLS: the element name 94 setElementHandler("double", DoubleElementHandler.class); // NON-NLS: the element name 95 setElementHandler("boolean", BooleanElementHandler.class); // NON-NLS: the element name 96 97 // some handlers for new elements 98 setElementHandler("new", NewElementHandler.class); // NON-NLS: the element name 99 setElementHandler("var", VarElementHandler.class); // NON-NLS: the element name 100 setElementHandler("true", TrueElementHandler.class); // NON-NLS: the element name 101 setElementHandler("false", FalseElementHandler.class); // NON-NLS: the element name 102 setElementHandler("field", FieldElementHandler.class); // NON-NLS: the element name 103 setElementHandler("method", MethodElementHandler.class); // NON-NLS: the element name 104 setElementHandler("property", PropertyElementHandler.class); // NON-NLS: the element name 105 } 106 107 /** 108 * Returns the class loader used to instantiate objects. 109 * If the class loader has not been explicitly set 110 * then {@code null} is returned. 111 * 112 * @return the class loader used to instantiate objects 113 */ 114 public ClassLoader getClassLoader() { 115 return (this.loader != null) 116 ? this.loader.get() 117 : null; 118 } 119 120 /** 121 * Sets the class loader used to instantiate objects. 122 * If the class loader is not set 123 * then default class loader will be used. 124 * 125 * @param loader a classloader to use 126 */ 127 public void setClassLoader(ClassLoader loader) { 128 this.loader = new WeakReference<ClassLoader>(loader); 129 } 130 131 /** 132 * Returns the exception listener for parsing. 133 * The exception listener is notified 134 * when handler catches recoverable exceptions. 135 * If the exception listener has not been explicitly set 136 * then default exception listener is returned. 137 * 138 * @return the exception listener for parsing 139 */ 140 public ExceptionListener getExceptionListener() { 141 return this.listener; 142 } 143 144 /** 145 * Sets the exception listener for parsing. 146 * The exception listener is notified 147 * when handler catches recoverable exceptions. 148 * 149 * @param listener the exception listener for parsing 150 */ 151 public void setExceptionListener(ExceptionListener listener) { 152 this.listener = listener; 153 } 154 155 /** 156 * Returns the owner of this document handler. 157 * 158 * @return the owner of this document handler 159 */ 160 public Object getOwner() { 161 return this.owner; 162 } 163 164 /** 165 * Sets the owner of this document handler. 166 * 167 * @param owner the owner of this document handler 168 */ 169 public void setOwner(Object owner) { 170 this.owner = owner; 171 } 172 173 /** 174 * Returns the handler for the element with specified name. 175 * 176 * @param name the name of the element 177 * @return the corresponding element handler 178 */ 179 public Class<? extends ElementHandler> getElementHandler(String name) { 180 Class<? extends ElementHandler> type = this.handlers.get(name); 181 if (type == null) { 182 throw new IllegalArgumentException("Unsupported element: " + name); 183 } 184 return type; 185 } 186 187 /** 188 * Sets the handler for the element with specified name. 189 * 190 * @param name the name of the element 191 * @param handler the corresponding element handler 192 */ 193 public void setElementHandler(String name, Class<? extends ElementHandler> handler) { 194 this.handlers.put(name, handler); 195 } 196 197 /** 198 * Indicates whether the variable with specified identifier is defined. 199 * 200 * @param id the identifier 201 * @return @{code true} if the variable is defined; 202 * @{code false} otherwise 203 */ 204 public boolean hasVariable(String id) { 205 return this.environment.containsKey(id); 206 } 207 208 /** 209 * Returns the value of the variable with specified identifier. 210 * 211 * @param id the identifier 212 * @return the value of the variable 213 */ 214 public Object getVariable(String id) { 215 if (!this.environment.containsKey(id)) { 216 throw new IllegalArgumentException("Unbound variable: " + id); 217 } 218 return this.environment.get(id); 219 } 220 221 /** 222 * Sets new value of the variable with specified identifier. 223 * 224 * @param id the identifier 225 * @param value new value of the variable 226 */ 227 public void setVariable(String id, Object value) { 228 this.environment.put(id, value); 229 } 230 231 /** 232 * Returns the array of readed objects. 233 * 234 * @return the array of readed objects 235 */ 236 public Object[] getObjects() { 237 return this.objects.toArray(); 238 } 239 240 /** 241 * Adds the object to the list of readed objects. 242 * 243 * @param object the object that is readed from XML document 244 */ 245 void addObject(Object object) { 246 this.objects.add(object); 247 } 248 249 /** 250 * Disables any external entities. 251 */ 252 @Override 253 public InputSource resolveEntity(String publicId, String systemId) { 254 return new InputSource(new StringReader("")); 255 } 256 257 /** 258 * Prepares this handler to read objects from XML document. 259 */ 260 @Override 261 public void startDocument() { 262 this.objects.clear(); 263 this.handler = null; 264 } 265 266 /** 267 * Parses opening tag of XML element 268 * using corresponding element handler. 269 * 270 * @param uri the namespace URI, or the empty string 271 * if the element has no namespace URI or 272 * if namespace processing is not being performed 273 * @param localName the local name (without prefix), or the empty string 274 * if namespace processing is not being performed 275 * @param qName the qualified name (with prefix), or the empty string 276 * if qualified names are not available 277 * @param attributes the attributes attached to the element 278 */ 279 @Override 280 public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { 281 ElementHandler parent = this.handler; 282 try { 283 this.handler = 284 getElementHandler(qName).getDeclaredConstructor().newInstance(); 285 this.handler.setOwner(this); 286 this.handler.setParent(parent); 287 } 288 catch (Exception exception) { 289 throw new SAXException(exception); 290 } 291 for (int i = 0; i < attributes.getLength(); i++) 292 try { 293 String name = attributes.getQName(i); 294 String value = attributes.getValue(i); 295 this.handler.addAttribute(name, value); 296 } 297 catch (RuntimeException exception) { 298 handleException(exception); 299 } 300 301 this.handler.startElement(); 302 } 303 304 /** 305 * Parses closing tag of XML element 306 * using corresponding element handler. 307 * 308 * @param uri the namespace URI, or the empty string 309 * if the element has no namespace URI or 310 * if namespace processing is not being performed 311 * @param localName the local name (without prefix), or the empty string 312 * if namespace processing is not being performed 313 * @param qName the qualified name (with prefix), or the empty string 314 * if qualified names are not available 315 */ 316 @Override 317 public void endElement(String uri, String localName, String qName) { 318 try { 319 this.handler.endElement(); 320 } 321 catch (RuntimeException exception) { 322 handleException(exception); 323 } 324 finally { 325 this.handler = this.handler.getParent(); 326 } 327 } 328 329 /** 330 * Parses character data inside XML element. 331 * 332 * @param chars the array of characters 333 * @param start the start position in the character array 334 * @param length the number of characters to use 335 */ 336 @Override 337 public void characters(char[] chars, int start, int length) { 338 if (this.handler != null) { 339 try { 340 while (0 < length--) { 341 this.handler.addCharacter(chars[start++]); 342 } 343 } 344 catch (RuntimeException exception) { 345 handleException(exception); 346 } 347 } 348 } 349 350 /** 351 * Handles an exception using current exception listener. 352 * 353 * @param exception an exception to handle 354 * @see #setExceptionListener 355 */ 356 public void handleException(Exception exception) { 357 if (this.listener == null) { 358 throw new IllegalStateException(exception); 359 } 360 this.listener.exceptionThrown(exception); 361 } 362 363 /** 364 * Starts parsing of the specified input source. 365 * 366 * @param input the input source to parse 367 */ 368 public void parse(final InputSource input) { 369 if ((this.acc == null) && (null != System.getSecurityManager())) { 370 throw new SecurityException("AccessControlContext is not set"); 371 } 372 AccessControlContext stack = AccessController.getContext(); 373 SharedSecrets.getJavaSecurityAccess().doIntersectionPrivilege(new PrivilegedAction<Void>() { 374 public Void run() { 375 try { 376 SAXParserFactory.newInstance().newSAXParser().parse(input, DocumentHandler.this); 377 } 378 catch (ParserConfigurationException exception) { 379 handleException(exception); 380 } 381 catch (SAXException wrapper) { 382 Exception exception = wrapper.getException(); 383 if (exception == null) { 384 exception = wrapper; 385 } 386 handleException(exception); 387 } 388 catch (IOException exception) { 389 handleException(exception); 390 } 391 return null; 392 } 393 }, stack, this.acc); 394 } 395 396 /** 397 * Resolves class by name using current class loader. 398 * This method handles exception using current exception listener. 399 * 400 * @param name the name of the class 401 * @return the object that represents the class 402 */ 403 public Class<?> findClass(String name) { 404 try { 405 return ClassFinder.resolveClass(name, getClassLoader()); 406 } 407 catch (ClassNotFoundException exception) { 408 handleException(exception); 409 return null; 410 } 411 } 412 } --- EOF ---