rubysl-rexml 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (179) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +17 -0
  3. data/.travis.yml +8 -0
  4. data/Gemfile +4 -0
  5. data/LICENSE +25 -0
  6. data/README.md +29 -0
  7. data/Rakefile +1 -0
  8. data/lib/rexml/attlistdecl.rb +62 -0
  9. data/lib/rexml/attribute.rb +185 -0
  10. data/lib/rexml/cdata.rb +67 -0
  11. data/lib/rexml/child.rb +96 -0
  12. data/lib/rexml/comment.rb +80 -0
  13. data/lib/rexml/doctype.rb +271 -0
  14. data/lib/rexml/document.rb +230 -0
  15. data/lib/rexml/dtd/attlistdecl.rb +10 -0
  16. data/lib/rexml/dtd/dtd.rb +51 -0
  17. data/lib/rexml/dtd/elementdecl.rb +17 -0
  18. data/lib/rexml/dtd/entitydecl.rb +56 -0
  19. data/lib/rexml/dtd/notationdecl.rb +39 -0
  20. data/lib/rexml/element.rb +1227 -0
  21. data/lib/rexml/encoding.rb +71 -0
  22. data/lib/rexml/encodings/CP-1252.rb +103 -0
  23. data/lib/rexml/encodings/EUC-JP.rb +35 -0
  24. data/lib/rexml/encodings/ICONV.rb +22 -0
  25. data/lib/rexml/encodings/ISO-8859-1.rb +7 -0
  26. data/lib/rexml/encodings/ISO-8859-15.rb +72 -0
  27. data/lib/rexml/encodings/SHIFT-JIS.rb +37 -0
  28. data/lib/rexml/encodings/SHIFT_JIS.rb +1 -0
  29. data/lib/rexml/encodings/UNILE.rb +34 -0
  30. data/lib/rexml/encodings/US-ASCII.rb +30 -0
  31. data/lib/rexml/encodings/UTF-16.rb +35 -0
  32. data/lib/rexml/encodings/UTF-8.rb +18 -0
  33. data/lib/rexml/entity.rb +166 -0
  34. data/lib/rexml/formatters/default.rb +109 -0
  35. data/lib/rexml/formatters/pretty.rb +138 -0
  36. data/lib/rexml/formatters/transitive.rb +56 -0
  37. data/lib/rexml/functions.rb +382 -0
  38. data/lib/rexml/instruction.rb +70 -0
  39. data/lib/rexml/light/node.rb +196 -0
  40. data/lib/rexml/namespace.rb +47 -0
  41. data/lib/rexml/node.rb +75 -0
  42. data/lib/rexml/output.rb +24 -0
  43. data/lib/rexml/parent.rb +166 -0
  44. data/lib/rexml/parseexception.rb +51 -0
  45. data/lib/rexml/parsers/baseparser.rb +503 -0
  46. data/lib/rexml/parsers/lightparser.rb +60 -0
  47. data/lib/rexml/parsers/pullparser.rb +196 -0
  48. data/lib/rexml/parsers/sax2parser.rb +238 -0
  49. data/lib/rexml/parsers/streamparser.rb +46 -0
  50. data/lib/rexml/parsers/treeparser.rb +97 -0
  51. data/lib/rexml/parsers/ultralightparser.rb +56 -0
  52. data/lib/rexml/parsers/xpathparser.rb +698 -0
  53. data/lib/rexml/quickpath.rb +266 -0
  54. data/lib/rexml/rexml.rb +32 -0
  55. data/lib/rexml/sax2listener.rb +97 -0
  56. data/lib/rexml/source.rb +251 -0
  57. data/lib/rexml/streamlistener.rb +92 -0
  58. data/lib/rexml/syncenumerator.rb +33 -0
  59. data/lib/rexml/text.rb +344 -0
  60. data/lib/rexml/undefinednamespaceexception.rb +8 -0
  61. data/lib/rexml/validation/relaxng.rb +559 -0
  62. data/lib/rexml/validation/validation.rb +155 -0
  63. data/lib/rexml/validation/validationexception.rb +9 -0
  64. data/lib/rexml/xmldecl.rb +119 -0
  65. data/lib/rexml/xmltokens.rb +18 -0
  66. data/lib/rexml/xpath.rb +66 -0
  67. data/lib/rexml/xpath_parser.rb +792 -0
  68. data/lib/rubysl/rexml.rb +1 -0
  69. data/lib/rubysl/rexml/version.rb +5 -0
  70. data/rubysl-rexml.gemspec +23 -0
  71. data/spec/attribute/clone_spec.rb +10 -0
  72. data/spec/attribute/element_spec.rb +22 -0
  73. data/spec/attribute/equal_value_spec.rb +17 -0
  74. data/spec/attribute/hash_spec.rb +12 -0
  75. data/spec/attribute/initialize_spec.rb +28 -0
  76. data/spec/attribute/inspect_spec.rb +19 -0
  77. data/spec/attribute/namespace_spec.rb +23 -0
  78. data/spec/attribute/node_type_spec.rb +9 -0
  79. data/spec/attribute/prefix_spec.rb +17 -0
  80. data/spec/attribute/remove_spec.rb +19 -0
  81. data/spec/attribute/to_s_spec.rb +13 -0
  82. data/spec/attribute/to_string_spec.rb +14 -0
  83. data/spec/attribute/value_spec.rb +14 -0
  84. data/spec/attribute/write_spec.rb +22 -0
  85. data/spec/attribute/xpath_spec.rb +19 -0
  86. data/spec/attributes/add_spec.rb +6 -0
  87. data/spec/attributes/append_spec.rb +6 -0
  88. data/spec/attributes/delete_all_spec.rb +30 -0
  89. data/spec/attributes/delete_spec.rb +26 -0
  90. data/spec/attributes/each_attribute_spec.rb +24 -0
  91. data/spec/attributes/each_spec.rb +24 -0
  92. data/spec/attributes/element_reference_spec.rb +18 -0
  93. data/spec/attributes/element_set_spec.rb +25 -0
  94. data/spec/attributes/get_attribute_ns_spec.rb +13 -0
  95. data/spec/attributes/get_attribute_spec.rb +28 -0
  96. data/spec/attributes/initialize_spec.rb +18 -0
  97. data/spec/attributes/length_spec.rb +6 -0
  98. data/spec/attributes/namespaces_spec.rb +5 -0
  99. data/spec/attributes/prefixes_spec.rb +23 -0
  100. data/spec/attributes/shared/add.rb +17 -0
  101. data/spec/attributes/shared/length.rb +12 -0
  102. data/spec/attributes/size_spec.rb +6 -0
  103. data/spec/attributes/to_a_spec.rb +20 -0
  104. data/spec/cdata/clone_spec.rb +9 -0
  105. data/spec/cdata/initialize_spec.rb +24 -0
  106. data/spec/cdata/shared/to_s.rb +11 -0
  107. data/spec/cdata/to_s_spec.rb +6 -0
  108. data/spec/cdata/value_spec.rb +6 -0
  109. data/spec/document/add_element_spec.rb +30 -0
  110. data/spec/document/add_spec.rb +60 -0
  111. data/spec/document/clone_spec.rb +19 -0
  112. data/spec/document/doctype_spec.rb +14 -0
  113. data/spec/document/encoding_spec.rb +21 -0
  114. data/spec/document/expanded_name_spec.rb +15 -0
  115. data/spec/document/new_spec.rb +37 -0
  116. data/spec/document/node_type_spec.rb +7 -0
  117. data/spec/document/root_spec.rb +11 -0
  118. data/spec/document/stand_alone_spec.rb +18 -0
  119. data/spec/document/version_spec.rb +13 -0
  120. data/spec/document/write_spec.rb +38 -0
  121. data/spec/document/xml_decl_spec.rb +14 -0
  122. data/spec/element/add_attribute_spec.rb +40 -0
  123. data/spec/element/add_attributes_spec.rb +21 -0
  124. data/spec/element/add_element_spec.rb +38 -0
  125. data/spec/element/add_namespace_spec.rb +23 -0
  126. data/spec/element/add_text_spec.rb +23 -0
  127. data/spec/element/attribute_spec.rb +16 -0
  128. data/spec/element/attributes_spec.rb +18 -0
  129. data/spec/element/cdatas_spec.rb +23 -0
  130. data/spec/element/clone_spec.rb +28 -0
  131. data/spec/element/comments_spec.rb +20 -0
  132. data/spec/element/delete_attribute_spec.rb +38 -0
  133. data/spec/element/delete_element_spec.rb +50 -0
  134. data/spec/element/delete_namespace_spec.rb +24 -0
  135. data/spec/element/document_spec.rb +17 -0
  136. data/spec/element/each_element_with_attribute_spec.rb +34 -0
  137. data/spec/element/each_element_with_text_spec.rb +30 -0
  138. data/spec/element/get_text_spec.rb +17 -0
  139. data/spec/element/has_attributes_spec.rb +16 -0
  140. data/spec/element/has_elements_spec.rb +17 -0
  141. data/spec/element/has_text_spec.rb +15 -0
  142. data/spec/element/inspect_spec.rb +26 -0
  143. data/spec/element/instructions_spec.rb +20 -0
  144. data/spec/element/namespace_spec.rb +26 -0
  145. data/spec/element/namespaces_spec.rb +31 -0
  146. data/spec/element/new_spec.rb +34 -0
  147. data/spec/element/next_element_spec.rb +18 -0
  148. data/spec/element/node_type_spec.rb +7 -0
  149. data/spec/element/prefixes_spec.rb +22 -0
  150. data/spec/element/previous_element_spec.rb +19 -0
  151. data/spec/element/raw_spec.rb +23 -0
  152. data/spec/element/root_spec.rb +27 -0
  153. data/spec/element/text_spec.rb +45 -0
  154. data/spec/element/texts_spec.rb +15 -0
  155. data/spec/element/whitespace_spec.rb +22 -0
  156. data/spec/node/each_recursive_spec.rb +20 -0
  157. data/spec/node/find_first_recursive_spec.rb +24 -0
  158. data/spec/node/index_in_parent_spec.rb +14 -0
  159. data/spec/node/next_sibling_node_spec.rb +20 -0
  160. data/spec/node/parent_spec.rb +20 -0
  161. data/spec/node/previous_sibling_node_spec.rb +20 -0
  162. data/spec/shared/each_element.rb +35 -0
  163. data/spec/shared/elements_to_a.rb +35 -0
  164. data/spec/text/append_spec.rb +9 -0
  165. data/spec/text/clone_spec.rb +9 -0
  166. data/spec/text/comparison_spec.rb +24 -0
  167. data/spec/text/empty_spec.rb +11 -0
  168. data/spec/text/indent_text_spec.rb +23 -0
  169. data/spec/text/inspect_spec.rb +7 -0
  170. data/spec/text/new_spec.rb +48 -0
  171. data/spec/text/node_type_spec.rb +7 -0
  172. data/spec/text/normalize_spec.rb +7 -0
  173. data/spec/text/read_with_substitution_spec.rb +12 -0
  174. data/spec/text/to_s_spec.rb +17 -0
  175. data/spec/text/unnormalize_spec.rb +7 -0
  176. data/spec/text/value_spec.rb +36 -0
  177. data/spec/text/wrap_spec.rb +20 -0
  178. data/spec/text/write_with_substitution_spec.rb +32 -0
  179. metadata +385 -0
@@ -0,0 +1,60 @@
1
+ require 'rexml/parsers/streamparser'
2
+ require 'rexml/parsers/baseparser'
3
+ require 'rexml/light/node'
4
+
5
+ module REXML
6
+ module Parsers
7
+ class LightParser
8
+ def initialize stream
9
+ @stream = stream
10
+ @parser = REXML::Parsers::BaseParser.new( stream )
11
+ end
12
+
13
+ def add_listener( listener )
14
+ @parser.add_listener( listener )
15
+ end
16
+
17
+ def rewind
18
+ @stream.rewind
19
+ @parser.stream = @stream
20
+ end
21
+
22
+ def parse
23
+ root = context = [ :document ]
24
+ while true
25
+ event = @parser.pull
26
+ case event[0]
27
+ when :end_document
28
+ break
29
+ when :end_doctype
30
+ context = context[1]
31
+ when :start_element, :start_doctype
32
+ new_node = event
33
+ context << new_node
34
+ new_node[1,0] = [context]
35
+ context = new_node
36
+ when :end_element, :end_doctype
37
+ context = context[1]
38
+ else
39
+ new_node = event
40
+ context << new_node
41
+ new_node[1,0] = [context]
42
+ end
43
+ end
44
+ root
45
+ end
46
+ end
47
+
48
+ # An element is an array. The array contains:
49
+ # 0 The parent element
50
+ # 1 The tag name
51
+ # 2 A hash of attributes
52
+ # 3..-1 The child elements
53
+ # An element is an array of size > 3
54
+ # Text is a String
55
+ # PIs are [ :processing_instruction, target, data ]
56
+ # Comments are [ :comment, data ]
57
+ # DocTypes are DocType structs
58
+ # The root is an array with XMLDecls, Text, DocType, Array, Text
59
+ end
60
+ end
@@ -0,0 +1,196 @@
1
+ require 'forwardable'
2
+
3
+ require 'rexml/parseexception'
4
+ require 'rexml/parsers/baseparser'
5
+ require 'rexml/xmltokens'
6
+
7
+ module REXML
8
+ module Parsers
9
+ # = Using the Pull Parser
10
+ # <em>This API is experimental, and subject to change.</em>
11
+ # parser = PullParser.new( "<a>text<b att='val'/>txet</a>" )
12
+ # while parser.has_next?
13
+ # res = parser.next
14
+ # puts res[1]['att'] if res.start_tag? and res[0] == 'b'
15
+ # end
16
+ # See the PullEvent class for information on the content of the results.
17
+ # The data is identical to the arguments passed for the various events to
18
+ # the StreamListener API.
19
+ #
20
+ # Notice that:
21
+ # parser = PullParser.new( "<a>BAD DOCUMENT" )
22
+ # while parser.has_next?
23
+ # res = parser.next
24
+ # raise res[1] if res.error?
25
+ # end
26
+ #
27
+ # Nat Price gave me some good ideas for the API.
28
+ class PullParser
29
+ include XMLTokens
30
+ extend Forwardable
31
+
32
+ def_delegators( :@parser, :has_next? )
33
+ def_delegators( :@parser, :entity )
34
+ def_delegators( :@parser, :empty? )
35
+ def_delegators( :@parser, :source )
36
+
37
+ def initialize stream
38
+ @entities = {}
39
+ @listeners = nil
40
+ @parser = BaseParser.new( stream )
41
+ @my_stack = []
42
+ end
43
+
44
+ def add_listener( listener )
45
+ @listeners = [] unless @listeners
46
+ @listeners << listener
47
+ end
48
+
49
+ def each
50
+ while has_next?
51
+ yield self.pull
52
+ end
53
+ end
54
+
55
+ def peek depth=0
56
+ if @my_stack.length <= depth
57
+ (depth - @my_stack.length + 1).times {
58
+ e = PullEvent.new(@parser.pull)
59
+ @my_stack.push(e)
60
+ }
61
+ end
62
+ @my_stack[depth]
63
+ end
64
+
65
+ def pull
66
+ return @my_stack.shift if @my_stack.length > 0
67
+
68
+ event = @parser.pull
69
+ case event[0]
70
+ when :entitydecl
71
+ @entities[ event[1] ] =
72
+ event[2] unless event[2] =~ /PUBLIC|SYSTEM/
73
+ when :text
74
+ unnormalized = @parser.unnormalize( event[1], @entities )
75
+ event << unnormalized
76
+ end
77
+ PullEvent.new( event )
78
+ end
79
+
80
+ def unshift token
81
+ @my_stack.unshift token
82
+ end
83
+ end
84
+
85
+ # A parsing event. The contents of the event are accessed as an +Array?,
86
+ # and the type is given either by the ...? methods, or by accessing the
87
+ # +type+ accessor. The contents of this object vary from event to event,
88
+ # but are identical to the arguments passed to +StreamListener+s for each
89
+ # event.
90
+ class PullEvent
91
+ # The type of this event. Will be one of :tag_start, :tag_end, :text,
92
+ # :processing_instruction, :comment, :doctype, :attlistdecl, :entitydecl,
93
+ # :notationdecl, :entity, :cdata, :xmldecl, or :error.
94
+ def initialize(arg)
95
+ @contents = arg
96
+ end
97
+
98
+ def []( start, endd=nil)
99
+ if start.kind_of? Range
100
+ @contents.slice( start.begin+1 .. start.end )
101
+ elsif start.kind_of? Numeric
102
+ if endd.nil?
103
+ @contents.slice( start+1 )
104
+ else
105
+ @contents.slice( start+1, endd )
106
+ end
107
+ else
108
+ raise "Illegal argument #{start.inspect} (#{start.class})"
109
+ end
110
+ end
111
+
112
+ def event_type
113
+ @contents[0]
114
+ end
115
+
116
+ # Content: [ String tag_name, Hash attributes ]
117
+ def start_element?
118
+ @contents[0] == :start_element
119
+ end
120
+
121
+ # Content: [ String tag_name ]
122
+ def end_element?
123
+ @contents[0] == :end_element
124
+ end
125
+
126
+ # Content: [ String raw_text, String unnormalized_text ]
127
+ def text?
128
+ @contents[0] == :text
129
+ end
130
+
131
+ # Content: [ String text ]
132
+ def instruction?
133
+ @contents[0] == :processing_instruction
134
+ end
135
+
136
+ # Content: [ String text ]
137
+ def comment?
138
+ @contents[0] == :comment
139
+ end
140
+
141
+ # Content: [ String name, String pub_sys, String long_name, String uri ]
142
+ def doctype?
143
+ @contents[0] == :start_doctype
144
+ end
145
+
146
+ # Content: [ String text ]
147
+ def attlistdecl?
148
+ @contents[0] == :attlistdecl
149
+ end
150
+
151
+ # Content: [ String text ]
152
+ def elementdecl?
153
+ @contents[0] == :elementdecl
154
+ end
155
+
156
+ # Due to the wonders of DTDs, an entity declaration can be just about
157
+ # anything. There's no way to normalize it; you'll have to interpret the
158
+ # content yourself. However, the following is true:
159
+ #
160
+ # * If the entity declaration is an internal entity:
161
+ # [ String name, String value ]
162
+ # Content: [ String text ]
163
+ def entitydecl?
164
+ @contents[0] == :entitydecl
165
+ end
166
+
167
+ # Content: [ String text ]
168
+ def notationdecl?
169
+ @contents[0] == :notationdecl
170
+ end
171
+
172
+ # Content: [ String text ]
173
+ def entity?
174
+ @contents[0] == :entity
175
+ end
176
+
177
+ # Content: [ String text ]
178
+ def cdata?
179
+ @contents[0] == :cdata
180
+ end
181
+
182
+ # Content: [ String version, String encoding, String standalone ]
183
+ def xmldecl?
184
+ @contents[0] == :xmldecl
185
+ end
186
+
187
+ def error?
188
+ @contents[0] == :error
189
+ end
190
+
191
+ def inspect
192
+ @contents[0].to_s + ": " + @contents[1..-1].inspect
193
+ end
194
+ end
195
+ end
196
+ end
@@ -0,0 +1,238 @@
1
+ require 'rexml/parsers/baseparser'
2
+ require 'rexml/parseexception'
3
+ require 'rexml/namespace'
4
+ require 'rexml/text'
5
+
6
+ module REXML
7
+ module Parsers
8
+ # SAX2Parser
9
+ class SAX2Parser
10
+ def initialize source
11
+ @parser = BaseParser.new(source)
12
+ @listeners = []
13
+ @procs = []
14
+ @namespace_stack = []
15
+ @has_listeners = false
16
+ @tag_stack = []
17
+ @entities = {}
18
+ end
19
+
20
+ def source
21
+ @parser.source
22
+ end
23
+
24
+ def add_listener( listener )
25
+ @parser.add_listener( listener )
26
+ end
27
+
28
+ # Listen arguments:
29
+ #
30
+ # Symbol, Array, Block
31
+ # Listen to Symbol events on Array elements
32
+ # Symbol, Block
33
+ # Listen to Symbol events
34
+ # Array, Listener
35
+ # Listen to all events on Array elements
36
+ # Array, Block
37
+ # Listen to :start_element events on Array elements
38
+ # Listener
39
+ # Listen to All events
40
+ #
41
+ # Symbol can be one of: :start_element, :end_element,
42
+ # :start_prefix_mapping, :end_prefix_mapping, :characters,
43
+ # :processing_instruction, :doctype, :attlistdecl, :elementdecl,
44
+ # :entitydecl, :notationdecl, :cdata, :xmldecl, :comment
45
+ #
46
+ # There is an additional symbol that can be listened for: :progress.
47
+ # This will be called for every event generated, passing in the current
48
+ # stream position.
49
+ #
50
+ # Array contains regular expressions or strings which will be matched
51
+ # against fully qualified element names.
52
+ #
53
+ # Listener must implement the methods in SAX2Listener
54
+ #
55
+ # Block will be passed the same arguments as a SAX2Listener method would
56
+ # be, where the method name is the same as the matched Symbol.
57
+ # See the SAX2Listener for more information.
58
+ def listen( *args, &blok )
59
+ if args[0].kind_of? Symbol
60
+ if args.size == 2
61
+ args[1].each { |match| @procs << [args[0], match, blok] }
62
+ else
63
+ add( [args[0], nil, blok] )
64
+ end
65
+ elsif args[0].kind_of? Array
66
+ if args.size == 2
67
+ args[0].each { |match| add( [nil, match, args[1]] ) }
68
+ else
69
+ args[0].each { |match| add( [ :start_element, match, blok ] ) }
70
+ end
71
+ else
72
+ add([nil, nil, args[0]])
73
+ end
74
+ end
75
+
76
+ def deafen( listener=nil, &blok )
77
+ if listener
78
+ @listeners.delete_if {|item| item[-1] == listener }
79
+ @has_listeners = false if @listeners.size == 0
80
+ else
81
+ @procs.delete_if {|item| item[-1] == blok }
82
+ end
83
+ end
84
+
85
+ def parse
86
+ @procs.each { |sym,match,block| block.call if sym == :start_document }
87
+ @listeners.each { |sym,match,block|
88
+ block.start_document if sym == :start_document or sym.nil?
89
+ }
90
+ root = context = []
91
+ while true
92
+ event = @parser.pull
93
+ case event[0]
94
+ when :end_document
95
+ handle( :end_document )
96
+ break
97
+ when :start_doctype
98
+ handle( :doctype, *event[1..-1])
99
+ when :end_doctype
100
+ context = context[1]
101
+ when :start_element
102
+ @tag_stack.push(event[1])
103
+ # find the observers for namespaces
104
+ procs = get_procs( :start_prefix_mapping, event[1] )
105
+ listeners = get_listeners( :start_prefix_mapping, event[1] )
106
+ if procs or listeners
107
+ # break out the namespace declarations
108
+ # The attributes live in event[2]
109
+ event[2].each {|n, v| event[2][n] = @parser.normalize(v)}
110
+ nsdecl = event[2].find_all { |n, value| n =~ /^xmlns(:|$)/ }
111
+ nsdecl.collect! { |n, value| [ n[6..-1], value ] }
112
+ @namespace_stack.push({})
113
+ nsdecl.each do |n,v|
114
+ @namespace_stack[-1][n] = v
115
+ # notify observers of namespaces
116
+ procs.each { |ob| ob.call( n, v ) } if procs
117
+ listeners.each { |ob| ob.start_prefix_mapping(n, v) } if listeners
118
+ end
119
+ end
120
+ event[1] =~ Namespace::NAMESPLIT
121
+ prefix = $1
122
+ local = $2
123
+ uri = get_namespace(prefix)
124
+ # find the observers for start_element
125
+ procs = get_procs( :start_element, event[1] )
126
+ listeners = get_listeners( :start_element, event[1] )
127
+ # notify observers
128
+ procs.each { |ob| ob.call( uri, local, event[1], event[2] ) } if procs
129
+ listeners.each { |ob|
130
+ ob.start_element( uri, local, event[1], event[2] )
131
+ } if listeners
132
+ when :end_element
133
+ @tag_stack.pop
134
+ event[1] =~ Namespace::NAMESPLIT
135
+ prefix = $1
136
+ local = $2
137
+ uri = get_namespace(prefix)
138
+ # find the observers for start_element
139
+ procs = get_procs( :end_element, event[1] )
140
+ listeners = get_listeners( :end_element, event[1] )
141
+ # notify observers
142
+ procs.each { |ob| ob.call( uri, local, event[1] ) } if procs
143
+ listeners.each { |ob|
144
+ ob.end_element( uri, local, event[1] )
145
+ } if listeners
146
+
147
+ namespace_mapping = @namespace_stack.pop
148
+ # find the observers for namespaces
149
+ procs = get_procs( :end_prefix_mapping, event[1] )
150
+ listeners = get_listeners( :end_prefix_mapping, event[1] )
151
+ if procs or listeners
152
+ namespace_mapping.each do |prefix, uri|
153
+ # notify observers of namespaces
154
+ procs.each { |ob| ob.call( prefix ) } if procs
155
+ listeners.each { |ob| ob.end_prefix_mapping(prefix) } if listeners
156
+ end
157
+ end
158
+ when :text
159
+ #normalized = @parser.normalize( event[1] )
160
+ #handle( :characters, normalized )
161
+ copy = event[1].clone
162
+ @entities.each { |key, value| copy = copy.gsub("&#{key};", value) }
163
+ copy.gsub!( Text::NUMERICENTITY ) {|m|
164
+ m=$1
165
+ m = "0#{m}" if m[0] == ?x
166
+ [Integer(m)].pack('U*')
167
+ }
168
+ handle( :characters, copy )
169
+ when :entitydecl
170
+ @entities[ event[1] ] = event[2] if event.size == 3
171
+ handle( *event )
172
+ when :processing_instruction, :comment, :attlistdecl,
173
+ :elementdecl, :cdata, :notationdecl, :xmldecl
174
+ handle( *event )
175
+ end
176
+ handle( :progress, @parser.position )
177
+ end
178
+ end
179
+
180
+ private
181
+ def handle( symbol, *arguments )
182
+ tag = @tag_stack[-1]
183
+ procs = get_procs( symbol, tag )
184
+ listeners = get_listeners( symbol, tag )
185
+ # notify observers
186
+ procs.each { |ob| ob.call( *arguments ) } if procs
187
+ listeners.each { |l|
188
+ l.send( symbol.to_s, *arguments )
189
+ } if listeners
190
+ end
191
+
192
+ # The following methods are duplicates, but it is faster than using
193
+ # a helper
194
+ def get_procs( symbol, name )
195
+ return nil if @procs.size == 0
196
+ @procs.find_all do |sym, match, block|
197
+ #puts sym.inspect+"=="+symbol.inspect+ "\t"+match.inspect+"=="+name.inspect+ "\t"+( (sym.nil? or symbol == sym) and ((name.nil? and match.nil?) or match.nil? or ( (name == match) or (match.kind_of? Regexp and name =~ match)))).to_s
198
+ (
199
+ (sym.nil? or symbol == sym) and
200
+ ((name.nil? and match.nil?) or match.nil? or (
201
+ (name == match) or
202
+ (match.kind_of? Regexp and name =~ match)
203
+ )
204
+ )
205
+ )
206
+ end.collect{|x| x[-1]}
207
+ end
208
+ def get_listeners( symbol, name )
209
+ return nil if @listeners.size == 0
210
+ @listeners.find_all do |sym, match, block|
211
+ (
212
+ (sym.nil? or symbol == sym) and
213
+ ((name.nil? and match.nil?) or match.nil? or (
214
+ (name == match) or
215
+ (match.kind_of? Regexp and name =~ match)
216
+ )
217
+ )
218
+ )
219
+ end.collect{|x| x[-1]}
220
+ end
221
+
222
+ def add( pair )
223
+ if pair[-1].respond_to? :call
224
+ @procs << pair unless @procs.include? pair
225
+ else
226
+ @listeners << pair unless @listeners.include? pair
227
+ @has_listeners = true
228
+ end
229
+ end
230
+
231
+ def get_namespace( prefix )
232
+ uris = (@namespace_stack.find_all { |ns| not ns[prefix].nil? }) ||
233
+ (@namespace_stack.find { |ns| not ns[nil].nil? })
234
+ uris[-1][prefix] unless uris.nil? or 0 == uris.size
235
+ end
236
+ end
237
+ end
238
+ end