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