Project

General

Profile

Bug #1426 ยป document.rb

luzan8 (Luciano Zanotto), 05/01/2009 12:51 AM

 
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
			super()
36
			@context = context
37
			return if source.nil?
38
			if source.kind_of? Document
39
				@context = source.context
40
				super source
41
			else
42
				build(  source )
43
			end
44
		end
45

    
46
    def node_type
47
      :document
48
    end
49

    
50
		# Should be obvious
51
		def clone
52
			Document.new self
53
		end
54

    
55
		# According to the XML spec, a root node has no expanded name
56
		def expanded_name
57
			''
58
			#d = doc_type
59
			#d ? d.name : "UNDEFINED"
60
		end
61

    
62
		alias :name :expanded_name
63

    
64
		# We override this, because XMLDecls and DocTypes must go at the start
65
		# of the document
66
		def add( child )
67
			if child.kind_of? XMLDecl
68
				@children.unshift child
69
			elsif child.kind_of? DocType
70
        # Find first Element or DocType node and insert the decl right 
71
        # before it.  If there is no such node, just insert the child at the
72
        # end.  If there is a child and it is an DocType, then replace it.
73
        insert_before_index = 0
74
        @children.find { |x| 
75
          insert_before_index += 1
76
          x.kind_of?(Element) || x.kind_of?(DocType)
77
        }
78
        if @children[ insert_before_index ] # Not null = not end of list
79
          if @children[ insert_before_index ].kind_of DocType
80
            @children[ insert_before_index ] = child
81
          else
82
            @children[ index_before_index-1, 0 ] = child
83
          end
84
        else  # Insert at end of list
85
          @children[insert_before_index] = child
86
        end
87
				child.parent = self
88
			else
89
				rv = super
90
				raise "attempted adding second root element to document" if @elements.size > 1
91
				rv
92
			end
93
		end
94
		alias :<< :add
95

    
96
		def add_element(arg=nil, arg2=nil)
97
			rv = super
98
			raise "attempted adding second root element to document" if @elements.size > 1
99
			rv
100
		end
101

    
102
		# @return the root Element of the document, or nil if this document
103
		# has no children.
104
		def root
105
      elements[1]
106
      #self
107
      #@children.find { |item| item.kind_of? Element }
108
		end
109

    
110
		# @return the DocType child of the document, if one exists,
111
		# and nil otherwise.
112
		def doctype
113
			@children.find { |item| item.kind_of? DocType }
114
		end
115

    
116
		# @return the XMLDecl of this document; if no XMLDecl has been
117
		# set, the default declaration is returned.
118
		def xml_decl
119
			rv = @children[0]
120
      return rv if rv.kind_of? XMLDecl
121
      rv = @children.unshift(XMLDecl.default)[0]
122
		end
123

    
124
		# @return the XMLDecl version of this document as a String.
125
		# If no XMLDecl has been set, returns the default version.
126
		def version
127
			xml_decl().version
128
		end
129

    
130
		# @return the XMLDecl encoding of this document as a String.
131
		# If no XMLDecl has been set, returns the default encoding.
132
		def encoding
133
			xml_decl().encoding
134
		end
135

    
136
		# @return the XMLDecl standalone value of this document as a String.
137
		# If no XMLDecl has been set, returns the default setting.
138
		def stand_alone?
139
			xml_decl().stand_alone?
140
		end
141

    
142
    # Write the XML tree out, optionally with indent.  This writes out the
143
    # entire XML document, including XML declarations, doctype declarations,
144
    # and processing instructions (if any are given).
145
    #
146
    # A controversial point is whether Document should always write the XML
147
    # declaration (<?xml version='1.0'?>) whether or not one is given by the
148
    # user (or source document).  REXML does not write one if one was not
149
    # specified, because it adds unneccessary bandwidth to applications such
150
    # as XML-RPC.
151
    #
152
    # See also the classes in the rexml/formatters package for the proper way
153
    # to change the default formatting of XML output
154
    #
155
    # _Examples_
156
    #   Document.new("<a><b/></a>").serialize
157
    #
158
    #   output_string = ""
159
    #   tr = Transitive.new( output_string )
160
    #   Document.new("<a><b/></a>").serialize( tr )
161
    #
162
    # output::
163
    #	  output an object which supports '<< string'; this is where the
164
    #   document will be written.
165
    # indent::
166
    #   An integer.  If -1, no indenting will be used; otherwise, the
167
    #   indentation will be twice this number of spaces, and children will be
168
    #   indented an additional amount.  For a value of 3, every item will be 
169
    #   indented 3 more levels, or 6 more spaces (2 * 3). Defaults to -1
170
    # trans::
171
    #   If transitive is true and indent is >= 0, then the output will be
172
    #   pretty-printed in such a way that the added whitespace does not affect
173
    #   the absolute *value* of the document -- that is, it leaves the value
174
    #   and number of Text nodes in the document unchanged.
175
    # ie_hack::
176
    #   Internet Explorer is the worst piece of crap to have ever been
177
    #   written, with the possible exception of Windows itself.  Since IE is
178
    #   unable to parse proper XML, we have to provide a hack to generate XML
179
    #   that IE's limited abilities can handle.  This hack inserts a space 
180
    #   before the /> on empty tags.  Defaults to false
181
		def write( output=$stdout, indent=-1, trans=false, ie_hack=false )
182
      if xml_decl.encoding != "UTF-8" && !output.kind_of?(Output)
183
        output = Output.new( output, xml_decl.encoding )
184
      end
185
      formatter = if indent > -1
186
          #if transitive
187
          if trans
188
            REXML::Formatters::Transitive.new( indent, ie_hack )
189
          else
190
            REXML::Formatters::Pretty.new( indent, ie_hack )
191
          end
192
        else
193
          REXML::Formatters::Default.new( ie_hack )
194
        end
195
      formatter.write( self, output )
196
		end
197

    
198
		
199
		def Document::parse_stream( source, listener )
200
			Parsers::StreamParser.new( source, listener ).parse
201
		end
202

    
203
		private
204
		def build( source )
205
      Parsers::TreeParser.new( source, self ).parse
206
		end
207
	end
208
end