1require 'rexml/xmltokens'
2
3# [ :element, parent, name, attributes, children* ]
4  # a = Node.new
5  # a << "B"            # => <a>B</a>
6  # a.b                 # => <a>B<b/></a>
7  # a.b[1]                      # => <a>B<b/><b/><a>
8  # a.b[1]["x"] = "y"   # => <a>B<b/><b x="y"/></a>
9  # a.b[0].c            # => <a>B<b><c/></b><b x="y"/></a>
10  # a.b.c << "D"                # => <a>B<b><c>D</c></b><b x="y"/></a>
11module REXML
12  module Light
13    # Represents a tagged XML element.  Elements are characterized by
14    # having children, attributes, and names, and can themselves be
15    # children.
16    class Node
17      NAMESPLIT = /^(?:(#{XMLTokens::NCNAME_STR}):)?(#{XMLTokens::NCNAME_STR})/u
18      PARENTS = [ :element, :document, :doctype ]
19      # Create a new element.
20      def initialize node=nil
21        @node = node
22        if node.kind_of? String
23          node = [ :text, node ]
24        elsif node.nil?
25          node = [ :document, nil, nil ]
26        elsif node[0] == :start_element
27          node[0] = :element
28        elsif node[0] == :start_doctype
29          node[0] = :doctype
30        elsif node[0] == :start_document
31          node[0] = :document
32        end
33      end
34
35      def size
36        if PARENTS.include? @node[0]
37          @node[-1].size
38        else
39          0
40        end
41      end
42
43      def each
44        size.times { |x| yield( at(x+4) ) }
45      end
46
47      def name
48        at(2)
49      end
50
51      def name=( name_str, ns=nil )
52        pfx = ''
53        pfx = "#{prefix(ns)}:" if ns
54        _old_put(2, "#{pfx}#{name_str}")
55      end
56
57      def parent=( node )
58        _old_put(1,node)
59      end
60
61      def local_name
62        namesplit
63        @name
64      end
65
66      def local_name=( name_str )
67        _old_put( 1, "#@prefix:#{name_str}" )
68      end
69
70      def prefix( namespace=nil )
71        prefix_of( self, namespace )
72      end
73
74      def namespace( prefix=prefix() )
75        namespace_of( self, prefix )
76      end
77
78      def namespace=( namespace )
79        @prefix = prefix( namespace )
80        pfx = ''
81        pfx = "#@prefix:" if @prefix.size > 0
82        _old_put(1, "#{pfx}#@name")
83      end
84
85      def []( reference, ns=nil )
86        if reference.kind_of? String
87          pfx = ''
88          pfx = "#{prefix(ns)}:" if ns
89          at(3)["#{pfx}#{reference}"]
90        elsif reference.kind_of? Range
91          _old_get( Range.new(4+reference.begin, reference.end, reference.exclude_end?) )
92        else
93          _old_get( 4+reference )
94        end
95      end
96
97      def =~( path )
98        XPath.match( self, path )
99      end
100
101      # Doesn't handle namespaces yet
102      def []=( reference, ns, value=nil )
103        if reference.kind_of? String
104          value = ns unless value
105          at( 3 )[reference] = value
106        elsif reference.kind_of? Range
107          _old_put( Range.new(3+reference.begin, reference.end, reference.exclude_end?), ns )
108        else
109          if value
110            _old_put( 4+reference, ns, value )
111          else
112            _old_put( 4+reference, ns )
113          end
114        end
115      end
116
117      # Append a child to this element, optionally under a provided namespace.
118      # The namespace argument is ignored if the element argument is an Element
119      # object.  Otherwise, the element argument is a string, the namespace (if
120      # provided) is the namespace the element is created in.
121      def << element
122        if node_type() == :text
123          at(-1) << element
124        else
125          newnode = Node.new( element )
126          newnode.parent = self
127          self.push( newnode )
128        end
129        at(-1)
130      end
131
132      def node_type
133        _old_get(0)
134      end
135
136      def text=( foo )
137        replace = at(4).kind_of?(String)? 1 : 0
138        self._old_put(4,replace, normalizefoo)
139      end
140
141      def root
142        context = self
143        context = context.at(1) while context.at(1)
144      end
145
146      def has_name?( name, namespace = '' )
147        at(3) == name and namespace() == namespace
148      end
149
150      def children
151        self
152      end
153
154      def parent
155        at(1)
156      end
157
158      def to_s
159
160      end
161
162      private
163
164      def namesplit
165        return if @name.defined?
166        at(2) =~ NAMESPLIT
167        @prefix = '' || $1
168        @name = $2
169      end
170
171      def namespace_of( node, prefix=nil )
172        if not prefix
173          name = at(2)
174          name =~ NAMESPLIT
175          prefix = $1
176        end
177        to_find = 'xmlns'
178        to_find = "xmlns:#{prefix}" if not prefix.nil?
179        ns = at(3)[ to_find ]
180        ns ? ns : namespace_of( @node[0], prefix )
181      end
182
183      def prefix_of( node, namespace=nil )
184        if not namespace
185          name = node.name
186          name =~ NAMESPLIT
187          $1
188        else
189          ns = at(3).find { |k,v| v == namespace }
190          ns ? ns : prefix_of( node.parent, namespace )
191        end
192      end
193    end
194  end
195end
196