xmlcodec 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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