/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

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