xmlcodec 0.0.1

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.
@@ -0,0 +1,116 @@
1
+ require "rexml/document"
2
+ require "XMLUtils"
3
+
4
+ module XMLUtils
5
+ class XMLSParserContents
6
+ def initialize
7
+ @contents = []
8
+ end
9
+
10
+ def add(content)
11
+ @contents << content
12
+ end
13
+
14
+ def <<(content)
15
+ add(content)
16
+ end
17
+
18
+ def array_from(start)
19
+ @contents[start..-1]
20
+ end
21
+
22
+ def text_from(start)
23
+ @contents[start..-1].join
24
+ end
25
+
26
+ def size
27
+ @contents.size
28
+ end
29
+
30
+ def erase_from(start)
31
+ @contents = @contents[0..(start-1)]
32
+ end
33
+ end
34
+
35
+ class XMLSParserElement
36
+ attr_reader :name
37
+ attr_reader :element_id
38
+ attr_reader :parent_id
39
+ attr_reader :elstart
40
+
41
+ def initialize(name, elstart, parser, parent_id)
42
+ @name = name
43
+ @parser = parser
44
+ @elstart = elstart
45
+ @element_id = parser.new_element_id
46
+ @parent_id = parent_id
47
+ end
48
+
49
+ def self.root(parser)
50
+ self.new("__XML_ROOT__", 0, parser, nil)
51
+ end
52
+
53
+ def content
54
+ @parser.contents.text_from(@elstart)
55
+ end
56
+
57
+ def consume
58
+ @parser.consume
59
+ end
60
+ end
61
+
62
+ class XMLStreamParser
63
+ attr_reader :contents
64
+
65
+ def initialize(listener=nil)
66
+ @listener = listener
67
+ @contents = XMLSParserContents.new
68
+ @elid = 0
69
+ @elements = [XMLSParserElement.root(self)]
70
+ end
71
+
72
+ def new_element_id
73
+ previous = @elid
74
+ @elid += 1
75
+ previous
76
+ end
77
+
78
+ def parse(text)
79
+ REXML::Document.parse_stream(text, self)
80
+ end
81
+
82
+ def tag_start(name, attrs)
83
+ @elements << XMLSParserElement.new(name, @contents.size,
84
+ self, @elements[-1].element_id)
85
+ @contents << XMLUtils.create_open_tag(name, attrs)
86
+ end
87
+
88
+ def text(text)
89
+ @contents << text
90
+ end
91
+
92
+ def tag_end(name)
93
+ @contents << "</"+name+">"
94
+ element(name)
95
+ @elements.pop()
96
+ end
97
+
98
+ def content
99
+ @contents.text_from(@elements[-1].elstart)
100
+ end
101
+
102
+ def element(name)
103
+ if @listener
104
+ @listener.element(@elements[-1])
105
+ end
106
+ end
107
+
108
+ def consume
109
+ @contents.erase_from(@elements[-1].elstart)
110
+ end
111
+
112
+ # Ignore everything except tags and text for now
113
+ def method_missing(methId, *args)
114
+ end
115
+ end
116
+ end
@@ -0,0 +1,103 @@
1
+ require 'XMLUtils'
2
+
3
+ module XMLCodec
4
+ # A simple element that has only text inside. This is used inside
5
+ # XMLSubElements to be able to directly add strings to it and have them be
6
+ # treated as XML text elements.
7
+ class XMLTextElement
8
+ attr_accessor :__parent
9
+
10
+ # Create a new element for the given string.
11
+ def initialize(string)
12
+ @value = XMLUtils::escape_xml(string)
13
+ end
14
+
15
+ # Simple to_s method that just returns the included string
16
+ def to_s
17
+ @value
18
+ end
19
+
20
+ # Creates the XML for the element by using add_text on the value.
21
+ def create_xml(parent)
22
+ parent.add_text(@value)
23
+ end
24
+
25
+ # The XML text of the element is simply it's string value.
26
+ def xml_text
27
+ @value
28
+ end
29
+
30
+ def end_partial_export(file)
31
+ end
32
+
33
+ def partial_export(file, except=nil)
34
+ file << __parent.indentation(1)+self.xml_text+"\n"
35
+ end
36
+ end
37
+
38
+ # This is the container class used to hold the elements for the xmlsubelements
39
+ # and xmlsubel_mult in a XMLElement.
40
+ class XMLSubElements
41
+ include Enumerable
42
+
43
+ # Create a new instance of the container
44
+ def initialize(parent)
45
+ @elements = []
46
+ @parent = parent
47
+ end
48
+
49
+ private
50
+ # Get the class for a given element name
51
+ def elclass(name)
52
+ XMLElement.get_element_class(name)
53
+ end
54
+
55
+ public
56
+ # Adds a value to the collection. The value may be either a String or a
57
+ # descendant of XMLElement. If it's a string it's converted into a
58
+ # XMLTextElement
59
+ def <<(value)
60
+ if value.instance_of? String
61
+ value = XMLTextElement.new(value)
62
+ end
63
+ value.__parent = @parent
64
+ @elements << value
65
+ end
66
+
67
+ # Get the element with the given number
68
+ def [](num)
69
+ @elements[num]
70
+ end
71
+
72
+ # Get the number of elements in the container
73
+ def size
74
+ @elements.size
75
+ end
76
+
77
+ # Create the XML of all the elements by creating the XML for each of them.
78
+ def create_xml(parent)
79
+ @elements.each{|e| e.create_xml(parent)}
80
+ end
81
+
82
+ # Import the XML of all the elements
83
+ def import_xml(xmlelements)
84
+ xmlelements.each do |xmlel|
85
+ if xmlel.kind_of? REXML::Text
86
+ self << xmlel.to_s
87
+ else
88
+ self << elclass(xmlel.name.to_sym).import_xml(xmlel)
89
+ end
90
+ end
91
+ end
92
+
93
+ # Iterate all the values in the collection
94
+ def each
95
+ @elements.each {|e| yield e}
96
+ end
97
+
98
+ # Delete a certain element from the collection
99
+ def delete_element(element)
100
+ @elements.delete(element)
101
+ end
102
+ end
103
+ end
data/lib/XMLUtils.rb ADDED
@@ -0,0 +1,58 @@
1
+ require "rexml/document"
2
+
3
+ # This module holds generic XML utilities. The module's methods are simple
4
+ # XML utilities. The module also contains XMLStreamParser, a Generic XML Stream
5
+ # parser whose events are the text of whole elements instead of start and end
6
+ # tags.
7
+
8
+ module XMLUtils
9
+ # Gets the REXML DOM for a given filename that must be a XML file.
10
+ def self.getdoc(filename)
11
+ file = File.new(filename, 'r')
12
+ REXML::Document.new file
13
+ end
14
+
15
+ # Count the number of elements that correspond to a given xpath in a file.
16
+ def self.count_elements(path, filename)
17
+ doc = getdoc(filename)
18
+ i = 0
19
+ XPath.each(doc, path) {|element| i+=1}
20
+ return i
21
+ end
22
+
23
+ # Test if a given xpath exists in the file.
24
+ def self.element_exists(path, filename)
25
+ count_elements(path,filename)>0
26
+ end
27
+
28
+ # Get a xpath from a REXML document.
29
+ def self.select_path_doc(path, doc)
30
+ element = REXML::XPath.first(doc, path)
31
+ return "" if not element
32
+ if element.respond_to?("value")
33
+ return element.value || ""
34
+ end
35
+ return element.text || ""
36
+ end
37
+
38
+ # Get a xpath from a file.
39
+ def self.select_path(path, filename)
40
+ XMLUtils::select_path_doc(path, getdoc(filename))
41
+ end
42
+
43
+ # Create an open tag.
44
+ def self.create_open_tag(name, attrs)
45
+ str = "<"+name
46
+ attrs.each {|name, value| str << " #{name}='#{value}'"}
47
+ str << ">"
48
+ str
49
+ end
50
+
51
+ # Escape a string so that it can be included in a XML document
52
+ def self.escape_xml(string)
53
+ t = REXML::Text.new('')
54
+ str = ''
55
+ t.write_with_substitution(str, string)
56
+ str
57
+ end
58
+ end
data/lib/xmlcodec.rb ADDED
@@ -0,0 +1,4 @@
1
+ require 'XMLElement'
2
+ require 'XMLUtils'
3
+ require 'XMLStreamObjectParser'
4
+ require 'XMLStreamParser'
@@ -0,0 +1,145 @@
1
+ require 'test/unit'
2
+ require 'XMLUtils'
3
+ require 'simple_objects'
4
+
5
+ class TestPartialExport < Test::Unit::TestCase
6
+ def validate_well_formed(filename)
7
+ assert(system("rxp", "-xs", filename))
8
+ end
9
+
10
+ def compare_xpath(value, filename, path)
11
+ assert_equal(value.strip, XMLUtils::select_path(path, filename).strip)
12
+ end
13
+
14
+ def test_simple
15
+ value = "somevalue"
16
+ filename = 'test_partial_export_simple.xml'
17
+
18
+ file = File.open(filename, "w")
19
+ sel = SimpleElement.new(value)
20
+ sel.partial_export(file)
21
+ file.close
22
+
23
+ validate_well_formed(filename)
24
+ compare_xpath(value, filename, "/abc")
25
+ end
26
+
27
+ def test_double
28
+ value = 'somevalue'
29
+ filename = 'test_partial_export_double.xml'
30
+
31
+ file = File.open(filename, "w")
32
+ sel = SimpleElement.new(value)
33
+ el = TestElement.new
34
+ el.abc = sel
35
+
36
+ sel.partial_export(file)
37
+ el.end_partial_export(file)
38
+ file.close
39
+
40
+ validate_well_formed(filename)
41
+ compare_xpath(value, filename, "/subel/abc")
42
+ end
43
+
44
+ def test_triple
45
+ value = 'somevalue'
46
+ filename = 'test_partial_export_double.xml'
47
+
48
+ file = File.open(filename, "w")
49
+ sel = SimpleElement.new(value)
50
+ el1 = TestElement.new
51
+ el2 = TestElement.new
52
+ el1.subel = el2
53
+ el2.abc = sel
54
+
55
+ sel.partial_export(file)
56
+ el1.end_partial_export(file)
57
+ file.close
58
+
59
+ validate_well_formed(filename)
60
+ compare_xpath(value, filename, "/subel/subel/abc")
61
+ end
62
+
63
+ def test_attr
64
+ value = 'somevalue'
65
+ filename = 'test_partial_export_double.xml'
66
+
67
+ file = File.open(filename, "w")
68
+ el = TestElement.new
69
+ el.someattr = value
70
+
71
+ el.partial_export(file)
72
+ file.close
73
+
74
+ compare_xpath(value, filename, "/subel/@someattr")
75
+ end
76
+
77
+ def test_mult
78
+ value1 = 'somevalue1'
79
+ value2 = 'somevalue2'
80
+ filename = 'test_partial_export_mult.xml'
81
+
82
+ file = File.open(filename, "w")
83
+ sel1 = SimpleElement.new(value1)
84
+ sel2 = SimpleElement.new(value2)
85
+
86
+ el = SubelMultElement.new
87
+ el.abc << sel1
88
+ sel1.partial_export(file)
89
+
90
+ el.abc << sel2
91
+ sel2.partial_export(file)
92
+ el.end_partial_export(file)
93
+
94
+ file.close
95
+
96
+ validate_well_formed(filename)
97
+ compare_xpath(value1, filename, "/mult/abc[1]")
98
+ compare_xpath(value2, filename, "/mult/abc[2]")
99
+ end
100
+
101
+ def test_subelements
102
+ value1 = 'somevalue1'
103
+ value2 = 'somevalue2'
104
+ filename = 'test_partial_export_subelements.xml'
105
+
106
+ file = File.open(filename, "w")
107
+ sel1 = SimpleElement.new(value1)
108
+ sel2 = SimpleElement.new(value2)
109
+
110
+ el = SubelElement.new
111
+ el.subelements << sel1
112
+ sel1.partial_export(file)
113
+
114
+ el.subelements << sel2
115
+ sel2.partial_export(file)
116
+ el.end_partial_export(file)
117
+
118
+ file.close
119
+
120
+ validate_well_formed(filename)
121
+ compare_xpath(value1, filename, "/subels/abc[1]")
122
+ compare_xpath(value2, filename, "/subels/abc[2]")
123
+ end
124
+
125
+ def test_subelements_multiple
126
+ value1 = 'somevalue1'
127
+ value2 = 'somevalue2'
128
+ filename = 'test_partial_export_subelements2.xml'
129
+
130
+ file = File.open(filename, "w")
131
+ sel1 = SimpleElement.new(value1)
132
+ sel2 = SimpleElement.new(value2)
133
+
134
+ el = SubelElement.new
135
+ el.subelements << sel1
136
+ el.subelements << sel2
137
+ el.partial_export(file)
138
+
139
+ file.close
140
+
141
+ validate_well_formed(filename)
142
+ compare_xpath(value1, filename, "/subels/abc[1]")
143
+ compare_xpath(value2, filename, "/subels/abc[2]")
144
+ end
145
+ end
@@ -0,0 +1,177 @@
1
+ require 'test/unit'
2
+ require 'xmlcodec'
3
+ require 'rexml/document'
4
+ require 'simple_objects'
5
+
6
+ class TestXMLElement < Test::Unit::TestCase
7
+ def test_xmlsubel
8
+ value = 'test'
9
+
10
+ el = TestElement.new
11
+ sel = SimpleElement.new(value)
12
+
13
+ el.abc = sel
14
+ assert_equal(sel, el.abc)
15
+ assert_equal(value, sel.value)
16
+ assert_equal(el, sel.__parent)
17
+
18
+ assert_equal('<abc>'+value+'</abc>', sel.xml_text)
19
+ assert_equal('<subel><abc>'+value+'</abc></subel>', el.xml_text)
20
+
21
+ dom = REXML::Document.new
22
+ el.create_xml(dom)
23
+
24
+ assert_equal 1, dom.elements.size
25
+ eldom = dom.elements['subel']
26
+ assert_not_nil eldom
27
+ assert_equal 1, eldom.elements.size
28
+
29
+ seldom = eldom.elements['abc']
30
+ assert_not_nil seldom
31
+ assert_equal value, seldom.text
32
+
33
+ el = TestElement.import_xml(dom)
34
+ assert_equal value, el.abc.value
35
+ end
36
+
37
+ def test_xmlattr
38
+ value = 'test'
39
+ el = TestElement.new
40
+
41
+ el.someattr = value
42
+ assert_equal(value, el.someattr)
43
+
44
+ assert_equal("<subel someattr='"+value+"'></subel>", el.xml_text)
45
+
46
+ dom = REXML::Document.new
47
+ el.create_xml(dom)
48
+
49
+ assert_equal 1, dom.elements.size
50
+ eldom = dom.elements['subel']
51
+ assert_not_nil eldom
52
+ assert_equal value, eldom.attributes['someattr']
53
+
54
+ el = TestElement.import_xml(dom)
55
+ assert_equal value, el.someattr
56
+ end
57
+
58
+ def test_xmlsubelements
59
+ value = 'test'
60
+ el = SubelElement.new
61
+ sel = SimpleElement.new(value)
62
+
63
+ el.subelements << sel
64
+ assert_equal(sel, el.subelements[0])
65
+ assert_equal(el, sel.__parent)
66
+
67
+ assert_equal('<subels><abc>'+value+'</abc></subels>', el.xml_text)
68
+
69
+ dom = REXML::Document.new
70
+ el.create_xml(dom)
71
+
72
+ assert_equal 1, dom.elements.size
73
+ eldom = dom.elements['subels']
74
+ assert_not_nil eldom
75
+ assert_equal 1, eldom.elements.size
76
+
77
+ seldom = eldom.elements['abc']
78
+ assert_not_nil seldom
79
+ assert_equal value, seldom.text
80
+
81
+ el = SubelElement.import_xml(dom)
82
+ assert_equal 1, el.subelements.size
83
+ assert_equal value, el.subelements[0].value
84
+ end
85
+
86
+ def test_xmlsubel_mult
87
+ value1 = 'test'
88
+ value2 = 'test2'
89
+ el = SubelMultElement.new
90
+ sel1 = SimpleElement.new(value1)
91
+ sel2 = SimpleElement.new(value2)
92
+
93
+ el.abc << sel1
94
+ el.abc << sel2
95
+
96
+ assert_equal(sel1, el.abc[0])
97
+ assert_equal(el, sel1.__parent)
98
+ assert_equal(el, sel2.__parent)
99
+
100
+ assert_equal('<mult><abc>'+value1+'</abc><abc>'+value2+'</abc></mult>', el.xml_text)
101
+
102
+ dom = REXML::Document.new
103
+ el.create_xml(dom)
104
+
105
+ assert_equal 1, dom.elements.size
106
+ eldom = dom.elements['mult']
107
+ assert_not_nil eldom
108
+ assert_equal 2, eldom.elements.size
109
+
110
+ [value1, value2].each_with_index do |value, index|
111
+ seldom = eldom.elements[index+1]
112
+ assert_not_nil seldom
113
+ assert_equal value, seldom.text
114
+ assert_equal 'abc', seldom.name
115
+ end
116
+
117
+ el = SubelMultElement.import_xml(dom)
118
+ assert_equal 2, el.abc.size
119
+ assert_equal value1, el.abc[0].value
120
+ assert_equal value2, el.abc[1].value
121
+ end
122
+
123
+ def test_delete_element_simple
124
+ el = TestElement.new
125
+ sel = SimpleElement.new('')
126
+ el.abc = sel
127
+ assert_equal sel, el.abc
128
+
129
+ el.delete_element(sel)
130
+ assert_nil el.abc
131
+ end
132
+
133
+ def test_delete_element_double
134
+ el = TestElement.new
135
+ sel1 = SimpleElement.new('')
136
+ sel2 = SimpleElement2.new('')
137
+ el.abc = sel1
138
+ el.abc2 = sel2
139
+
140
+ assert_equal sel1, el.abc
141
+ assert_equal sel2, el.abc2
142
+
143
+ el.delete_element(sel1)
144
+ assert_nil el.abc
145
+ assert_equal sel2, el.abc2
146
+ end
147
+
148
+ def test_delete_element_multiple
149
+ el = SubelMultElement.new
150
+ sel = SimpleElement.new('')
151
+ el.abc << sel
152
+ assert_equal sel, el.abc[0]
153
+
154
+ el.delete_element(sel)
155
+ assert_nil el.abc[0]
156
+ end
157
+
158
+ def test_delete_element_subelements
159
+ el = SubelElement.new
160
+ sel1 = SimpleElement.new('')
161
+ sel2 = SimpleElement.new('')
162
+ el.subelements << sel1
163
+ el.subelements << sel2
164
+
165
+ assert_equal 2, el.subelements.size
166
+ assert_equal sel1, el.subelements[0]
167
+ assert_equal sel2, el.subelements[1]
168
+
169
+ el.delete_element(sel1)
170
+ assert_equal 1, el.subelements.size
171
+ assert_equal sel2, el.subelements[0]
172
+
173
+ el.delete_element(sel2)
174
+ assert_equal 0, el.subelements.size
175
+ assert_nil el.subelements[0]
176
+ end
177
+ end