File: rexml/document.rb

Overview
Module Structure
Class Hierarchy
Code

Overview

Module Structure

  module: <Toplevel Module>
  module: REXML#14
  class: Document#20
inherits from
  Element ( REXML )
has properties
constant: DECLARATION #25
method: initialize / 2 #34
method: node_type #47
method: clone #52
method: expanded_name #57
alias: name expanded_name #63
method: add / 1 #67
alias: << add #96
method: add_element / 2 #98
method: root #106
method: doctype #114
method: xml_decl #120
method: version #128
method: encoding #134
method: stand_alone? #140
method: write / 4 #183
class method: parse_stream / 2 #200
class method: entity_expansion_limit= / 1 #207
class method: entity_expansion_limit #212
attribute: entity_expansion_count [R] #216
method: record_entity_expansion #218
method: build / 1 #226

Class Hierarchy

Object ( Builtin-Module )
Child ( REXML )
Parent ( REXML )
Element ( REXML )
  Document    #20

Code

   1  require "rexml/element"
   2  require "rexml/xmldecl"
   3  require "rexml/source"
   4  require "rexml/comment"
   5  require "rexml/doctype"
   6  require "rexml/instruction"
   7  require "rexml/rexml"
   8  require "rexml/parseexception"
   9  require "rexml/output"
  10  require "rexml/parsers/baseparser"
  11  require "rexml/parsers/streamparser"
  12  require "rexml/parsers/treeparser"
  13 
  14  module REXML
  15    # Represents a full XML document, including PIs, a doctype, etc.  A
  16    # Document has a single child that can be accessed by root().
  17    # Note that if you want to have an XML declaration written for a document
  18    # you create, you must add one; REXML documents do not write a default
  19    # declaration for you.  See |DECLARATION| and |write|.
  20    class Document < Element
  21      # A convenient default XML declaration.  If you want an XML declaration,
  22      # the easiest way to add one is mydoc << Document::DECLARATION
  23      # +DEPRECATED+
  24      # Use: mydoc << XMLDecl.default
  25      DECLARATION = XMLDecl.default
  26 
  27      # Constructor
  28      # @param source if supplied, must be a Document, String, or IO. 
  29      # Documents have their context and Element attributes cloned.
  30      # Strings are expected to be valid XML documents.  IOs are expected
  31      # to be sources of valid XML documents.
  32      # @param context if supplied, contains the context of the document;
  33      # this should be a Hash.
  34      def initialize( source = nil, context = {} )
  35        @entity_expansion_count = 0
  36        super()
  37        @context = context
  38        return if source.nil?
  39        if source.kind_of? Document
  40          @context = source.context
  41          super source
  42        else
  43          build(  source )
  44        end
  45      end
  46 
  47      def node_type
  48        :document
  49      end
  50 
  51      # Should be obvious
  52      def clone
  53        Document.new self
  54      end
  55 
  56      # According to the XML spec, a root node has no expanded name
  57      def expanded_name
  58        ''
  59        #d = doc_type
  60        #d ? d.name : "UNDEFINED"
  61      end
  62 
  63      alias :name :expanded_name
  64 
  65      # We override this, because XMLDecls and DocTypes must go at the start
  66      # of the document
  67      def add( child )
  68        if child.kind_of? XMLDecl
  69          @children.unshift child
  70          child.parent = self
  71        elsif child.kind_of? DocType
  72          # Find first Element or DocType node and insert the decl right 
  73          # before it.  If there is no such node, just insert the child at the
  74          # end.  If there is a child and it is an DocType, then replace it.
  75          insert_before_index = 0
  76          @children.find { |x| 
  77            insert_before_index += 1
  78            x.kind_of?(Element) || x.kind_of?(DocType)
  79          }
  80          if @children[ insert_before_index ] # Not null = not end of list
  81            if @children[ insert_before_index ].kind_of DocType
  82              @children[ insert_before_index ] = child
  83            else
  84              @children[ index_before_index-1, 0 ] = child
  85            end
  86          else  # Insert at end of list
  87            @children[insert_before_index] = child
  88          end
  89          child.parent = self
  90        else
  91          rv = super
  92          raise "attempted adding second root element to document" if @elements.size > 1
  93          rv
  94        end
  95      end
  96      alias :<< :add
  97 
  98      def add_element(arg=nil, arg2=nil)
  99        rv = super
 100        raise "attempted adding second root element to document" if @elements.size > 1
 101        rv
 102      end
 103 
 104      # @return the root Element of the document, or nil if this document
 105      # has no children.
 106      def root
 107        elements[1]
 108        #self
 109        #@children.find { |item| item.kind_of? Element }
 110      end
 111 
 112      # @return the DocType child of the document, if one exists,
 113      # and nil otherwise.
 114      def doctype
 115        @children.find { |item| item.kind_of? DocType }
 116      end
 117 
 118      # @return the XMLDecl of this document; if no XMLDecl has been
 119      # set, the default declaration is returned.
 120      def xml_decl
 121        rv = @children[0]
 122        return rv if rv.kind_of? XMLDecl
 123        rv = @children.unshift(XMLDecl.default)[0]
 124      end
 125 
 126      # @return the XMLDecl version of this document as a String.
 127      # If no XMLDecl has been set, returns the default version.
 128      def version
 129        xml_decl().version
 130      end
 131 
 132      # @return the XMLDecl encoding of this document as a String.
 133      # If no XMLDecl has been set, returns the default encoding.
 134      def encoding
 135        xml_decl().encoding
 136      end
 137 
 138      # @return the XMLDecl standalone value of this document as a String.
 139      # If no XMLDecl has been set, returns the default setting.
 140      def stand_alone?
 141        xml_decl().stand_alone?
 142      end
 143 
 144      # Write the XML tree out, optionally with indent.  This writes out the
 145      # entire XML document, including XML declarations, doctype declarations,
 146      # and processing instructions (if any are given).
 147      #
 148      # A controversial point is whether Document should always write the XML
 149      # declaration (<?xml version='1.0'?>) whether or not one is given by the
 150      # user (or source document).  REXML does not write one if one was not
 151      # specified, because it adds unnecessary bandwidth to applications such
 152      # as XML-RPC.
 153      #
 154      # See also the classes in the rexml/formatters package for the proper way
 155      # to change the default formatting of XML output
 156      #
 157      # _Examples_
 158      #   Document.new("<a><b/></a>").serialize
 159      #
 160      #   output_string = ""
 161      #   tr = Transitive.new( output_string )
 162      #   Document.new("<a><b/></a>").serialize( tr )
 163      #
 164      # output::
 165      #   output an object which supports '<< string'; this is where the
 166      #   document will be written.
 167      # indent::
 168      #   An integer.  If -1, no indenting will be used; otherwise, the
 169      #   indentation will be twice this number of spaces, and children will be
 170      #   indented an additional amount.  For a value of 3, every item will be 
 171      #   indented 3 more levels, or 6 more spaces (2 * 3). Defaults to -1
 172      # trans::
 173      #   If transitive is true and indent is >= 0, then the output will be
 174      #   pretty-printed in such a way that the added whitespace does not affect
 175      #   the absolute *value* of the document -- that is, it leaves the value
 176      #   and number of Text nodes in the document unchanged.
 177      # ie_hack::
 178      #   Internet Explorer is the worst piece of crap to have ever been
 179      #   written, with the possible exception of Windows itself.  Since IE is
 180      #   unable to parse proper XML, we have to provide a hack to generate XML
 181      #   that IE's limited abilities can handle.  This hack inserts a space 
 182      #   before the /> on empty tags.  Defaults to false
 183      def write( output=$stdout, indent=-1, trans=false, ie_hack=false )
 184        if xml_decl.encoding != "UTF-8" && !output.kind_of?(Output)
 185          output = Output.new( output, xml_decl.encoding )
 186        end
 187        formatter = if indent > -1
 188            if trans
 189              REXML::Formatters::Transitive.new( indent, ie_hack )
 190            else
 191              REXML::Formatters::Pretty.new( indent, ie_hack )
 192            end
 193          else
 194            REXML::Formatters::Default.new( ie_hack )
 195          end
 196        formatter.write( self, output )
 197      end
 198 
 199 
 200      def Document::parse_stream( source, listener )
 201        Parsers::StreamParser.new( source, listener ).parse
 202      end
 203 
 204      @@entity_expansion_limit = 10_000
 205 
 206      # Set the entity expansion limit. By default the limit is set to 10000.
 207      def Document::entity_expansion_limit=( val )
 208        @@entity_expansion_limit = val
 209      end
 210 
 211      # Get the entity expansion limit. By default the limit is set to 10000.
 212      def Document::entity_expansion_limit
 213        return @@entity_expansion_limit
 214      end
 215 
 216      attr_reader :entity_expansion_count
 217      
 218      def record_entity_expansion
 219        @entity_expansion_count += 1
 220        if @entity_expansion_count > @@entity_expansion_limit
 221          raise "number of entity expansions exceeded, processing aborted."
 222        end
 223      end
 224 
 225      private
 226      def build( source )
 227        Parsers::TreeParser.new( source, self ).parse
 228      end
 229    end
 230  end