1 /* gnu.java.beans.PersistenceParser 2 Copyright (C) 2004, 2005 Free Software Foundation, Inc. 3 4 This file is part of GNU Classpath. 5 6 GNU Classpath is free software; you can redistribute it and/or modify 7 it under the terms of the GNU General Public License as published by 8 the Free Software Foundation; either version 2, or (at your option) 9 any later version. 10 11 GNU Classpath is distributed in the hope that it will be useful, but 12 WITHOUT ANY WARRANTY; without even the implied warranty of 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 General Public License for more details. 15 16 You should have received a copy of the GNU General Public License 17 along with GNU Classpath; see the file COPYING. If not, write to the 18 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 19 02110-1301 USA. 20 21 Linking this library statically or dynamically with other modules is 22 making a combined work based on this library. Thus, the terms and 23 conditions of the GNU General Public License cover the whole 24 combination. 25 26 As a special exception, the copyright holders of this library give you 27 permission to link this library with independent modules to produce an 28 executable, regardless of the license terms of these independent 29 modules, and to copy and distribute the resulting executable under 30 terms of your choice, provided that you also meet, for each linked 31 independent module, the terms and conditions of the license of that 32 module. An independent module is a module which is not derived from 33 or based on this library. If you modify this library, you may extend 34 this exception to your version of the library, but you are not 35 obligated to do so. If you do not wish to do so, delete this 36 exception statement from your version. */ 37 38 package gnu.java.beans.decoder; 39 40 import java.beans.ExceptionListener; 41 import java.beans.XMLDecoder; 42 import java.io.IOException; 43 import java.io.InputStream; 44 import java.util.HashMap; 45 import java.util.Iterator; 46 import java.util.LinkedList; 47 import java.util.List; 48 49 import javax.xml.parsers.ParserConfigurationException; 50 import javax.xml.parsers.SAXParser; 51 import javax.xml.parsers.SAXParserFactory; 52 53 import org.xml.sax.Attributes; 54 import org.xml.sax.SAXException; 55 import org.xml.sax.helpers.DefaultHandler; 56 57 /** The PersistenceParser parses an XML data stream and delegates actions to ElementHandler 58 * instances. The parser catches and recovers from all exception which reside from wrong usage 59 * of attributes and tags. 60 * 61 * @author Robert Schuster 62 */ 63 public class PersistenceParser extends DefaultHandler implements Context 64 { 65 /** The ExceptionListener instance which is informed of non-critical parsing exceptions. 66 */ 67 private ExceptionListener exceptionListener; 68 69 /** When an element was not usable all elements inside it should be skipped. 70 * This is done by skipping startElement() and endElement() invocations whenever 71 * this value is above 0. 72 */ 73 private int skipElement; 74 75 /** Stores the Creator instances which can instantiate the appropriate handler implementation 76 * for a given element. 77 */ 78 private HashMap handlerCreators = new HashMap(); 79 80 /** Denotes the current ElementHandler. To avoid checking for null-values it is pre-assigned 81 * with a DummyHandler instance which must not be used but acts as a root element. 82 */ 83 private ElementHandler currentHandler; 84 85 /** The real root element that stores all objects created during parsing. 86 * Package-private to avoid an accessor method. 87 */ 88 JavaHandler javaHandler; 89 90 /** Stores the decoded objects. */ 91 private List objects = new LinkedList(); 92 93 /** The XMLDecoder instance that started this PersistenceParser */ 94 private XMLDecoder decoder; 95 96 /** Creates a PersistenceParser which reads XML data from the given InputStream, reports 97 * exceptions to ExceptionListener instance, stores resulting object in the DecoderContext 98 * and uses the given ClassLoader to resolve classes. 99 * 100 * @param inputStream 101 * @param exceptionListener 102 * @param decoderContext 103 * @param cl 104 */ PersistenceParser( InputStream inputStream, ExceptionListener exceptionListener, ClassLoader cl, XMLDecoder decoder)105 public PersistenceParser( 106 InputStream inputStream, 107 ExceptionListener exceptionListener, 108 ClassLoader cl, 109 XMLDecoder decoder) 110 { 111 112 this.exceptionListener = exceptionListener; 113 this.decoder = decoder; 114 115 DummyHandler dummyHandler = new DummyHandler(); 116 currentHandler = dummyHandler; 117 javaHandler = new JavaHandler(dummyHandler, this, cl); 118 119 SAXParserFactory factory = SAXParserFactory.newInstance(); 120 121 SAXParser parser; 122 try 123 { 124 parser = factory.newSAXParser(); 125 } 126 catch (ParserConfigurationException pce) 127 { 128 // should not happen when a parser is available because we did 129 // not request any requirements on the XML parser 130 throw (InternalError) new InternalError( 131 "No SAX Parser available.").initCause( 132 pce); 133 } 134 catch (SAXException saxe) 135 { 136 // should not happen when a parser is available because we did 137 // not request any requirements on the XML parser 138 throw (InternalError) new InternalError( 139 "No SAX Parser available.").initCause( 140 saxe); 141 } 142 143 // prepares a map of Creator instances which can instantiate a handler which is 144 // appropriate for the tag that is used as a key for the Creator 145 handlerCreators.put("java", new JavaHandlerCreator()); 146 147 // calls methods (properties), constructors, access fields 148 handlerCreators.put("object", new ObjectHandlerCreator()); 149 handlerCreators.put("void", new VoidHandlerCreator()); 150 151 handlerCreators.put("array", new ArrayHandlerCreator()); 152 153 // these handler directly create an Object (or null) 154 handlerCreators.put("class", new ClassHandlerCreator()); 155 handlerCreators.put("null", new NullHandlerCreator()); 156 157 handlerCreators.put("char", new CharHandlerCreator()); 158 handlerCreators.put("string", new StringHandlerCreator()); 159 handlerCreators.put("boolean", new BooleanHandlerCreator()); 160 handlerCreators.put("byte", new ByteHandlerCreator()); 161 handlerCreators.put("short", new ShortHandlerCreator()); 162 handlerCreators.put("int", new IntHandlerCreator()); 163 handlerCreators.put("long", new LongHandlerCreator()); 164 handlerCreators.put("float", new FloatHandlerCreator()); 165 handlerCreators.put("double", new DoubleHandlerCreator()); 166 167 // parses the data and sends all exceptions to the ExceptionListener 168 try 169 { 170 parser.parse(inputStream, this); 171 } 172 catch (SAXException saxe) 173 { 174 exceptionListener.exceptionThrown( 175 new IllegalArgumentException("XML data not well-formed.")); 176 } 177 catch (IOException ioe) 178 { 179 exceptionListener.exceptionThrown(ioe); 180 } 181 } 182 startElement( String uri, String localName, String qName, Attributes attributes)183 public void startElement( 184 String uri, 185 String localName, 186 String qName, 187 Attributes attributes) 188 throws SAXException 189 { 190 /* The element is skipped if 191 * a) the current handler has already failed or a previous error occured 192 * which makes all children obsolete 193 */ 194 if (currentHandler.hasFailed() || skipElement > 0) 195 { 196 exceptionListener.exceptionThrown( 197 new IllegalArgumentException( 198 "Element unusable due to previous error: " + qName)); 199 200 skipElement++; 201 202 return; 203 } 204 205 /* b) Subelements are not allowed within the current ElementHandler. 206 */ 207 if (!currentHandler.isSubelementAllowed(qName)) 208 { 209 exceptionListener.exceptionThrown( 210 new IllegalArgumentException( 211 "Element is not allowed here: " + qName)); 212 213 skipElement++; 214 215 return; 216 } 217 218 /* c) The tag name is not a key in the map of Creator instances. This means that 219 * either the XML data is of a newer version or simply contains a miss-spelled element. 220 */ 221 if (!handlerCreators.containsKey(qName)) 222 { 223 exceptionListener.exceptionThrown( 224 new IllegalArgumentException( 225 "Element unusable because tag is unknown: " + qName)); 226 227 skipElement++; 228 229 return; 230 } 231 232 // creates a new handler for the new element 233 AbstractElementHandler handler = 234 ((Creator) handlerCreators.get(qName)).createHandler( 235 currentHandler); 236 237 // makes it the current handler to receive character data 238 currentHandler = handler; 239 240 // starts the handler 241 currentHandler.start(attributes, exceptionListener); 242 } 243 endElement(String uri, String localName, String qName)244 public void endElement(String uri, String localName, String qName) 245 throws SAXException 246 { 247 // skips processing the current handler if we are parsing an element 248 // which was marked invalid (in startElement() ) 249 if (skipElement > 0) 250 { 251 skipElement--; 252 return; 253 } 254 255 // invokes the handler's finishing method 256 currentHandler.end(exceptionListener); 257 258 // removes the current handler and reactivates its parent 259 currentHandler = currentHandler.getParent(); 260 } 261 262 /** Transfers character data to the current handler 263 */ characters(char[] ch, int start, int length)264 public void characters(char[] ch, int start, int length) 265 throws SAXException 266 { 267 // prevents sending character data of invalid elements 268 if (skipElement > 0) 269 return; 270 271 currentHandler.characters(ch, start, length); 272 } 273 274 /** Creator interface provided a mechanism to instantiate ElementHandler instances 275 * for the appropriate tag. 276 * 277 * @author Robert Schuster 278 */ 279 interface Creator 280 { 281 /** Creates an ElementHandler instance using the given ElementHandler as its parent. 282 * 283 * @param parent The parent ElementHandler of the result. 284 * @return A new ElementHandler instance. 285 */ createHandler(ElementHandler parent)286 AbstractElementHandler createHandler(ElementHandler parent); 287 } 288 289 class BooleanHandlerCreator implements Creator 290 { createHandler(ElementHandler parent)291 public AbstractElementHandler createHandler(ElementHandler parent) 292 { 293 return new BooleanHandler(parent); 294 } 295 } 296 297 class ByteHandlerCreator implements Creator 298 { createHandler(ElementHandler parent)299 public AbstractElementHandler createHandler(ElementHandler parent) 300 { 301 return new ByteHandler(parent); 302 } 303 } 304 305 class ShortHandlerCreator implements Creator 306 { createHandler(ElementHandler parent)307 public AbstractElementHandler createHandler(ElementHandler parent) 308 { 309 return new ShortHandler(parent); 310 } 311 } 312 313 class IntHandlerCreator implements Creator 314 { createHandler(ElementHandler parent)315 public AbstractElementHandler createHandler(ElementHandler parent) 316 { 317 return new IntHandler(parent); 318 } 319 } 320 321 class LongHandlerCreator implements Creator 322 { createHandler(ElementHandler parent)323 public AbstractElementHandler createHandler(ElementHandler parent) 324 { 325 return new LongHandler(parent); 326 } 327 } 328 329 class FloatHandlerCreator implements Creator 330 { createHandler(ElementHandler parent)331 public AbstractElementHandler createHandler(ElementHandler parent) 332 { 333 return new FloatHandler(parent); 334 } 335 } 336 337 class DoubleHandlerCreator implements Creator 338 { createHandler(ElementHandler parent)339 public AbstractElementHandler createHandler(ElementHandler parent) 340 { 341 return new DoubleHandler(parent); 342 } 343 } 344 345 class CharHandlerCreator implements Creator 346 { createHandler(ElementHandler parent)347 public AbstractElementHandler createHandler(ElementHandler parent) 348 { 349 return new CharHandler(parent); 350 } 351 } 352 353 class StringHandlerCreator implements Creator 354 { createHandler(ElementHandler parent)355 public AbstractElementHandler createHandler(ElementHandler parent) 356 { 357 return new StringHandler(parent); 358 } 359 } 360 361 class JavaHandlerCreator implements Creator 362 { createHandler(ElementHandler parent)363 public AbstractElementHandler createHandler(ElementHandler parent) 364 { 365 return javaHandler; 366 } 367 } 368 369 class ObjectHandlerCreator implements Creator 370 { createHandler(ElementHandler parent)371 public AbstractElementHandler createHandler(ElementHandler parent) 372 { 373 return new ObjectHandler(parent); 374 } 375 } 376 377 class VoidHandlerCreator implements Creator 378 { createHandler(ElementHandler parent)379 public AbstractElementHandler createHandler(ElementHandler parent) 380 { 381 return new VoidHandler(parent); 382 } 383 } 384 385 class ClassHandlerCreator implements Creator 386 { createHandler(ElementHandler parent)387 public AbstractElementHandler createHandler(ElementHandler parent) 388 { 389 return new ClassHandler(parent); 390 } 391 } 392 393 class NullHandlerCreator implements Creator 394 { createHandler(ElementHandler parent)395 public AbstractElementHandler createHandler(ElementHandler parent) 396 { 397 return new NullHandler(parent); 398 } 399 } 400 401 class ArrayHandlerCreator implements Creator 402 { createHandler(ElementHandler parent)403 public AbstractElementHandler createHandler(ElementHandler parent) 404 { 405 return new ArrayHandler(parent); 406 } 407 } 408 409 /** Adds a decoded object to the Context. */ addParameterObject(Object o)410 public void addParameterObject(Object o) throws AssemblyException 411 { 412 objects.add(o); 413 } 414 notifyStatement(Context outerContext)415 public void notifyStatement(Context outerContext) throws AssemblyException 416 { 417 // can be ignored because theis Context does not react to statement and expressions 418 // differently 419 } 420 endContext(Context outerContext)421 public Object endContext(Context outerContext) throws AssemblyException 422 { 423 return null; 424 } 425 subContextFailed()426 public boolean subContextFailed() 427 { 428 // failing of subcontexts is no problem for the mother of all contexts 429 return false; 430 } 431 set(int index, Object o)432 public void set(int index, Object o) throws AssemblyException 433 { 434 // not supported 435 throw new AssemblyException( 436 new IllegalArgumentException("Set method is not allowed in decoder context.")); 437 } 438 get(int index)439 public Object get(int index) throws AssemblyException 440 { 441 // not supported 442 throw new AssemblyException( 443 new IllegalArgumentException("Get method is not allowed in decoder context.")); 444 } 445 getResult()446 public Object getResult() 447 { 448 // returns the XMLDecoder instance which is requested by child contexts this way. 449 // That is needed to invoke methods on the decoder. 450 return decoder; 451 } 452 setId(String id)453 public void setId(String id) 454 { 455 exceptionListener.exceptionThrown(new IllegalArgumentException("id attribute is not allowed for <java> tag.")); 456 } 457 getId()458 public String getId() 459 { 460 // appears to have no id 461 return null; 462 } 463 isStatement()464 public boolean isStatement() 465 { 466 // this context is a statement by definition because it never returns anything to a parent because 467 // there is no such parent (DummyContext does not count!) 468 return true; 469 } 470 setStatement(boolean b)471 public void setStatement(boolean b) 472 { 473 // ignores that because this Context is always a statement 474 } 475 476 /** Returns an Iterator instance which returns the decoded objects. 477 * 478 * This method is used by the XMLDecoder directly. 479 */ iterator()480 public Iterator iterator() 481 { 482 return objects.iterator(); 483 } 484 485 } 486