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,18 @@
1
+ module REXML
2
+ module Encoding
3
+ def encode_utf8 content
4
+ content
5
+ end
6
+
7
+ def decode_utf8(str)
8
+ str
9
+ end
10
+
11
+ register(UTF_8) do |obj|
12
+ class << obj
13
+ alias decode decode_utf8
14
+ alias encode encode_utf8
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,166 @@
1
+ require 'rexml/child'
2
+ require 'rexml/source'
3
+ require 'rexml/xmltokens'
4
+
5
+ module REXML
6
+ # God, I hate DTDs. I really do. Why this idiot standard still
7
+ # plagues us is beyond me.
8
+ class Entity < Child
9
+ include XMLTokens
10
+ PUBIDCHAR = "\x20\x0D\x0Aa-zA-Z0-9\\-()+,./:=?;!*@$_%#"
11
+ SYSTEMLITERAL = %Q{((?:"[^"]*")|(?:'[^']*'))}
12
+ PUBIDLITERAL = %Q{("[#{PUBIDCHAR}']*"|'[#{PUBIDCHAR}]*')}
13
+ EXTERNALID = "(?:(?:(SYSTEM)\\s+#{SYSTEMLITERAL})|(?:(PUBLIC)\\s+#{PUBIDLITERAL}\\s+#{SYSTEMLITERAL}))"
14
+ NDATADECL = "\\s+NDATA\\s+#{NAME}"
15
+ PEREFERENCE = "%#{NAME};"
16
+ ENTITYVALUE = %Q{((?:"(?:[^%&"]|#{PEREFERENCE}|#{REFERENCE})*")|(?:'([^%&']|#{PEREFERENCE}|#{REFERENCE})*'))}
17
+ PEDEF = "(?:#{ENTITYVALUE}|#{EXTERNALID})"
18
+ ENTITYDEF = "(?:#{ENTITYVALUE}|(?:#{EXTERNALID}(#{NDATADECL})?))"
19
+ PEDECL = "<!ENTITY\\s+(%)\\s+#{NAME}\\s+#{PEDEF}\\s*>"
20
+ GEDECL = "<!ENTITY\\s+#{NAME}\\s+#{ENTITYDEF}\\s*>"
21
+ ENTITYDECL = /\s*(?:#{GEDECL})|(?:#{PEDECL})/um
22
+
23
+ attr_reader :name, :external, :ref, :ndata, :pubid
24
+
25
+ # Create a new entity. Simple entities can be constructed by passing a
26
+ # name, value to the constructor; this creates a generic, plain entity
27
+ # reference. For anything more complicated, you have to pass a Source to
28
+ # the constructor with the entity definiton, or use the accessor methods.
29
+ # +WARNING+: There is no validation of entity state except when the entity
30
+ # is read from a stream. If you start poking around with the accessors,
31
+ # you can easily create a non-conformant Entity. The best thing to do is
32
+ # dump the stupid DTDs and use XMLSchema instead.
33
+ #
34
+ # e = Entity.new( 'amp', '&' )
35
+ def initialize stream, value=nil, parent=nil, reference=false
36
+ super(parent)
37
+ @ndata = @pubid = @value = @external = nil
38
+ if stream.kind_of? Array
39
+ @name = stream[1]
40
+ if stream[-1] == '%'
41
+ @reference = true
42
+ stream.pop
43
+ else
44
+ @reference = false
45
+ end
46
+ if stream[2] =~ /SYSTEM|PUBLIC/
47
+ @external = stream[2]
48
+ if @external == 'SYSTEM'
49
+ @ref = stream[3]
50
+ @ndata = stream[4] if stream.size == 5
51
+ else
52
+ @pubid = stream[3]
53
+ @ref = stream[4]
54
+ end
55
+ else
56
+ @value = stream[2]
57
+ end
58
+ else
59
+ @reference = reference
60
+ @external = nil
61
+ @name = stream
62
+ @value = value
63
+ end
64
+ end
65
+
66
+ # Evaluates whether the given string matchs an entity definition,
67
+ # returning true if so, and false otherwise.
68
+ def Entity::matches? string
69
+ (ENTITYDECL =~ string) == 0
70
+ end
71
+
72
+ # Evaluates to the unnormalized value of this entity; that is, replacing
73
+ # all entities -- both %ent; and &ent; entities. This differs from
74
+ # +value()+ in that +value+ only replaces %ent; entities.
75
+ def unnormalized
76
+ document.record_entity_expansion unless document.nil?
77
+ v = value()
78
+ return nil if v.nil?
79
+ @unnormalized = Text::unnormalize(v, parent)
80
+ @unnormalized
81
+ end
82
+
83
+ #once :unnormalized
84
+
85
+ # Returns the value of this entity unprocessed -- raw. This is the
86
+ # normalized value; that is, with all %ent; and &ent; entities intact
87
+ def normalized
88
+ @value
89
+ end
90
+
91
+ # Write out a fully formed, correct entity definition (assuming the Entity
92
+ # object itself is valid.)
93
+ #
94
+ # out::
95
+ # An object implementing <TT>&lt;&lt;<TT> to which the entity will be
96
+ # output
97
+ # indent::
98
+ # *DEPRECATED* and ignored
99
+ def write out, indent=-1
100
+ out << '<!ENTITY '
101
+ out << '% ' if @reference
102
+ out << @name
103
+ out << ' '
104
+ if @external
105
+ out << @external << ' '
106
+ if @pubid
107
+ q = @pubid.include?('"')?"'":'"'
108
+ out << q << @pubid << q << ' '
109
+ end
110
+ q = @ref.include?('"')?"'":'"'
111
+ out << q << @ref << q
112
+ out << ' NDATA ' << @ndata if @ndata
113
+ else
114
+ q = @value.include?('"')?"'":'"'
115
+ out << q << @value << q
116
+ end
117
+ out << '>'
118
+ end
119
+
120
+ # Returns this entity as a string. See write().
121
+ def to_s
122
+ rv = ''
123
+ write rv
124
+ rv
125
+ end
126
+
127
+ PEREFERENCE_RE = /#{PEREFERENCE}/um
128
+ # Returns the value of this entity. At the moment, only internal entities
129
+ # are processed. If the value contains internal references (IE,
130
+ # %blah;), those are replaced with their values. IE, if the doctype
131
+ # contains:
132
+ # <!ENTITY % foo "bar">
133
+ # <!ENTITY yada "nanoo %foo; nanoo>
134
+ # then:
135
+ # doctype.entity('yada').value #-> "nanoo bar nanoo"
136
+ def value
137
+ if @value
138
+ matches = @value.scan(PEREFERENCE_RE)
139
+ rv = @value.clone
140
+ if @parent
141
+ matches.each do |entity_reference|
142
+ entity_value = @parent.entity( entity_reference[0] )
143
+ rv.gsub!( /%#{entity_reference};/um, entity_value )
144
+ end
145
+ end
146
+ return rv
147
+ end
148
+ nil
149
+ end
150
+ end
151
+
152
+ # This is a set of entity constants -- the ones defined in the XML
153
+ # specification. These are +gt+, +lt+, +amp+, +quot+ and +apos+.
154
+ module EntityConst
155
+ # +>+
156
+ GT = Entity.new( 'gt', '>' )
157
+ # +<+
158
+ LT = Entity.new( 'lt', '<' )
159
+ # +&+
160
+ AMP = Entity.new( 'amp', '&' )
161
+ # +"+
162
+ QUOT = Entity.new( 'quot', '"' )
163
+ # +'+
164
+ APOS = Entity.new( 'apos', "'" )
165
+ end
166
+ end
@@ -0,0 +1,109 @@
1
+ module REXML
2
+ module Formatters
3
+ class Default
4
+ # Prints out the XML document with no formatting -- except if id_hack is
5
+ # set.
6
+ #
7
+ # ie_hack::
8
+ # If set to true, then inserts whitespace before the close of an empty
9
+ # tag, so that IE's bad XML parser doesn't choke.
10
+ def initialize( ie_hack=false )
11
+ @ie_hack = ie_hack
12
+ end
13
+
14
+ # Writes the node to some output.
15
+ #
16
+ # node::
17
+ # The node to write
18
+ # output::
19
+ # A class implementing <TT>&lt;&lt;</TT>. Pass in an Output object to
20
+ # change the output encoding.
21
+ def write( node, output )
22
+ case node
23
+
24
+ when Document
25
+ if node.xml_decl.encoding != "UTF-8" && !output.kind_of?(Output)
26
+ output = Output.new( output, node.xml_decl.encoding )
27
+ end
28
+ write_document( node, output )
29
+
30
+ when Element
31
+ write_element( node, output )
32
+
33
+ when Declaration, ElementDecl, NotationDecl, ExternalEntity, Entity,
34
+ Attribute, AttlistDecl
35
+ node.write( output,-1 )
36
+
37
+ when Instruction
38
+ write_instruction( node, output )
39
+
40
+ when DocType, XMLDecl
41
+ node.write( output )
42
+
43
+ when Comment
44
+ write_comment( node, output )
45
+
46
+ when CData
47
+ write_cdata( node, output )
48
+
49
+ when Text
50
+ write_text( node, output )
51
+
52
+ else
53
+ raise Exception.new("XML FORMATTING ERROR")
54
+
55
+ end
56
+ end
57
+
58
+ protected
59
+ def write_document( node, output )
60
+ node.children.each { |child| write( child, output ) }
61
+ end
62
+
63
+ def write_element( node, output )
64
+ output << "<#{node.expanded_name}"
65
+
66
+ node.attributes.each_attribute do |attr|
67
+ output << " "
68
+ attr.write( output )
69
+ end unless node.attributes.empty?
70
+
71
+ if node.children.empty?
72
+ output << " " if @ie_hack
73
+ output << "/"
74
+ else
75
+ output << ">"
76
+ node.children.each { |child|
77
+ write( child, output )
78
+ }
79
+ output << "</#{node.expanded_name}"
80
+ end
81
+ output << ">"
82
+ end
83
+
84
+ def write_text( node, output )
85
+ output << node.to_s()
86
+ end
87
+
88
+ def write_comment( node, output )
89
+ output << Comment::START
90
+ output << node.to_s
91
+ output << Comment::STOP
92
+ end
93
+
94
+ def write_cdata( node, output )
95
+ output << CData::START
96
+ output << node.to_s
97
+ output << CData::STOP
98
+ end
99
+
100
+ def write_instruction( node, output )
101
+ output << Instruction::START.sub(/\\/u, '')
102
+ output << node.target
103
+ output << ' '
104
+ output << node.content
105
+ output << Instruction::STOP.sub(/\\/u, '')
106
+ end
107
+ end
108
+ end
109
+ end
@@ -0,0 +1,138 @@
1
+ require 'rexml/formatters/default'
2
+
3
+ module REXML
4
+ module Formatters
5
+ # Pretty-prints an XML document. This destroys whitespace in text nodes
6
+ # and will insert carriage returns and indentations.
7
+ #
8
+ # TODO: Add an option to print attributes on new lines
9
+ class Pretty < Default
10
+
11
+ # If compact is set to true, then the formatter will attempt to use as
12
+ # little space as possible
13
+ attr_accessor :compact
14
+ # The width of a page. Used for formatting text
15
+ attr_accessor :width
16
+
17
+ # Create a new pretty printer.
18
+ #
19
+ # output::
20
+ # An object implementing '<<(String)', to which the output will be written.
21
+ # indentation::
22
+ # An integer greater than 0. The indentation of each level will be
23
+ # this number of spaces. If this is < 1, the behavior of this object
24
+ # is undefined. Defaults to 2.
25
+ # ie_hack::
26
+ # If true, the printer will insert whitespace before closing empty
27
+ # tags, thereby allowing Internet Explorer's feeble XML parser to
28
+ # function. Defaults to false.
29
+ def initialize( indentation=2, ie_hack=false )
30
+ @indentation = indentation
31
+ @level = 0
32
+ @ie_hack = ie_hack
33
+ @width = 80
34
+ end
35
+
36
+ protected
37
+ def write_element(node, output)
38
+ output << ' '*@level
39
+ output << "<#{node.expanded_name}"
40
+
41
+ node.attributes.each_attribute do |attr|
42
+ output << " "
43
+ attr.write( output )
44
+ end unless node.attributes.empty?
45
+
46
+ if node.children.empty?
47
+ if @ie_hack
48
+ output << " "
49
+ end
50
+ output << "/"
51
+ else
52
+ output << ">"
53
+ # If compact and all children are text, and if the formatted output
54
+ # is less than the specified width, then try to print everything on
55
+ # one line
56
+ skip = false
57
+ if compact
58
+ if node.children.inject(true) {|s,c| s & c.kind_of?(Text)}
59
+ string = ""
60
+ old_level = @level
61
+ @level = 0
62
+ node.children.each { |child| write( child, string ) }
63
+ @level = old_level
64
+ if string.length < @width
65
+ output << string
66
+ skip = true
67
+ end
68
+ end
69
+ end
70
+ unless skip
71
+ output << "\n"
72
+ @level += @indentation
73
+ node.children.each { |child|
74
+ next if child.kind_of?(Text) and child.to_s.strip.length == 0
75
+ write( child, output )
76
+ output << "\n"
77
+ }
78
+ @level -= @indentation
79
+ output << ' '*@level
80
+ end
81
+ output << "</#{node.expanded_name}"
82
+ end
83
+ output << ">"
84
+ end
85
+
86
+ def write_text( node, output )
87
+ s = node.to_s()
88
+ s.gsub!(/\s/,' ')
89
+ s.squeeze!(" ")
90
+ s = wrap(s, 80-@level)
91
+ s = indent_text(s, @level, " ", true)
92
+ output << (' '*@level + s)
93
+ end
94
+
95
+ def write_comment( node, output)
96
+ output << ' ' * @level
97
+ super
98
+ end
99
+
100
+ def write_cdata( node, output)
101
+ output << ' ' * @level
102
+ super
103
+ end
104
+
105
+ def write_document( node, output )
106
+ # Ok, this is a bit odd. All XML documents have an XML declaration,
107
+ # but it may not write itself if the user didn't specifically add it,
108
+ # either through the API or in the input document. If it doesn't write
109
+ # itself, then we don't need a carriage return... which makes this
110
+ # logic more complex.
111
+ node.children.each { |child|
112
+ next if child == node.children[-1] and child.instance_of?(Text)
113
+ unless child == node.children[0] or child.instance_of?(Text) or
114
+ (child == node.children[1] and !node.children[0].writethis)
115
+ output << "\n"
116
+ end
117
+ write( child, output )
118
+ }
119
+ end
120
+
121
+ private
122
+ def indent_text(string, level=1, style="\t", indentfirstline=true)
123
+ return string if level < 0
124
+ string.gsub(/\n/, "\n#{style*level}")
125
+ end
126
+
127
+ def wrap(string, width)
128
+ # Recursively wrap string at width.
129
+ return string if string.length <= width
130
+ place = string.rindex(' ', width) # Position in string with last ' ' before cutoff
131
+ return string if place.nil?
132
+ return string[0,place] + "\n" + wrap(string[place+1..-1], width)
133
+ end
134
+
135
+ end
136
+ end
137
+ end
138
+
@@ -0,0 +1,56 @@
1
+ require 'rexml/formatters/pretty'
2
+
3
+ module REXML
4
+ module Formatters
5
+ # The Transitive formatter writes an XML document that parses to an
6
+ # identical document as the source document. This means that no extra
7
+ # whitespace nodes are inserted, and whitespace within text nodes is
8
+ # preserved. Within these constraints, the document is pretty-printed,
9
+ # with whitespace inserted into the metadata to introduce formatting.
10
+ #
11
+ # Note that this is only useful if the original XML is not already
12
+ # formatted. Since this formatter does not alter whitespace nodes, the
13
+ # results of formatting already formatted XML will be odd.
14
+ class Transitive < Default
15
+ def initialize( indentation=2 )
16
+ @indentation = indentation
17
+ @level = 0
18
+ end
19
+
20
+ protected
21
+ def write_element( node, output )
22
+ output << "<#{node.expanded_name}"
23
+
24
+ node.attributes.each_attribute do |attr|
25
+ output << " "
26
+ attr.write( output )
27
+ end unless node.attributes.empty?
28
+
29
+ output << "\n"
30
+ output << ' '*@level
31
+ if node.children.empty?
32
+ output << "/"
33
+ else
34
+ output << ">"
35
+ # If compact and all children are text, and if the formatted output
36
+ # is less than the specified width, then try to print everything on
37
+ # one line
38
+ skip = false
39
+ @level += @indentation
40
+ node.children.each { |child|
41
+ write( child, output )
42
+ }
43
+ @level -= @indentation
44
+ output << "</#{node.expanded_name}"
45
+ output << "\n"
46
+ output << ' '*@level
47
+ end
48
+ output << ">"
49
+ end
50
+
51
+ def write_text( node, output )
52
+ output << node.to_s()
53
+ end
54
+ end
55
+ end
56
+ end