PageRenderTime 115ms CodeModel.GetById 98ms app.highlight 14ms RepoModel.GetById 1ms app.codeStats 0ms

/tools/Ruby/lib/ruby/1.8/rexml/document.rb

http://github.com/agross/netopenspace
Ruby | 230 lines | 127 code | 23 blank | 80 comment | 16 complexity | fd8df9f3a5e1d84e453459c173da11d8 MD5 | raw file
  1require "rexml/element"
  2require "rexml/xmldecl"
  3require "rexml/source"
  4require "rexml/comment"
  5require "rexml/doctype"
  6require "rexml/instruction"
  7require "rexml/rexml"
  8require "rexml/parseexception"
  9require "rexml/output"
 10require "rexml/parsers/baseparser"
 11require "rexml/parsers/streamparser"
 12require "rexml/parsers/treeparser"
 13
 14module 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
230end