flowplayer flash flv no longer work, move to mp4
[booh] / lib / booh / rexml / document.rb
1 require "booh/rexml/element"
2 require "booh/rexml/xmldecl"
3 require "booh/rexml/source"
4 require "booh/rexml/comment"
5 require "booh/rexml/doctype"
6 require "booh/rexml/instruction"
7 require "booh/rexml/rexml"
8 require "booh/rexml/parseexception"
9 require "booh/rexml/output"
10 require "booh/rexml/parsers/baseparser"
11 require "booh/rexml/parsers/streamparser"
12 require "booh/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           # NOTE that I'm not sure what the context is for; I cloned it out of
35           # the Electric XML API (in which it also seems to do nothing), and it
36           # is now legacy.  It may do something, someday... it may disappear.
37                 def initialize( source = nil, context = {} )
38                         super()
39                         @context = context
40                         return if source.nil?
41                         if source.kind_of? Document
42                                 @context = source.context
43                                 super source
44                         else
45                                 build(  source )
46                         end
47                 end
48
49     def node_type
50       :document
51     end
52
53                 # Should be obvious
54                 def clone
55                         Document.new self
56                 end
57
58                 # According to the XML spec, a root node has no expanded name
59                 def expanded_name
60                         ''
61                         #d = doc_type
62                         #d ? d.name : "UNDEFINED"
63                 end
64
65                 alias :name :expanded_name
66
67                 # We override this, because XMLDecls and DocTypes must go at the start
68                 # of the document
69                 def add( child )
70                         if child.kind_of? XMLDecl
71                                 @children.unshift child
72                         elsif child.kind_of? DocType
73         # Find first Element or DocType node and insert the decl right 
74         # before it.  If there is no such node, just insert the child at the
75         # end.  If there is a child and it is an DocType, then replace it.
76         insert_before_index = 0
77         @children.find { |x| 
78           insert_before_index += 1
79           x.kind_of?(Element) || x.kind_of?(DocType)
80         }
81         if @children[ insert_before_index ] # Not null = not end of list
82           if @children[ insert_before_index ].kind_of DocType
83             @children[ insert_before_index ] = child
84           else
85             @children[ index_before_index-1, 0 ] = child
86           end
87         else  # Insert at end of list
88           @children[insert_before_index] = child
89         end
90                                 child.parent = self
91                         else
92                                 rv = super
93                                 raise "attempted adding second root element to document" if @elements.size > 1
94                                 rv
95                         end
96                 end
97                 alias :<< :add
98
99                 def add_element(arg=nil, arg2=nil)
100                         rv = super
101                         raise "attempted adding second root element to document" if @elements.size > 1
102                         rv
103                 end
104
105                 # @return the root Element of the document, or nil if this document
106                 # has no children.
107                 def root
108       elements[1]
109       #self
110       #@children.find { |item| item.kind_of? Element }
111                 end
112
113                 # @return the DocType child of the document, if one exists,
114                 # and nil otherwise.
115                 def doctype
116                         @children.find { |item| item.kind_of? DocType }
117                 end
118
119                 # @return the XMLDecl of this document; if no XMLDecl has been
120                 # set, the default declaration is returned.
121                 def xml_decl
122                         rv = @children[0]
123       return rv if rv.kind_of? XMLDecl
124       rv = @children.unshift(XMLDecl.default)[0]
125                 end
126
127                 # @return the XMLDecl version of this document as a String.
128                 # If no XMLDecl has been set, returns the default version.
129                 def version
130                         xml_decl().version
131                 end
132
133                 # @return the XMLDecl encoding of this document as a String.
134                 # If no XMLDecl has been set, returns the default encoding.
135                 def encoding
136                         xml_decl().encoding
137                 end
138
139                 # @return the XMLDecl standalone value of this document as a String.
140                 # If no XMLDecl has been set, returns the default setting.
141                 def stand_alone?
142                         xml_decl().stand_alone?
143                 end
144
145                 # Write the XML tree out, optionally with indent.  This writes out the
146                 # entire XML document, including XML declarations, doctype declarations,
147                 # and processing instructions (if any are given).
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 unneccessary bandwidth to applications such
152                 # as XML-RPC.
153                 #
154                 #
155                 # output::
156                 #         output an object which supports '<< string'; this is where the
157                 #   document will be written.
158                 # indent::
159                 #   An integer.  If -1, no indenting will be used; otherwise, the
160                 #   indentation will be this number of spaces, and children will be
161                 #   indented an additional amount.  Defaults to -1
162                 # transitive::
163                 #   If transitive is true and indent is >= 0, then the output will be
164                 #   pretty-printed in such a way that the added whitespace does not affect
165                 #   the absolute *value* of the document -- that is, it leaves the value
166                 #   and number of Text nodes in the document unchanged.
167                 # ie_hack::
168                 #   Internet Explorer is the worst piece of crap to have ever been
169                 #   written, with the possible exception of Windows itself.  Since IE is
170                 #   unable to parse proper XML, we have to provide a hack to generate XML
171                 #   that IE's limited abilities can handle.  This hack inserts a space 
172                 #   before the /> on empty tags.  Defaults to false
173                 def write( output=$stdout, indent=-1, transitive=false, ie_hack=false )
174                         output = Output.new( output, xml_decl.encoding ) if xml_decl.encoding != "UTF-8" && !output.kind_of?(Output)
175                         @children.each { |node|
176                                 indent( output, indent ) if node.node_type == :element
177                                 if node.write( output, indent, transitive, ie_hack )
178           output << "\n" unless indent<0 or node == @children[-1]
179         end
180                         }
181                 end
182
183                 
184                 def Document::parse_stream( source, listener )
185                         Parsers::StreamParser.new( source, listener ).parse
186                 end
187
188                 private
189                 def build( source )
190       Parsers::TreeParser.new( source, self ).parse
191                 end
192         end
193 end