Coverage Report - org.simpleframework.xml.stream.DocumentReader
 
Classes in this File Line Coverage Branch Coverage Complexity
DocumentReader
97%
46/47
81%
18/22
1.586
DocumentReader$1
N/A
N/A
1.586
DocumentReader$End
100%
2/2
N/A
1.586
DocumentReader$Entry
100%
13/13
100%
2/2
1.586
DocumentReader$Start
100%
8/8
N/A
1.586
DocumentReader$Text
83%
5/6
N/A
1.586
 
 1  
 /*
 2  
  * DocumentReader.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 static org.w3c.dom.Node.ELEMENT_NODE;
 22  
 
 23  
 import org.w3c.dom.Document;
 24  
 import org.w3c.dom.Element;
 25  
 import org.w3c.dom.NamedNodeMap;
 26  
 import org.w3c.dom.Node;
 27  
 
 28  
 /**
 29  
  * The <code>DocumentReader</code> object provides an implementation
 30  
  * for reading XML events using DOM. This reader flattens a document
 31  
  * in to a series of nodes, and provides these nodes as events as
 32  
  * they are encountered. Essentially what this does is adapt the 
 33  
  * document approach to navigating the XML and provides a streaming
 34  
  * approach. Having an implementation based on DOM ensures that the
 35  
  * library can be used on a wider variety of platforms. 
 36  
  * 
 37  
  * @author Niall Gallagher
 38  
  * 
 39  
  * @see org.simpleframework.xml.stream.DocumentProvider
 40  
  */
 41  
 class DocumentReader implements EventReader {
 42  
    
 43  
    /**
 44  
     * Any attribute beginning with this string has been reserved.
 45  
     */
 46  
    private static final String RESERVED = "xml";
 47  
    
 48  
    /**
 49  
     * This is used to extract the nodes from the provided document.
 50  
     */
 51  
    private NodeExtractor queue;
 52  
    
 53  
    /**
 54  
     * This is used to keep track of which elements are in context.
 55  
     */
 56  
    private NodeStack stack;
 57  
    
 58  
    /**
 59  
     * This is used to keep track of any events that were peeked.
 60  
     */
 61  
    private EventNode peek;
 62  
    
 63  
    /**
 64  
     * Constructor for the <code>DocumentReader</code> object. This
 65  
     * makes use of a DOM document to extract events and provide them
 66  
     * to the core framework. All nodes will be extracted from the
 67  
     * document and queued for extraction as they are requested. This
 68  
     * will ignore any comment nodes as they should not be considered.
 69  
     * 
 70  
     * @param document this is the document that is to be read
 71  
     */
 72  74070
    public DocumentReader(Document document) {
 73  74070
       this.queue = new NodeExtractor(document);
 74  74070
       this.stack = new NodeStack();
 75  74070
       this.stack.push(document);
 76  74070
    }
 77  
    
 78  
    /**
 79  
     * This is used to peek at the node from the document. This will
 80  
     * scan through the document, ignoring any comments to find the
 81  
     * next relevant XML event to acquire. Typically events will be
 82  
     * the start and end of an element, as well as any text nodes.
 83  
     * 
 84  
     * @return this returns the next event taken from the document
 85  
     */
 86  
    public EventNode peek() throws Exception {
 87  4050374
       if(peek == null) {
 88  2028159
          peek = next();
 89  
       }
 90  4050374
       return peek;
 91  
    }   
 92  
    
 93  
    /**
 94  
     * This is used to take the next node from the document. This will
 95  
     * scan through the document, ignoring any comments to find the
 96  
     * next relevant XML event to acquire. Typically events will be
 97  
     * the start and end of an element, as well as any text nodes.
 98  
     * 
 99  
     * @return this returns the next event taken from the document
 100  
     */
 101  
    public EventNode next() throws Exception {
 102  9172527
       EventNode next = peek;
 103  
       
 104  9172527
       if(next == null) {
 105  7144385
          next = read();
 106  
       } else {
 107  2028142
          peek = null;
 108  
       }
 109  9172527
       return next;
 110  
    }
 111  
 
 112  
    /**
 113  
     * This is used to read the next node from the document. This will
 114  
     * scan through the document, ignoring any comments to find the
 115  
     * next relevant XML event to acquire. Typically events will be
 116  
     * the start and end of an element, as well as any text nodes.
 117  
     * 
 118  
     * @return this returns the next event taken from the document 
 119  
     */
 120  
    private EventNode read() throws Exception {
 121  7144385
       Node node = queue.peek();
 122  
       
 123  7144385
       if(node == null) {
 124  74069
          return end();
 125  
       }
 126  7070316
       return read(node);
 127  
    }
 128  
    
 129  
    /**
 130  
     * This is used to read the next node from the document. This will
 131  
     * scan through the document, ignoring any comments to find the
 132  
     * next relevant XML event to acquire. Typically events will be
 133  
     * the start and end of an element, as well as any text nodes.
 134  
     * 
 135  
     * @param node this is the XML node that has been read
 136  
     * 
 137  
     * @return this returns the next event taken from the document 
 138  
     */
 139  
    private EventNode read(Node node) throws Exception {
 140  7070316
       Node parent = node.getParentNode();
 141  7070316
       Node top = stack.top();
 142  
       
 143  7070316
       if(parent != top) {
 144  1729101
          if(top != null) {
 145  1729101
             stack.pop();
 146  
          }
 147  1729101
          return end();
 148  
       }
 149  5341215
       if(node != null) {
 150  5341215
          queue.poll();
 151  
       }
 152  5341215
       return convert(node);
 153  
    }
 154  
    
 155  
    /**
 156  
     * This is used to convert the provided node in to an event. The
 157  
     * conversion process ensures the node can be digested by the core
 158  
     * reader and used to provide an <code>InputNode</code> that can
 159  
     * be used to represent the XML elements or attributes. If the
 160  
     * provided node is not an element then it is considered text.
 161  
     * 
 162  
     * @param node the node that is to be converted to an event
 163  
     *
 164  
     * @return this returns an event created from the given node
 165  
     */
 166  
    private EventNode convert(Node node) throws Exception{
 167  5341215
       short type = node.getNodeType();
 168  
       
 169  5341215
       if(type == ELEMENT_NODE) {    
 170  1803260
          if(node != null) {
 171  1803260
             stack.push(node);
 172  
          }
 173  1803260
          return start(node);
 174  
       }
 175  3537955
       return text(node);
 176  
    }
 177  
    
 178  
    /**
 179  
     * This is used to convert the provided node to a start event. The
 180  
     * conversion process ensures the node can be digested by the core
 181  
     * reader and used to provide an <code>InputNode</code> that can
 182  
     * be used to represent an XML elements within the source document.
 183  
     * 
 184  
     * @param node the node that is to be converted to a start event
 185  
     *
 186  
     * @return this returns a start event created from the given node
 187  
     */
 188  
    private Start start(Node node) {
 189  1803260
       Start event = new Start(node);
 190  
 
 191  1803260
       if(event.isEmpty()) {
 192  1803260
          return build(event);
 193  
       }
 194  0
       return event;
 195  
    }
 196  
    
 197  
    /**
 198  
     * This is used to build the attributes that are to be used to 
 199  
     * populate the start event. Populating the start event with the
 200  
     * attributes it contains is required so that each element will
 201  
     * contain its associated attributes. Only attributes that are
 202  
     * not reserved will be added to the start event.
 203  
     * 
 204  
     * @param event this is the start event that is to be populated
 205  
     * 
 206  
     * @return this returns a start event with its attributes
 207  
     */
 208  
    private Start build(Start event) {
 209  1803260
       NamedNodeMap list = event.getAttributes();
 210  1803260
       int length = list.getLength();
 211  
 
 212  2609725
       for (int i = 0; i < length; i++) {
 213  806465
          Node node = list.item(i);
 214  806465
          Attribute value = attribute(node);
 215  
          
 216  806465
          if(!value.isReserved()) {
 217  806286
             event.add(value);
 218  
          }
 219  
       }
 220  1803260
       return event;
 221  
    }
 222  
    
 223  
    /**
 224  
     * This is used to convert the provided node to an attribute. The
 225  
     * conversion process ensures the node can be digested by the core
 226  
     * reader and used to provide an <code>InputNode</code> that can
 227  
     * be used to represent an XML attribute within the source document.
 228  
     * 
 229  
     * @param node the node that is to be converted to an attribute
 230  
     *
 231  
     * @return this returns an attribute created from the given node
 232  
     */
 233  
    private Entry attribute(Node node) {
 234  806465
       return new Entry(node);
 235  
    }
 236  
    
 237  
    /**
 238  
     * This is used to convert the provided node to a text event. The
 239  
     * conversion process ensures the node can be digested by the core
 240  
     * reader and used to provide an <code>InputNode</code> that can
 241  
     * be used to represent an XML attribute within the source document.
 242  
     * 
 243  
     * @param node the node that is to be converted to a text event
 244  
     *
 245  
     * @return this returns the text event created from the given node
 246  
     */
 247  
    private Text text(Node node) {
 248  3537955
       return new Text(node);
 249  
    }
 250  
    
 251  
    /**
 252  
     * This is used to create a node event to signify that an element
 253  
     * has just ended. End events are important as they allow the core
 254  
     * reader to determine if a node is still in context. This provides
 255  
     * a more convenient way to use <code>InputNode</code> objects as
 256  
     * they should only ever be able to extract their children. 
 257  
     * 
 258  
     * @return this returns an end event to signify an element close
 259  
     */
 260  
    private End end() {
 261  1803170
       return new End();
 262  
    }
 263  
    
 264  
    /**
 265  
     * The <code>Entry</code> object is used to represent an attribute
 266  
     * within a start element. This holds the name and value of the
 267  
     * attribute as well as the namespace prefix and reference. These
 268  
     * details can be used to represent the attribute so that should
 269  
     * the core reader require these details they can be acquired.
 270  
     * 
 271  
     * @author Niall Gallagher
 272  
     */
 273  
    private static class Entry extends EventAttribute {
 274  
       
 275  
       /**
 276  
        * This is the node that is to be represented as an attribute.
 277  
        */
 278  
       private final Node node;
 279  
       
 280  
       /**
 281  
        * Constructor for the <code>Entry</code> object. This creates
 282  
        * an attribute object that is used to extract the name, value
 283  
        * namespace prefix, and namespace reference from the provided
 284  
        * node. This is used to populate any start events created.
 285  
        * 
 286  
        * @param node this is the node that represents the attribute
 287  
        */
 288  806465
       public Entry(Node node) {
 289  806465
          this.node = node;
 290  806465
       }
 291  
       
 292  
       /**
 293  
        * This provides the name of the attribute. This will be the
 294  
        * name of the XML attribute without any namespace prefix. If
 295  
        * the name begins with "xml" then this attribute is reserved.
 296  
        * according to the namespaces for XML 1.0 specification.
 297  
        * 
 298  
        * @return this returns the name of this attribute object
 299  
        */
 300  
       public String getName() {
 301  2419031
          return node.getLocalName();
 302  
       }
 303  
       
 304  
       /**
 305  
        * This returns the value of the event. This will be the value
 306  
        * that the attribute contains. If the attribute does not have
 307  
        * a value then this returns null or an empty string.
 308  
        * 
 309  
        * @return this returns the value represented by this object
 310  
        */
 311  
       public String getValue() {
 312  806283
          return node.getNodeValue();
 313  
       }
 314  
       
 315  
       /**
 316  
        * This is used to acquire the namespace prefix associated with
 317  
        * this attribute. A prefix is used to qualify the attribute
 318  
        * within a namespace. So, if this has a prefix then it should
 319  
        * have a reference associated with it.
 320  
        * 
 321  
        * @return this returns the namespace prefix for the attribute
 322  
        */
 323  
       public String getPrefix() {
 324  2419031
          return node.getPrefix();
 325  
       }
 326  
       
 327  
       /**
 328  
        * This is used to acquire the namespace reference that this 
 329  
        * attribute is in. A namespace is normally associated with an
 330  
        * attribute if that attribute is prefixed with a known token.
 331  
        * If there is no prefix then this will return null.
 332  
        * 
 333  
        * @return this provides the associated namespace reference
 334  
        */
 335  
       public String getReference() {
 336  806283
          return node.getNamespaceURI();
 337  
       }
 338  
       
 339  
       /**
 340  
        * This returns true if the attribute is reserved. An attribute
 341  
        * is considered reserved if it begins with "xml" according to 
 342  
        * the namespaces in XML 1.0 specification. Such attributes are
 343  
        * used for namespaces and other such details.
 344  
        *
 345  
        * @return this returns true if the attribute is reserved
 346  
        */
 347  
       public boolean isReserved() {
 348  1612748
          String prefix = getPrefix();
 349  1612748
          String name = getName();
 350  
          
 351  1612748
          if(prefix != null) {
 352  155
             return prefix.startsWith(RESERVED);
 353  
          }
 354  1612593
          return name.startsWith(RESERVED);
 355  
       }
 356  
       
 357  
       /**
 358  
        * This is used to return the node for the attribute. Because 
 359  
        * this represents a DOM attribute the DOM node is returned.
 360  
        * Returning the node helps with certain debugging issues.
 361  
        * 
 362  
        * @return this will return the source object for this
 363  
        */
 364  
       public Object getSource() {
 365  806283
          return node;
 366  
       }
 367  
    }
 368  
    
 369  
    /**
 370  
     * The <code>Start</code> object is used to represent the start of
 371  
     * an XML element. This will hold the attributes associated with
 372  
     * the element and will provide the name, the namespace reference
 373  
     * and the namespace prefix. For debugging purposes the source XML
 374  
     * element is provided for this start event.
 375  
     * 
 376  
     * @author Niall Gallagher
 377  
     */
 378  
    private static class Start extends EventElement {
 379  
       
 380  
       /**
 381  
        * This is the element that is represented by this start event.
 382  
        */
 383  
       private final Element element;
 384  
       
 385  
       /**
 386  
        * Constructor for the <code>Start</code> object. This will 
 387  
        * wrap the provided node and expose the required details such
 388  
        * as the name, namespace prefix and namespace reference. The
 389  
        * provided element node can be acquired for debugging purposes.
 390  
        * 
 391  
        * @param element this is the element being wrapped by this
 392  
        */
 393  1803260
       public Start(Node element) {
 394  1803260
          this.element = (Element)element;
 395  1803260
       }
 396  
       
 397  
       /**
 398  
        * This provides the name of the event. This will be the name 
 399  
        * of an XML element the event represents. If there is a prefix
 400  
        * associated with the element, this extracts that prefix.
 401  
        * 
 402  
        * @return this returns the name without the namespace prefix
 403  
        */
 404  
       public String getName() {
 405  2741186
          return element.getLocalName();
 406  
       }
 407  
       
 408  
       /**
 409  
        * This is used to acquire the namespace prefix associated with
 410  
        * this node. A prefix is used to qualify an XML element or
 411  
        * attribute within a namespace. So, if this represents a text
 412  
        * event then a namespace prefix is not required.
 413  
        * 
 414  
        * @return this returns the namespace prefix for this event
 415  
        */
 416  
       public String getPrefix() {
 417  12
          return element.getPrefix();
 418  
       }
 419  
       
 420  
       /**
 421  
        * This is used to acquire the namespace reference that this 
 422  
        * node is in. A namespace is normally associated with an XML
 423  
        * element or attribute, so text events and element close events
 424  
        * are not required to contain any namespace references. 
 425  
        * 
 426  
        * @return this will provide the associated namespace reference
 427  
        */
 428  
       public String getReference() {
 429  136
          return element.getNamespaceURI();
 430  
       }
 431  
       
 432  
       /**
 433  
        * This is used to acquire the attributes associated with the
 434  
        * element. Providing the attributes in this format allows 
 435  
        * the reader to build a list of attributes for the event.
 436  
        * 
 437  
        * @return this returns the attributes associated with this
 438  
        */
 439  
       public NamedNodeMap getAttributes(){
 440  1803260
          return element.getAttributes();
 441  
       }
 442  
       
 443  
       /**
 444  
        * This is used to return the node for the event. Because this
 445  
        * represents a DOM element node the DOM node will be returned.
 446  
        * Returning the node helps with certain debugging issues.
 447  
        * 
 448  
        * @return this will return the source object for this event
 449  
        */
 450  
       public Object getSource() {
 451  476
          return element;
 452  
       }
 453  
    }
 454  
    
 455  
    /**
 456  
     * The <code>Text</code> object is used to represent a text event.
 457  
     * If wraps a node that holds text consumed from the document. 
 458  
     * These are used by <code>InputNode</code> objects to extract the
 459  
     * text values for elements For debugging this exposes the node.
 460  
     * 
 461  
     * @author Niall Gallagher
 462  
     */
 463  
    private static class Text extends EventToken {
 464  
       
 465  
       /**
 466  
        * This is the node that is used to represent the text value.
 467  
        */
 468  
       private final Node node;
 469  
       
 470  
       /**
 471  
        * Constructor for the <code>Text</code> object. This creates
 472  
        * an event that provides text to the core reader. Text can be
 473  
        * in the form of a CDATA section or a normal text entry.
 474  
        * 
 475  
        * @param node this is the node that represents the text value
 476  
        */
 477  3537955
       public Text(Node node) {
 478  3537955
          this.node = node;
 479  3537955
       } 
 480  
       
 481  
       /**
 482  
        * This is true as this event represents a text token. Text 
 483  
        * tokens are required to provide a value only. So namespace
 484  
        * details and the node name will always return null.
 485  
        *  
 486  
        * @return this returns true as this event represents text  
 487  
        */
 488  
       public boolean isText() {
 489  2029415
          return true;
 490  
       }
 491  
       
 492  
       /**
 493  
        * This returns the value of the event. This will return the
 494  
        * text value contained within the node. If there is no
 495  
        * text within the node this should return an empty string. 
 496  
        * 
 497  
        * @return this returns the value represented by this event
 498  
        */
 499  
       public String getValue(){
 500  1014704
          return node.getNodeValue();
 501  
       }
 502  
       
 503  
       /**
 504  
        * This is used to return the node for the event. Because this
 505  
        * represents a DOM text value the DOM node will be returned.
 506  
        * Returning the node helps with certain debugging issues.
 507  
        * 
 508  
        * @return this will return the source object for this event
 509  
        */
 510  
       public Object getSource() {
 511  0
          return node;
 512  
       }
 513  
    }
 514  
    
 515  
    /**
 516  
     * The <code>End</code> object is used to represent the end of an
 517  
     * element. It is used by the core reader to determine which nodes
 518  
     * are in context and which ones are out of context. This allows
 519  
     * the input nodes to determine if it can read any more children.
 520  
     * 
 521  
     * @author Niall Gallagher
 522  
     */
 523  3606340
    private static class End extends EventToken {
 524  
  
 525  
       /**
 526  
        * This is true as this event represents an element end. Such
 527  
        * events are required by the core reader to determine if a 
 528  
        * node is still in context. This helps to determine if there
 529  
        * are any more children to be read from a specific node.
 530  
        * 
 531  
        * @return this returns true as this token represents an end
 532  
        */
 533  
       public boolean isEnd() {
 534  1803556
          return true;
 535  
       }
 536  
    }
 537  
 }