document.rb

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

Download (6.8 kB)

 
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