xml-mapping 0.8.1 → 0.9.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.
Files changed (71) hide show
  1. data/ChangeLog +64 -3
  2. data/README +871 -173
  3. data/README_XPATH +40 -13
  4. data/Rakefile +37 -26
  5. data/TODO.txt +39 -8
  6. data/examples/README +5 -0
  7. data/examples/company_usage.intout +34 -22
  8. data/examples/documents_folders.rb +31 -0
  9. data/examples/documents_folders.xml +16 -0
  10. data/examples/documents_folders_usage.intin.rb +18 -0
  11. data/examples/documents_folders_usage.intout +46 -0
  12. data/examples/order_signature_enhanced_usage.intout +21 -11
  13. data/examples/order_usage.intin.rb +52 -5
  14. data/examples/order_usage.intout +154 -80
  15. data/examples/person.intin.rb +44 -0
  16. data/examples/person.intout +27 -0
  17. data/examples/person_mm.intin.rb +119 -0
  18. data/examples/person_mm.intout +114 -0
  19. data/examples/publication.intin.rb +44 -0
  20. data/examples/publication.intout +20 -0
  21. data/examples/reader.intin.rb +33 -0
  22. data/examples/reader.intout +19 -0
  23. data/examples/stringarray.rb +5 -0
  24. data/examples/stringarray.xml +10 -0
  25. data/examples/stringarray_usage.intin.rb +11 -0
  26. data/examples/stringarray_usage.intout +31 -0
  27. data/examples/time_augm.intout +19 -7
  28. data/examples/time_augm_loading.intin.rb +44 -0
  29. data/examples/time_augm_loading.intout +12 -0
  30. data/examples/time_node.intin.rb +79 -0
  31. data/examples/time_node.rb +3 -2
  32. data/examples/time_node_w_marshallers.intin.rb +48 -0
  33. data/examples/time_node_w_marshallers.intout +25 -0
  34. data/examples/time_node_w_marshallers.xml +9 -0
  35. data/examples/xpath_create_new.intout +132 -114
  36. data/examples/xpath_ensure_created.intout +86 -65
  37. data/examples/xpath_pathological.intout +16 -16
  38. data/examples/xpath_usage.intout +1 -1
  39. data/install.rb +1 -0
  40. data/lib/xml/mapping.rb +3 -1
  41. data/lib/xml/mapping/base.rb +442 -272
  42. data/lib/xml/mapping/core_classes_mapping.rb +32 -0
  43. data/lib/xml/mapping/standard_nodes.rb +176 -86
  44. data/lib/xml/mapping/version.rb +2 -2
  45. data/lib/xml/rexml_ext.rb +186 -0
  46. data/lib/xml/xxpath.rb +28 -265
  47. data/lib/xml/xxpath/steps.rb +345 -0
  48. data/lib/xml/xxpath_methods.rb +96 -0
  49. data/test/all_tests.rb +4 -1
  50. data/test/benchmark_fixtures.rb +14 -0
  51. data/test/{multiple_mappings.rb → bookmarks.rb} +0 -0
  52. data/test/company.rb +47 -0
  53. data/test/documents_folders.rb +11 -1
  54. data/test/examples_test.rb +29 -0
  55. data/test/fixtures/benchmark.xml +77 -0
  56. data/test/fixtures/company1.xml +9 -0
  57. data/test/fixtures/documents_folders.xml +0 -8
  58. data/test/fixtures/documents_folders2.xml +13 -19
  59. data/test/fixtures/triangle_m1.xml +17 -0
  60. data/test/fixtures/triangle_m2.xml +19 -0
  61. data/test/inheritance_test.rb +50 -0
  62. data/test/multiple_mappings_test.rb +155 -0
  63. data/test/rexml_xpath_benchmark.rb +29 -0
  64. data/test/triangle_mm.rb +57 -0
  65. data/test/xml_mapping_adv_test.rb +36 -1
  66. data/test/xml_mapping_test.rb +136 -7
  67. data/test/xpath_test.rb +154 -0
  68. data/test/xxpath_benchmark.rb +36 -0
  69. data/test/xxpath_benchmark.result1.txt +17 -0
  70. data/test/xxpath_methods_test.rb +61 -0
  71. metadata +139 -90
@@ -14,10 +14,22 @@ nowxml=Time.now.save_to_xml
14
14
  => <time> ... </>
15
15
  nowxml.write($stdout,2)
16
16
  <time>
17
- <year>2005</year>
18
- <month>12</month>
19
- <mday>7</mday>
20
- <hours>20</hours>
21
- <minutes>52</minutes>
22
- <seconds>4</seconds>
23
- </time>
17
+ <year>
18
+ 2010
19
+ </year>
20
+ <month>
21
+ 5
22
+ </month>
23
+ <mday>
24
+ 21
25
+ </mday>
26
+ <hours>
27
+ 16
28
+ </hours>
29
+ <minutes>
30
+ 42
31
+ </minutes>
32
+ <seconds>
33
+ 15
34
+ </seconds>
35
+ </time>
@@ -0,0 +1,44 @@
1
+ #:invisible:
2
+ $:.unshift "../lib"
3
+ require 'xml/mapping'
4
+ require 'xml/xxpath_methods'
5
+
6
+ class Time
7
+ include XML::Mapping
8
+
9
+ numeric_node :year, "year"
10
+ numeric_node :month, "month"
11
+ numeric_node :day, "mday"
12
+ numeric_node :hour, "hours"
13
+ numeric_node :min, "minutes"
14
+ numeric_node :sec, "seconds"
15
+ end
16
+
17
+ #<=
18
+ #:invisible_retval:
19
+ #:visible:
20
+
21
+ def Time.load_from_xml(xml, options={:mapping=>:_default})
22
+ year,month,day,hour,min,sec =
23
+ [xml.first_xpath("year").text.to_i,
24
+ xml.first_xpath("month").text.to_i,
25
+ xml.first_xpath("mday").text.to_i,
26
+ xml.first_xpath("hours").text.to_i,
27
+ xml.first_xpath("minutes").text.to_i,
28
+ xml.first_xpath("seconds").text.to_i]
29
+ Time.local(year,month,day,hour,min,sec)
30
+ end
31
+ #<=
32
+ #:invisible:
33
+ require 'test/unit/assertions'
34
+ include Test::Unit::Assertions
35
+
36
+ t = Time.now
37
+ t2 = Time.load_from_xml(t.save_to_xml)
38
+
39
+ assert_equal t.year, t2.year
40
+ assert_equal t.month, t2.month
41
+ assert_equal t.day, t2.day
42
+ assert_equal t.hour, t2.hour
43
+ assert_equal t.min, t2.min
44
+ assert_equal t.sec, t2.sec
@@ -0,0 +1,12 @@
1
+
2
+ def Time.load_from_xml(xml, options={:mapping=>:_default})
3
+ year,month,day,hour,min,sec =
4
+ [xml.first_xpath("year").text.to_i,
5
+ xml.first_xpath("month").text.to_i,
6
+ xml.first_xpath("mday").text.to_i,
7
+ xml.first_xpath("hours").text.to_i,
8
+ xml.first_xpath("minutes").text.to_i,
9
+ xml.first_xpath("seconds").text.to_i]
10
+ Time.local(year,month,day,hour,min,sec)
11
+ end
12
+
@@ -0,0 +1,79 @@
1
+ #:invisible:
2
+ $:.unshift "../lib"
3
+ #<=
4
+ #:visible:
5
+ #:invisible_retval:
6
+ STDERR.puts "time_node top:\n#{$:.inspect}"
7
+
8
+ require 'xml/mapping/base'
9
+
10
+ STDERR.puts "time_node after require:\n#{$:.inspect}"
11
+
12
+ class TimeNode < XML::Mapping::SingleAttributeNode
13
+ def initialize(*args)
14
+ STDERR.puts "TimeNode#initialize top:\n#{$:.inspect}"
15
+ path,*args = super(*args)
16
+ @y_path = XML::XXPath.new(path+"/year")
17
+ @m_path = XML::XXPath.new(path+"/month")
18
+ @d_path = XML::XXPath.new(path+"/day")
19
+ args
20
+ end
21
+
22
+ def extract_attr_value(xml)
23
+ y,m,d = default_when_xpath_err{ [@y_path.first(xml).text.to_i,
24
+ @m_path.first(xml).text.to_i,
25
+ @d_path.first(xml).text.to_i]
26
+ }
27
+ Time.local(y,m,d)
28
+ end
29
+
30
+ def set_attr_value(xml, value)
31
+ @y_path.first(xml,:ensure_created=>true).text = value.year
32
+ @m_path.first(xml,:ensure_created=>true).text = value.month
33
+ @d_path.first(xml,:ensure_created=>true).text = value.day
34
+ end
35
+ end
36
+
37
+ XML::Mapping.add_node_class TimeNode
38
+
39
+ #<=
40
+ #:invisible:
41
+ require 'xml/mapping'
42
+
43
+ class Signature
44
+ include XML::Mapping
45
+
46
+ text_node :name, "Name"
47
+ text_node :position, "Position", :default_value=>"Some Employee"
48
+ time_node :signed_on, "signed-on", :default_value=>Time.now
49
+ end
50
+
51
+
52
+ require 'test/unit/assertions'
53
+ include Test::Unit::Assertions
54
+
55
+ xml = REXML::Document.new('
56
+ <Signature>
57
+ <Name>John Doe</Name>
58
+ <Position>product manager</Position>
59
+ <signed-on>
60
+ <day>13</day>
61
+ <month>2</month>
62
+ <year>2005</year>
63
+ </signed-on>
64
+ </Signature>
65
+ ').root
66
+
67
+
68
+ s = Signature.load_from_xml xml
69
+ assert_equal Time.local(2005,2,13), s.signed_on
70
+
71
+ s.signed_on = Time.local(2006,6,15)
72
+ xml2 = s.save_to_xml
73
+
74
+ require 'xml/xxpath_methods'
75
+ assert_equal "15", xml2.first_xpath("signed-on/day").text
76
+ assert_equal "6", xml2.first_xpath("signed-on/month").text
77
+ assert_equal "2006", xml2.first_xpath("signed-on/year").text
78
+
79
+ #<=
@@ -1,10 +1,12 @@
1
1
  require 'xml/mapping/base'
2
2
 
3
3
  class TimeNode < XML::Mapping::SingleAttributeNode
4
- def initialize_impl(path)
4
+ def initialize(*args)
5
+ path,*args = super(*args)
5
6
  @y_path = XML::XXPath.new(path+"/year")
6
7
  @m_path = XML::XXPath.new(path+"/month")
7
8
  @d_path = XML::XXPath.new(path+"/day")
9
+ args
8
10
  end
9
11
 
10
12
  def extract_attr_value(xml)
@@ -16,7 +18,6 @@ class TimeNode < XML::Mapping::SingleAttributeNode
16
18
  end
17
19
 
18
20
  def set_attr_value(xml, value)
19
- raise "Not a Time: #{value}" unless Time===value
20
21
  @y_path.first(xml,:ensure_created=>true).text = value.year
21
22
  @m_path.first(xml,:ensure_created=>true).text = value.month
22
23
  @d_path.first(xml,:ensure_created=>true).text = value.day
@@ -0,0 +1,48 @@
1
+ #:invisible:
2
+ #:invisible_retval:
3
+ $:.unshift "../lib" #<=
4
+ #:visible:
5
+ require 'xml/mapping'
6
+ require 'xml/xxpath_methods'
7
+
8
+ class Signature
9
+ include XML::Mapping
10
+
11
+ text_node :name, "Name"
12
+ text_node :position, "Position", :default_value=>"Some Employee"
13
+ object_node :signed_on, "signed-on",
14
+ :unmarshaller=>proc{|xml|
15
+ y,m,d = [xml.first_xpath("year").text.to_i,
16
+ xml.first_xpath("month").text.to_i,
17
+ xml.first_xpath("day").text.to_i]
18
+ Time.local(y,m,d)
19
+ },
20
+ :marshaller=>proc{|xml,value|
21
+ e = xml.elements.add; e.name = "year"; e.text = value.year
22
+ e = xml.elements.add; e.name = "month"; e.text = value.month
23
+ e = xml.elements.add; e.name = "day"; e.text = value.day
24
+
25
+ # xml.first("year",:ensure_created=>true).text = value.year
26
+ # xml.first("month",:ensure_created=>true).text = value.month
27
+ # xml.first("day",:ensure_created=>true).text = value.day
28
+ }
29
+ end #<=
30
+ #:invisible:
31
+ require 'test/unit/assertions'
32
+
33
+ include Test::Unit::Assertions
34
+
35
+ t=Time.local(2005,12,1)
36
+
37
+ s=Signature.new
38
+ s.name = "Olaf Klischat"; s.position="chief"; s.signed_on=t
39
+ xml = s.save_to_xml
40
+
41
+ assert_equal "2005", xml.first_xpath("signed-on/year").text
42
+ assert_equal "12", xml.first_xpath("signed-on/month").text
43
+ assert_equal "1", xml.first_xpath("signed-on/day").text
44
+
45
+ s2 = Signature.load_from_xml xml
46
+ assert_equal "Olaf Klischat", s2.name
47
+ assert_equal "chief", s2.position
48
+ assert_equal t, s2.signed_on
@@ -0,0 +1,25 @@
1
+ require 'xml/mapping'
2
+ require 'xml/xxpath_methods'
3
+
4
+ class Signature
5
+ include XML::Mapping
6
+
7
+ text_node :name, "Name"
8
+ text_node :position, "Position", :default_value=>"Some Employee"
9
+ object_node :signed_on, "signed-on",
10
+ :unmarshaller=>proc{|xml|
11
+ y,m,d = [xml.first_xpath("year").text.to_i,
12
+ xml.first_xpath("month").text.to_i,
13
+ xml.first_xpath("day").text.to_i]
14
+ Time.local(y,m,d)
15
+ },
16
+ :marshaller=>proc{|xml,value|
17
+ e = xml.elements.add; e.name = "year"; e.text = value.year
18
+ e = xml.elements.add; e.name = "month"; e.text = value.month
19
+ e = xml.elements.add; e.name = "day"; e.text = value.day
20
+
21
+ # xml.first("year",:ensure_created=>true).text = value.year
22
+ # xml.first("month",:ensure_created=>true).text = value.month
23
+ # xml.first("day",:ensure_created=>true).text = value.day
24
+ }
25
+ end
@@ -0,0 +1,9 @@
1
+ <Signature>
2
+ <Name>John Doe</Name>
3
+ <Position>product manager</Position>
4
+ <signed-on>
5
+ <day>13</day>
6
+ <month>2</month>
7
+ <year>2005</year>
8
+ </signed-on>
9
+ </Signature>
@@ -17,17 +17,20 @@ path1=XML::XXPath.new("/bar/baz[@key='work']")
17
17
  path1.create_new(rootelt)
18
18
  => <baz key='work'/>
19
19
  d.write($stdout,2)
20
- <foo>
21
- <bar>
22
- <baz key='work'>Java</baz>
23
- <baz key='play'>Ruby</baz>
24
- </bar>
20
+
21
+ <foo>
25
22
  <bar>
26
- <baz key='work'/>
27
- </bar>
28
- </foo>
29
-
30
- ### a new element is created for *each* path element, regardless of
23
+ <baz key='work'>
24
+ Java
25
+ </baz>
26
+ <baz key='play'>
27
+ Ruby
28
+ </baz>
29
+ </bar>
30
+ <bar>
31
+ <baz key='work'/>
32
+ </bar>
33
+ </foo>### a new element is created for *each* path element, regardless of
31
34
  ### what existed before. So a new "bar" element was added, with a new
32
35
  ### "baz" element inside it
33
36
 
@@ -35,20 +38,23 @@ d.write($stdout,2)
35
38
  path1.create_new(rootelt)
36
39
  => <baz key='work'/>
37
40
  d.write($stdout,2)
38
- <foo>
39
- <bar>
40
- <baz key='work'>Java</baz>
41
- <baz key='play'>Ruby</baz>
42
- </bar>
41
+
42
+ <foo>
43
43
  <bar>
44
- <baz key='work'/>
45
- </bar>
46
- <bar>
47
- <baz key='work'/>
48
- </bar>
49
- </foo>
50
-
51
- ### same procedure -- new elements added for each path element
44
+ <baz key='work'>
45
+ Java
46
+ </baz>
47
+ <baz key='play'>
48
+ Ruby
49
+ </baz>
50
+ </bar>
51
+ <bar>
52
+ <baz key='work'/>
53
+ </bar>
54
+ <bar>
55
+ <baz key='work'/>
56
+ </bar>
57
+ </foo>### same procedure -- new elements added for each path element
52
58
 
53
59
 
54
60
  ## get reference to 1st "baz" element
@@ -58,89 +64,98 @@ firstbazelt=XML::XXPath.new("/bar/baz").first(rootelt)
58
64
  path2=XML::XXPath.new("@key2")
59
65
 
60
66
  path2.create_new(firstbazelt)
61
- => #<XML::XXPath::Accessors::Attribute:0x314bf8 @parent=<baz key2='[unset]' key='work'> ... </>, @name="key2">
67
+ => #<XML::XXPath::Accessors::Attribute:0x7f48764e8688 @name="key2", @parent=<baz key='work' key2='[unset]'> ... </>>
62
68
  d.write($stdout,2)
63
- <foo>
64
- <bar>
65
- <baz key2='[unset]' key='work'>Java</baz>
66
- <baz key='play'>Ruby</baz>
67
- </bar>
69
+
70
+ <foo>
68
71
  <bar>
69
- <baz key='work'/>
70
- </bar>
71
- <bar>
72
- <baz key='work'/>
73
- </bar>
74
- </foo>
75
-
76
- ### ok, new attribute node added
72
+ <baz key='work' key2='[unset]'>
73
+ Java
74
+ </baz>
75
+ <baz key='play'>
76
+ Ruby
77
+ </baz>
78
+ </bar>
79
+ <bar>
80
+ <baz key='work'/>
81
+ </bar>
82
+ <bar>
83
+ <baz key='work'/>
84
+ </bar>
85
+ </foo>### ok, new attribute node added
77
86
 
78
87
  ### same call again...
79
88
  path2.create_new(firstbazelt)
80
- XML::XXPathError: XPath (): @key2: create_new and attribute already exists
81
- from ../lib/xml/../xml/xxpath.rb:341:in `create_subnode_by_attr_name'
82
- from ../lib/xml/../xml/xxpath.rb:68:in `initialize'
83
- from ../lib/xml/../xml/xxpath.rb:67:in `call'
84
- from ../lib/xml/../xml/xxpath.rb:146:in `all'
85
- from ../lib/xml/../xml/xxpath.rb:125:in `first'
86
- from ../lib/xml/../xml/xxpath.rb:167:in `create_new'
89
+ XML::XXPathError: XPath (@key2): create_new and attribute already exists
90
+ from ../lib/xml/../xml/xxpath/steps.rb:215:in `create_on'
91
+ from ../lib/xml/../xml/xxpath/steps.rb:80:in `creator'
92
+ from ../lib/xml/xxpath.rb:91:in `call'
93
+ from ../lib/xml/xxpath.rb:91:in `all'
94
+ from ../lib/xml/xxpath.rb:70:in `first'
95
+ from ../lib/xml/xxpath.rb:112:in `create_new'
87
96
  ### can't create that path anew again -- an element can't have more
88
97
  ### than one attribute with the same name
89
98
 
90
99
  ### the document hasn't changed
91
100
  d.write($stdout,2)
92
- <foo>
93
- <bar>
94
- <baz key2='[unset]' key='work'>Java</baz>
95
- <baz key='play'>Ruby</baz>
96
- </bar>
101
+
102
+ <foo>
97
103
  <bar>
98
- <baz key='work'/>
99
- </bar>
100
- <bar>
101
- <baz key='work'/>
102
- </bar>
103
- </foo>
104
-
105
-
104
+ <baz key='work' key2='[unset]'>
105
+ Java
106
+ </baz>
107
+ <baz key='play'>
108
+ Ruby
109
+ </baz>
110
+ </bar>
111
+ <bar>
112
+ <baz key='work'/>
113
+ </bar>
114
+ <bar>
115
+ <baz key='work'/>
116
+ </bar>
117
+ </foo>
106
118
 
107
119
 
108
120
  ### create_new the same path as in the ensure_created example
109
121
  baz6elt=XML::XXPath.new("/bar/baz[6]").create_new(rootelt)
110
122
  => <baz/>
111
123
  d.write($stdout,2)
112
- <foo>
113
- <bar>
114
- <baz key2='[unset]' key='work'>Java</baz>
115
- <baz key='play'>Ruby</baz>
116
- </bar>
124
+
125
+ <foo>
117
126
  <bar>
118
- <baz key='work'/>
119
- </bar>
120
- <bar>
121
- <baz key='work'/>
122
- </bar>
123
- <bar>
124
- <baz/>
125
- <baz/>
126
- <baz/>
127
- <baz/>
128
- <baz/>
129
- <baz/>
130
- </bar>
131
- </foo>
132
-
133
- ### ok, new "bar" element and 6th "baz" element inside it created
127
+ <baz key='work' key2='[unset]'>
128
+ Java
129
+ </baz>
130
+ <baz key='play'>
131
+ Ruby
132
+ </baz>
133
+ </bar>
134
+ <bar>
135
+ <baz key='work'/>
136
+ </bar>
137
+ <bar>
138
+ <baz key='work'/>
139
+ </bar>
140
+ <bar>
141
+ <baz/>
142
+ <baz/>
143
+ <baz/>
144
+ <baz/>
145
+ <baz/>
146
+ <baz/>
147
+ </bar>
148
+ </foo>### ok, new "bar" element and 6th "baz" element inside it created
134
149
 
135
150
 
136
151
  XML::XXPath.new("baz[6]").create_new(baz6elt.parent)
137
- XML::XXPathError: XPath (): baz[6]: create_new and element already exists
138
- from ../lib/xml/../xml/xxpath.rb:330:in `create_subnode_by_name_and_index'
139
- from ../lib/xml/../xml/xxpath.rb:52:in `initialize'
140
- from ../lib/xml/../xml/xxpath.rb:51:in `call'
141
- from ../lib/xml/../xml/xxpath.rb:146:in `all'
142
- from ../lib/xml/../xml/xxpath.rb:125:in `first'
143
- from ../lib/xml/../xml/xxpath.rb:167:in `create_new'
152
+ XML::XXPathError: XPath: baz[6]: create_new and element already exists
153
+ from ../lib/xml/../xml/xxpath/steps.rb:167:in `create_on'
154
+ from ../lib/xml/../xml/xxpath/steps.rb:80:in `creator'
155
+ from ../lib/xml/xxpath.rb:91:in `call'
156
+ from ../lib/xml/xxpath.rb:91:in `all'
157
+ from ../lib/xml/xxpath.rb:70:in `first'
158
+ from ../lib/xml/xxpath.rb:112:in `create_new'
144
159
  ### yep, baz[6] already existed and thus couldn't be created once
145
160
  ### again
146
161
 
@@ -148,34 +163,37 @@ XML::XXPathError: XPath (): baz[6]: create_new and element already exists
148
163
  XML::XXPath.new("/bar/baz[6]").create_new(rootelt)
149
164
  => <baz/>
150
165
  d.write($stdout,2)
151
- <foo>
152
- <bar>
153
- <baz key2='[unset]' key='work'>Java</baz>
154
- <baz key='play'>Ruby</baz>
155
- </bar>
166
+
167
+ <foo>
168
+ <bar>
169
+ <baz key='work' key2='[unset]'>
170
+ Java
171
+ </baz>
172
+ <baz key='play'>
173
+ Ruby
174
+ </baz>
175
+ </bar>
176
+ <bar>
177
+ <baz key='work'/>
178
+ </bar>
179
+ <bar>
180
+ <baz key='work'/>
181
+ </bar>
182
+ <bar>
183
+ <baz/>
184
+ <baz/>
185
+ <baz/>
186
+ <baz/>
187
+ <baz/>
188
+ <baz/>
189
+ </bar>
156
190
  <bar>
157
- <baz key='work'/>
158
- </bar>
159
- <bar>
160
- <baz key='work'/>
161
- </bar>
162
- <bar>
163
- <baz/>
164
- <baz/>
165
- <baz/>
166
- <baz/>
167
- <baz/>
168
- <baz/>
169
- </bar>
170
- <bar>
171
- <baz/>
172
- <baz/>
173
- <baz/>
174
- <baz/>
175
- <baz/>
176
- <baz/>
177
- </bar>
178
- </foo>
179
-
180
- ### this works because *all* path elements are newly created
191
+ <baz/>
192
+ <baz/>
193
+ <baz/>
194
+ <baz/>
195
+ <baz/>
196
+ <baz/>
197
+ </bar>
198
+ </foo>### this works because *all* path elements are newly created
181
199