Coverage Report - org.simpleframework.xml.stream.StreamReader
 
Classes in this File Line Coverage Branch Coverage Complexity
StreamReader
94%
34/36
83%
15/18
1.5
StreamReader$1
N/A
N/A
1.5
StreamReader$End
100%
2/2
N/A
1.5
StreamReader$Entry
100%
9/9
N/A
1.5
StreamReader$Start
60%
6/10
N/A
1.5
StreamReader$Text
83%
5/6
N/A
1.5
 
 1  
 /*
 2  
  * StreamReader.java January 2010
 3  
  *
 4  
  * Copyright (C) 2010, Niall Gallagher <niallg@users.sf.net>
 5  
  *
 6  
  * Licensed under the Apache License, Version 2.0 (the "License");
 7  
  * you may not use this file except in compliance with the License.
 8  
  * You may obtain a copy of the License at
 9  
  *
 10  
  *     http://www.apache.org/licenses/LICENSE-2.0
 11  
  *
 12  
  * Unless required by applicable law or agreed to in writing, software
 13  
  * distributed under the License is distributed on an "AS IS" BASIS,
 14  
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 
 15  
  * implied. See the License for the specific language governing 
 16  
  * permissions and limitations under the License.
 17  
  */
 18  
 
 19  
 package org.simpleframework.xml.stream;
 20  
 
 21  
 import java.util.Iterator;
 22  
 
 23  
 import javax.xml.stream.Location;
 24  
 import javax.xml.stream.XMLEventReader;
 25  
 import javax.xml.stream.events.Attribute;
 26  
 import javax.xml.stream.events.Characters;
 27  
 import javax.xml.stream.events.StartElement;
 28  
 import javax.xml.stream.events.XMLEvent;
 29  
 
 30  
 /**
 31  
  * The <code>StreamReader</code> object provides an implementation
 32  
  * for reading XML events using StAX. This will pretty much wrap
 33  
  * core StAX events as the framework is very closely related. The
 34  
  * implementation is basically required to ensure StAX events can
 35  
  * be digested by the core reader. For performance this will match
 36  
  * the underlying implementation closely as all this basically
 37  
  * does is act as a means to adapt the underlying framework events.
 38  
  * 
 39  
  * @author Niall Gallagher
 40  
  * 
 41  
  * @see org.simpleframework.xml.stream.StreamProvider
 42  
  */
 43  
 class StreamReader implements EventReader {
 44  
    
 45  
    /**
 46  
     * This is the reader that is used to parse the XML document.
 47  
     */
 48  
    private XMLEventReader reader;
 49  
  
 50  
    /**
 51  
     * This is used to keep track of any events that were peeked.
 52  
     */
 53  
    private EventNode peek;
 54  
    
 55  
    /**
 56  
     * Constructor for the <code>StreamReader</code> object. This 
 57  
     * creates a reader that extracts events from the provided object.
 58  
     * All StAX events returned from the provided instance will be
 59  
     * adapted so that they can be digested by the core reader.
 60  
     * 
 61  
     * @param reader this is the reader used to parse the XML source
 62  
     */
 63  4
    public StreamReader(XMLEventReader reader) {
 64  4
       this.reader = reader;
 65  4
    }
 66  
 
 67  
    /**
 68  
     * This is used to peek at the node from the document. This will
 69  
     * scan through the document, ignoring any comments to find the
 70  
     * next relevant XML event to acquire. Typically events will be
 71  
     * the start and end of an element, as well as any text nodes.
 72  
     * 
 73  
     * @return this returns the next event taken from the document
 74  
     */
 75  
    public EventNode peek() throws Exception {
 76  180
       if(peek == null) {
 77  102
          peek = next();
 78  
       }
 79  180
       return peek;
 80  
    }   
 81  
    
 82  
    /**
 83  
     * This is used to take the next node from the document. This will
 84  
     * scan through the document, ignoring any comments to find the
 85  
     * next relevant XML event to acquire. Typically events will be
 86  
     * the start and end of an element, as well as any text nodes.
 87  
     * 
 88  
     * @return this returns the next event taken from the source XML
 89  
     */
 90  
    public EventNode next() throws Exception {
 91  238
       EventNode next = peek;
 92  
       
 93  238
       if(next == null) {
 94  136
          next = read();
 95  
       } else {
 96  102
          peek = null;
 97  
       }
 98  238
       return next;
 99  
    }
 100  
       
 101  
    /**
 102  
     * This is used to read the next node from the document. This will
 103  
     * scan through the document, ignoring any comments to find the
 104  
     * next relevant XML event to acquire. Typically events will be
 105  
     * the start and end of an element, as well as any text nodes.
 106  
     * 
 107  
     * @return this returns the next event taken from the document 
 108  
     */
 109  
    private EventNode read() throws Exception {
 110  140
       XMLEvent event = reader.nextEvent();
 111  
       
 112  140
       if(!event.isEndDocument()) {
 113  140
          if(event.isStartElement()) {
 114  38
             return start(event);
 115  
          } 
 116  102
          if(event.isCharacters()) {
 117  60
             return text(event);
 118  
          } 
 119  42
          if(event.isEndElement()) {
 120  38
             return end();
 121  
          }
 122  4
          return read();
 123  
       }
 124  0
       return null;
 125  
    }
 126  
    
 127  
    /**
 128  
     * This is used to convert the provided event to a start event. The
 129  
     * conversion process ensures the node can be digested by the core
 130  
     * reader and used to provide an <code>InputNode</code> that can
 131  
     * be used to represent an XML elements within the source document.
 132  
     * 
 133  
     * @param event the event that is to be converted to a start event
 134  
     *
 135  
     * @return this returns a start event created from the given event
 136  
     */
 137  
    private Start start(XMLEvent event) {
 138  38
       Start node = new Start(event);
 139  
       
 140  38
       if(node.isEmpty()) {
 141  38
          return build(node);
 142  
       }
 143  0
       return node;
 144  
    }
 145  
    
 146  
    /**
 147  
     * This is used to build the attributes that are to be used to 
 148  
     * populate the start event. Populating the start event with the
 149  
     * attributes it contains is required so that each element will
 150  
     * contain its associated attributes. Only attributes that are
 151  
     * not reserved will be added to the start event.
 152  
     * 
 153  
     * @param event this is the start event that is to be populated
 154  
     * 
 155  
     * @return this returns a start event with its attributes
 156  
     */
 157  
    private Start build(Start event) {
 158  38
       Iterator<Attribute> list = event.getAttributes();
 159  
 
 160  72
       while (list.hasNext()) {
 161  34
          Attribute node = list.next();
 162  34
          Entry entry = attribute(node);
 163  
          
 164  34
          if(!entry.isReserved()) {
 165  34
             event.add(entry);
 166  
          }
 167  34
       }
 168  38
       return event;
 169  
    }
 170  
    
 171  
    /**
 172  
     * This is used to convert the provided object to an attribute. The
 173  
     * conversion process ensures the node can be digested by the core
 174  
     * reader and used to provide an <code>InputNode</code> that can
 175  
     * be used to represent an XML attribute within the source document.
 176  
     * 
 177  
     * @param entry the object that is to be converted to an attribute
 178  
     *
 179  
     * @return this returns an attribute created from the given object
 180  
     */
 181  
    private Entry attribute(Attribute entry) {
 182  34
       return new Entry(entry);
 183  
    }
 184  
 
 185  
    /**
 186  
     * This is used to convert the provided event to a text event. The
 187  
     * conversion process ensures the node can be digested by the core
 188  
     * reader and used to provide an <code>InputNode</code> that can
 189  
     * be used to represent an XML attribute within the source document.
 190  
     * 
 191  
     * @param event the event that is to be converted to a text event
 192  
     *
 193  
     * @return this returns the text event created from the given event
 194  
     */
 195  
    private Text text(XMLEvent event) {
 196  60
       return new Text(event);
 197  
    }
 198  
    
 199  
    /**
 200  
     * This is used to create an event to signify that an element has
 201  
     * just ended. End events are important as they allow the core
 202  
     * reader to determine if a node is still in context. This provides
 203  
     * a more convenient way to use <code>InputNode</code> objects as
 204  
     * they should only ever be able to extract their children. 
 205  
     * 
 206  
     * @return this returns an end event to signify an element close
 207  
     */
 208  
    private End end() {
 209  38
       return new End();
 210  
    }
 211  
    
 212  
    /**
 213  
     * The <code>Entry</code> object is used to represent an attribute
 214  
     * within a start element. This holds the name and value of the
 215  
     * attribute as well as the namespace prefix and reference. These
 216  
     * details can be used to represent the attribute so that should
 217  
     * the core reader require these details they can be acquired.
 218  
     * 
 219  
     * @author Niall Gallagher
 220  
     */
 221  
    private static class Entry extends EventAttribute {
 222  
       
 223  
       /**
 224  
        * This is the attribute object representing this attribute.
 225  
        */
 226  
       private final Attribute entry;
 227  
       
 228  
       /**
 229  
        * Constructor for the <code>Entry</code> object. This creates
 230  
        * an attribute object that is used to extract the name, value
 231  
        * namespace prefix, and namespace reference from the provided
 232  
        * node. This is used to populate any start events created.
 233  
        * 
 234  
        * @param entry this is the node that represents the attribute
 235  
        */
 236  34
       public Entry(Attribute entry) {
 237  34
          this.entry = entry;
 238  34
       }
 239  
       
 240  
       /**
 241  
        * This provides the name of the attribute. This will be the
 242  
        * name of the XML attribute without any namespace prefix. If
 243  
        * the name begins with "xml" then this attribute is reserved.
 244  
        * according to the namespaces for XML 1.0 specification.
 245  
        * 
 246  
        * @return this returns the name of this attribute object
 247  
        */
 248  
       public String getName() {
 249  34
          return entry.getName().getLocalPart();
 250  
       }
 251  
       
 252  
       /**
 253  
        * This is used to acquire the namespace prefix associated with
 254  
        * this attribute. A prefix is used to qualify the attribute
 255  
        * within a namespace. So, if this has a prefix then it should
 256  
        * have a reference associated with it.
 257  
        * 
 258  
        * @return this returns the namespace prefix for the attribute
 259  
        */
 260  
       public String getPrefix() {
 261  34
          return entry.getName().getPrefix();
 262  
       }
 263  
       
 264  
       /**
 265  
        * This is used to acquire the namespace reference that this 
 266  
        * attribute is in. A namespace is normally associated with an
 267  
        * attribute if that attribute is prefixed with a known token.
 268  
        * If there is no prefix then this will return null.
 269  
        * 
 270  
        * @return this provides the associated namespace reference
 271  
        */
 272  
       public String getReference() {
 273  34
          return entry.getName().getNamespaceURI();
 274  
       }
 275  
       
 276  
       /**
 277  
        * This returns the value of the event. This will be the value
 278  
        * that the attribute contains. If the attribute does not have
 279  
        * a value then this returns null or an empty string.
 280  
        * 
 281  
        * @return this returns the value represented by this object
 282  
        */
 283  
       public String getValue() {
 284  34
          return entry.getValue();
 285  
       }
 286  
       
 287  
       /**
 288  
        * This returns true if the attribute is reserved. An attribute
 289  
        * is considered reserved if it begins with "xml" according to 
 290  
        * the namespaces in XML 1.0 specification. Such attributes are
 291  
        * used for namespaces and other such details.
 292  
        *
 293  
        * @return this returns true if the attribute is reserved
 294  
        */
 295  
       public boolean isReserved() {
 296  68
          return false;
 297  
       }
 298  
       
 299  
       /**
 300  
        * This is used to return the node for the attribute. Because 
 301  
        * this represents a StAX attribute the StAX object is returned.
 302  
        * Returning the node helps with certain debugging issues.
 303  
        * 
 304  
        * @return this will return the source object for this
 305  
        */
 306  
       public Object getSource() {
 307  34
          return entry;
 308  
       }
 309  
    }
 310  
    
 311  
    /**
 312  
     * The <code>Start</code> object is used to represent the start of
 313  
     * an XML element. This will hold the attributes associated with
 314  
     * the element and will provide the name, the namespace reference
 315  
     * and the namespace prefix. For debugging purposes the source XML
 316  
     * element is provided for this start event.
 317  
     * 
 318  
     * @author Niall Gallagher
 319  
     */
 320  
    private static class Start extends EventElement {
 321  
       
 322  
       /**
 323  
        * This is the start element to be used by this start event.
 324  
        */
 325  
       private final StartElement element;
 326  
       
 327  
       /**
 328  
        * This is the element location used to detmine line numbers.
 329  
        */
 330  
       private final Location location;
 331  
       
 332  
       /**
 333  
        * Constructor for the <code>Start</code> object. This will 
 334  
        * wrap the provided node and expose the required details such
 335  
        * as the name, namespace prefix and namespace reference. The
 336  
        * provided element node can be acquired for debugging purposes.
 337  
        * 
 338  
        * @param event this is the element being wrapped by this
 339  
        */
 340  38
       public Start(XMLEvent event) {
 341  38
          this.element = event.asStartElement();
 342  38
          this.location = event.getLocation();
 343  38
       }
 344  
       
 345  
       /**
 346  
        * This is used to provide the line number the XML event was
 347  
        * encountered at within the XML document. If there is no line
 348  
        * number available for the node then this will return a -1.
 349  
        * 
 350  
        * @return this returns the line number if it is available
 351  
        */
 352  
       public int getLine() {
 353  0
          return location.getLineNumber();
 354  
       }
 355  
       
 356  
       /**
 357  
        * This provides the name of the event. This will be the name 
 358  
        * of an XML element the event represents. If there is a prefix
 359  
        * associated with the element, this extracts that prefix.
 360  
        * 
 361  
        * @return this returns the name without the namespace prefix
 362  
        */
 363  
       public String getName() {
 364  50
          return element.getName().getLocalPart();
 365  
       }
 366  
       
 367  
       /**
 368  
        * This is used to acquire the namespace prefix associated with
 369  
        * this node. A prefix is used to qualify an XML element or
 370  
        * attribute within a namespace. So, if this represents a text
 371  
        * event then a namespace prefix is not required.
 372  
        * 
 373  
        * @return this returns the namespace prefix for this event
 374  
        */
 375  
       public String getPrefix() {
 376  0
          return element.getName().getPrefix();
 377  
       }
 378  
       
 379  
       /**
 380  
        * This is used to acquire the namespace reference that this 
 381  
        * node is in. A namespace is normally associated with an XML
 382  
        * element or attribute, so text events and element close events
 383  
        * are not required to contain any namespace references. 
 384  
        * 
 385  
        * @return this will provide the associated namespace reference
 386  
        */
 387  
       public String getReference() {
 388  0
          return element.getName().getNamespaceURI();
 389  
       }
 390  
       
 391  
       /**
 392  
        * This is used to acquire the attributes associated with the
 393  
        * element. Providing the attributes in this format allows 
 394  
        * the reader to build a list of attributes for the event.
 395  
        * 
 396  
        * @return this returns the attributes associated with this
 397  
        */
 398  
       public Iterator<Attribute> getAttributes() {
 399  38
          return element.getAttributes();
 400  
       }
 401  
       
 402  
       /**
 403  
        * This is used to return the node for the element. Because 
 404  
        * this represents a StAX event the StAX event is returned.
 405  
        * Returning the node helps with certain debugging issues.
 406  
        * 
 407  
        * @return this will return the source object for this
 408  
        */
 409  
       public Object getSource() {
 410  0
          return element;
 411  
       }
 412  
    }
 413  
    
 414  
    /**
 415  
     * The <code>Text</code> object is used to represent a text event.
 416  
     * If wraps a node that holds text consumed from the document. 
 417  
     * These are used by <code>InputNode</code> objects to extract the
 418  
     * text values for elements For debugging this exposes the node.
 419  
     * 
 420  
     * @author Niall Gallagher
 421  
     */
 422  
    private static class Text extends EventToken {
 423  
       
 424  
       /**
 425  
        * This is the event that is used to represent the text value.
 426  
        */
 427  
       private final Characters text;
 428  
       
 429  
       /**
 430  
        * Constructor for the <code>Text</code> object. This creates
 431  
        * an event that provides text to the core reader. Text can be
 432  
        * in the form of a CDATA section or a normal text entry.
 433  
        * 
 434  
        * @param event this is the node that represents the text value
 435  
        */
 436  60
       public Text(XMLEvent event) {
 437  60
          this.text = event.asCharacters();
 438  60
       }
 439  
       
 440  
       /**
 441  
        * This is true as this event represents a text token. Text 
 442  
        * tokens are required to provide a value only. So namespace
 443  
        * details and the node name will always return null.
 444  
        *  
 445  
        * @return this returns true as this event represents text  
 446  
        */
 447  
       public boolean isText() {
 448  96
          return true;
 449  
       }
 450  
       
 451  
       /**
 452  
        * This returns the value of the event. This will return the
 453  
        * text value contained within the node. If there is no
 454  
        * text within the node this should return an empty string. 
 455  
        * 
 456  
        * @return this returns the value represented by this event
 457  
        */
 458  
       public String getValue() {
 459  48
          return text.getData();
 460  
       }
 461  
       
 462  
       /**
 463  
        * This is used to return the node for the text. Because 
 464  
        * this represents a StAX event the StAX event is returned.
 465  
        * Returning the node helps with certain debugging issues.
 466  
        * 
 467  
        * @return this will return the source object for this
 468  
        */
 469  
       public Object getSource() {
 470  0
          return text;
 471  
       }
 472  
    }
 473  
    
 474  
    /**
 475  
     * The <code>End</code> object is used to represent the end of an
 476  
     * element. It is used by the core reader to determine which nodes
 477  
     * are in context and which ones are out of context. This allows
 478  
     * the input nodes to determine if it can read any more children.
 479  
     * 
 480  
     * @author Niall Gallagher
 481  
     */
 482  76
    private static class End extends EventToken {
 483  
  
 484  
       /**
 485  
        * This is true as this event represents an element end. Such
 486  
        * events are required by the core reader to determine if a 
 487  
        * node is still in context. This helps to determine if there
 488  
        * are any more children to be read from a specific node.
 489  
        * 
 490  
        * @return this returns true as this token represents an end
 491  
        */
 492  
       public boolean isEnd() {
 493  40
          return true;
 494  
       }
 495  
    }
 496  
 }