dc-kwalify 0.7.2

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 (209) hide show
  1. data/CHANGES.txt +252 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.txt +61 -0
  4. data/bin/kwalify +13 -0
  5. data/contrib/inline-require +179 -0
  6. data/contrib/kwalify +4160 -0
  7. data/doc/docstyle.css +188 -0
  8. data/doc/img/fig01.png +0 -0
  9. data/doc/users-guide.html +2050 -0
  10. data/doc-api/classes/CommandOptionError.html +184 -0
  11. data/doc-api/classes/CommandOptionParser.html +325 -0
  12. data/doc-api/classes/Kwalify/AssertionError.html +148 -0
  13. data/doc-api/classes/Kwalify/BaseError.html +297 -0
  14. data/doc-api/classes/Kwalify/BaseParser.html +461 -0
  15. data/doc-api/classes/Kwalify/CommandOptionError.html +168 -0
  16. data/doc-api/classes/Kwalify/ErrorHelper.html +223 -0
  17. data/doc-api/classes/Kwalify/HashInterface.html +118 -0
  18. data/doc-api/classes/Kwalify/Json.html +105 -0
  19. data/doc-api/classes/Kwalify/KwalifyError.html +111 -0
  20. data/doc-api/classes/Kwalify/Main.html +339 -0
  21. data/doc-api/classes/Kwalify/MetaValidator.html +448 -0
  22. data/doc-api/classes/Kwalify/Parser.html +155 -0
  23. data/doc-api/classes/Kwalify/PlainYamlParser/Alias.html +165 -0
  24. data/doc-api/classes/Kwalify/PlainYamlParser.html +523 -0
  25. data/doc-api/classes/Kwalify/Rule.html +433 -0
  26. data/doc-api/classes/Kwalify/SchemaError.html +148 -0
  27. data/doc-api/classes/Kwalify/SyntaxError.html +185 -0
  28. data/doc-api/classes/Kwalify/Types.html +302 -0
  29. data/doc-api/classes/Kwalify/Util/HashLike.html +246 -0
  30. data/doc-api/classes/Kwalify/Util/OrderedHash.html +330 -0
  31. data/doc-api/classes/Kwalify/Util.html +390 -0
  32. data/doc-api/classes/Kwalify/ValidationError.html +148 -0
  33. data/doc-api/classes/Kwalify/Validator.html +381 -0
  34. data/doc-api/classes/Kwalify/Yaml/Parser.html +1538 -0
  35. data/doc-api/classes/Kwalify/Yaml.html +194 -0
  36. data/doc-api/classes/Kwalify/YamlParser.html +542 -0
  37. data/doc-api/classes/Kwalify/YamlSyntaxError.html +119 -0
  38. data/doc-api/classes/Kwalify.html +292 -0
  39. data/doc-api/classes/Test/Unit.html +101 -0
  40. data/doc-api/classes/Test.html +107 -0
  41. data/doc-api/created.rid +1 -0
  42. data/doc-api/files/__/README_txt.html +172 -0
  43. data/doc-api/files/kwalify/errors_rb.html +114 -0
  44. data/doc-api/files/kwalify/main_rb.html +118 -0
  45. data/doc-api/files/kwalify/messages_rb.html +107 -0
  46. data/doc-api/files/kwalify/meta-validator_rb.html +117 -0
  47. data/doc-api/files/kwalify/parser/base_rb.html +116 -0
  48. data/doc-api/files/kwalify/parser/yaml_rb.html +117 -0
  49. data/doc-api/files/kwalify/rule_rb.html +116 -0
  50. data/doc-api/files/kwalify/types_rb.html +114 -0
  51. data/doc-api/files/kwalify/util/assert-text-equal_rb.html +115 -0
  52. data/doc-api/files/kwalify/util/hash-interface_rb.html +114 -0
  53. data/doc-api/files/kwalify/util/hashlike_rb.html +107 -0
  54. data/doc-api/files/kwalify/util/option-parser_rb.html +107 -0
  55. data/doc-api/files/kwalify/util/ordered-hash_rb.html +107 -0
  56. data/doc-api/files/kwalify/util/testcase-helper_rb.html +115 -0
  57. data/doc-api/files/kwalify/util_rb.html +107 -0
  58. data/doc-api/files/kwalify/validator_rb.html +117 -0
  59. data/doc-api/files/kwalify/yaml-parser_rb.html +117 -0
  60. data/doc-api/files/kwalify_rb.html +121 -0
  61. data/doc-api/fr_class_index.html +57 -0
  62. data/doc-api/fr_file_index.html +45 -0
  63. data/doc-api/fr_method_index.html +168 -0
  64. data/doc-api/index.html +24 -0
  65. data/doc-api/rdoc-style.css +208 -0
  66. data/examples/address-book/Makefile +10 -0
  67. data/examples/address-book/address-book.schema.yaml +45 -0
  68. data/examples/address-book/address-book.yaml +36 -0
  69. data/examples/data-binding/BABEL.data.yaml +63 -0
  70. data/examples/data-binding/BABEL.schema.yaml +31 -0
  71. data/examples/data-binding/Makefile +8 -0
  72. data/examples/data-binding/Rakefile +13 -0
  73. data/examples/data-binding/main.rb +27 -0
  74. data/examples/invoice/Makefile +9 -0
  75. data/examples/invoice/invoice.schema.yaml +43 -0
  76. data/examples/invoice/invoice.yaml +32 -0
  77. data/examples/tapkit/Makefile +10 -0
  78. data/examples/tapkit/main.rb +7 -0
  79. data/examples/tapkit/tapkit.schema.yaml +146 -0
  80. data/examples/tapkit/tapkit.yaml +85 -0
  81. data/lib/kwalify/errors.rb +127 -0
  82. data/lib/kwalify/kwalify.schema.yaml +58 -0
  83. data/lib/kwalify/main.rb +442 -0
  84. data/lib/kwalify/messages.rb +173 -0
  85. data/lib/kwalify/meta-validator.rb +275 -0
  86. data/lib/kwalify/parser/base.rb +127 -0
  87. data/lib/kwalify/parser/yaml.rb +841 -0
  88. data/lib/kwalify/rule.rb +559 -0
  89. data/lib/kwalify/templates/genclass-java.eruby +222 -0
  90. data/lib/kwalify/templates/genclass-php.eruby +104 -0
  91. data/lib/kwalify/templates/genclass-ruby.eruby +113 -0
  92. data/lib/kwalify/types.rb +156 -0
  93. data/lib/kwalify/util/assert-text-equal.rb +46 -0
  94. data/lib/kwalify/util/hash-interface.rb +18 -0
  95. data/lib/kwalify/util/hashlike.rb +51 -0
  96. data/lib/kwalify/util/option-parser.rb +220 -0
  97. data/lib/kwalify/util/ordered-hash.rb +57 -0
  98. data/lib/kwalify/util/testcase-helper.rb +112 -0
  99. data/lib/kwalify/util.rb +158 -0
  100. data/lib/kwalify/validator.rb +282 -0
  101. data/lib/kwalify/yaml-parser.rb +870 -0
  102. data/lib/kwalify.rb +67 -0
  103. data/setup.rb +1585 -0
  104. data/test/Rookbook.yaml +10 -0
  105. data/test/data/users-guide/AddressBook.java.expected +40 -0
  106. data/test/data/users-guide/BABEL.data.yaml +24 -0
  107. data/test/data/users-guide/BABEL.schema.yaml +30 -0
  108. data/test/data/users-guide/ExampleAddressBook.java +47 -0
  109. data/test/data/users-guide/Group.java.expected +24 -0
  110. data/test/data/users-guide/Person.java.expected +44 -0
  111. data/test/data/users-guide/address_book.rb +52 -0
  112. data/test/data/users-guide/address_book.schema.yaml +28 -0
  113. data/test/data/users-guide/address_book.yaml +27 -0
  114. data/test/data/users-guide/answers-schema.yaml +12 -0
  115. data/test/data/users-guide/answers-validator.rb +52 -0
  116. data/test/data/users-guide/babel_genclass.result +26 -0
  117. data/test/data/users-guide/config.schema.yaml +7 -0
  118. data/test/data/users-guide/config.yaml +4 -0
  119. data/test/data/users-guide/document01a.yaml +3 -0
  120. data/test/data/users-guide/document01b.yaml +3 -0
  121. data/test/data/users-guide/document02a.yaml +4 -0
  122. data/test/data/users-guide/document02b.yaml +4 -0
  123. data/test/data/users-guide/document03a.yaml +6 -0
  124. data/test/data/users-guide/document03b.yaml +6 -0
  125. data/test/data/users-guide/document04a.yaml +9 -0
  126. data/test/data/users-guide/document04b.yaml +9 -0
  127. data/test/data/users-guide/document05a.yaml +11 -0
  128. data/test/data/users-guide/document05b.yaml +12 -0
  129. data/test/data/users-guide/document06a.yaml +15 -0
  130. data/test/data/users-guide/document06b.yaml +16 -0
  131. data/test/data/users-guide/document07a.yaml +9 -0
  132. data/test/data/users-guide/document07b.yaml +7 -0
  133. data/test/data/users-guide/document12a.json +10 -0
  134. data/test/data/users-guide/document12b.json +6 -0
  135. data/test/data/users-guide/document13a.yaml +17 -0
  136. data/test/data/users-guide/document14a.yaml +3 -0
  137. data/test/data/users-guide/document14b.yaml +3 -0
  138. data/test/data/users-guide/document15a.yaml +6 -0
  139. data/test/data/users-guide/document15b.yaml +5 -0
  140. data/test/data/users-guide/example_address_book.rb +10 -0
  141. data/test/data/users-guide/example_address_book_java.result +32 -0
  142. data/test/data/users-guide/example_address_book_ruby.result +31 -0
  143. data/test/data/users-guide/genclass_java.result +4 -0
  144. data/test/data/users-guide/howto-validation-with-parsing.rb +28 -0
  145. data/test/data/users-guide/howto-validation.rb +25 -0
  146. data/test/data/users-guide/howto3.rb +6 -0
  147. data/test/data/users-guide/howto3.result +5 -0
  148. data/test/data/users-guide/howto3.yaml +8 -0
  149. data/test/data/users-guide/howto5_databinding.result +111 -0
  150. data/test/data/users-guide/invalid01.result +3 -0
  151. data/test/data/users-guide/invalid02.result +5 -0
  152. data/test/data/users-guide/invalid03.result +5 -0
  153. data/test/data/users-guide/invalid04.result +4 -0
  154. data/test/data/users-guide/invalid05.result +11 -0
  155. data/test/data/users-guide/invalid06.result +4 -0
  156. data/test/data/users-guide/invalid07.result +3 -0
  157. data/test/data/users-guide/invalid08.result +3 -0
  158. data/test/data/users-guide/invalid12.json +8 -0
  159. data/test/data/users-guide/invalid14.result +4 -0
  160. data/test/data/users-guide/invalid15.result +4 -0
  161. data/test/data/users-guide/loadbabel.rb +27 -0
  162. data/test/data/users-guide/loadconfig.rb +16 -0
  163. data/test/data/users-guide/loadconfig.result +6 -0
  164. data/test/data/users-guide/models.rb +22 -0
  165. data/test/data/users-guide/option_ha.result +6 -0
  166. data/test/data/users-guide/option_ha_genclass_java.result +7 -0
  167. data/test/data/users-guide/schema01.yaml +3 -0
  168. data/test/data/users-guide/schema02.yaml +12 -0
  169. data/test/data/users-guide/schema03.yaml +9 -0
  170. data/test/data/users-guide/schema04.yaml +20 -0
  171. data/test/data/users-guide/schema05.yaml +29 -0
  172. data/test/data/users-guide/schema06.yaml +11 -0
  173. data/test/data/users-guide/schema12.json +12 -0
  174. data/test/data/users-guide/schema13.yaml +13 -0
  175. data/test/data/users-guide/schema14.yaml +5 -0
  176. data/test/data/users-guide/schema15.yaml +21 -0
  177. data/test/data/users-guide/valid01.result +2 -0
  178. data/test/data/users-guide/valid02.result +2 -0
  179. data/test/data/users-guide/valid03.result +2 -0
  180. data/test/data/users-guide/valid04.result +2 -0
  181. data/test/data/users-guide/valid05.result +2 -0
  182. data/test/data/users-guide/valid06.result +2 -0
  183. data/test/data/users-guide/valid07.result +2 -0
  184. data/test/data/users-guide/valid08.result +2 -0
  185. data/test/data/users-guide/valid12.result +2 -0
  186. data/test/data/users-guide/valid13.result +2 -0
  187. data/test/data/users-guide/valid14.result +2 -0
  188. data/test/data/users-guide/valid15.result +2 -0
  189. data/test/data/users-guide/validate08.rb +37 -0
  190. data/test/test-action.rb +78 -0
  191. data/test/test-action.yaml +738 -0
  192. data/test/test-databinding.rb +83 -0
  193. data/test/test-databinding.yaml +339 -0
  194. data/test/test-main.rb +157 -0
  195. data/test/test-main.yaml +415 -0
  196. data/test/test-metavalidator.rb +80 -0
  197. data/test/test-metavalidator.yaml +1179 -0
  198. data/test/test-parser-yaml.rb +57 -0
  199. data/test/test-parser-yaml.yaml +1749 -0
  200. data/test/test-rule.rb +26 -0
  201. data/test/test-rule.yaml +317 -0
  202. data/test/test-users-guide.rb +75 -0
  203. data/test/test-util.rb +125 -0
  204. data/test/test-validator.rb +95 -0
  205. data/test/test-validator.yaml +986 -0
  206. data/test/test-yaml-parser.rb +47 -0
  207. data/test/test-yaml-parser.yaml +1226 -0
  208. data/test/test.rb +61 -0
  209. metadata +274 -0
@@ -0,0 +1,841 @@
1
+ ###
2
+ ### $Rev$
3
+ ### $Release: 0.7.2 $
4
+ ### copyright(c) 2005-2010 kuwata-lab all rights reserved.
5
+ ###
6
+
7
+ require 'kwalify/validator'
8
+ require 'kwalify/errors'
9
+ require 'kwalify/util'
10
+ require 'kwalify/parser/base'
11
+
12
+
13
+
14
+ module Kwalify
15
+
16
+ module Yaml
17
+ end
18
+
19
+ end
20
+
21
+
22
+ ##
23
+ ## YAML parser with validator
24
+ ##
25
+ ## ex.
26
+ ## schema = YAML.load_file('schema.yaml')
27
+ ## require 'kwalify'
28
+ ## validator = Kwalify::Validator.new(schema)
29
+ ## parser = Kwalify::Yaml::Parser.new(validator) # validator is optional
30
+ ## #parser.preceding_alias = true # optional
31
+ ## #parser.data_binding = true # optional
32
+ ## ydoc = parser.parse_file('data.yaml')
33
+ ## errors = parser.errors
34
+ ## if errors && !errors.empty?
35
+ ## errors.each do |e|
36
+ ## puts "line=#{e.linenum}, path=#{e.path}, mesg=#{e.message}"
37
+ ## end
38
+ ## end
39
+ ##
40
+ class Kwalify::Yaml::Parser < Kwalify::BaseParser
41
+
42
+
43
+ alias reset_scanner reset
44
+
45
+
46
+ def initialize(validator=nil, properties={})
47
+ @validator = validator.is_a?(Hash) ? Kwalify::Validator.new(validator) : validator
48
+ @data_binding = properties[:data_binding] # enable data binding or not
49
+ @preceding_alias = properties[:preceding_alias] # allow preceding alias or not
50
+ @sequence_class = properties[:sequence_class] || Array
51
+ @mapping_class = properties[:mapping_class] || Hash
52
+ end
53
+ attr_accessor :validator # Validator
54
+ attr_accessor :data_binding # boolean
55
+ attr_accessor :preceding_alias # boolean
56
+ attr_accessor :sequence_class # Class
57
+ attr_accessor :mapping_class # Class
58
+
59
+
60
+ def reset_parser()
61
+ @anchors = {}
62
+ @errors = []
63
+ @done = {}
64
+ @preceding_aliases = []
65
+ @location_table = {} # object_id -> sequence or mapping
66
+ @doc = nil
67
+ end
68
+ attr_reader :errors
69
+
70
+
71
+ def _error(klass, message, path, linenum, column)
72
+ ex = klass.new(message)
73
+ ex.path = path.is_a?(Array) ? '/' + path.join('/') : path
74
+ ex.linenum = linenum
75
+ ex.column = column
76
+ ex.filename = @filename
77
+ return ex
78
+ end
79
+ private :_error
80
+
81
+
82
+ # def _validate_error(message, path, linenum=@linenum, column=@column)
83
+ # #message = _build_message(message_key)
84
+ # error = _error(ValidationError, message.to_s, path, linenum, column)
85
+ # @errors << error
86
+ # end
87
+ # private :_validate_error
88
+
89
+
90
+ def _set_error_info(linenum=@linenum, column=@column, &block)
91
+ len = @errors.length
92
+ yield
93
+ n = @errors.length - len
94
+ (1..n).each do |i|
95
+ error = @errors[-i]
96
+ error.linenum ||= linenum
97
+ error.column ||= column
98
+ error.filename ||= @filename
99
+ end if n > 0
100
+ end
101
+
102
+
103
+ def skip_spaces_and_comments()
104
+ scan(/\s+/)
105
+ while match?(/\#/)
106
+ scan(/.*?\n/)
107
+ scan(/\s+/)
108
+ end
109
+ end
110
+
111
+
112
+ def document_start?()
113
+ return match?(/---\s/) && @column == 1
114
+ end
115
+
116
+
117
+ def stream_end?()
118
+ return match?(/\.\.\.\s/) && @column == 1
119
+ end
120
+
121
+
122
+ def has_next?()
123
+ return !(eos? || stream_end?)
124
+ end
125
+
126
+
127
+ def parse(input=nil, opts={})
128
+ reset_scanner(input, opts[:filename], opts[:untabify]) if input
129
+ return parse_next()
130
+ end
131
+
132
+
133
+ def parse_file(filename, opts={})
134
+ opts[:filename] = filename
135
+ return parse(File.read(filename), opts)
136
+ end
137
+
138
+
139
+ def parse_next()
140
+ reset_parser()
141
+ path = []
142
+ skip_spaces_and_comments()
143
+ if document_start?()
144
+ scan(/.*\n/)
145
+ skip_spaces_and_comments()
146
+ end
147
+ _linenum = @linenum #*V
148
+ _column = @column #*V
149
+ rule = @validator ? @validator.rule : nil #*V
150
+ uniq_table = nil #*V
151
+ parent = nil #*V
152
+ val = parse_block_value(0, rule, path, uniq_table, parent)
153
+ _set_error_info(_linenum, _column) do #*V
154
+ @validator._validate(val, rule, [], @errors, @done, uniq_table, false) #*V
155
+ end if rule #*V
156
+ resolve_preceding_aliases(val) if @preceding_alias
157
+ unless eos? || document_start?() || stream_end?()
158
+ raise _syntax_error("document end expected (maybe invalid tab char found).", path)
159
+ end
160
+ @doc = val
161
+ @location_table[-1] = [_linenum, _column]
162
+ return val
163
+ end
164
+
165
+
166
+ def parse_stream(input, opts={}, &block)
167
+ reset_scanner(input, opts[:filename], opts[:untabify])
168
+ ydocs = block_given? ? nil : []
169
+ while true
170
+ ydoc = parse_next()
171
+ ydocs ? (ydocs << ydoc) : (yield ydoc)
172
+ break if eos? || stream_end?()
173
+ document_start?() or raise "** internal error"
174
+ scan(/.*\n/)
175
+ end
176
+ return ydocs
177
+ end
178
+
179
+ alias parse_documents parse_stream
180
+
181
+
182
+ MAPKEY_PATTERN = /([\w.][-\w.:]*\*?|".*?"|'.*?'|:\w+|=|<<)[ \t]*:\s+/ # :nodoc:
183
+
184
+ PRECEDING_ALIAS_PLACEHOLDER = Object.new # :nodoc:
185
+
186
+
187
+ def parse_anchor(rule, path, uniq_table, container)
188
+ name = group(1)
189
+ if @anchors.key?(name)
190
+ raise _syntax_error("&#{name}: anchor duplicated.", path,
191
+ @linenum, @column - name.length)
192
+ end
193
+ skip_spaces_and_comments()
194
+ return name
195
+ end
196
+
197
+
198
+ def parse_alias(rule, path, uniq_table, container)
199
+ name = group(1)
200
+ if @anchors.key?(name)
201
+ val = @anchors[name]
202
+ elsif @preceding_alias
203
+ @preceding_aliases << [name, rule, path.dup, container,
204
+ @linenum, @column - name.length - 1]
205
+ val = PRECEDING_ALIAS_PLACEHOLDER
206
+ else
207
+ raise _syntax_error("*#{name}: anchor not found.", path,
208
+ @linenum, @column - name.length - 1)
209
+ end
210
+ skip_spaces_and_comments()
211
+ return val
212
+ end
213
+
214
+
215
+ def resolve_preceding_aliases(val)
216
+ @preceding_aliases.each do |name, rule, path, container, _linenum, _column|
217
+ unless @anchors.key?(name)
218
+ raise _syntax_error("*#{name}: anchor not found.", path, _linenum, _column)
219
+ end
220
+ key = path[-1]
221
+ val = @anchors[name]
222
+ raise unless !container.respond_to?('[]') || container[key].equal?(PRECEDING_ALIAS_PLACEHOLDER)
223
+ if container.is_a?(Array)
224
+ container[key] = val
225
+ else
226
+ put_to_map(rule, container, key, val, _linenum, _column)
227
+ end
228
+ _set_error_info(_linenum, _column) do #*V
229
+ @validator._validate(val, rule, path, @errors, @done, false) #*V
230
+ end if rule #*V
231
+ end
232
+ end
233
+
234
+
235
+ def parse_block_value(level, rule, path, uniq_table, container)
236
+ skip_spaces_and_comments()
237
+ ## nil
238
+ return nil if @column <= level || eos?
239
+ ## anchor and alias
240
+ name = nil
241
+ if scan(/\&([-\w]+)/)
242
+ name = parse_anchor(rule, path, uniq_table, container)
243
+ elsif scan(/\*([-\w]+)/)
244
+ return parse_alias(rule, path, uniq_table, container)
245
+ end
246
+ ## type
247
+ if scan(/!!?\w+/)
248
+ skip_spaces_and_comments()
249
+ end
250
+ ## sequence
251
+ if match?(/-\s+/)
252
+ if rule && !rule.sequence
253
+ #_validate_error("sequence is not expected.", path)
254
+ rule = nil
255
+ end
256
+ seq = create_sequence(rule, @linenum, @column)
257
+ @anchors[name] = seq if name
258
+ parse_block_seq(seq, rule, path, uniq_table)
259
+ return seq
260
+ end
261
+ ## mapping
262
+ if match?(MAPKEY_PATTERN)
263
+ if rule && !rule.mapping
264
+ #_validate_error("mapping is not expected.", path)
265
+ rule = nil
266
+ end
267
+ map = create_mapping(rule, @linenum, @column)
268
+ @anchors[name] = map if name
269
+ parse_block_map(map, rule, path, uniq_table)
270
+ return map
271
+ end
272
+ ## sequence (flow-style)
273
+ if match?(/\[/)
274
+ if rule && !rule.sequence
275
+ #_validate_error("sequence is not expected.", path)
276
+ rule = nil
277
+ end
278
+ seq = create_sequence(rule, @linenum, @column)
279
+ @anchors[name] = seq if name
280
+ parse_flow_seq(seq, rule, path, uniq_table)
281
+ return seq
282
+ end
283
+ ## mapping (flow-style)
284
+ if match?(/\{/)
285
+ if rule && !rule.mapping
286
+ #_validate_error("mapping is not expected.", path)
287
+ rule = nil
288
+ end
289
+ map = create_mapping(rule, @linenum, @column)
290
+ @anchors[name] = map if name
291
+ parse_flow_map(map, rule, path, uniq_table)
292
+ return map
293
+ end
294
+ ## block text
295
+ if match?(/[|>]/)
296
+ text = parse_block_text(level, rule, path, uniq_table)
297
+ @anchors[name] = text if name
298
+ return text
299
+ end
300
+ ## scalar
301
+ scalar = parse_block_scalar(rule, path, uniq_table)
302
+ @anchors[name] = scalar if name
303
+ return scalar
304
+ end
305
+
306
+
307
+ def parse_block_seq(seq, seq_rule, path, uniq_table)
308
+ level = @column
309
+ rule = seq_rule ? seq_rule.sequence[0] : nil
310
+ path.push(nil)
311
+ i = 0
312
+ _linenum = @linenum #*V
313
+ _column = @column #*V
314
+ uniq_table = rule ? rule._uniqueness_check_table() : nil #*V
315
+ while level == @column && scan(/-\s+/)
316
+ path[-1] = i
317
+ skip_spaces_and_comments() #*V
318
+ _linenum2 = @linenum
319
+ _column2 = @column
320
+ val = parse_block_value(level, rule, path, uniq_table, seq)
321
+ add_to_seq(rule, seq, val, _linenum2, _column2) # seq << val
322
+ _set_error_info(_linenum, _column) do #*V
323
+ @validator._validate(val, rule, path, @errors, @done, uniq_table, false) #*V
324
+ end if rule && !val.equal?(PRECEDING_ALIAS_PLACEHOLDER) #*V
325
+ skip_spaces_and_comments()
326
+ i += 1
327
+ _linenum = @linenum #*V
328
+ _column = @column #*V
329
+ end
330
+ path.pop()
331
+ return seq
332
+ end
333
+
334
+
335
+ def _parse_map_value(map, map_rule, path, level, key, is_merged, uniq_table,
336
+ _linenum, _column, _linenum2, _column2) #:nodoc:
337
+ key = to_mapkey(key)
338
+ path[-1] = key
339
+ #if map.is_a?(Hash) && map.key?(key) && !is_merged
340
+ if map.respond_to?('key?') && map.key?(key) && !is_merged
341
+ rule = map_rule.mapping[key]
342
+ unless rule && rule.default
343
+ raise _syntax_error("mapping key is duplicated.", path)
344
+ end
345
+ end
346
+ #
347
+ if key == '=' # default
348
+ val = level ? parse_block_value(level, nil, path, uniq_table, map) \
349
+ : parse_flow_value(nil, path, uniq_table, map)
350
+ map.default = val
351
+ elsif key == '<<' # merge
352
+ classobj = nil
353
+ if map_rule && map_rule.classname
354
+ map_rule = map_rule.dup()
355
+ classobj = map_rule.classobj
356
+ map_rule.classname = nil
357
+ map_rule.classobj = nil
358
+ end
359
+ val = level ? parse_block_value(level, map_rule, path, uniq_table, map) \
360
+ : parse_flow_value(map_rule, path, uniq_table, map)
361
+ if val.is_a?(Array)
362
+ val.each_with_index do |v, i|
363
+ unless v.is_a?(Hash) || (classobj && val.is_a?(classobj))
364
+ raise _syntax_error("'<<': mapping required.", path + [i])
365
+ end
366
+ end
367
+ values = val
368
+ elsif val.is_a?(Hash) || (classobj && val.is_a?(classobj))
369
+ values = [val]
370
+ else
371
+ raise _syntax_error("'<<': mapping (or sequence of mapping) required.", path)
372
+ end
373
+ #
374
+ values.each do |hash|
375
+ if !hash.is_a?(Hash)
376
+ assert_error "hash=#{hash.inspect}" unless classobj && hash.is_a?(classobj)
377
+ obj = hash
378
+ hash = {}
379
+ obj.instance_variables.each do |name|
380
+ key = name[1..-1] # '@foo' => 'foo'
381
+ val = obj.instane_variable_get(name)
382
+ hash[key] = val
383
+ end
384
+ end
385
+ for key, val in hash
386
+ path[-1] = key #*V
387
+ rule = map_rule ? map_rule.mapping[key] : nil #*V
388
+ utable = uniq_table ? uniq_table[key] : nil #*V
389
+ _validate_map_value(map, map_rule, rule, path, utable, #*V
390
+ key, val, _linenum, _column) #*V
391
+ put_to_map(rule, map, key, val, _linenum2, _column2)
392
+ end
393
+ end
394
+ is_merged = true
395
+ else # other
396
+ rule = map_rule ? map_rule.mapping[key] : nil #*V
397
+ utable = uniq_table ? uniq_table[key] : nil #*V
398
+ val = level ? parse_block_value(level, rule, path, utable, map) \
399
+ : parse_flow_value(rule, path, utable, map)
400
+ _validate_map_value(map, map_rule, rule, path, utable, key, val, #*V
401
+ _linenum, _column) #*V
402
+ put_to_map(rule, map, key, val, _linenum2, _column2)
403
+ end
404
+ return is_merged
405
+ end
406
+
407
+
408
+ def _validate_map_value(map, map_rule, rule, path, uniq_table, key, val, #*V
409
+ _linenum, _column) #*V
410
+ if map_rule && !rule #*V
411
+ #_validate_error("unknown mapping key.", path) #*V
412
+ _set_error_info(_linenum, _column) do #*V
413
+ error = Kwalify::ErrorHelper.validate_error(:key_undefined, #*V
414
+ rule, path, map, ["#{key}:"]) #*V
415
+ @errors << error #*V
416
+ #error.linenum = _linenum #*V
417
+ #error.column = _column #*V
418
+ end #*V
419
+ end #*V
420
+ _set_error_info(_linenum, _column) do #*V
421
+ @validator._validate(val, rule, path, @errors, @done, uniq_table, false) #*V
422
+ end if rule && !val.equal?(PRECEDING_ALIAS_PLACEHOLDER) #*V
423
+ end
424
+
425
+
426
+ def parse_block_map(map, map_rule, path, uniq_table)
427
+ _start_linenum = @linenum #*V
428
+ _start_column = @column #*V
429
+ level = @column
430
+ path.push(nil)
431
+ is_merged = false
432
+ while true
433
+ _linenum = @linenum #*V
434
+ _column = @column #*V
435
+ break unless level == @column && scan(MAPKEY_PATTERN)
436
+ key = group(1)
437
+ skip_spaces_and_comments() #*V
438
+ _linenum2 = @linenum #*V
439
+ _column2 = @column #*V
440
+ is_merged = _parse_map_value(map, map_rule, path, level, key, is_merged,
441
+ uniq_table, _linenum, _column, _linenum2, _column2)
442
+ #skip_spaces_and_comments()
443
+ end
444
+ path.pop()
445
+ _set_error_info(_start_linenum, _start_column) do #*V
446
+ @validator._validate_mapping_required_keys(map, map_rule, #*V
447
+ path, @errors) #*V
448
+ end if map_rule #*V
449
+ return map
450
+ end
451
+
452
+
453
+ def to_mapkey(str)
454
+ if str[0] == ?" || str[0] == ?'
455
+ return str[1..-2]
456
+ else
457
+ return to_scalar(str)
458
+ end
459
+ end
460
+ private :to_mapkey
461
+
462
+
463
+ def parse_block_scalar(rule, path, uniq_table)
464
+ _linenum = @linenum #*V
465
+ _column = @column #*V
466
+ ch = peep(1)
467
+ if ch == '"' || ch == "'"
468
+ val = scan_string()
469
+ scan(/[ \t]*(?:\#.*)?$/)
470
+ else
471
+ scan(/(.*?)[ \t]*(?:\#.*)?$/)
472
+ #str.rstrip!
473
+ val = to_scalar(group(1))
474
+ end
475
+ val = create_scalar(rule, val, _linenum, _column) #*V
476
+ #_set_error_info(_linenum, _column) do #*V
477
+ # @validator._validate_unique(val, rule, path, @errors, uniq_table) #*V
478
+ #end if uniq_table #*V
479
+ skip_spaces_and_comments()
480
+ return val
481
+ end
482
+
483
+
484
+ def parse_block_text(column, rule, path, uniq_table)
485
+ _linenum = @linenum #*V
486
+ _column = @column #*V
487
+ indicator = scan(/[|>]/)
488
+ chomping = scan(/[-+]/)
489
+ num = scan(/\d+/)
490
+ indent = num ? column + num.to_i - 1 : nil
491
+ unless scan(/[ \t]*(.*?)(\#.*)?\r?\n/) # /[ \t]*(\#.*)?\r?\n/
492
+ raise _syntax_error("Syntax Error (line break or comment are expected)", path)
493
+ end
494
+ s = group(1)
495
+ is_folded = false
496
+ while match?(/( *)(.*?)(\r?\n)/)
497
+ spaces = group(1)
498
+ text = group(2)
499
+ nl = group(3)
500
+ if indent.nil?
501
+ if spaces.length >= column
502
+ indent = spaces.length
503
+ elsif text.empty?
504
+ s << nl
505
+ scan(/.*?\n/)
506
+ next
507
+ else
508
+ @diagnostic = 'text indent in block text may be shorter than that of first line or specified column.'
509
+ break
510
+ end
511
+ else
512
+ if spaces.length < indent && !text.empty?
513
+ @diagnostic = 'text indent in block text may be shorter than that of first line or specified column.'
514
+ break
515
+ end
516
+ end
517
+ scan(/.*?\n/)
518
+ if indicator == '|'
519
+ s << spaces[indent..-1] if spaces.length >= indent
520
+ s << text << nl
521
+ else # indicator == '>'
522
+ if !text.empty? && spaces.length == indent
523
+ if s.sub!(/\r?\n((\r?\n)+)\z/, '\1')
524
+ nil
525
+ elsif is_folded
526
+ s.sub!(/\r?\n\z/, ' ')
527
+ end
528
+ #s.sub!(/\r?\n\z/, '') if !s.sub!(/\r?\n(\r?\n)+\z/, '\1') && is_folded
529
+ is_folded = true
530
+ else
531
+ is_folded = false
532
+ s << spaces[indent..-1] if spaces.length > indent
533
+ end
534
+ s << text << nl
535
+ end
536
+ end
537
+ ## chomping
538
+ if chomping == '+'
539
+ nil
540
+ elsif chomping == '-'
541
+ s.sub!(/(\r?\n)+\z/, '')
542
+ else
543
+ s.sub!(/(\r?\n)(\r?\n)+\z/, '\1')
544
+ end
545
+ #
546
+ skip_spaces_and_comments()
547
+ val = s
548
+ #_set_error_info(_linenum, _column) do #*V
549
+ # @validator._validate_unique(val, rule, path, @errors, uniq_table) #*V
550
+ #end if uniq_table #*V
551
+ return val
552
+ end
553
+
554
+
555
+ def parse_flow_value(rule, path, uniq_table, container)
556
+ skip_spaces_and_comments()
557
+ ## anchor and alias
558
+ name = nil
559
+ if scan(/\&([-\w]+)/)
560
+ name = parse_anchor(rule, path, uniq_table, container)
561
+ elsif scan(/\*([-\w]+)/)
562
+ return parse_alias(rule, path, uniq_table, container)
563
+ end
564
+ ## type
565
+ if scan(/!!?\w+/)
566
+ skip_spaces_and_comments()
567
+ end
568
+ ## sequence
569
+ if match?(/\[/)
570
+ if rule && !rule.sequence #*V
571
+ #_validate_error("sequence is not expected.", path) #*V
572
+ rule = nil #*V
573
+ end #*V
574
+ seq = create_sequence(rule, @linenum, @column)
575
+ @anchors[name] = seq if name
576
+ parse_flow_seq(seq, rule, path, uniq_table)
577
+ return seq
578
+ end
579
+ ## mapping
580
+ if match?(/\{/)
581
+ if rule && !rule.mapping #*V
582
+ #_validate_error("mapping is not expected.", path) #*V
583
+ rule = nil #*V
584
+ end #*V
585
+ map = create_mapping(rule, @linenum, @column)
586
+ @anchors[name] = map if name
587
+ parse_flow_map(map, rule, path, uniq_table)
588
+ return map
589
+ end
590
+ ## scalar
591
+ scalar = parse_flow_scalar(rule, path, uniq_table)
592
+ @anchors[name] = scalar if name
593
+ return scalar
594
+ end
595
+
596
+
597
+ def parse_flow_seq(seq, seq_rule, path, uniq_table)
598
+ #scan(/\[\s*/)
599
+ scan(/\[/)
600
+ skip_spaces_and_comments()
601
+ if scan(/\]/)
602
+ nil
603
+ else
604
+ rule = seq_rule ? seq_rule.sequence[0] : nil #*V
605
+ uniq_table = rule ? rule._uniqueness_check_table() : nil #*V
606
+ path.push(nil)
607
+ i = 0
608
+ while true
609
+ path[-1] = i
610
+ _linenum = @linenum #*V
611
+ _column = @column #*V
612
+ val = parse_flow_value(rule, path, uniq_table, seq)
613
+ add_to_seq(rule, seq, val, _linenum, _column) # seq << val
614
+ _set_error_info(_linenum, _column) do #*V
615
+ @validator._validate(val, rule, path, @errors, @done, uniq_table, false) #*V
616
+ end if rule && !val.equal?(PRECEDING_ALIAS_PLACEHOLDER) #*V
617
+ skip_spaces_and_comments()
618
+ break unless scan(/,\s+/)
619
+ i += 1
620
+ if match?(/\]/)
621
+ raise _syntax_error("sequence item required (or last comma is extra).", path)
622
+ end
623
+ end
624
+ path.pop()
625
+ unless scan(/\]/)
626
+ raise _syntax_error("flow sequence is not closed by ']'.", path)
627
+ end
628
+ end
629
+ skip_spaces_and_comments()
630
+ return seq
631
+ end
632
+
633
+
634
+ def parse_flow_map(map, map_rule, path, uniq_table)
635
+ #scan(/\{\s*/) # not work?
636
+ _start_linenum = @linenum #*V
637
+ _start_column = @column #*V
638
+ scan(/\{/)
639
+ skip_spaces_and_comments()
640
+ if scan(/\}/)
641
+ nil
642
+ else
643
+ path.push(nil)
644
+ is_merged = false
645
+ while true
646
+ _linenum = @linenum #*V
647
+ _column = @column #*V
648
+ unless scan(MAPKEY_PATTERN)
649
+ raise _syntax_error("mapping key is expected.", path)
650
+ end
651
+ key = group(1)
652
+ skip_spaces_and_comments()
653
+ _linenum2 = @linenum #*V
654
+ _column2 = @column #*V
655
+ is_merged = _parse_map_value(map, map_rule, path, nil, key, is_merged,
656
+ uniq_table, _linenum, _column, _linenum2, _column2)
657
+ #skip_spaces_and_comments()
658
+ break unless scan(/,\s+/)
659
+ end
660
+ path.pop()
661
+ unless scan(/\}/)
662
+ raise _syntax_error("flow mapping is not closed by '}'.", path)
663
+ end
664
+ end
665
+ skip_spaces_and_comments()
666
+ _set_error_info(_start_linenum, _start_column) do #*V
667
+ @validator._validate_mapping_required_keys(map, map_rule, path, @errors) #*V
668
+ end if map_rule #*V
669
+ return map
670
+ end
671
+
672
+
673
+ def parse_flow_scalar(rule, path, uniq_table)
674
+ ch = peep(1)
675
+ _linenum = @linenum #*V
676
+ _column = @column #*V
677
+ if ch == '"' || ch == "'"
678
+ val = scan_string()
679
+ else
680
+ str = scan(/[^,\]\}\#]*/)
681
+ if match?(/,\S/)
682
+ while match?(/,\S/)
683
+ str << scan(/./)
684
+ str << scan(/[^,\]\}\#]*/)
685
+ end
686
+ end
687
+ str.rstrip!
688
+ val = to_scalar(str)
689
+ end
690
+ val = create_scalar(rule, val, _linenum, _column) #*V
691
+ #_set_error_info(_linenum, _column) do #*V
692
+ # @validator._validate_unique(val, rule, path, @errors, uniq_table) #*V
693
+ #end if uniq_table #*V
694
+ skip_spaces_and_comments()
695
+ return val
696
+ end
697
+
698
+
699
+ ####
700
+
701
+
702
+ def to_scalar(str)
703
+ case str
704
+ when nil ; val = nil
705
+ when /\A-?\d+\.\d+\z/ ; val = str.to_f
706
+ when /\A-?\d+\z/ ; val = str.to_i
707
+ when /\A(true|yes)\z/ ; val = true
708
+ when /\A(false|no)\z/ ; val = false
709
+ when /\A(null|~)\z/ ; val = nil
710
+ when /\A"(.*)"\z/ ; val = $1
711
+ when /\A'(.*)'\z/ ; val = $1
712
+ when /\A:(\w+)\z/ ; val = $1.intern
713
+ when /\A(\d\d\d\d)-(\d\d)-(\d\d)(?: (\d\d):(\d\d):(\d\d))?\z/
714
+ year, month, day, hour, min, sec = $1, $2, $3, $4, $5, $6
715
+ if hour
716
+ val = Time.mktime(year, month, day, hour, min, sec)
717
+ else
718
+ val = Date.new(year.to_i, month.to_i, day.to_i)
719
+ end
720
+ ## or
721
+ #params = [$1, $2, $3, $4, $5, $6]
722
+ #val = Time.mktime(*params)
723
+ else
724
+ val = str.empty? ? nil : str
725
+ end
726
+ skip_spaces_and_comments()
727
+ return val
728
+ end
729
+
730
+
731
+ ##
732
+
733
+ protected
734
+
735
+
736
+ def create_sequence(rule, linenum, column)
737
+ seq = @sequence_class.new
738
+ @location_table[seq.__id__] = []
739
+ return seq
740
+ end
741
+
742
+
743
+ def create_mapping(rule, linenum, column)
744
+ if rule && rule.classobj && @data_binding
745
+ classobj = rule.classobj
746
+ map = classobj.new
747
+ else
748
+ classobj = nil
749
+ map = @mapping_class.new # {}
750
+ end
751
+ @location_table[map.__id__] = hash = {}
752
+ hash[:classobj] = classobj if classobj
753
+ return map
754
+ end
755
+
756
+
757
+ def create_scalar(rule, value, linenum, column)
758
+ return value
759
+ end
760
+
761
+
762
+ def add_to_seq(rule, seq, val, linenum, column)
763
+ seq << val
764
+ @location_table[seq.__id__] << [linenum, column]
765
+ end
766
+
767
+
768
+ def put_to_map(rule, map, key, val, linenum, column)
769
+ #if map.is_a?(Hash)
770
+ # map[key] = val
771
+ #elsif map.respond_to?(name="#{key}=")
772
+ # map.__send__(name, val)
773
+ #elsif map.respond_to?('[]=')
774
+ # map[key] = val
775
+ #else
776
+ # map.instance_variable_set("@#{key}", val)
777
+ #end
778
+ map[key] = val
779
+ @location_table[map.__id__][key] = [linenum, column]
780
+ end
781
+
782
+
783
+ def _getclass(classname)
784
+ mod = Object
785
+ classname.split(/::/).each do |modname|
786
+ mod = mod.const_get(modname) # raises NameError when module not found
787
+ end
788
+ return mod
789
+ end
790
+
791
+
792
+ public
793
+
794
+
795
+ def location(path)
796
+ if path.empty? || path == '/'
797
+ return @location_table[-1] # return value is [linenum, column]
798
+ end
799
+ if path.is_a?(Array)
800
+ items = path.collect { |item| to_scalar(item) }
801
+ elsif path.is_a?(String)
802
+ items = path.split('/').collect { |item| to_scalar(item) }
803
+ items.shift if path[0] == ?/ # delete empty string on head
804
+ else
805
+ raise ArgumentError.new("path should be Array or String.")
806
+ end
807
+ last_item = items.pop()
808
+ c = @doc # collection
809
+ items.each do |item|
810
+ if c.is_a?(Array)
811
+ c = c[item.to_i]
812
+ elsif c.is_a?(Hash)
813
+ c = c[item]
814
+ elsif (table = @location_table[c.__id__]) && table[:classobj]
815
+ if c.respond_to?(item)
816
+ c = c.__send__(item)
817
+ elsif c.respond_to?("[]=")
818
+ c = c[item]
819
+ else
820
+ assert false
821
+ end
822
+ else
823
+ #assert false
824
+ raise ArgumentError.new("#{path.inspect}: invalid path.")
825
+ end
826
+ end
827
+ collection = @location_table[c.__id__]
828
+ return nil if collection.nil?
829
+ index = c.is_a?(Array) ? last_item.to_i : last_item
830
+ return collection[index] # return value is [linenum, column]
831
+ end
832
+
833
+
834
+ def set_errors_linenum(errors)
835
+ errors.each do |error|
836
+ error.linenum, error.column = location(error.path)
837
+ end
838
+ end
839
+
840
+
841
+ end