ddao-kwalify 0.7.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 (208) hide show
  1. data/CHANGES.txt +243 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.txt +61 -0
  4. data/bin/kwalify +13 -0
  5. data/contrib/inline-require +153 -0
  6. data/contrib/kwalify +4159 -0
  7. data/doc-api/classes/CommandOptionError.html +184 -0
  8. data/doc-api/classes/CommandOptionParser.html +325 -0
  9. data/doc-api/classes/Kwalify.html +292 -0
  10. data/doc-api/classes/Kwalify/AssertionError.html +148 -0
  11. data/doc-api/classes/Kwalify/BaseError.html +297 -0
  12. data/doc-api/classes/Kwalify/BaseParser.html +461 -0
  13. data/doc-api/classes/Kwalify/CommandOptionError.html +168 -0
  14. data/doc-api/classes/Kwalify/ErrorHelper.html +223 -0
  15. data/doc-api/classes/Kwalify/HashInterface.html +118 -0
  16. data/doc-api/classes/Kwalify/Json.html +105 -0
  17. data/doc-api/classes/Kwalify/KwalifyError.html +111 -0
  18. data/doc-api/classes/Kwalify/Main.html +339 -0
  19. data/doc-api/classes/Kwalify/MetaValidator.html +448 -0
  20. data/doc-api/classes/Kwalify/Parser.html +155 -0
  21. data/doc-api/classes/Kwalify/PlainYamlParser.html +523 -0
  22. data/doc-api/classes/Kwalify/PlainYamlParser/Alias.html +165 -0
  23. data/doc-api/classes/Kwalify/Rule.html +433 -0
  24. data/doc-api/classes/Kwalify/SchemaError.html +148 -0
  25. data/doc-api/classes/Kwalify/SyntaxError.html +185 -0
  26. data/doc-api/classes/Kwalify/Types.html +302 -0
  27. data/doc-api/classes/Kwalify/Util.html +389 -0
  28. data/doc-api/classes/Kwalify/Util/HashLike.html +246 -0
  29. data/doc-api/classes/Kwalify/Util/OrderedHash.html +330 -0
  30. data/doc-api/classes/Kwalify/ValidationError.html +148 -0
  31. data/doc-api/classes/Kwalify/Validator.html +381 -0
  32. data/doc-api/classes/Kwalify/Yaml.html +194 -0
  33. data/doc-api/classes/Kwalify/Yaml/Parser.html +1538 -0
  34. data/doc-api/classes/Kwalify/YamlParser.html +542 -0
  35. data/doc-api/classes/Kwalify/YamlSyntaxError.html +119 -0
  36. data/doc-api/classes/Test.html +107 -0
  37. data/doc-api/classes/Test/Unit.html +101 -0
  38. data/doc-api/created.rid +1 -0
  39. data/doc-api/files/__/README_txt.html +172 -0
  40. data/doc-api/files/kwalify/errors_rb.html +114 -0
  41. data/doc-api/files/kwalify/main_rb.html +118 -0
  42. data/doc-api/files/kwalify/messages_rb.html +107 -0
  43. data/doc-api/files/kwalify/meta-validator_rb.html +117 -0
  44. data/doc-api/files/kwalify/parser/base_rb.html +116 -0
  45. data/doc-api/files/kwalify/parser/yaml_rb.html +117 -0
  46. data/doc-api/files/kwalify/rule_rb.html +116 -0
  47. data/doc-api/files/kwalify/types_rb.html +114 -0
  48. data/doc-api/files/kwalify/util/assert-text-equal_rb.html +115 -0
  49. data/doc-api/files/kwalify/util/hash-interface_rb.html +114 -0
  50. data/doc-api/files/kwalify/util/hashlike_rb.html +107 -0
  51. data/doc-api/files/kwalify/util/option-parser_rb.html +107 -0
  52. data/doc-api/files/kwalify/util/ordered-hash_rb.html +107 -0
  53. data/doc-api/files/kwalify/util/testcase-helper_rb.html +115 -0
  54. data/doc-api/files/kwalify/util_rb.html +107 -0
  55. data/doc-api/files/kwalify/validator_rb.html +117 -0
  56. data/doc-api/files/kwalify/yaml-parser_rb.html +117 -0
  57. data/doc-api/files/kwalify_rb.html +121 -0
  58. data/doc-api/fr_class_index.html +57 -0
  59. data/doc-api/fr_file_index.html +45 -0
  60. data/doc-api/fr_method_index.html +168 -0
  61. data/doc-api/index.html +24 -0
  62. data/doc-api/rdoc-style.css +208 -0
  63. data/doc/docstyle.css +188 -0
  64. data/doc/img/fig01.png +0 -0
  65. data/doc/users-guide.html +2050 -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.rb +67 -0
  82. data/lib/kwalify/errors.rb +128 -0
  83. data/lib/kwalify/kwalify.schema.yaml +58 -0
  84. data/lib/kwalify/main.rb +442 -0
  85. data/lib/kwalify/messages.rb +173 -0
  86. data/lib/kwalify/meta-validator.rb +276 -0
  87. data/lib/kwalify/parser/base.rb +127 -0
  88. data/lib/kwalify/parser/yaml.rb +841 -0
  89. data/lib/kwalify/rule.rb +560 -0
  90. data/lib/kwalify/templates/genclass-java.eruby +222 -0
  91. data/lib/kwalify/templates/genclass-php.eruby +104 -0
  92. data/lib/kwalify/templates/genclass-ruby.eruby +113 -0
  93. data/lib/kwalify/types.rb +156 -0
  94. data/lib/kwalify/util.rb +157 -0
  95. data/lib/kwalify/util/assert-text-equal.rb +46 -0
  96. data/lib/kwalify/util/hash-interface.rb +18 -0
  97. data/lib/kwalify/util/hashlike.rb +51 -0
  98. data/lib/kwalify/util/option-parser.rb +220 -0
  99. data/lib/kwalify/util/ordered-hash.rb +57 -0
  100. data/lib/kwalify/util/testcase-helper.rb +112 -0
  101. data/lib/kwalify/validator.rb +282 -0
  102. data/lib/kwalify/yaml-parser.rb +870 -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 +384 -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-validator.rb +95 -0
  204. data/test/test-validator.yaml +986 -0
  205. data/test/test-yaml-parser.rb +47 -0
  206. data/test/test-yaml-parser.yaml +1226 -0
  207. data/test/test.rb +60 -0
  208. metadata +261 -0
@@ -0,0 +1,4159 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ ###
4
+ ### $Rev: 83 $
5
+ ### $Release: 0.7.1 $
6
+ ### copyright(c) 2005-2008 kuwata-lab all rights reserved.
7
+ ###
8
+
9
+ #--begin of require 'kwalify'
10
+ ###
11
+ ### $Rev: 99 $
12
+ ### $Release: 0.7.1 $
13
+ ### copyright(c) 2006 kuwata-lab.com all rights reserved.
14
+ ###
15
+
16
+
17
+ module Kwalify
18
+
19
+ RELEASE = ("$Release: 0.7.1 $" =~ /[.\d]+/) && $&
20
+
21
+ end
22
+
23
+ #--begin of require 'kwalify/types'
24
+ ###
25
+ ### $Rev: 87 $
26
+ ### $Release: 0.7.1 $
27
+ ### copyright(c) 2005-2008 kuwata-lab all rights reserved.
28
+ ###
29
+
30
+ require 'date'
31
+
32
+
33
+ module Kwalify
34
+ module Boolean # :nodoc:
35
+ end
36
+ end
37
+ class TrueClass # :nodoc:
38
+ include Kwalify::Boolean
39
+ end
40
+ class FalseClass # :nodoc:
41
+ include Kwalify::Boolean
42
+ end
43
+ #module Boolean; end
44
+ #class TrueClass
45
+ # include Boolean
46
+ #end
47
+ #class FalseClass
48
+ # include Boolean
49
+ #end
50
+
51
+
52
+ module Kwalify
53
+ module Text # :nodoc:
54
+ end
55
+ end
56
+ class String # :nodoc:
57
+ include Kwalify::Text
58
+ end
59
+ class Numeric # :nodoc:
60
+ include Kwalify::Text
61
+ end
62
+ #module Text; end
63
+ #class String
64
+ # include Text
65
+ #end
66
+ #class Numeric
67
+ # include Text
68
+ #end
69
+
70
+
71
+ module Kwalify
72
+ module Scalar # :nodoc:
73
+ end
74
+ end
75
+ class String # :nodoc:
76
+ include Kwalify::Scalar
77
+ end
78
+ class Numeric # :nodoc:
79
+ include Kwalify::Scalar
80
+ end
81
+ class Date # :nodoc:
82
+ include Kwalify::Scalar
83
+ end
84
+ class Time # :nodoc:
85
+ include Kwalify::Scalar
86
+ end
87
+ class TrueClass # :nodoc:
88
+ include Kwalify::Scalar
89
+ end
90
+ class FalseClass # :nodoc:
91
+ include Kwalify::Scalar
92
+ end
93
+ class NilClass # :nodoc:
94
+ include Kwalify::Scalar
95
+ end
96
+ module Kwalify
97
+ module Text # :nodoc:
98
+ include Kwalify::Scalar
99
+ end
100
+ end
101
+
102
+
103
+ module Kwalify
104
+
105
+
106
+ module Types
107
+
108
+
109
+ DEFAULT_TYPE = "str" ## use "str" as default of @type
110
+
111
+ @@type_table = {
112
+ "seq" => Array,
113
+ "map" => Hash,
114
+ "str" => String,
115
+ #"string" => String,
116
+ "text" => Text,
117
+ "int" => Integer,
118
+ #"integer" => Integer,
119
+ "float" => Float,
120
+ "number" => Numeric,
121
+ #"numeric" => Numeric,
122
+ "date" => Date,
123
+ "time" => Time,
124
+ "timestamp" => Time,
125
+ "bool" => Boolean,
126
+ #"boolean" => Boolean,
127
+ #"object" => Object,
128
+ "any" => Object,
129
+ "scalar" => Scalar,
130
+ }
131
+
132
+ def self.type_table
133
+ return @@type_table
134
+ end
135
+
136
+ def self.type_class(type)
137
+ klass = @@type_table[type]
138
+ #assert_error('type=#{type.inspect}') unless klass
139
+ return klass
140
+ end
141
+
142
+ def self.get_type_class(type)
143
+ return type_class(type)
144
+ end
145
+
146
+
147
+
148
+ #--
149
+ #def collection_class?(klass)
150
+ # return klass.is_a?(Array) || klass.is_a?(Hash)
151
+ #end
152
+ #
153
+ #def scalar_class?(klass)
154
+ # return !klass.is_a?(Array) && !klass.is_a?(Hash) && klass != Object
155
+ #end
156
+
157
+ def collection?(val)
158
+ return val.is_a?(Array) || val.is_a?(Hash)
159
+ end
160
+
161
+ def scalar?(val)
162
+ #return !val.is_a?(Array) && !val.is_a?(Hash) && val.class != Object
163
+ return val.is_a?(Kwalify::Scalar) #&& val.class != Object
164
+ end
165
+
166
+ def collection_type?(type)
167
+ return type == 'seq' || type == 'map'
168
+ end
169
+
170
+ def scalar_type?(type)
171
+ return type != 'seq' && type != 'map' && type == 'any'
172
+ end
173
+
174
+ module_function 'collection?', 'scalar?', 'collection_type?', 'scalar_type?'
175
+ end
176
+
177
+ extend Types
178
+
179
+ end
180
+ #--end of require 'kwalify/types'
181
+ #--begin of require 'kwalify/messages'
182
+ ###
183
+ ### $Rev: 95 $
184
+ ### $Release: 0.7.1 $
185
+ ### copyright(c) 2005-2008 kuwata-lab all rights reserved.
186
+ ###
187
+
188
+ module Kwalify
189
+
190
+ @@messages = {}
191
+
192
+ def self.msg(key)
193
+ return @@messages[key]
194
+ end
195
+
196
+
197
+
198
+ @@messages[:command_help] = <<END
199
+ kwalify - schema validator and data binding tool for YAML and JSON
200
+ ## Usage1: validate yaml document
201
+ kwalify [..options..] -f schema.yaml doc.yaml [doc2.yaml ...]
202
+ ## Usage2: validate schema definition
203
+ kwalify [..options..] -m schema.yaml [schema2.yaml ...]
204
+ ## Usage3: do action
205
+ kwalify [..options..] -a action -f schema.yaml [schema2.yaml ...]
206
+ -h, --help : help
207
+ -v : version
208
+ -q : quiet
209
+ -s : silent (obsolete, use '-q' instead)
210
+ -f schema.yaml : schema definition file
211
+ -m : meta-validation mode
212
+ -t : expand tab characters
213
+ -l : show linenumber when errored (experimental)
214
+ -E : show errors in emacs-style (experimental, implies '-l')
215
+ -a action : action ('genclass-ruby', 'genclass-php', 'genclass-java')
216
+ (try '-ha genclass-ruby' for details)
217
+ -I path : template path (for '-a')
218
+ -P : allow preceding alias
219
+ END
220
+ # -z : syntax checking of schema file
221
+ # -I path : path for template of action
222
+
223
+
224
+
225
+ ##----- begin
226
+ # filename: lib/kwalify/main.rb
227
+ @@messages[:command_option_actionnoschema] = "schema filename is not specified."
228
+ @@messages[:command_option_noaction] = "command-line option '-f' or '-m' required."
229
+ @@messages[:command_option_notemplate] = "%s: invalid action (template not found).\n"
230
+ @@messages[:schema_empty] = "%s: empty schema.\n"
231
+ @@messages[:validation_empty] = "%s#%d: empty."
232
+ @@messages[:validation_empty] = "%s#%d: empty.\n"
233
+ @@messages[:validation_valid] = "%s#%d: valid."
234
+ @@messages[:command_option_schema_required] = "-%s: schema filename required."
235
+ @@messages[:command_option_action_required] = "-%s: action required."
236
+ @@messages[:command_option_tpath_required] = "-%s: template path required."
237
+ @@messages[:command_property_invalid] = "%s: invalid property."
238
+ @@messages[:command_option_invalid] = "-%s: invalid command option."
239
+ # --
240
+ # filename: lib/kwalify/rule.rb
241
+ @@messages[:schema_notmap] = "schema definition is not a mapping."
242
+ @@messages[:key_unknown] = "unknown key."
243
+ @@messages[:type_notstr] = "not a string."
244
+ @@messages[:type_unknown] = "unknown type."
245
+ @@messages[:class_notmap] = "available only with map type."
246
+ @@messages[:required_notbool] = "not a boolean."
247
+ @@messages[:pattern_notstr] = "not a string (or regexp)"
248
+ @@messages[:pattern_notmatch] = "should be '/..../'."
249
+ @@messages[:pattern_syntaxerr] = "has regexp error."
250
+ @@messages[:enum_notseq] = "not a sequence."
251
+ @@messages[:enum_notscalar] = "not available with seq or map."
252
+ @@messages[:enum_type_unmatch] = "%s type expected."
253
+ @@messages[:enum_duplicate] = "duplicated enum value."
254
+ @@messages[:assert_notstr] = "not a string."
255
+ @@messages[:assert_noval] = "'val' is not used."
256
+ @@messages[:assert_syntaxerr] = "expression syntax error."
257
+ @@messages[:range_notmap] = "not a mapping."
258
+ @@messages[:range_notscalar] = "is available only with scalar type."
259
+ @@messages[:range_type_unmatch] = "not a %s."
260
+ @@messages[:range_undefined] = "undefined key."
261
+ @@messages[:range_twomax] = "both 'max' and 'max-ex' are not available at once."
262
+ @@messages[:range_twomin] = "both 'min' and 'min-ex' are not available at once."
263
+ @@messages[:range_maxltmin] = "max '%s' is less than min '%s'."
264
+ @@messages[:range_maxleminex] = "max '%s' is less than or equal to min-ex '%s'."
265
+ @@messages[:range_maxexlemin] = "max-ex '%s' is less than or equal to min '%s'."
266
+ @@messages[:range_maxexleminex] = "max-ex '%s' is less than or equal to min-ex '%s'."
267
+ @@messages[:length_notmap] = "not a mapping."
268
+ @@messages[:length_nottext] = "is available only with string or text."
269
+ @@messages[:length_notint] = "not an integer."
270
+ @@messages[:length_undefined] = "undefined key."
271
+ @@messages[:length_twomax] = "both 'max' and 'max-ex' are not available at once."
272
+ @@messages[:length_twomin] = "both 'min' and 'min-ex' are not available at once."
273
+ @@messages[:length_maxltmin] = "max '%s' is less than min '%s'."
274
+ @@messages[:length_maxleminex] = "max '%s' is less than or equal to min-ex '%s'."
275
+ @@messages[:length_maxexlemin] = "max-ex '%s' is less than or equal to min '%s'."
276
+ @@messages[:length_maxexleminex] = "max-ex '%s' is less than or equal to min-ex '%s'."
277
+ @@messages[:ident_notbool] = "not a boolean."
278
+ @@messages[:ident_notscalar] = "is available only with a scalar type."
279
+ @@messages[:ident_onroot] = "is not available on root element."
280
+ @@messages[:ident_notmap] = "is available only with an element of mapping."
281
+ @@messages[:unique_notbool] = "not a boolean."
282
+ @@messages[:unique_notscalar] = "is available only with a scalar type."
283
+ @@messages[:unique_onroot] = "is not available on root element."
284
+ @@messages[:default_nonscalarval] = "not a scalar."
285
+ @@messages[:default_notscalar] = "is available only with a scalar type."
286
+ @@messages[:default_unmatch] = "not a %s."
287
+ @@messages[:sequence_notseq] = "not a sequence."
288
+ @@messages[:sequence_noelem] = "required one element."
289
+ @@messages[:sequence_toomany] = "required just one element."
290
+ @@messages[:mapping_notmap] = "not a mapping."
291
+ @@messages[:mapping_noelem] = "required at least one element."
292
+ @@messages[:seq_nosequence] = "type 'seq' requires 'sequence:'."
293
+ @@messages[:seq_conflict] = "not available with sequence."
294
+ @@messages[:map_nomapping] = "type 'map' requires 'mapping:'."
295
+ @@messages[:map_conflict] = "not available with mapping."
296
+ @@messages[:scalar_conflict] = "not available with scalar type."
297
+ @@messages[:enum_conflict] = "not available with 'enum:'."
298
+ @@messages[:default_conflict] = "not available when 'required:' is true."
299
+ # --
300
+ # filename: lib/kwalify/validator.rb
301
+ @@messages[:required_novalue] = "value required but none."
302
+ @@messages[:type_unmatch] = "not a %s."
303
+ @@messages[:key_undefined] = "key '%s' is undefined."
304
+ @@messages[:required_nokey] = "key '%s:' is required."
305
+ @@messages[:value_notunique] = "is already used at '%s'."
306
+ @@messages[:assert_failed] = "assertion expression failed (%s)."
307
+ @@messages[:enum_notexist] = "invalid %s value."
308
+ @@messages[:pattern_unmatch] = "not matched to pattern %s."
309
+ @@messages[:range_toolarge] = "too large (> max %s)."
310
+ @@messages[:range_toosmall] = "too small (< min %s)."
311
+ @@messages[:range_toolargeex] = "too large (>= max %s)."
312
+ @@messages[:range_toosmallex] = "too small (<= min %s)."
313
+ @@messages[:length_toolong] = "too long (length %d > max %d)."
314
+ @@messages[:length_tooshort] = "too short (length %d < min %d)."
315
+ @@messages[:length_toolongex] = "too long (length %d >= max %d)."
316
+ @@messages[:length_tooshortex] = "too short (length %d <= min %d)."
317
+ # --
318
+ # filename: lib/kwalify/yaml-parser.rb
319
+ @@messages[:flow_hastail] = "flow style sequence is closed but got '%s'."
320
+ @@messages[:flow_eof] = "found EOF when parsing flow style."
321
+ @@messages[:flow_alias_label] = "alias name expected."
322
+ @@messages[:flow_anchor_label] = "anchor name expected."
323
+ @@messages[:flow_noseqitem] = "sequence item required (or last comma is extra)."
324
+ @@messages[:flow_seqnotclosed] = "flow style sequence requires ']'."
325
+ @@messages[:flow_mapnoitem] = "mapping item required (or last comma is extra)."
326
+ @@messages[:flow_mapnotclosed] = "flow style mapping requires '}'."
327
+ @@messages[:flow_nocolon] = "':' expected but got %s."
328
+ @@messages[:flow_str_notclosed] = "%s: string not closed."
329
+ @@messages[:anchor_duplicated] = "anchor '%s' is already used."
330
+ @@messages[:alias_extradata] = "alias cannot take any data."
331
+ @@messages[:anchor_notfound] = "anchor '%s' not found"
332
+ @@messages[:sequence_noitem] = "sequence item is expected."
333
+ @@messages[:sequence_badindent] = "illegal indent of sequence."
334
+ @@messages[:mapping_noitem] = "mapping item is expected."
335
+ @@messages[:mapping_badindent] = "illegal indent of mapping."
336
+ # --
337
+ ##----- end
338
+
339
+
340
+
341
+
342
+ @@words = {}
343
+
344
+ def self.word(key)
345
+ return @@words[key] || key
346
+ end
347
+
348
+ @@words['str'] = 'string'
349
+ @@words['int'] = 'integer'
350
+ @@words['bool'] = 'boolean'
351
+ @@words['seq'] = 'sequence'
352
+ @@words['map'] = 'mapping'
353
+
354
+ end
355
+ #--end of require 'kwalify/messages'
356
+ #--begin of require 'kwalify/errors'
357
+ ###
358
+ ### $Rev: 99 $
359
+ ### $Release: 0.7.1 $
360
+ ### copyright(c) 2005-2008 kuwata-lab all rights reserved.
361
+ ###
362
+
363
+ #--already included require 'kwalify/messages'
364
+
365
+ module Kwalify
366
+
367
+ class KwalifyError < StandardError
368
+ end
369
+
370
+
371
+ class AssertionError < KwalifyError
372
+ def initialize(msg)
373
+ super("*** assertion error: " + msg)
374
+ end
375
+ end
376
+
377
+
378
+ class BaseError < KwalifyError
379
+ def initialize(message="", path=nil, value=nil, rule=nil, error_symbol=nil)
380
+ super(message)
381
+ @path = path.is_a?(Array) ? '/'+path.join('/') : path
382
+ @rule = rule
383
+ @value = value
384
+ @error_symbol = error_symbol
385
+ end
386
+ attr_accessor :error_symbol, :rule, :path, :value
387
+ attr_accessor :filename, :linenum, :column
388
+
389
+ def path
390
+ return @path == '' ? "/" : @path
391
+ end
392
+
393
+ alias _to_s to_s
394
+ alias message to_s
395
+
396
+ def to_s
397
+ s = ''
398
+ s << @filename << ":" if @filename
399
+ s << "#{@linenum}:#{@column} " if @linenum
400
+ s << "[#{path()}] " if @path
401
+ s << _to_s()
402
+ return s
403
+ end
404
+
405
+ def <=>(ex)
406
+ #return @linenum <=> ex.linenum
407
+ v = 0
408
+ v = @linenum <=> ex.linenum if @linenum && ex.linenum
409
+ v = @column <=> ex.column if v == 0 && @column && ex.column
410
+ v = @path <=> ex.path if v == 0
411
+ return v
412
+ end
413
+ end
414
+
415
+
416
+ class SchemaError < BaseError
417
+ def initialize(message="", path=nil, rule=nil, value=nil, error_symbol=nil)
418
+ super(message, path, rule, value, error_symbol)
419
+ end
420
+ end
421
+
422
+
423
+ class ValidationError < BaseError
424
+ def initialize(message="", path=nil, rule=nil, value=nil, error_symbol=nil)
425
+ super(message, path, rule, value, error_symbol)
426
+ end
427
+ end
428
+
429
+
430
+ ## syntax error for YAML and JSON
431
+ class SyntaxError < BaseError #KwalifyError
432
+ def initialize(msg, linenum=nil, error_symbol=nil)
433
+ super(linenum ? "line #{linenum}: #{msg}" : msg)
434
+ @linenum = linenum
435
+ @error_symbol = error_symbol
436
+ end
437
+ #attr_accessor :linenum, :error_symbol
438
+ def message
439
+ "file: #{@filename}, line #{@linenum}: #{super}"
440
+ end
441
+ end
442
+
443
+
444
+ ## (obsolete) use Kwalify::SyntaxError instead
445
+ class YamlSyntaxError < SyntaxError
446
+ end
447
+
448
+
449
+ module ErrorHelper
450
+
451
+ #module_function
452
+
453
+ def assert_error(message="")
454
+ raise AssertionError.new(message)
455
+ end
456
+
457
+ def validate_error(error_symbol, rule, path, val, args=nil)
458
+ msg = _build_message(error_symbol, val, args);
459
+ path = '/'+path.join('/') if path.is_a?(Array)
460
+ return ValidationError.new(msg, path, val, rule, error_symbol)
461
+ end
462
+ module_function :validate_error
463
+
464
+ def schema_error(error_symbol, rule, path, val, args=nil)
465
+ msg = _build_message(error_symbol, val, args);
466
+ path = '/'+path.join('/') if path.is_a?(Array)
467
+ return SchemaError.new(msg, path, val, rule, error_symbol)
468
+ end
469
+
470
+ def _build_message(message_key, val, args)
471
+ msg = Kwalify.msg(message_key)
472
+ assert_error("message_key=#{message_key.inspect}") unless msg
473
+ msg = msg % args if args
474
+ msg = "'#{val.to_s.gsub(/\n/, '\n')}': #{msg}" if !val.nil? && Types.scalar?(val)
475
+ return msg;
476
+ end
477
+ module_function :_build_message
478
+
479
+ end
480
+
481
+ extend ErrorHelper
482
+
483
+ end
484
+ #--end of require 'kwalify/errors'
485
+ #--begin of require 'kwalify/rule'
486
+ ###
487
+ ### $Rev: 95 $
488
+ ### $Release: 0.7.1 $
489
+ ### copyright(c) 2005-2008 kuwata-lab all rights reserved.
490
+ ###
491
+
492
+ #--already included require 'kwalify/messages'
493
+ #--already included require 'kwalify/errors'
494
+ #--already included require 'kwalify/types'
495
+
496
+
497
+ module Kwalify
498
+
499
+
500
+ class Rule
501
+ include Kwalify::ErrorHelper
502
+
503
+ attr_accessor :parent
504
+ attr_reader :name
505
+ attr_reader :desc
506
+ attr_reader :enum
507
+ attr_reader :required
508
+ attr_reader :type
509
+ attr_reader :type_class
510
+ attr_reader :pattern
511
+ attr_reader :regexp
512
+ attr_reader :sequence
513
+ attr_reader :mapping
514
+ attr_reader :assert
515
+ attr_reader :assert_proc
516
+ attr_reader :range
517
+ attr_reader :length
518
+ attr_reader :ident
519
+ attr_reader :unique
520
+ attr_reader :default
521
+ attr_reader :classname
522
+ attr_reader :classobj
523
+
524
+
525
+ def initialize(hash=nil, parent=nil)
526
+ _init(hash, "", {}) if hash
527
+ @parent = parent
528
+ end
529
+
530
+
531
+ def _init(hash, path="", rule_table={})
532
+ unless hash.is_a?(Hash)
533
+ #* key=:schema_notmap msg="schema definition is not a mapping."
534
+ raise Kwalify.schema_error(:schema_notmap, nil, (!path || path.empty? ? "/" : path), nil)
535
+ end
536
+ rule = self
537
+ rule_table[hash.__id__] = rule
538
+ ## 'type:' entry
539
+ curr_path = "#{path}/type"
540
+ _init_type_value(hash['type'], rule, curr_path)
541
+ ## other entries
542
+ hash.each do |key, val|
543
+ curr_path = "#{path}/#{key}"
544
+ sym = key.intern
545
+ method = get_init_method(sym)
546
+ unless method
547
+ #* key=:key_unknown msg="unknown key."
548
+ raise schema_error(:key_unknown, rule, curr_path, "#{key}:")
549
+ end
550
+ if sym == :sequence || sym == :mapping
551
+ __send__(method, val, rule, curr_path, rule_table)
552
+ else
553
+ __send__(method, val, rule, curr_path)
554
+ end
555
+ end
556
+ _check_confliction(hash, rule, path)
557
+ return self
558
+ end
559
+
560
+
561
+ keys = %w[type name desc required pattern enum assert range length
562
+ ident unique default sequence mapping class]
563
+ #table = keys.inject({}) {|h, k| h[k.intern] = "_init_#{k}_value".intern; h }
564
+ table = {}; keys.each {|k| table[k.intern] = "_init_#{k}_value".intern }
565
+ @@dispatch_table = table
566
+
567
+
568
+ protected
569
+
570
+
571
+ def get_init_method(sym)
572
+ @_dispatch_table ||= @@dispatch_table
573
+ return @_dispatch_table[sym]
574
+ end
575
+
576
+
577
+ private
578
+
579
+
580
+ def _init_type_value(val, rule, path)
581
+ @type = val
582
+ @type = Types::DEFAULT_TYPE if @type.nil?
583
+ unless @type.is_a?(String)
584
+ #* key=:type_notstr msg="not a string."
585
+ raise schema_error(:type_notstr, rule, path, @type.to_s)
586
+ end
587
+ @type_class = Types.type_class(@type)
588
+ #if @type_class.nil?
589
+ # begin
590
+ # @type_class = Kernel.const_get(@type)
591
+ # rescue NameError
592
+ # end
593
+ #end
594
+ unless @type_class
595
+ #* key=:type_unknown msg="unknown type."
596
+ raise schema_error(:type_unknown, rule, path, @type.to_s)
597
+ end
598
+ end
599
+
600
+
601
+ def _init_class_value(val, rule, path)
602
+ @classname = val
603
+ unless @type == 'map'
604
+ #* key=:class_notmap msg="available only with map type."
605
+ raise schema_error(:class_notmap, rule, path, 'class:')
606
+ end
607
+ begin
608
+ @classobj = Util.get_class(val)
609
+ rescue NameError
610
+ @classobj = nil
611
+ end
612
+ end
613
+
614
+
615
+ def _init_name_value(val, rule, path)
616
+ @name = val
617
+ end
618
+
619
+
620
+ def _init_desc_value(val, rule, path)
621
+ @desc = val
622
+ end
623
+
624
+
625
+ def _init_required_value(val, rule, path)
626
+ @required = val
627
+ unless val.is_a?(Boolean) #|| val.nil?
628
+ #* key=:required_notbool msg="not a boolean."
629
+ raise schema_error(:required_notbool, rule, path, val)
630
+ end
631
+ end
632
+
633
+
634
+ def _init_pattern_value(val, rule, path)
635
+ @pattern = val
636
+ unless val.is_a?(String) || val.is_a?(Regexp)
637
+ #* key=:pattern_notstr msg="not a string (or regexp)"
638
+ raise schema_error(:pattern_notstr, rule, path, val)
639
+ end
640
+ unless val =~ /\A\/(.*)\/([mi]?[mi]?)\z/
641
+ #* key=:pattern_notmatch msg="should be '/..../'."
642
+ raise schema_error(:pattern_notmatch, rule, path, val)
643
+ end
644
+ pat = $1; opt = $2
645
+ flag = 0
646
+ flag += Regexp::IGNORECASE if opt.include?("i")
647
+ flag += Regexp::MULTILINE if opt.include?("m")
648
+ begin
649
+ @regexp = Regexp.compile(pat, flag)
650
+ rescue RegexpError => ex
651
+ #* key=:pattern_syntaxerr msg="has regexp error."
652
+ raise schema_error(:pattern_syntaxerr, rule, path, val)
653
+ end
654
+ end
655
+
656
+
657
+ def _init_enum_value(val, rule, path)
658
+ @enum = val
659
+ unless val.is_a?(Array)
660
+ #* key=:enum_notseq msg="not a sequence."
661
+ raise schema_error(:enum_notseq, rule, path, val)
662
+ end
663
+ if Types.collection_type?(@type) # unless Kwalify.scalar_class?(@type_class)
664
+ #* key=:enum_notscalar msg="not available with seq or map."
665
+ raise schema_error(:enum_notscalar, rule, File.dirname(path), 'enum:')
666
+ end
667
+ elem_table = {}
668
+ @enum.each do |elem|
669
+ unless elem.is_a?(@type_class)
670
+ #* key=:enum_type_unmatch msg="%s type expected."
671
+ raise schema_error(:enum_type_unmatch, rule, path, elem, [Kwalify.word(@type)])
672
+ end
673
+ if elem_table[elem]
674
+ #* key=:enum_duplicate msg="duplicated enum value."
675
+ raise schema_error(:enum_duplicate, rule, path, elem.to_s)
676
+ end
677
+ elem_table[elem] = true
678
+ end
679
+ end
680
+
681
+
682
+ def _init_assert_value(val, rule, path)
683
+ @assert = val
684
+ unless val.is_a?(String)
685
+ #* key=:assert_notstr msg="not a string."
686
+ raise schema_error(:assert_notstr, rule, path, val)
687
+ end
688
+ unless val =~ /\bval\b/
689
+ #* key=:assert_noval msg="'val' is not used."
690
+ raise schema_error(:assert_noval, rule, path, val)
691
+ end
692
+ begin
693
+ @assert_proc = eval "proc { |val| #{val} }"
694
+ rescue ::SyntaxError => ex
695
+ #* key=:assert_syntaxerr msg="expression syntax error."
696
+ raise schema_error(:assert_syntaxerr, rule, path, val)
697
+ end
698
+ end
699
+
700
+
701
+ def _init_range_value(val, rule, path)
702
+ @range = val
703
+ unless val.is_a?(Hash)
704
+ #* key=:range_notmap msg="not a mapping."
705
+ raise schema_error(:range_notmap, rule, path, val)
706
+ end
707
+ if Types.collection_type?(@type) || @type == 'bool'
708
+ #* key=:range_notscalar msg="is available only with scalar type."
709
+ raise schema_error(:range_notscalar, rule, File.dirname(path), 'range:')
710
+ end
711
+ val.each do |k, v|
712
+ case k
713
+ when 'max', 'min', 'max-ex', 'min-ex'
714
+ unless v.is_a?(@type_class)
715
+ typename = Kwalify.word(@type) || @type
716
+ #* key=:range_type_unmatch msg="not a %s."
717
+ raise schema_error(:range_type_unmatch, rule, "#{path}/#{k}", v, [typename])
718
+ end
719
+ else
720
+ #* key=:range_undefined msg="undefined key."
721
+ raise schema_error(:range_undefined, rule, "#{path}/#{k}", "#{k}:")
722
+ end
723
+ end
724
+ if val.key?('max') && val.key?('max-ex')
725
+ #* key=:range_twomax msg="both 'max' and 'max-ex' are not available at once."
726
+ raise schema_error(:range_twomax, rule, path, nil)
727
+ end
728
+ if val.key?('min') && val.key?('min-ex')
729
+ #* key=:range_twomin msg="both 'min' and 'min-ex' are not available at once."
730
+ raise schema_error(:range_twomin, rule, path, nil)
731
+ end
732
+ max, min, max_ex, min_ex = val['max'], val['min'], val['max-ex'], val['min-ex']
733
+ if max
734
+ if min && max < min
735
+ #* key=:range_maxltmin msg="max '%s' is less than min '%s'."
736
+ raise validate_error(:range_maxltmin, rule, path, nil, [max, min])
737
+ elsif min_ex && max <= min_ex
738
+ #* key=:range_maxleminex msg="max '%s' is less than or equal to min-ex '%s'."
739
+ raise validate_error(:range_maxleminex, rule, path, nil, [max, min_ex])
740
+ end
741
+ elsif max_ex
742
+ if min && max_ex <= min
743
+ #* key=:range_maxexlemin msg="max-ex '%s' is less than or equal to min '%s'."
744
+ raise validate_error(:range_maxexlemin, rule, path, nil, [max_ex, min])
745
+ elsif min_ex && max_ex <= min_ex
746
+ #* key=:range_maxexleminex msg="max-ex '%s' is less than or equal to min-ex '%s'."
747
+ raise validate_error(:range_maxexleminex, rule, path, nil, [max_ex, min_ex])
748
+ end
749
+ end
750
+ end
751
+
752
+
753
+ def _init_length_value(val, rule, path)
754
+ @length = val
755
+ unless val.is_a?(Hash)
756
+ #* key=:length_notmap msg="not a mapping."
757
+ raise schema_error(:length_notmap, rule, path, val)
758
+ end
759
+ unless @type == 'str' || @type == 'text'
760
+ #* key=:length_nottext msg="is available only with string or text."
761
+ raise schema_error(:length_nottext, rule, File.dirname(path), 'length:')
762
+ end
763
+ val.each do |k, v|
764
+ case k
765
+ when 'max', 'min', 'max-ex', 'min-ex'
766
+ unless v.is_a?(Integer)
767
+ #* key=:length_notint msg="not an integer."
768
+ raise schema_error(:length_notint, rule, "#{path}/#{k}", v)
769
+ end
770
+ else
771
+ #* key=:length_undefined msg="undefined key."
772
+ raise schema_error(:length_undefined, rule, "#{path}/#{k}", "#{k}:")
773
+ end
774
+ end
775
+ if val.key?('max') && val.key?('max-ex')
776
+ #* key=:length_twomax msg="both 'max' and 'max-ex' are not available at once."
777
+ raise schema_error(:length_twomax, rule, path, nil)
778
+ end
779
+ if val.key?('min') && val.key?('min-ex')
780
+ #* key=:length_twomin msg="both 'min' and 'min-ex' are not available at once."
781
+ raise schema_error(:length_twomin, rule, path, nil)
782
+ end
783
+ max, min, max_ex, min_ex = val['max'], val['min'], val['max-ex'], val['min-ex']
784
+ if max
785
+ if min && max < min
786
+ #* key=:length_maxltmin msg="max '%s' is less than min '%s'."
787
+ raise validate_error(:length_maxltmin, rule, path, nil, [max, min])
788
+ elsif min_ex && max <= min_ex
789
+ #* key=:length_maxleminex msg="max '%s' is less than or equal to min-ex '%s'."
790
+ raise validate_error(:length_maxleminex, rule, path, nil, [max, min_ex])
791
+ end
792
+ elsif max_ex
793
+ if min && max_ex <= min
794
+ #* key=:length_maxexlemin msg="max-ex '%s' is less than or equal to min '%s'."
795
+ raise validate_error(:length_maxexlemin, rule, path, nil, [max_ex, min])
796
+ elsif min_ex && max_ex <= min_ex
797
+ #* key=:length_maxexleminex msg="max-ex '%s' is less than or equal to min-ex '%s'."
798
+ raise validate_error(:length_maxexleminex, rule, path, nil, [max_ex, min_ex])
799
+ end
800
+ end
801
+ end
802
+
803
+
804
+ def _init_ident_value(val, rule, path)
805
+ @ident = val
806
+ @required = true
807
+ unless val.is_a?(Boolean)
808
+ #* key=:ident_notbool msg="not a boolean."
809
+ raise schema_error(:ident_notbool, rule, path, val)
810
+ end
811
+ if @type == 'map' || @type == 'seq'
812
+ #* key=:ident_notscalar msg="is available only with a scalar type."
813
+ raise schema_error(:ident_notscalar, rule, File.dirname(path), "ident:")
814
+ end
815
+ if File.dirname(path) == "/"
816
+ #* key=:ident_onroot msg="is not available on root element."
817
+ raise schema_error(:ident_onroot, rule, "/", "ident:")
818
+ end
819
+ unless @parent && @parent.type == 'map'
820
+ #* key=:ident_notmap msg="is available only with an element of mapping."
821
+ raise schema_error(:ident_notmap, rule, File.dirname(path), "ident:")
822
+ end
823
+ end
824
+
825
+
826
+ def _init_unique_value(val, rule, path)
827
+ @unique = val
828
+ unless val.is_a?(Boolean)
829
+ #* key=:unique_notbool msg="not a boolean."
830
+ raise schema_error(:unique_notbool, rule, path, val)
831
+ end
832
+ if @type == 'map' || @type == 'seq'
833
+ #* key=:unique_notscalar msg="is available only with a scalar type."
834
+ raise schema_error(:unique_notscalar, rule, File.dirname(path), "unique:")
835
+ end
836
+ if File.dirname(path) == "/"
837
+ #* key=:unique_onroot msg="is not available on root element."
838
+ raise schema_error(:unique_onroot, rule, "/", "unique:")
839
+ end
840
+ end
841
+
842
+
843
+ def _init_default_value(val, rule, path)
844
+ @default = val
845
+ unless Types.scalar?(val)
846
+ #* key=:default_nonscalarval msg="not a scalar."
847
+ raise schema_error(:default_nonscalarval, rule, path, val)
848
+ end
849
+ if @type == 'map' || @type == 'seq'
850
+ #* key=:default_notscalar msg="is available only with a scalar type."
851
+ raise schema_error(:default_notscalar, rule, File.dirname(path), "default:")
852
+ end
853
+ unless val.nil? || val.is_a?(@type_class)
854
+ #* key=:default_unmatch msg="not a %s."
855
+ raise schema_error(:default_unmatch, rule, path, val, [Kwalify.word(@type)])
856
+ end
857
+ end
858
+
859
+
860
+ def _init_sequence_value(val, rule, path, rule_table)
861
+ if !val.nil? && !val.is_a?(Array)
862
+ #* key=:sequence_notseq msg="not a sequence."
863
+ raise schema_error(:sequence_notseq, rule, path, val)
864
+ elsif val.nil? || val.empty?
865
+ #* key=:sequence_noelem msg="required one element."
866
+ raise schema_error(:sequence_noelem, rule, path, val)
867
+ elsif val.length > 1
868
+ #* key=:sequence_toomany msg="required just one element."
869
+ raise schema_error(:sequence_toomany, rule, path, val)
870
+ else
871
+ elem = val[0]
872
+ elem ||= {}
873
+ i = 0 # or 1? *index*
874
+ rule = rule_table[elem.__id__]
875
+ rule ||= Rule.new(nil, self)._init(elem, "#{path}/#{i}", rule_table)
876
+ @sequence = [ rule ]
877
+ end
878
+ end
879
+
880
+
881
+ def _init_mapping_value(val, rule, path, rule_table)
882
+ if !val.nil? && !val.is_a?(Hash)
883
+ #* key=:mapping_notmap msg="not a mapping."
884
+ raise schema_error(:mapping_notmap, rule, path, val)
885
+ elsif val.nil? || (val.empty? && !val.default)
886
+ #* key=:mapping_noelem msg="required at least one element."
887
+ raise schema_error(:mapping_noelem, rule, path, val)
888
+ else
889
+ @mapping = {}
890
+ if val.default
891
+ elem = val.default # hash
892
+ rule = rule_table[elem.__id__]
893
+ rule ||= Rule.new(nil, self)._init(elem, "#{path}/=", rule_table)
894
+ @mapping.default = rule
895
+ end
896
+ val.each do |k, v|
897
+ ##* key=:key_duplicate msg="key duplicated."
898
+ #raise schema_error(:key_duplicate, rule, path, key) if @mapping.key?(key)
899
+ v ||= {}
900
+ rule = rule_table[v.__id__]
901
+ rule ||= Rule.new(nil, self)._init(v, "#{path}/#{k}", rule_table)
902
+ if k == '='
903
+ @mapping.default = rule
904
+ else
905
+ @mapping[k] = rule
906
+ end
907
+ end if val
908
+ end
909
+ end
910
+
911
+
912
+ def _check_confliction(hash, rule, path)
913
+ if @type == 'seq'
914
+ #* key=:seq_nosequence msg="type 'seq' requires 'sequence:'."
915
+ raise schema_error(:seq_nosequence, rule, path, nil) unless hash.key?('sequence')
916
+ #* key=:seq_conflict msg="not available with sequence."
917
+ raise schema_error(:seq_conflict, rule, path, 'enum:') if @enum
918
+ raise schema_error(:seq_conflict, rule, path, 'pattern:') if @pattern
919
+ raise schema_error(:seq_conflict, rule, path, 'mapping:') if @mapping
920
+ raise schema_error(:seq_conflict, rule, path, 'range:') if @range
921
+ raise schema_error(:seq_conflict, rule, path, 'length:') if @length
922
+ elsif @type == 'map'
923
+ #* key=:map_nomapping msg="type 'map' requires 'mapping:'."
924
+ raise schema_error(:map_nomapping, rule, path, nil) unless hash.key?('mapping')
925
+ #* key=:map_conflict msg="not available with mapping."
926
+ raise schema_error(:map_conflict, rule, path, 'enum:') if @enum
927
+ raise schema_error(:map_conflict, rule, path, 'pattern:') if @pattern
928
+ raise schema_error(:map_conflict, rule, path, 'sequence:') if @sequence
929
+ raise schema_error(:map_conflict, rule, path, 'range:') if @range
930
+ raise schema_error(:map_conflict, rule, path, 'length:') if @length
931
+ else
932
+ #* key=:scalar_conflict msg="not available with scalar type."
933
+ raise schema_error(:scalar_conflict, rule, path, 'sequence:') if @sequence
934
+ raise schema_error(:scalar_conflict, rule, path, 'mapping:') if @mapping
935
+ if @enum
936
+ #* key=:enum_conflict msg="not available with 'enum:'."
937
+ raise schema_error(:enum_conflict, rule, path, 'range:') if @range
938
+ raise schema_error(:enum_conflict, rule, path, 'length:') if @length
939
+ raise schema_error(:enum_conflict, rule, path, 'pattern:') if @pattern
940
+ end
941
+ unless @default.nil?
942
+ #* key=:default_conflict msg="not available when 'required:' is true."
943
+ raise schema_error(:default_conflict, rule, path, 'default:') if @required
944
+ end
945
+ end
946
+ end
947
+
948
+ #def inspect()
949
+ # str = ""; level = 0; done = {}
950
+ # _inspect(str, level, done)
951
+ # return str
952
+ #end
953
+
954
+
955
+ protected
956
+
957
+
958
+ def _inspect(str="", level=0, done={})
959
+ done[self.__id__] = true
960
+ str << " " * level << "name: #{@name}\n" unless @name.nil?
961
+ str << " " * level << "desc: #{@desc}\n" unless @desc.nil?
962
+ str << " " * level << "type: #{@type}\n" unless @type.nil?
963
+ str << " " * level << "klass: #{@type_class.name}\n" unless @type_class.nil?
964
+ str << " " * level << "required: #{@required}\n" unless @required.nil?
965
+ str << " " * level << "pattern: #{@regexp.inspect}\n" unless @pattern.nil?
966
+ str << " " * level << "assert: #{@assert}\n" unless @assert.nil?
967
+ str << " " * level << "ident: #{@ident}\n" unless @ident.nil?
968
+ str << " " * level << "unique: #{@unique}\n" unless @unique.nil?
969
+ if !@enum.nil?
970
+ str << " " * level << "enum:\n"
971
+ @enum.each do |item|
972
+ str << " " * (level+1) << "- #{item}\n"
973
+ end
974
+ end
975
+ if !@range.nil?
976
+ str << " " * level
977
+ str << "range: { "
978
+ colon = ""
979
+ %w[max max-ex min min-ex].each do |key|
980
+ val = @range[key]
981
+ unless val.nil?
982
+ str << colon << "#{key}: #{val.inspect}"
983
+ colon = ", "
984
+ end
985
+ end
986
+ str << " }\n"
987
+ end
988
+ if !@length.nil?
989
+ str << " " * level
990
+ str << "length: { "
991
+ colon = ""
992
+ %w[max max-ex min min-ex].each do |key|
993
+ val = @length[key]
994
+ if !val.nil?
995
+ str << colon << "#{key}: #{val.inspect}"
996
+ colon = ", "
997
+ end
998
+ end
999
+ str << " }\n"
1000
+ end
1001
+ @sequence.each do |rule|
1002
+ if done[rule.__id__]
1003
+ str << " " * (level+1) << "- ...\n"
1004
+ else
1005
+ str << " " * (level+1) << "- \n"
1006
+ rule._inspect(str, level+2, done)
1007
+ end
1008
+ end if @sequence
1009
+ @mapping.each do |key, rule|
1010
+ if done[rule.__id__]
1011
+ str << ' ' * (level+1) << '"' << key << "\": ...\n"
1012
+ else
1013
+ str << ' ' * (level+1) << '"' << key << "\":\n"
1014
+ rule._inspect(str, level+2, done)
1015
+ end
1016
+ end if @mapping
1017
+ return str
1018
+ end
1019
+
1020
+
1021
+ public
1022
+
1023
+
1024
+ def _uniqueness_check_table() # :nodoc:
1025
+ uniq_table = nil
1026
+ if @type == 'map'
1027
+ @mapping.keys.each do |key|
1028
+ rule = @mapping[key]
1029
+ if rule.unique || rule.ident
1030
+ uniq_table ||= {}
1031
+ uniq_table[key] = {}
1032
+ end
1033
+ end
1034
+ elsif @unique || @ident
1035
+ uniq_table = {}
1036
+ end
1037
+ return uniq_table
1038
+ end
1039
+
1040
+
1041
+ end
1042
+
1043
+
1044
+ end
1045
+ #--end of require 'kwalify/rule'
1046
+ #--begin of require 'kwalify/validator'
1047
+ ###
1048
+ ### $Rev: 99 $
1049
+ ### $Release: 0.7.1 $
1050
+ ### copyright(c) 2005-2008 kuwata-lab all rights reserved.
1051
+ ###
1052
+
1053
+ #--already included require 'kwalify/messages'
1054
+ #--already included require 'kwalify/errors'
1055
+ #--already included require 'kwalify/types'
1056
+ #--already included require 'kwalify/rule'
1057
+
1058
+ module Kwalify
1059
+
1060
+ ##
1061
+ ## validate YAML document
1062
+ ##
1063
+ ## ex1. validate yaml document
1064
+ ## schema = YAML.load_file('schema.yaml')
1065
+ ## validator = Kwalify::Validator.new(schema)
1066
+ ## document = YAML.load_file('document.yaml')
1067
+ ## erros = validator.validate(document)
1068
+ ## if errors && !errors.empty?
1069
+ ## errors.each do |err|
1070
+ ## puts "- [#{err.path}] #{err.message}"
1071
+ ## end
1072
+ ## end
1073
+ ##
1074
+ ## ex2. validate with parsing
1075
+ ## schema = YAML.load_file('schema.yaml')
1076
+ ## validator = Kwalify::Validator.new(schema)
1077
+ ## parser = Kwalify::Yaml::Parser.new(validator)
1078
+ ## document = parser.parse(File.read('document.yaml'))
1079
+ ## errors = parser.errors
1080
+ ## if errors && errors.empty?
1081
+ ## errors.each do |e|
1082
+ ## puts "#{e.linenum}:#{e.column} [#{e.path}] #{e.message}"
1083
+ ## end
1084
+ ## end
1085
+ ##
1086
+ class Validator
1087
+ include Kwalify::ErrorHelper
1088
+
1089
+
1090
+ def initialize(hash_or_rule, &block)
1091
+ obj = hash_or_rule
1092
+ @rule = (obj.nil? || obj.is_a?(Rule)) ? obj : Rule.new(obj)
1093
+ @block = block
1094
+ end
1095
+ attr_reader :rule
1096
+
1097
+
1098
+ def _inspect
1099
+ @rule._inspect
1100
+ end
1101
+
1102
+
1103
+ def validate(value)
1104
+ path = ''; errors = []; done = {}; uniq_table = nil
1105
+ _validate(value, @rule, path, errors, done, uniq_table)
1106
+ return errors
1107
+ end
1108
+
1109
+
1110
+ protected
1111
+
1112
+
1113
+ def validate_hook(value, rule, path, errors)
1114
+ ## may be overrided by subclass
1115
+ end
1116
+
1117
+
1118
+ public
1119
+
1120
+
1121
+ def _validate(value, rule, path, errors, done, uniq_table, recursive=true)
1122
+ #if Types.collection?(value)
1123
+ if !Types.scalar?(value)
1124
+ #if done[value.__id__]
1125
+ # rule2 = done[value.__id__]
1126
+ # if rule2.is_a?(Rule)
1127
+ # return if rule.equal?(rule2)
1128
+ # done[value.__id__] = [rule2, rule]
1129
+ # elsif rule2.is_a?(Array)
1130
+ # return if rule2.any? {|r| r.equal?(rule)}
1131
+ # done[value.__id__] << rule
1132
+ # else
1133
+ # raise "unreachable"
1134
+ # end
1135
+ #end
1136
+ return if done[value.__id__] # avoid infinite loop
1137
+ done[value.__id__] = rule
1138
+ end
1139
+ if rule.required && value.nil?
1140
+ #* key=:required_novalue msg="value required but none."
1141
+ errors << validate_error(:required_novalue, rule, path, value)
1142
+ return
1143
+ end
1144
+ if rule.type_class && !value.nil? && !value.is_a?(rule.type_class)
1145
+ unless rule.classobj && value.is_a?(rule.classobj)
1146
+ #* key=:type_unmatch msg="not a %s."
1147
+ errors << validate_error(:type_unmatch, rule, path, value, [Kwalify.word(rule.type)])
1148
+ return
1149
+ end
1150
+ end
1151
+ #
1152
+ n = errors.length
1153
+ if rule.sequence
1154
+ _validate_sequence(value, rule, path, errors, done, uniq_table, recursive)
1155
+ elsif rule.mapping
1156
+ _validate_mapping(value, rule, path, errors, done, uniq_table, recursive)
1157
+ else
1158
+ _validate_scalar(value, rule, path, errors, done, uniq_table)
1159
+ end
1160
+ return unless errors.length == n
1161
+ #
1162
+ #path_str = path.is_a?(Array) ? '/'+path.join('/') : path
1163
+ #validate_hook(value, rule, path_str, errors)
1164
+ #@block.call(value, rule, path_str, errors) if @block
1165
+ validate_hook(value, rule, path, errors)
1166
+ @block.call(value, rule, path, errors) if @block
1167
+ end
1168
+
1169
+
1170
+ private
1171
+
1172
+
1173
+ def _validate_sequence(list, seq_rule, path, errors, done, uniq_table, recursive=true)
1174
+ assert_error("seq_rule.sequence.class==#{seq_rule.sequence.class.name} (expected Array)") unless seq_rule.sequence.is_a?(Array)
1175
+ assert_error("seq_rule.sequence.length==#{seq_rule.sequence.length} (expected 1)") unless seq_rule.sequence.length == 1
1176
+ return if list.nil? || !recursive
1177
+ rule = seq_rule.sequence[0]
1178
+ uniq_table = rule._uniqueness_check_table()
1179
+ list.each_with_index do |val, i|
1180
+ child_path = path.is_a?(Array) ? path + [i] : "#{path}/#{i}"
1181
+ _validate(val, rule, child_path, errors, done, uniq_table) ## validate recursively
1182
+ end
1183
+ end
1184
+
1185
+
1186
+ def _validate_mapping(hash, map_rule, path, errors, done, uniq_table, recursive=true)
1187
+ assert_error("map_rule.mapping.class==#{map_rule.mapping.class.name} (expected Hash)") unless map_rule.mapping.is_a?(Hash)
1188
+ return if hash.nil?
1189
+ return if !recursive
1190
+ _validate_mapping_required_keys(hash, map_rule, path, errors)
1191
+ hash.each do |key, val|
1192
+ rule = map_rule.mapping[key]
1193
+ child_path = path.is_a?(Array) ? path+[key] : "#{path}/#{key}"
1194
+ unless rule
1195
+ #* key=:key_undefined msg="key '%s' is undefined."
1196
+ errors << validate_error(:key_undefined, rule, child_path, hash, ["#{key}:"])
1197
+ ##* key=:key_undefined msg="undefined key."
1198
+ #errors << validate_error(:key_undefined, rule, child_path, "#{key}:")
1199
+ else
1200
+ _validate(val, rule, child_path, errors, done,
1201
+ uniq_table ? uniq_table[key] : nil) ## validate recursively
1202
+ end
1203
+ end
1204
+ end
1205
+
1206
+
1207
+ def _validate_mapping_required_keys(hash, map_rule, path, errors) #:nodoc:
1208
+ map_rule.mapping.each do |key, rule|
1209
+ #next unless rule.required
1210
+ #val = hash.is_a?(Hash) ? hash[key] : hash.instance_variable_get("@#{key}")
1211
+ #if val.nil?
1212
+ if rule.required && hash[key].nil? # or !hash.key?(key)
1213
+ #* key=:required_nokey msg="key '%s:' is required."
1214
+ errors << validate_error(:required_nokey, rule, path, hash, [key])
1215
+ end
1216
+ end
1217
+ end
1218
+ public :_validate_mapping_required_keys
1219
+
1220
+
1221
+ def _validate_scalar(value, rule, path, errors, done, uniq_table)
1222
+ assert_error("rule.sequence.class==#{rule.sequence.class.name} (expected NilClass)") if rule.sequence
1223
+ assert_error("rule.mapping.class==#{rule.mapping.class.name} (expected NilClass)") if rule.mapping
1224
+ _validate_assert( value, rule, path, errors) if rule.assert_proc
1225
+ _validate_enum( value, rule, path, errors) if rule.enum
1226
+ return if value.nil?
1227
+ _validate_pattern(value, rule, path, errors) if rule.pattern
1228
+ _validate_range( value, rule, path, errors) if rule.range
1229
+ _validate_length( value, rule, path, errors) if rule.length
1230
+ _validate_unique( value, rule, path, errors, uniq_table) if uniq_table
1231
+ end
1232
+
1233
+
1234
+ def _validate_unique(value, rule, path, errors, uniq_table)
1235
+ assert_error "uniq_table=#{uniq_table.inspect}" unless rule.unique || rule.ident
1236
+ if uniq_table.key?(value)
1237
+ exist_at = uniq_table[value]
1238
+ exist_at = "/#{exist_at.join('/')}" if exist_at.is_a?(Array)
1239
+ #* key=:value_notunique msg="is already used at '%s'."
1240
+ errors << validate_error(:value_notunique, rule, path, value, exist_at)
1241
+ else
1242
+ uniq_table[value] = path.dup
1243
+ end
1244
+ end
1245
+ public :_validate_unique
1246
+
1247
+
1248
+ def _validate_assert(value, rule, path, errors)
1249
+ assert_error("rule=#{rule._inspect}") unless rule.assert_proc
1250
+ unless rule.assert_proc.call(value)
1251
+ #* key=:assert_failed msg="assertion expression failed (%s)."
1252
+ errors << validate_error(:assert_failed, rule, path, value, [rule.assert])
1253
+ end
1254
+ end
1255
+
1256
+
1257
+ def _validate_enum(value, rule, path, errors)
1258
+ assert_error("rule=#{rule._inspect}") unless rule.enum
1259
+ unless rule.enum.include?(value)
1260
+ keyname = path.is_a?(Array) ? path[-1] : File.basename(path)
1261
+ keyname = 'enum' if keyname =~ /\A\d+\z/
1262
+ #* key=:enum_notexist msg="invalid %s value."
1263
+ errors << validate_error(:enum_notexist, rule, path, value, [keyname])
1264
+ end
1265
+ end
1266
+
1267
+
1268
+ def _validate_pattern(value, rule, path, errors)
1269
+ assert_error("rule=#{rule._inspect}") unless rule.pattern
1270
+ unless value.to_s =~ rule.regexp
1271
+ #* key=:pattern_unmatch msg="not matched to pattern %s."
1272
+ errors << validate_error(:pattern_unmatch, rule, path, value, [rule.pattern])
1273
+ end
1274
+ end
1275
+
1276
+
1277
+ def _validate_range(value, rule, path, errors)
1278
+ assert_error("rule=#{rule._inspect}") unless rule.range
1279
+ assert_error("value.class=#{value.class.name}") unless Types.scalar?(value)
1280
+ h = rule.range
1281
+ max, min, max_ex, min_ex = h['max'], h['min'], h['max-ex'], h['min-ex']
1282
+ if max && max < value
1283
+ #* key=:range_toolarge msg="too large (> max %s)."
1284
+ errors << validate_error(:range_toolarge, rule, path, value, [max.to_s])
1285
+ end
1286
+ if min && min > value
1287
+ #* key=:range_toosmall msg="too small (< min %s)."
1288
+ errors << validate_error(:range_toosmall, rule, path, value, [min.to_s])
1289
+ end
1290
+ if max_ex && max_ex <= value
1291
+ #* key=:range_toolargeex msg="too large (>= max %s)."
1292
+ errors << validate_error(:range_toolargeex, rule, path, value, [max_ex.to_s])
1293
+ end
1294
+ if min_ex && min_ex >= value
1295
+ #* key=:range_toosmallex msg="too small (<= min %s)."
1296
+ errors << validate_error(:range_toosmallex, rule, path, value, [min_ex.to_s])
1297
+ end
1298
+ end
1299
+
1300
+
1301
+ def _validate_length(value, rule, path, errors)
1302
+ assert_error("rule=#{rule._inspect}") unless rule.length
1303
+ assert_error("value.class=#{value.class.name}") unless value.is_a?(String) || value.is_a?(Text)
1304
+ len = value.to_s.length
1305
+ h = rule.length
1306
+ max, min, max_ex, min_ex = h['max'], h['min'], h['max-ex'], h['min-ex']
1307
+ if max && max < len
1308
+ #* key=:length_toolong msg="too long (length %d > max %d)."
1309
+ errors << validate_error(:length_toolong, rule, path, value, [len, max])
1310
+ end
1311
+ if min && min > len
1312
+ #* key=:length_tooshort msg="too short (length %d < min %d)."
1313
+ errors << validate_error(:length_tooshort, rule, path, value, [len, min])
1314
+ end
1315
+ if max_ex && max_ex <= len
1316
+ #* key=:length_toolongex msg="too long (length %d >= max %d)."
1317
+ errors << validate_error(:length_toolongex, rule, path, value, [len, max_ex])
1318
+ end
1319
+ if min_ex && min_ex >= len
1320
+ #* key=:length_tooshortex msg="too short (length %d <= min %d)."
1321
+ errors << validate_error(:length_tooshortex, rule, path, value, [len, min_ex])
1322
+ end
1323
+ end
1324
+
1325
+
1326
+ end
1327
+
1328
+ end
1329
+ #--end of require 'kwalify/validator'
1330
+ #--begin of require 'kwalify/meta-validator'
1331
+ ###
1332
+ ### $Rev: 95 $
1333
+ ### $Release: 0.7.1 $
1334
+ ### copyright(c) 2005-2008 kuwata-lab all rights reserved.
1335
+ ###
1336
+
1337
+ #--already included require 'kwalify/errors'
1338
+ #--already included require 'kwalify/rule'
1339
+ #--already included require 'kwalify/validator'
1340
+ #--begin of require 'kwalify/parser/yaml'
1341
+ ###
1342
+ ### $Rev: 99 $
1343
+ ### $Release: 0.7.1 $
1344
+ ### copyright(c) 2005-2008 kuwata-lab all rights reserved.
1345
+ ###
1346
+
1347
+ #--already included require 'kwalify/validator'
1348
+ #--already included require 'kwalify/errors'
1349
+ #--begin of require 'kwalify/util'
1350
+ ###
1351
+ ### $Rev: 88 $
1352
+ ### $Release: 0.7.1 $
1353
+ ### copyright(c) 2005-2008 kuwata-lab all rights reserved.
1354
+ ###
1355
+
1356
+ module Kwalify
1357
+
1358
+ module Util
1359
+
1360
+ module_function
1361
+
1362
+ ##
1363
+ ## expand tab character to spaces
1364
+ ##
1365
+ ## ex.
1366
+ ## untabified_str = YamlHelper.untabify(tabbed_str)
1367
+ ##
1368
+ def untabify(str, width=8)
1369
+ list = str.split(/\t/)
1370
+ last = list.pop
1371
+ sb = ''
1372
+ list.each do |s|
1373
+ column = (n = s.rindex(?\n)) ? s.length - n - 1 : s.length
1374
+ n = width - (column % width)
1375
+ sb << s << (' ' * n)
1376
+ end
1377
+ sb << last if last
1378
+ return sb
1379
+ end
1380
+
1381
+
1382
+ ## traverse schema
1383
+ ##
1384
+ ## ex.
1385
+ ## schema = YAML.load_file('myschema.yaml')
1386
+ ## Kwalify::Util.traverse_schema(schema) do |rulehash|
1387
+ ## ## add module prefix to class name
1388
+ ## if rulehash['class']
1389
+ ## rulehash['class'] = "MyModule::' + rulehash['class']
1390
+ ## end
1391
+ ## end
1392
+ def traverse_schema(schema, &block) #:yield: rulehash
1393
+ hash = schema
1394
+ _done = {}
1395
+ _traverse_schema(hash, _done, &block)
1396
+ end
1397
+
1398
+ def _traverse_schema(hash, _done={}, &block)
1399
+ return if _done.key?(hash.__id__)
1400
+ _done[hash.__id__] = hash
1401
+ yield hash
1402
+ if hash['mapping']
1403
+ hash['mapping'].each {|k, v| _traverse_schema(v, _done, &block) }
1404
+ elsif hash['sequence']
1405
+ _traverse_schema(hash['sequence'][0], _done, &block)
1406
+ end
1407
+ end
1408
+ private :_traverse_schema
1409
+
1410
+
1411
+ ## traverse rule
1412
+ ##
1413
+ ## ex.
1414
+ ## schema = YAML.load_file('myschema.yaml')
1415
+ ## validator = Kwalify::Validator.new(schema)
1416
+ ## Kwalify::Util.traverse_rule(validator) do |rule|
1417
+ ## p rule if rule.classname
1418
+ ## end
1419
+ def traverse_rule(validator, &block) #:yield: rule
1420
+ rule = validator.is_a?(Rule) ? validator : validator.rule
1421
+ _done = {}
1422
+ _traverse_rule(rule, _done, &block)
1423
+ end
1424
+
1425
+ def _traverse_rule(rule, _done={}, &block)
1426
+ return if _done.key?(rule.__id__)
1427
+ _done[rule.__id__] = rule
1428
+ yield rule
1429
+ rule.sequence.each do |seq_rule|
1430
+ _traverse_rule(seq_rule, _done, &block)
1431
+ end if rule.sequence
1432
+ rule.mapping.each do |name, map_rule|
1433
+ _traverse_rule(map_rule, _done, &block)
1434
+ end if rule.mapping
1435
+ end
1436
+ private :_traverse_rule
1437
+
1438
+
1439
+ ##
1440
+ ## get class object. if not found, NameError raised.
1441
+ ##
1442
+ def get_class(classname)
1443
+ klass = Object
1444
+ classname.split('::').each do |name|
1445
+ klass = klass.const_get(name)
1446
+ end
1447
+ return klass
1448
+ end
1449
+
1450
+
1451
+ ##
1452
+ ## create a hash table from list of hash with primary key.
1453
+ ##
1454
+ ## ex.
1455
+ ## hashlist = [
1456
+ ## { "name"=>"Foo", "gender"=>"M", "age"=>20, },
1457
+ ## { "name"=>"Bar", "gender"=>"F", "age"=>25, },
1458
+ ## { "name"=>"Baz", "gender"=>"M", "age"=>30, },
1459
+ ## ]
1460
+ ## hashtable = YamlHelper.create_hashtable(hashlist, "name")
1461
+ ## p hashtable
1462
+ ## # => { "Foo" => { "name"=>"Foo", "gender"=>"M", "age"=>20, },
1463
+ ## # "Bar" => { "name"=>"Bar", "gender"=>"F", "age"=>25, },
1464
+ ## # "Baz" => { "name"=>"Baz", "gender"=>"M", "age"=>30, }, }
1465
+ ##
1466
+ def create_hashtable(hashlist, primarykey, flag_duplicate_check=true)
1467
+ hashtable = {}
1468
+ hashlist.each do |hash|
1469
+ key = hash[primarykey]
1470
+ unless key
1471
+ riase "primary key '#{key}' not found."
1472
+ end
1473
+ if flag_duplicate_check && hashtable.key?(key)
1474
+ raise "primary key '#{key}' duplicated (value '#{hashtable[key]}')"
1475
+ end
1476
+ hashtable[key] = hash
1477
+ end if hashlist
1478
+ return hashtable
1479
+ end
1480
+
1481
+
1482
+ ##
1483
+ ## get nested value directly.
1484
+ ##
1485
+ ## ex.
1486
+ ## val = YamlHelper.get_value(obj, ['aaa', 0, 'xxx'])
1487
+ ##
1488
+ ## This is equal to the following:
1489
+ ## begin
1490
+ ## val = obj['aaa'][0]['xxx']
1491
+ ## rescue NameError
1492
+ ## val = nil
1493
+ ## end
1494
+ ##
1495
+ def get_value(obj, path)
1496
+ val = obj
1497
+ path.each do |key|
1498
+ return nil unless val.is_a?(Hash) || val.is_a?(Array)
1499
+ val = val[key]
1500
+ end if path
1501
+ return val
1502
+ end
1503
+
1504
+ end
1505
+
1506
+ end
1507
+ #--end of require 'kwalify/util'
1508
+ #--begin of require 'kwalify/parser/base'
1509
+ ###
1510
+ ### $Rev: 92 $
1511
+ ### $Release: 0.7.1 $
1512
+ ### copyright(c) 2005-2008 kuwata-lab all rights reserved.
1513
+ ###
1514
+
1515
+ require 'strscan'
1516
+ #--already included require 'kwalify/errors'
1517
+ #--already included require 'kwalify/util'
1518
+
1519
+
1520
+ ##
1521
+ ## base class for Yaml::Parser
1522
+ ##
1523
+ class Kwalify::BaseParser
1524
+
1525
+
1526
+ def reset(input, filename=nil, untabify=false)
1527
+ input = Kwalify::Util.untabify(input) if untabify
1528
+ @scanner = StringScanner.new(input)
1529
+ @filename = filename
1530
+ @linenum = 1
1531
+ @column = 1
1532
+ end
1533
+ attr_reader :filename, :linenum, :column
1534
+
1535
+
1536
+ def scan(regexp)
1537
+ ret = @scanner.scan(regexp)
1538
+ return nil if ret.nil?
1539
+ _set_column_and_linenum(ret)
1540
+ return ret
1541
+ end
1542
+
1543
+
1544
+ def _set_column_and_linenum(s)
1545
+ pos = s.rindex(?\n)
1546
+ if pos
1547
+ @column = s.length - pos
1548
+ @linenum += s.count("\n")
1549
+ else
1550
+ @column += s.length
1551
+ end
1552
+ end
1553
+
1554
+
1555
+ def match?(regexp)
1556
+ return @scanner.match?(regexp)
1557
+ end
1558
+
1559
+
1560
+ def group(n)
1561
+ return @scanner[n]
1562
+ end
1563
+
1564
+
1565
+ def eos?
1566
+ return @scanner.eos?
1567
+ end
1568
+
1569
+
1570
+ def peep(n=1)
1571
+ return @scanner.peep(n)
1572
+ end
1573
+
1574
+
1575
+ def _getch
1576
+ ch = @scanner.getch()
1577
+ if ch == "\n"
1578
+ @linenum += 1
1579
+ @column = 0
1580
+ else
1581
+ @column += 1
1582
+ end
1583
+ return ch
1584
+ end
1585
+
1586
+
1587
+ CHAR_TABLE = { "\""=>"\"", "\\"=>"\\", "n"=>"\n", "r"=>"\r", "t"=>"\t", "b"=>"\b" }
1588
+
1589
+ def scan_string
1590
+ ch = _getch()
1591
+ ch == '"' || ch == "'" or raise "assertion error"
1592
+ endch = ch
1593
+ s = ''
1594
+ while !(ch = _getch()).nil? && ch != endch
1595
+ if ch != '\\'
1596
+ s << ch
1597
+ elsif (ch = _getch()).nil?
1598
+ raise _syntax_error("%s: string is not closed." % (endch == '"' ? "'\"'" : '"\'"'))
1599
+ elsif endch == '"'
1600
+ if CHAR_TABLE.key?(ch)
1601
+ s << CHAR_TABLE[ch]
1602
+ elsif ch == 'u'
1603
+ ch2 = scan(/(?:[0-9a-f][0-9a-f]){1,4}/)
1604
+ unless ch2
1605
+ raise _syntax_error("\\x: invalid unicode format.")
1606
+ end
1607
+ s << [ch2.hex].pack('U*')
1608
+ elsif ch == 'x'
1609
+ ch2 = scan(/[0-9a-zA-Z][0-9a-zA-Z]/)
1610
+ unless ch2
1611
+ raise _syntax_error("\\x: invalid binary format.")
1612
+ end
1613
+ s << [ch2].pack('H2')
1614
+ else
1615
+ s << "\\" << ch
1616
+ end
1617
+ elsif endch == "'"
1618
+ ch == '\'' || ch == '\\' ? s << ch : s << '\\' << ch
1619
+ else
1620
+ raise "unreachable"
1621
+ end
1622
+ end
1623
+ #_getch()
1624
+ return s
1625
+ end
1626
+
1627
+
1628
+ def _syntax_error(message, path=nil, linenum=@linenum, column=@column)
1629
+ #message = _build_message(message_key)
1630
+ return _error(Kwalify::SyntaxError, message.to_s, path, linenum, column)
1631
+ end
1632
+ protected :_syntax_error
1633
+
1634
+
1635
+ end
1636
+ #--end of require 'kwalify/parser/base'
1637
+
1638
+
1639
+
1640
+ module Kwalify
1641
+
1642
+ module Yaml
1643
+ end
1644
+
1645
+ end
1646
+
1647
+
1648
+ ##
1649
+ ## YAML parser with validator
1650
+ ##
1651
+ ## ex.
1652
+ ## schema = YAML.load_file('schema.yaml')
1653
+ ## require 'kwalify'
1654
+ ## validator = Kwalify::Validator.new(schema)
1655
+ ## parser = Kwalify::Yaml::Parser.new(validator) # validator is optional
1656
+ ## #parser.preceding_alias = true # optional
1657
+ ## #parser.data_binding = true # optional
1658
+ ## ydoc = parser.parse_file('data.yaml')
1659
+ ## errors = parser.errors
1660
+ ## if errors && !errors.empty?
1661
+ ## errors.each do |e|
1662
+ ## puts "line=#{e.linenum}, path=#{e.path}, mesg=#{e.message}"
1663
+ ## end
1664
+ ## end
1665
+ ##
1666
+ class Kwalify::Yaml::Parser < Kwalify::BaseParser
1667
+
1668
+
1669
+ alias reset_scanner reset
1670
+
1671
+
1672
+ def initialize(validator=nil, properties={})
1673
+ @validator = validator.is_a?(Hash) ? Kwalify::Validator.new(validator) : validator
1674
+ @data_binding = properties[:data_binding] # enable data binding or not
1675
+ @preceding_alias = properties[:preceding_alias] # allow preceding alias or not
1676
+ @sequence_class = properties[:sequence_class] || Array
1677
+ @mapping_class = properties[:mapping_class] || Hash
1678
+ end
1679
+ attr_accessor :validator # Validator
1680
+ attr_accessor :data_binding # boolean
1681
+ attr_accessor :preceding_alias # boolean
1682
+ attr_accessor :sequence_class # Class
1683
+ attr_accessor :mapping_class # Class
1684
+
1685
+
1686
+ def reset_parser()
1687
+ @anchors = {}
1688
+ @errors = []
1689
+ @done = {}
1690
+ @preceding_aliases = []
1691
+ @location_table = {} # object_id -> sequence or mapping
1692
+ @doc = nil
1693
+ end
1694
+ attr_reader :errors
1695
+
1696
+
1697
+ def _error(klass, message, path, linenum, column)
1698
+ ex = klass.new(message)
1699
+ ex.path = path.is_a?(Array) ? '/' + path.join('/') : path
1700
+ ex.linenum = linenum
1701
+ ex.column = column
1702
+ ex.filename = @filename
1703
+ return ex
1704
+ end
1705
+ private :_error
1706
+
1707
+
1708
+ # def _validate_error(message, path, linenum=@linenum, column=@column)
1709
+ # #message = _build_message(message_key)
1710
+ # error = _error(ValidationError, message.to_s, path, linenum, column)
1711
+ # @errors << error
1712
+ # end
1713
+ # private :_validate_error
1714
+
1715
+
1716
+ def _set_error_info(linenum=@linenum, column=@column, &block)
1717
+ len = @errors.length
1718
+ yield
1719
+ n = @errors.length - len
1720
+ (1..n).each do |i|
1721
+ error = @errors[-i]
1722
+ error.linenum ||= linenum
1723
+ error.column ||= column
1724
+ error.filename ||= @filename
1725
+ end if n > 0
1726
+ end
1727
+
1728
+
1729
+ def skip_spaces_and_comments()
1730
+ scan(/\s+/)
1731
+ while match?(/\#/)
1732
+ scan(/.*?\n/)
1733
+ scan(/\s+/)
1734
+ end
1735
+ end
1736
+
1737
+
1738
+ def document_start?()
1739
+ return match?(/---\s/) && @column == 1
1740
+ end
1741
+
1742
+
1743
+ def stream_end?()
1744
+ return match?(/\.\.\.\s/) && @column == 1
1745
+ end
1746
+
1747
+
1748
+ def has_next?()
1749
+ return !(eos? || stream_end?)
1750
+ end
1751
+
1752
+
1753
+ def parse(input=nil, opts={})
1754
+ reset_scanner(input, opts[:filename], opts[:untabify]) if input
1755
+ return parse_next()
1756
+ end
1757
+
1758
+
1759
+ def parse_file(filename, opts={})
1760
+ opts[:filename] = filename
1761
+ return parse(File.read(filename), opts)
1762
+ end
1763
+
1764
+
1765
+ def parse_next()
1766
+ reset_parser()
1767
+ path = []
1768
+ skip_spaces_and_comments()
1769
+ if document_start?()
1770
+ scan(/.*\n/)
1771
+ skip_spaces_and_comments()
1772
+ end
1773
+ _linenum = @linenum #*V
1774
+ _column = @column #*V
1775
+ rule = @validator ? @validator.rule : nil #*V
1776
+ uniq_table = nil #*V
1777
+ parent = nil #*V
1778
+ val = parse_block_value(0, rule, path, uniq_table, parent)
1779
+ _set_error_info(_linenum, _column) do #*V
1780
+ @validator._validate(val, rule, [], @errors, @done, uniq_table, false) #*V
1781
+ end if rule #*V
1782
+ resolve_preceding_aliases(val) if @preceding_alias
1783
+ unless eos? || document_start?() || stream_end?()
1784
+ raise _syntax_error("document end expected (maybe invalid tab char found).", path)
1785
+ end
1786
+ @doc = val
1787
+ @location_table[-1] = [_linenum, _column]
1788
+ return val
1789
+ end
1790
+
1791
+
1792
+ def parse_stream(input, opts={}, &block)
1793
+ reset_scanner(input, opts[:filename], opts[:untabify])
1794
+ ydocs = block_given? ? nil : []
1795
+ while true
1796
+ ydoc = parse_next()
1797
+ ydocs ? (ydocs << ydoc) : (yield ydoc)
1798
+ break if eos? || stream_end?()
1799
+ document_start?() or raise "** internal error"
1800
+ scan(/.*\n/)
1801
+ end
1802
+ return ydocs
1803
+ end
1804
+
1805
+ alias parse_documents parse_stream
1806
+
1807
+
1808
+ MAPKEY_PATTERN = /([\w.][-\w.:]*\*?|".*?"|'.*?'|:\w+|=|<<)[ \t]*:\s+/ # :nodoc:
1809
+
1810
+ PRECEDING_ALIAS_PLACEHOLDER = Object.new # :nodoc:
1811
+
1812
+
1813
+ def parse_anchor(rule, path, uniq_table, container)
1814
+ name = group(1)
1815
+ if @anchors.key?(name)
1816
+ raise _syntax_error("&#{name}: anchor duplicated.", path,
1817
+ @linenum, @column - name.length)
1818
+ end
1819
+ skip_spaces_and_comments()
1820
+ return name
1821
+ end
1822
+
1823
+
1824
+ def parse_alias(rule, path, uniq_table, container)
1825
+ name = group(1)
1826
+ if @anchors.key?(name)
1827
+ val = @anchors[name]
1828
+ elsif @preceding_alias
1829
+ @preceding_aliases << [name, rule, path.dup, container,
1830
+ @linenum, @column - name.length - 1]
1831
+ val = PRECEDING_ALIAS_PLACEHOLDER
1832
+ else
1833
+ raise _syntax_error("*#{name}: anchor not found.", path,
1834
+ @linenum, @column - name.length - 1)
1835
+ end
1836
+ skip_spaces_and_comments()
1837
+ return val
1838
+ end
1839
+
1840
+
1841
+ def resolve_preceding_aliases(val)
1842
+ @preceding_aliases.each do |name, rule, path, container, _linenum, _column|
1843
+ unless @anchors.key?(name)
1844
+ raise _syntax_error("*#{name}: anchor not found.", path, _linenum, _column)
1845
+ end
1846
+ key = path[-1]
1847
+ val = @anchors[name]
1848
+ raise unless !container.respond_to?('[]') || container[key].equal?(PRECEDING_ALIAS_PLACEHOLDER)
1849
+ if container.is_a?(Array)
1850
+ container[key] = val
1851
+ else
1852
+ put_to_map(rule, container, key, val, _linenum, _column)
1853
+ end
1854
+ _set_error_info(_linenum, _column) do #*V
1855
+ @validator._validate(val, rule, path, @errors, @done, false) #*V
1856
+ end if rule #*V
1857
+ end
1858
+ end
1859
+
1860
+
1861
+ def parse_block_value(level, rule, path, uniq_table, container)
1862
+ skip_spaces_and_comments()
1863
+ ## nil
1864
+ return nil if @column <= level || eos?
1865
+ ## anchor and alias
1866
+ name = nil
1867
+ if scan(/\&([-\w]+)/)
1868
+ name = parse_anchor(rule, path, uniq_table, container)
1869
+ elsif scan(/\*([-\w]+)/)
1870
+ return parse_alias(rule, path, uniq_table, container)
1871
+ end
1872
+ ## type
1873
+ if scan(/!!?\w+/)
1874
+ skip_spaces_and_comments()
1875
+ end
1876
+ ## sequence
1877
+ if match?(/-\s+/)
1878
+ if rule && !rule.sequence
1879
+ #_validate_error("sequence is not expected.", path)
1880
+ rule = nil
1881
+ end
1882
+ seq = create_sequence(rule, @linenum, @column)
1883
+ @anchors[name] = seq if name
1884
+ parse_block_seq(seq, rule, path, uniq_table)
1885
+ return seq
1886
+ end
1887
+ ## mapping
1888
+ if match?(MAPKEY_PATTERN)
1889
+ if rule && !rule.mapping
1890
+ #_validate_error("mapping is not expected.", path)
1891
+ rule = nil
1892
+ end
1893
+ map = create_mapping(rule, @linenum, @column)
1894
+ @anchors[name] = map if name
1895
+ parse_block_map(map, rule, path, uniq_table)
1896
+ return map
1897
+ end
1898
+ ## sequence (flow-style)
1899
+ if match?(/\[/)
1900
+ if rule && !rule.sequence
1901
+ #_validate_error("sequence is not expected.", path)
1902
+ rule = nil
1903
+ end
1904
+ seq = create_sequence(rule, @linenum, @column)
1905
+ @anchors[name] = seq if name
1906
+ parse_flow_seq(seq, rule, path, uniq_table)
1907
+ return seq
1908
+ end
1909
+ ## mapping (flow-style)
1910
+ if match?(/\{/)
1911
+ if rule && !rule.mapping
1912
+ #_validate_error("mapping is not expected.", path)
1913
+ rule = nil
1914
+ end
1915
+ map = create_mapping(rule, @linenum, @column)
1916
+ @anchors[name] = map if name
1917
+ parse_flow_map(map, rule, path, uniq_table)
1918
+ return map
1919
+ end
1920
+ ## block text
1921
+ if match?(/[|>]/)
1922
+ text = parse_block_text(level, rule, path, uniq_table)
1923
+ @anchors[name] = text if name
1924
+ return text
1925
+ end
1926
+ ## scalar
1927
+ scalar = parse_block_scalar(rule, path, uniq_table)
1928
+ @anchors[name] = scalar if name
1929
+ return scalar
1930
+ end
1931
+
1932
+
1933
+ def parse_block_seq(seq, seq_rule, path, uniq_table)
1934
+ level = @column
1935
+ rule = seq_rule ? seq_rule.sequence[0] : nil
1936
+ path.push(nil)
1937
+ i = 0
1938
+ _linenum = @linenum #*V
1939
+ _column = @column #*V
1940
+ uniq_table = rule ? rule._uniqueness_check_table() : nil #*V
1941
+ while level == @column && scan(/-\s+/)
1942
+ path[-1] = i
1943
+ skip_spaces_and_comments() #*V
1944
+ _linenum2 = @linenum
1945
+ _column2 = @column
1946
+ val = parse_block_value(level, rule, path, uniq_table, seq)
1947
+ add_to_seq(rule, seq, val, _linenum2, _column2) # seq << val
1948
+ _set_error_info(_linenum, _column) do #*V
1949
+ @validator._validate(val, rule, path, @errors, @done, uniq_table, false) #*V
1950
+ end if rule && !val.equal?(PRECEDING_ALIAS_PLACEHOLDER) #*V
1951
+ skip_spaces_and_comments()
1952
+ i += 1
1953
+ _linenum = @linenum #*V
1954
+ _column = @column #*V
1955
+ end
1956
+ path.pop()
1957
+ return seq
1958
+ end
1959
+
1960
+
1961
+ def _parse_map_value(map, map_rule, path, level, key, is_merged, uniq_table,
1962
+ _linenum, _column, _linenum2, _column2) #:nodoc:
1963
+ key = to_mapkey(key)
1964
+ path[-1] = key
1965
+ #if map.is_a?(Hash) && map.key?(key) && !is_merged
1966
+ if map.respond_to?('key?') && map.key?(key) && !is_merged
1967
+ rule = map_rule.mapping[key]
1968
+ unless rule && rule.default
1969
+ raise _syntax_error("mapping key is duplicated.", path)
1970
+ end
1971
+ end
1972
+ #
1973
+ if key == '=' # default
1974
+ val = level ? parse_block_value(level, nil, path, uniq_table, map) \
1975
+ : parse_flow_value(nil, path, uniq_table, map)
1976
+ map.default = val
1977
+ elsif key == '<<' # merge
1978
+ classobj = nil
1979
+ if map_rule && map_rule.classname
1980
+ map_rule = map_rule.dup()
1981
+ classobj = map_rule.classobj
1982
+ map_rule.classname = nil
1983
+ map_rule.classobj = nil
1984
+ end
1985
+ val = level ? parse_block_value(level, map_rule, path, uniq_table, map) \
1986
+ : parse_flow_value(map_rule, path, uniq_table, map)
1987
+ if val.is_a?(Array)
1988
+ val.each_with_index do |v, i|
1989
+ unless v.is_a?(Hash) || (classobj && val.is_a?(classobj))
1990
+ raise _syntax_error("'<<': mapping required.", path + [i])
1991
+ end
1992
+ end
1993
+ values = val
1994
+ elsif val.is_a?(Hash) || (classobj && val.is_a?(classobj))
1995
+ values = [val]
1996
+ else
1997
+ raise _syntax_error("'<<': mapping (or sequence of mapping) required.", path)
1998
+ end
1999
+ #
2000
+ values.each do |hash|
2001
+ if !hash.is_a?(Hash)
2002
+ assert_error "hash=#{hash.inspect}" unless classobj && hash.is_a?(classobj)
2003
+ obj = hash
2004
+ hash = {}
2005
+ obj.instance_variables.each do |name|
2006
+ key = name[1..-1] # '@foo' => 'foo'
2007
+ val = obj.instane_variable_get(name)
2008
+ hash[key] = val
2009
+ end
2010
+ end
2011
+ for key, val in hash
2012
+ path[-1] = key #*V
2013
+ rule = map_rule ? map_rule.mapping[key] : nil #*V
2014
+ utable = uniq_table ? uniq_table[key] : nil #*V
2015
+ _validate_map_value(map, map_rule, rule, path, utable, #*V
2016
+ key, val, _linenum, _column) #*V
2017
+ put_to_map(rule, map, key, val, _linenum2, _column2)
2018
+ end
2019
+ end
2020
+ is_merged = true
2021
+ else # other
2022
+ rule = map_rule ? map_rule.mapping[key] : nil #*V
2023
+ utable = uniq_table ? uniq_table[key] : nil #*V
2024
+ val = level ? parse_block_value(level, rule, path, utable, map) \
2025
+ : parse_flow_value(rule, path, utable, map)
2026
+ _validate_map_value(map, map_rule, rule, path, utable, key, val, #*V
2027
+ _linenum, _column) #*V
2028
+ put_to_map(rule, map, key, val, _linenum2, _column2)
2029
+ end
2030
+ return is_merged
2031
+ end
2032
+
2033
+
2034
+ def _validate_map_value(map, map_rule, rule, path, uniq_table, key, val, #*V
2035
+ _linenum, _column) #*V
2036
+ if map_rule && !rule #*V
2037
+ #_validate_error("unknown mapping key.", path) #*V
2038
+ _set_error_info(_linenum, _column) do #*V
2039
+ error = Kwalify::ErrorHelper.validate_error(:key_undefined, #*V
2040
+ rule, path, map, ["#{key}:"]) #*V
2041
+ @errors << error #*V
2042
+ #error.linenum = _linenum #*V
2043
+ #error.column = _column #*V
2044
+ end #*V
2045
+ end #*V
2046
+ _set_error_info(_linenum, _column) do #*V
2047
+ @validator._validate(val, rule, path, @errors, @done, uniq_table, false) #*V
2048
+ end if rule && !val.equal?(PRECEDING_ALIAS_PLACEHOLDER) #*V
2049
+ end
2050
+
2051
+
2052
+ def parse_block_map(map, map_rule, path, uniq_table)
2053
+ _start_linenum = @linenum #*V
2054
+ _start_column = @column #*V
2055
+ level = @column
2056
+ path.push(nil)
2057
+ is_merged = false
2058
+ while true
2059
+ _linenum = @linenum #*V
2060
+ _column = @column #*V
2061
+ break unless level == @column && scan(MAPKEY_PATTERN)
2062
+ key = group(1)
2063
+ skip_spaces_and_comments() #*V
2064
+ _linenum2 = @linenum #*V
2065
+ _column2 = @column #*V
2066
+ is_merged = _parse_map_value(map, map_rule, path, level, key, is_merged,
2067
+ uniq_table, _linenum, _column, _linenum2, _column2)
2068
+ #skip_spaces_and_comments()
2069
+ end
2070
+ path.pop()
2071
+ _set_error_info(_start_linenum, _start_column) do #*V
2072
+ @validator._validate_mapping_required_keys(map, map_rule, #*V
2073
+ path, @errors) #*V
2074
+ end if map_rule #*V
2075
+ return map
2076
+ end
2077
+
2078
+
2079
+ def to_mapkey(str)
2080
+ if str[0] == ?" || str[0] == ?'
2081
+ return str[1..-2]
2082
+ else
2083
+ return to_scalar(str)
2084
+ end
2085
+ end
2086
+ private :to_mapkey
2087
+
2088
+
2089
+ def parse_block_scalar(rule, path, uniq_table)
2090
+ _linenum = @linenum #*V
2091
+ _column = @column #*V
2092
+ ch = peep(1)
2093
+ if ch == '"' || ch == "'"
2094
+ val = scan_string()
2095
+ scan(/[ \t]*(?:\#.*)?$/)
2096
+ else
2097
+ scan(/(.*?)[ \t]*(?:\#.*)?$/)
2098
+ #str.rstrip!
2099
+ val = to_scalar(group(1))
2100
+ end
2101
+ val = create_scalar(rule, val, _linenum, _column) #*V
2102
+ #_set_error_info(_linenum, _column) do #*V
2103
+ # @validator._validate_unique(val, rule, path, @errors, uniq_table) #*V
2104
+ #end if uniq_table #*V
2105
+ skip_spaces_and_comments()
2106
+ return val
2107
+ end
2108
+
2109
+
2110
+ def parse_block_text(column, rule, path, uniq_table)
2111
+ _linenum = @linenum #*V
2112
+ _column = @column #*V
2113
+ indicator = scan(/[|>]/)
2114
+ chomping = scan(/[-+]/)
2115
+ num = scan(/\d+/)
2116
+ indent = num ? column + num.to_i - 1 : nil
2117
+ unless scan(/[ \t]*(.*?)(\#.*)?\r?\n/) # /[ \t]*(\#.*)?\r?\n/
2118
+ raise _syntax_error("Syntax Error (line break or comment are expected)", path)
2119
+ end
2120
+ s = group(1)
2121
+ is_folded = false
2122
+ while match?(/( *)(.*?)(\r?\n)/)
2123
+ spaces = group(1)
2124
+ text = group(2)
2125
+ nl = group(3)
2126
+ if indent.nil?
2127
+ if spaces.length >= column
2128
+ indent = spaces.length
2129
+ elsif text.empty?
2130
+ s << nl
2131
+ scan(/.*?\n/)
2132
+ next
2133
+ else
2134
+ @diagnostic = 'text indent in block text may be shorter than that of first line or specified column.'
2135
+ break
2136
+ end
2137
+ else
2138
+ if spaces.length < indent && !text.empty?
2139
+ @diagnostic = 'text indent in block text may be shorter than that of first line or specified column.'
2140
+ break
2141
+ end
2142
+ end
2143
+ scan(/.*?\n/)
2144
+ if indicator == '|'
2145
+ s << spaces[indent..-1] if spaces.length >= indent
2146
+ s << text << nl
2147
+ else # indicator == '>'
2148
+ if !text.empty? && spaces.length == indent
2149
+ if s.sub!(/\r?\n((\r?\n)+)\z/, '\1')
2150
+ nil
2151
+ elsif is_folded
2152
+ s.sub!(/\r?\n\z/, ' ')
2153
+ end
2154
+ #s.sub!(/\r?\n\z/, '') if !s.sub!(/\r?\n(\r?\n)+\z/, '\1') && is_folded
2155
+ is_folded = true
2156
+ else
2157
+ is_folded = false
2158
+ s << spaces[indent..-1] if spaces.length > indent
2159
+ end
2160
+ s << text << nl
2161
+ end
2162
+ end
2163
+ ## chomping
2164
+ if chomping == '+'
2165
+ nil
2166
+ elsif chomping == '-'
2167
+ s.sub!(/(\r?\n)+\z/, '')
2168
+ else
2169
+ s.sub!(/(\r?\n)(\r?\n)+\z/, '\1')
2170
+ end
2171
+ #
2172
+ skip_spaces_and_comments()
2173
+ val = s
2174
+ #_set_error_info(_linenum, _column) do #*V
2175
+ # @validator._validate_unique(val, rule, path, @errors, uniq_table) #*V
2176
+ #end if uniq_table #*V
2177
+ return val
2178
+ end
2179
+
2180
+
2181
+ def parse_flow_value(rule, path, uniq_table, container)
2182
+ skip_spaces_and_comments()
2183
+ ## anchor and alias
2184
+ name = nil
2185
+ if scan(/\&([-\w]+)/)
2186
+ name = parse_anchor(rule, path, uniq_table, container)
2187
+ elsif scan(/\*([-\w]+)/)
2188
+ return parse_alias(rule, path, uniq_table, container)
2189
+ end
2190
+ ## type
2191
+ if scan(/!!?\w+/)
2192
+ skip_spaces_and_comments()
2193
+ end
2194
+ ## sequence
2195
+ if match?(/\[/)
2196
+ if rule && !rule.sequence #*V
2197
+ #_validate_error("sequence is not expected.", path) #*V
2198
+ rule = nil #*V
2199
+ end #*V
2200
+ seq = create_sequence(rule, @linenum, @column)
2201
+ @anchors[name] = seq if name
2202
+ parse_flow_seq(seq, rule, path, uniq_table)
2203
+ return seq
2204
+ end
2205
+ ## mapping
2206
+ if match?(/\{/)
2207
+ if rule && !rule.mapping #*V
2208
+ #_validate_error("mapping is not expected.", path) #*V
2209
+ rule = nil #*V
2210
+ end #*V
2211
+ map = create_mapping(rule, @linenum, @column)
2212
+ @anchors[name] = map if name
2213
+ parse_flow_map(map, rule, path, uniq_table)
2214
+ return map
2215
+ end
2216
+ ## scalar
2217
+ scalar = parse_flow_scalar(rule, path, uniq_table)
2218
+ @anchors[name] = scalar if name
2219
+ return scalar
2220
+ end
2221
+
2222
+
2223
+ def parse_flow_seq(seq, seq_rule, path, uniq_table)
2224
+ #scan(/\[\s*/)
2225
+ scan(/\[/)
2226
+ skip_spaces_and_comments()
2227
+ if scan(/\]/)
2228
+ nil
2229
+ else
2230
+ rule = seq_rule ? seq_rule.sequence[0] : nil #*V
2231
+ uniq_table = rule ? rule._uniqueness_check_table() : nil #*V
2232
+ path.push(nil)
2233
+ i = 0
2234
+ while true
2235
+ path[-1] = i
2236
+ _linenum = @linenum #*V
2237
+ _column = @column #*V
2238
+ val = parse_flow_value(rule, path, uniq_table, seq)
2239
+ add_to_seq(rule, seq, val, _linenum, _column) # seq << val
2240
+ _set_error_info(_linenum, _column) do #*V
2241
+ @validator._validate(val, rule, path, @errors, @done, uniq_table, false) #*V
2242
+ end if rule && !val.equal?(PRECEDING_ALIAS_PLACEHOLDER) #*V
2243
+ skip_spaces_and_comments()
2244
+ break unless scan(/,\s+/)
2245
+ i += 1
2246
+ if match?(/\]/)
2247
+ raise _syntax_error("sequence item required (or last comma is extra).", path)
2248
+ end
2249
+ end
2250
+ path.pop()
2251
+ unless scan(/\]/)
2252
+ raise _syntax_error("flow sequence is not closed by ']'.", path)
2253
+ end
2254
+ end
2255
+ skip_spaces_and_comments()
2256
+ return seq
2257
+ end
2258
+
2259
+
2260
+ def parse_flow_map(map, map_rule, path, uniq_table)
2261
+ #scan(/\{\s*/) # not work?
2262
+ _start_linenum = @linenum #*V
2263
+ _start_column = @column #*V
2264
+ scan(/\{/)
2265
+ skip_spaces_and_comments()
2266
+ if scan(/\}/)
2267
+ nil
2268
+ else
2269
+ path.push(nil)
2270
+ is_merged = false
2271
+ while true
2272
+ _linenum = @linenum #*V
2273
+ _column = @column #*V
2274
+ unless scan(MAPKEY_PATTERN)
2275
+ raise _syntax_error("mapping key is expected.", path)
2276
+ end
2277
+ key = group(1)
2278
+ skip_spaces_and_comments()
2279
+ _linenum2 = @linenum #*V
2280
+ _column2 = @column #*V
2281
+ is_merged = _parse_map_value(map, map_rule, path, nil, key, is_merged,
2282
+ uniq_table, _linenum, _column, _linenum2, _column2)
2283
+ #skip_spaces_and_comments()
2284
+ break unless scan(/,\s+/)
2285
+ end
2286
+ path.pop()
2287
+ unless scan(/\}/)
2288
+ raise _syntax_error("flow mapping is not closed by '}'.", path)
2289
+ end
2290
+ end
2291
+ skip_spaces_and_comments()
2292
+ _set_error_info(_start_linenum, _start_column) do #*V
2293
+ @validator._validate_mapping_required_keys(map, map_rule, path, @errors) #*V
2294
+ end if map_rule #*V
2295
+ return map
2296
+ end
2297
+
2298
+
2299
+ def parse_flow_scalar(rule, path, uniq_table)
2300
+ ch = peep(1)
2301
+ _linenum = @linenum #*V
2302
+ _column = @column #*V
2303
+ if ch == '"' || ch == "'"
2304
+ val = scan_string()
2305
+ else
2306
+ str = scan(/[^,\]\}\#]*/)
2307
+ if match?(/,\S/)
2308
+ while match?(/,\S/)
2309
+ str << scan(/./)
2310
+ str << scan(/[^,\]\}\#]*/)
2311
+ end
2312
+ end
2313
+ str.rstrip!
2314
+ val = to_scalar(str)
2315
+ end
2316
+ val = create_scalar(rule, val, _linenum, _column) #*V
2317
+ #_set_error_info(_linenum, _column) do #*V
2318
+ # @validator._validate_unique(val, rule, path, @errors, uniq_table) #*V
2319
+ #end if uniq_table #*V
2320
+ skip_spaces_and_comments()
2321
+ return val
2322
+ end
2323
+
2324
+
2325
+ ####
2326
+
2327
+
2328
+ def to_scalar(str)
2329
+ case str
2330
+ when nil ; val = nil
2331
+ when /\A-?\d+\.\d+\z/ ; val = str.to_f
2332
+ when /\A-?\d+\z/ ; val = str.to_i
2333
+ when /\A(true|yes)\z/ ; val = true
2334
+ when /\A(false|no)\z/ ; val = false
2335
+ when /\A(null|~)\z/ ; val = nil
2336
+ when /\A"(.*)"\z/ ; val = $1
2337
+ when /\A'(.*)'\z/ ; val = $1
2338
+ when /\A:(\w+)\z/ ; val = $1.intern
2339
+ when /\A(\d\d\d\d)-(\d\d)-(\d\d)(?: (\d\d):(\d\d):(\d\d))?\z/
2340
+ year, month, day, hour, min, sec = $1, $2, $3, $4, $5, $6
2341
+ if hour
2342
+ val = Time.mktime(year, month, day, hour, min, sec)
2343
+ else
2344
+ val = Date.new(year.to_i, month.to_i, day.to_i)
2345
+ end
2346
+ ## or
2347
+ #params = [$1, $2, $3, $4, $5, $6]
2348
+ #val = Time.mktime(*params)
2349
+ else
2350
+ val = str.empty? ? nil : str
2351
+ end
2352
+ skip_spaces_and_comments()
2353
+ return val
2354
+ end
2355
+
2356
+
2357
+ ##
2358
+
2359
+ protected
2360
+
2361
+
2362
+ def create_sequence(rule, linenum, column)
2363
+ seq = @sequence_class.new
2364
+ @location_table[seq.__id__] = []
2365
+ return seq
2366
+ end
2367
+
2368
+
2369
+ def create_mapping(rule, linenum, column)
2370
+ if rule && rule.classobj && @data_binding
2371
+ classobj = rule.classobj
2372
+ map = classobj.new
2373
+ else
2374
+ classobj = nil
2375
+ map = @mapping_class.new # {}
2376
+ end
2377
+ @location_table[map.__id__] = hash = {}
2378
+ hash[:classobj] = classobj if classobj
2379
+ return map
2380
+ end
2381
+
2382
+
2383
+ def create_scalar(rule, value, linenum, column)
2384
+ return value
2385
+ end
2386
+
2387
+
2388
+ def add_to_seq(rule, seq, val, linenum, column)
2389
+ seq << val
2390
+ @location_table[seq.__id__] << [linenum, column]
2391
+ end
2392
+
2393
+
2394
+ def put_to_map(rule, map, key, val, linenum, column)
2395
+ #if map.is_a?(Hash)
2396
+ # map[key] = val
2397
+ #elsif map.respond_to?(name="#{key}=")
2398
+ # map.__send__(name, val)
2399
+ #elsif map.respond_to?('[]=')
2400
+ # map[key] = val
2401
+ #else
2402
+ # map.instance_variable_set("@#{key}", val)
2403
+ #end
2404
+ map[key] = val
2405
+ @location_table[map.__id__][key] = [linenum, column]
2406
+ end
2407
+
2408
+
2409
+ def _getclass(classname)
2410
+ mod = Object
2411
+ classname.split(/::/).each do |modname|
2412
+ mod = mod.const_get(modname) # raises NameError when module not found
2413
+ end
2414
+ return mod
2415
+ end
2416
+
2417
+
2418
+ public
2419
+
2420
+
2421
+ def location(path)
2422
+ if path.empty? || path == '/'
2423
+ return @location_table[-1] # return value is [linenum, column]
2424
+ end
2425
+ if path.is_a?(Array)
2426
+ items = path.collect { |item| to_scalar(item) }
2427
+ elsif path.is_a?(String)
2428
+ items = path.split('/').collect { |item| to_scalar(item) }
2429
+ items.shift if path[0] == ?/ # delete empty string on head
2430
+ else
2431
+ raise ArgumentError.new("path should be Array or String.")
2432
+ end
2433
+ last_item = items.pop()
2434
+ c = @doc # collection
2435
+ items.each do |item|
2436
+ if c.is_a?(Array)
2437
+ c = c[item.to_i]
2438
+ elsif c.is_a?(Hash)
2439
+ c = c[item]
2440
+ elsif (table = @location_table[c.__id__]) && table[:classobj]
2441
+ if c.respond_to?(item)
2442
+ c = c.__send__(item)
2443
+ elsif c.respond_to?("[]=")
2444
+ c = c[item]
2445
+ else
2446
+ assert false
2447
+ end
2448
+ else
2449
+ #assert false
2450
+ raise ArgumentError.new("#{path.inspect}: invalid path.")
2451
+ end
2452
+ end
2453
+ collection = @location_table[c.__id__]
2454
+ return nil if collection.nil?
2455
+ index = c.is_a?(Array) ? last_item.to_i : last_item
2456
+ return collection[index] # return value is [linenum, column]
2457
+ end
2458
+
2459
+
2460
+ def set_errors_linenum(errors)
2461
+ errors.each do |error|
2462
+ error.linenum, error.column = location(error.path)
2463
+ end
2464
+ end
2465
+
2466
+
2467
+ end
2468
+ #--end of require 'kwalify/parser/yaml'
2469
+ #require 'yaml'
2470
+
2471
+ module Kwalify
2472
+
2473
+
2474
+ ##
2475
+ ## ex.
2476
+ ## meta_validator = Kwalify::MetaValidator.instance()
2477
+ ## schema = File.load_file('schema.yaml')
2478
+ ## errors = meta_validator.validate(schema)
2479
+ ## if !errors.empty?
2480
+ ## errors.each do |error|
2481
+ ## puts "[#{error.path}] #{error.message}"
2482
+ ## end
2483
+ ## end
2484
+ ##
2485
+ class MetaValidator < Validator
2486
+
2487
+ filename = File.join(File.dirname(__FILE__), 'kwalify.schema.yaml')
2488
+ META_SCHEMA = File.read(filename)
2489
+
2490
+ def self.instance()
2491
+ unless @instance
2492
+ schema = Kwalify::Yaml::Parser.new().parse(META_SCHEMA)
2493
+ @instance = MetaValidator.new(schema)
2494
+ end
2495
+ return @instance
2496
+ end
2497
+
2498
+ def initialize(schema, &block)
2499
+ super
2500
+ end
2501
+
2502
+ def validate_hook(value, rule, path, errors)
2503
+ return if value.nil? ## realy?
2504
+ return unless rule.name == "MAIN"
2505
+ #
2506
+ hash = value
2507
+ type = hash['type']
2508
+ type = Types::DEFAULT_TYPE if type.nil?
2509
+ klass = Types.type_class(type)
2510
+ #unless klass
2511
+ # errors << validate_error(:type_unknown, rule, "#{path}/type", type)
2512
+ #end
2513
+ #
2514
+ if hash.key?('class')
2515
+ val = hash['class']
2516
+ unless val.nil? || type == 'map'
2517
+ errors << validate_error(:class_notmap, rule, "#{path}/class", 'class:')
2518
+ end
2519
+ end
2520
+ #
2521
+ if hash.key?('pattern')
2522
+ val = hash['pattern']
2523
+ pat = (val =~ /\A\/(.*)\/([mi]?[mi]?)\z/ ? $1 : val)
2524
+ begin
2525
+ Regexp.compile(pat)
2526
+ rescue RegexpError => ex
2527
+ errors << validate_error(:pattern_syntaxerr, rule, "#{path}/pattern", val)
2528
+ end
2529
+ end
2530
+ #
2531
+ if hash.key?('enum')
2532
+ if Types.collection_type?(type)
2533
+ errors << validate_error(:enum_notscalar, rule, path, 'enum:')
2534
+ else
2535
+ #elem_table = {}
2536
+ hash['enum'].each do |elem|
2537
+ #if elem_table[elem]
2538
+ # errors << validate_error(:enum_duplicate, rule, "#{path}/enum", elem.to_s)
2539
+ #end
2540
+ #elem_table[elem] = true
2541
+ unless elem.is_a?(klass)
2542
+ errors << validate_error(:enum_type_unmatch, rule, "#{path}/enum", elem, [Kwalify.word(type)])
2543
+ end
2544
+ end
2545
+ end
2546
+ end
2547
+ #
2548
+ if hash.key?('assert')
2549
+ val = hash['assert']
2550
+ #val =~ /\bval\b/ or errors << validate_error(:assert_noval, rule, "#{path}/assert", val)
2551
+ begin
2552
+ eval "proc { |val| #{val} }"
2553
+ rescue ::SyntaxError => ex
2554
+ errors << validate_error(:assert_syntaxerr, rule, "#{path}/assert", val)
2555
+ end
2556
+ end
2557
+ #
2558
+ if hash.key?('range')
2559
+ val = hash['range']
2560
+ curr_path = path + "/range"
2561
+ #if ! val.is_a?(Hash)
2562
+ # errors << validate_error(:range_notmap, rule, curr_path, val)
2563
+ #elsif ...
2564
+ if Types.collection_type?(type) || type == 'bool' || type == 'any'
2565
+ errors << validate_error(:range_notscalar, rule, path, 'range:')
2566
+ else
2567
+ val.each do |rkey, rval|
2568
+ #case rkey
2569
+ #when 'max', 'min', 'max-ex', 'min-ex'
2570
+ unless rval.is_a?(klass)
2571
+ typename = Kwalify.word(type) || type
2572
+ errors << validate_error(:range_type_unmatch, rule, "#{curr_path}/#{rkey}", rval, [typename])
2573
+ end
2574
+ #else
2575
+ # errors << validate_error(:range_undefined, rule, curr_path, "#{rkey}:")
2576
+ #end
2577
+ end
2578
+ end
2579
+ if val.key?('max') && val.key?('max-ex')
2580
+ errors << validate_error(:range_twomax, rule, curr_path, nil)
2581
+ end
2582
+ if val.key?('min') && val.key?('min-ex')
2583
+ errors << validate_error(:range_twomin, rule, curr_path, nil)
2584
+ end
2585
+ max, min, max_ex, min_ex = val['max'], val['min'], val['max-ex'], val['min-ex']
2586
+ if max
2587
+ if min && max < min
2588
+ errors << validate_error(:range_maxltmin, rule, curr_path, nil, [max, min])
2589
+ elsif min_ex && max <= min_ex
2590
+ errors << validate_error(:range_maxleminex, rule, curr_path, nil, [max, min_ex])
2591
+ end
2592
+ elsif max_ex
2593
+ if min && max_ex <= min
2594
+ errors << validate_error(:range_maxexlemin, rule, curr_path, nil, [max_ex, min])
2595
+ elsif min_ex && max_ex <= min_ex
2596
+ errors << validate_error(:range_maxexleminex, rule, curr_path, nil, [max_ex, min_ex])
2597
+ end
2598
+ end
2599
+ end
2600
+ #
2601
+ if hash.key?('length')
2602
+ val = hash['length']
2603
+ curr_path = path + "/length"
2604
+ #val.is_a?(Hash) or errors << validate_error(:length_notmap, rule, curr_path, val)
2605
+ unless type == 'str' || type == 'text'
2606
+ errors << validate_error(:length_nottext, rule, path, 'length:')
2607
+ end
2608
+ #val.each do |lkey, lval|
2609
+ # #case lkey
2610
+ # #when 'max', 'min', 'max-ex', 'min-ex'
2611
+ # unless lval.is_a?(Integer)
2612
+ # errors << validate_error(:length_notint, rule, "#{curr_path}/#{lkey}", lval)
2613
+ # end
2614
+ # #else
2615
+ # # errors << validate_error(:length_undefined, rule, curr_path, "#{lkey}:")
2616
+ # #end
2617
+ #end
2618
+ if val.key?('max') && val.key?('max-ex')
2619
+ errors << validate_error(:length_twomax, rule, curr_path, nil)
2620
+ end
2621
+ if val.key?('min') && val.key?('min-ex')
2622
+ errors << validate_error(:length_twomin, rule, curr_path, nil)
2623
+ end
2624
+ max, min, max_ex, min_ex = val['max'], val['min'], val['max-ex'], val['min-ex']
2625
+ if max
2626
+ if min && max < min
2627
+ errors << validate_error(:length_maxltmin, rule, curr_path, nil, [max, min])
2628
+ elsif min_ex && max <= min_ex
2629
+ errors << validate_error(:length_maxleminex, rule, curr_path, nil, [max, min_ex])
2630
+ end
2631
+ elsif max_ex
2632
+ if min && max_ex <= min
2633
+ errors << validate_error(:length_maxexlemin, rule, curr_path, nil, [max_ex, min])
2634
+ elsif min_ex && max_ex <= min_ex
2635
+ errors << validate_error(:length_maxexleminex, rule, curr_path, nil, [max_ex, min_ex])
2636
+ end
2637
+ end
2638
+ end
2639
+ #
2640
+ if hash.key?('unique')
2641
+ if hash['unique'] && Types.collection_type?(type)
2642
+ errors << validate_error(:unique_notscalar, rule, path, "unique:")
2643
+ end
2644
+ if path.empty?
2645
+ errors << validate_error(:unique_onroot, rule, "/", "unique:")
2646
+ end
2647
+ end
2648
+ #
2649
+ if hash.key?('ident')
2650
+ if hash['ident'] && Types.collection_type?(type)
2651
+ errors << validate_error(:ident_notscalar, rule, path, "ident:")
2652
+ end
2653
+ if path.empty?
2654
+ errors << validate_error(:ident_onroot, rule, "/", "ident:")
2655
+ end
2656
+ end
2657
+ #
2658
+ if hash.key?('default')
2659
+ val = hash['default']
2660
+ if Types.collection_type?(type)
2661
+ errors << validate_error(:default_notscalar, rule, path, "default:")
2662
+ elsif !val.nil? && !val.is_a?(klass)
2663
+ errors << validate_error(:default_unmatch, rule, "#{path}/default", val, [Kwalify.word(type)])
2664
+ end
2665
+ end
2666
+ #
2667
+ if hash.key?('sequence')
2668
+ val = hash['sequence']
2669
+ #if !val.nil? && !val.is_a?(Array)
2670
+ # errors << validate_error(:sequence_notseq, rule, "#{path}/sequence", val)
2671
+ #elsif ...
2672
+ if val.nil? || val.empty?
2673
+ errors << validate_error(:sequence_noelem, rule, "#{path}/sequence", val)
2674
+ elsif val.length > 1
2675
+ errors << validate_error(:sequence_toomany, rule, "#{path}/sequence", val)
2676
+ else
2677
+ elem = val[0]
2678
+ assert_error("elem.class=#{elem.class}") unless elem.is_a?(Hash)
2679
+ if elem['ident'] && elem['type'] != 'map'
2680
+ errors << validate_error(:ident_notmap, nil, "#{path}/sequence/0", 'ident:')
2681
+ end
2682
+ end
2683
+ end
2684
+ #
2685
+ if hash.key?('mapping')
2686
+ val = hash['mapping']
2687
+ if !val.nil? && !val.is_a?(Hash)
2688
+ errors << validate_error(:mapping_notmap, rule, "#{path}/mapping", val)
2689
+ elsif val.nil? || (val.empty? && !val.default)
2690
+ errors << validate_error(:mapping_noelem, rule, "#{path}/mapping", val)
2691
+ end
2692
+ end
2693
+ #
2694
+ if type == 'seq'
2695
+ errors << validate_error(:seq_nosequence, rule, path, nil) unless hash.key?('sequence')
2696
+ #errors << validate_error(:seq_conflict, rule, path, 'enum:') if hash.key?('enum')
2697
+ errors << validate_error(:seq_conflict, rule, path, 'pattern:') if hash.key?('pattern')
2698
+ errors << validate_error(:seq_conflict, rule, path, 'mapping:') if hash.key?('mapping')
2699
+ #errors << validate_error(:seq_conflict, rule, path, 'range:') if hash.key?('range')
2700
+ #errors << validate_error(:seq_conflict, rule, path, 'length:') if hash.key?('length')
2701
+ elsif type == 'map'
2702
+ errors << validate_error(:map_nomapping, rule, path, nil) unless hash.key?('mapping')
2703
+ #errors << validate_error(:map_conflict, rule, path, 'enum:') if hash.key?('enum')
2704
+ errors << validate_error(:map_conflict, rule, path, 'pattern:') if hash.key?('pattern')
2705
+ errors << validate_error(:map_conflict, rule, path, 'sequence:') if hash.key?('sequence')
2706
+ #errors << validate_error(:map_conflict, rule, path, 'range:') if hash.key?('range')
2707
+ #errors << validate_error(:map_conflict, rule, path, 'length:') if hash.key?('length')
2708
+ else
2709
+ errors << validate_error(:scalar_conflict, rule, path, 'sequence:') if hash.key?('sequence')
2710
+ errors << validate_error(:scalar_conflict, rule, path, 'mapping:') if hash.key?('mapping')
2711
+ if hash.key?('enum')
2712
+ errors << validate_error(:enum_conflict, rule, path, 'range:') if hash.key?('range')
2713
+ errors << validate_error(:enum_conflict, rule, path, 'length:') if hash.key?('length')
2714
+ errors << validate_error(:enum_conflict, rule, path, 'pattern:') if hash.key?('pattern')
2715
+ end
2716
+ if hash.key?('default')
2717
+ errors << validate_error(:default_conflict, rule, path, 'default:') if hash['required']
2718
+ end
2719
+ end
2720
+
2721
+ end # end of def validate_hook()
2722
+
2723
+
2724
+ end # end of class MetaValidator
2725
+
2726
+
2727
+ META_VALIDATOR = MetaValidator.instance()
2728
+
2729
+ def self.meta_validator # obsolete
2730
+ return META_VALIDATOR
2731
+ end
2732
+
2733
+ end
2734
+ #--end of require 'kwalify/meta-validator'
2735
+ #--begin of require 'kwalify/yaml-parser'
2736
+ ###
2737
+ ### $Rev: 81 $
2738
+ ### $Release: 0.7.1 $
2739
+ ### copyright(c) 2005-2008 kuwata-lab all rights reserved.
2740
+ ###
2741
+
2742
+ #--already included require 'kwalify/messages'
2743
+ #--already included require 'kwalify/errors'
2744
+ #--already included require 'kwalify/types'
2745
+
2746
+ require 'date'
2747
+
2748
+
2749
+ module Kwalify
2750
+
2751
+ ##
2752
+ ## base class of yaml parser
2753
+ ##
2754
+ ## ex.
2755
+ ## str = ARGF.read()
2756
+ ## parser = Kwalify::PlainYamlParser.new(str)
2757
+ ## doc = parser.parse()
2758
+ ## p doc
2759
+ ##
2760
+ class PlainYamlParser
2761
+
2762
+ class Alias
2763
+ def initialize(label, linenum)
2764
+ @label = label
2765
+ @linenum = linenum
2766
+ end
2767
+ attr_reader :label, :linenum
2768
+ end
2769
+
2770
+
2771
+ def initialize(yaml_str)
2772
+ @lines = yaml_str.to_a()
2773
+ @line = nil
2774
+ @linenum = 0
2775
+ @anchors = {}
2776
+ @aliases = {}
2777
+ end
2778
+
2779
+
2780
+ def parse()
2781
+ data = parse_child(0)
2782
+ if data.nil? && @end_flag == '---'
2783
+ data = parse_child(0)
2784
+ end
2785
+ resolve_aliases(data) unless @aliases.empty?
2786
+ return data
2787
+ end
2788
+
2789
+
2790
+ def has_next?
2791
+ return @end_flag != 'EOF'
2792
+ end
2793
+
2794
+
2795
+ def parse_all
2796
+ list = []
2797
+ while has_next()
2798
+ doc = parse()
2799
+ list << doc
2800
+ end
2801
+ return list
2802
+ end
2803
+
2804
+
2805
+ protected
2806
+
2807
+
2808
+ def create_sequence(linenum=nil)
2809
+ return []
2810
+ end
2811
+
2812
+ def add_to_seq(seq, value, linenum)
2813
+ seq << value
2814
+ end
2815
+
2816
+ def set_seq_at(seq, i, value, linenum)
2817
+ seq[i] = value
2818
+ end
2819
+
2820
+ def create_mapping(linenum=nil)
2821
+ return {}
2822
+ end
2823
+
2824
+ def add_to_map(map, key, value, linenum)
2825
+ map[key] = value
2826
+ end
2827
+
2828
+ def set_map_with(map, key, value, linenum)
2829
+ map[key] = value
2830
+ end
2831
+
2832
+ def set_default(map, value, linenum)
2833
+ map.value = value
2834
+ end
2835
+
2836
+ def merge_map(map, map2, linenum)
2837
+ map2.each do |key, val|
2838
+ map[key] = value unless map.key?(key)
2839
+ end
2840
+ end
2841
+
2842
+ def create_scalar(value, linenum=nil)
2843
+ return value
2844
+ end
2845
+
2846
+
2847
+ def current_line
2848
+ return @line
2849
+ end
2850
+
2851
+ def current_linenum
2852
+ return @linenum
2853
+ end
2854
+
2855
+
2856
+ private
2857
+
2858
+
2859
+ def getline
2860
+ line = _getline()
2861
+ line = _getline() while line && line =~ /^\s*($|\#)/
2862
+ return line
2863
+ end
2864
+
2865
+ def _getline
2866
+ @line = @lines[@linenum]
2867
+ @linenum += 1
2868
+ case @line
2869
+ when nil ; @end_flag = 'EOF'
2870
+ when /^\.\.\.$/ ; @end_flag = '...'; @line = nil
2871
+ when /^---(\s+.*)?$/ ; @end_flag = '---'; @line = nil
2872
+ else ; @end_flag = nil
2873
+ end
2874
+ return @line
2875
+ end
2876
+
2877
+
2878
+ def reset_sbuf(str)
2879
+ @sbuf = str[-1] == ?\n ? str : str + "\n"
2880
+ @index = -1
2881
+ end
2882
+
2883
+
2884
+ def _getchar
2885
+ @index += 1
2886
+ ch = @sbuf[@index]
2887
+ while ch.nil?
2888
+ break if (line = getline()).nil?
2889
+ reset_sbuf(line)
2890
+ @index += 1
2891
+ ch = @sbuf[@index]
2892
+ end
2893
+ return ch
2894
+ end
2895
+
2896
+ def getchar
2897
+ ch = _getchar()
2898
+ ch = _getchar() while ch && white?(ch)
2899
+ return ch
2900
+ end
2901
+
2902
+ def getchar_or_nl
2903
+ ch = _getchar()
2904
+ ch = _getchar() while ch && white?(ch) && ch != ?\n
2905
+ return ch
2906
+ end
2907
+
2908
+ def current_char
2909
+ return @sbuf[@index]
2910
+ end
2911
+
2912
+ def getlabel
2913
+ if @sbuf[@index..-1] =~ /\A\w[-\w]*/
2914
+ label = $&
2915
+ @index += label.length
2916
+ else
2917
+ label = nil
2918
+ end
2919
+ return label
2920
+ end
2921
+
2922
+ #--
2923
+ #def syntax_error(error_symbol, linenum=@linenum)
2924
+ # msg = Kwalify.msg(error_symbol) % [linenum]
2925
+ # return Kwalify::YamlSyntaxError.new(msg, linenum,error_symbol)
2926
+ #end
2927
+ #++
2928
+ def syntax_error(error_symbol, arg=nil, linenum=@linenum)
2929
+ msg = Kwalify.msg(error_symbol)
2930
+ msg = msg % arg.to_a unless arg.nil?
2931
+ return Kwalify::YamlSyntaxError.new(msg, linenum, error_symbol)
2932
+ end
2933
+
2934
+ def parse_child(column)
2935
+ line = getline()
2936
+ return create_scalar(nil) if !line
2937
+ line =~ /^( *)(.*)/
2938
+ indent = $1.length
2939
+ return create_scalar(nil) if indent < column
2940
+ value = $2
2941
+ return parse_value(column, value, indent)
2942
+ end
2943
+
2944
+
2945
+ def parse_value(column, value, value_start_column)
2946
+ case value
2947
+ when /^-( |$)/
2948
+ data = parse_sequence(value_start_column, value)
2949
+ when /^(:?:?[-.\w]+\*?|'.*?'|".*?"|=|<<) *:( |$)/
2950
+ #when /^:?["']?[-.\w]+["']? *:( |$)/ #'
2951
+ data = parse_mapping(value_start_column, value)
2952
+ when /^\[/, /^\{/
2953
+ data = parse_flowstyle(column, value)
2954
+ when /^\&[-\w]+( |$)/
2955
+ data = parse_anchor(column, value)
2956
+ when /^\*[-\w]+( |$)/
2957
+ data = parse_alias(column, value)
2958
+ when /^[|>]/
2959
+ data = parse_block_text(column, value)
2960
+ when /^!/
2961
+ data = parse_tag(column, value)
2962
+ when /^\#/
2963
+ data = parse_child(column)
2964
+ else
2965
+ data = parse_scalar(column, value)
2966
+ end
2967
+ return data
2968
+ end
2969
+
2970
+ def white?(ch)
2971
+ return ch == ?\ || ch == ?\t || ch == ?\n || ch == ?\r
2972
+ end
2973
+
2974
+
2975
+ ##
2976
+ ## flowstyle ::= flow_seq | flow_map | flow_scalar | alias
2977
+ ##
2978
+ ## flow_seq ::= '[' [ flow_seq_item { ',' sp flow_seq_item } ] ']'
2979
+ ## flow_seq_item ::= flowstyle
2980
+ ##
2981
+ ## flow_map ::= '{' [ flow_map_item { ',' sp flow_map_item } ] '}'
2982
+ ## flow_map_item ::= flowstyle ':' sp flowstyle
2983
+ ##
2984
+ ## flow_scalar ::= string | number | boolean | symbol | date
2985
+ ##
2986
+
2987
+ def parse_flowstyle(column, value)
2988
+ reset_sbuf(value)
2989
+ getchar()
2990
+ data = parse_flow(0)
2991
+ ch = current_char
2992
+ assert ch == ?] || ch == ?}
2993
+ ch = getchar_or_nl()
2994
+ unless ch == ?\n || ch == ?# || ch.nil?
2995
+ #* key=:flow_hastail msg="flow style sequence is closed but got '%s'."
2996
+ raise syntax_error(:flow_hastail, [ch.chr])
2997
+ end
2998
+ getline() if !ch.nil?
2999
+ return data
3000
+ end
3001
+
3002
+ def parse_flow(depth)
3003
+ ch = current_char()
3004
+ #ch = getchar()
3005
+ if ch.nil?
3006
+ #* key=:flow_eof msg="found EOF when parsing flow style."
3007
+ rase syntax_error(:flow_eof)
3008
+ end
3009
+ ## alias
3010
+ if ch == ?*
3011
+ _getchar()
3012
+ label = getlabel()
3013
+ unless label
3014
+ #* key=:flow_alias_label msg="alias name expected."
3015
+ rase syntax_error(:flow_alias_label)
3016
+ end
3017
+ data = @anchors[label]
3018
+ unless data
3019
+ data = register_alias(label)
3020
+ #raise syntax_error("anchor '#{label}' not found (cannot refer to backward or child anchor).")
3021
+ end
3022
+ return data
3023
+ end
3024
+ ## anchor
3025
+ label = nil
3026
+ if ch == ?&
3027
+ _getchar()
3028
+ label = getlabel()
3029
+ unless label
3030
+ #* key=:flow_anchor_label msg="anchor name expected."
3031
+ rase syntax_error(:flow_anchor_label)
3032
+ end
3033
+ ch = current_char()
3034
+ ch = getchar() if white?(ch)
3035
+ end
3036
+ ## flow data
3037
+ if ch == ?[
3038
+ data = parse_flow_seq(depth)
3039
+ elsif ch == ?{
3040
+ data = parse_flow_map(depth)
3041
+ else
3042
+ data = parse_flow_scalar(depth)
3043
+ end
3044
+ ## register anchor
3045
+ register_anchor(label, data) if label
3046
+ return data
3047
+ end
3048
+
3049
+ def parse_flow_seq(depth)
3050
+ assert current_char() == ?[
3051
+ seq = create_sequence() # []
3052
+ ch = getchar()
3053
+ if ch != ?}
3054
+ linenum = current_linenum()
3055
+ #seq << parse_flow_seq_item(depth + 1)
3056
+ add_to_seq(seq, parse_flow_seq_item(depth + 1), linenum)
3057
+ while (ch = current_char()) == ?,
3058
+ ch = getchar()
3059
+ if ch == ?]
3060
+ #* key=:flow_noseqitem msg="sequence item required (or last comma is extra)."
3061
+ raise syntax_error(:flow_noseqitem)
3062
+ end
3063
+ #break if ch == ?]
3064
+ linenum = current_linenum()
3065
+ #seq << parse_flow_seq_item(depth + 1)
3066
+ add_to_seq(seq, parse_flow_seq_item(depth + 1), linenum)
3067
+ end
3068
+ end
3069
+ unless current_char() == ?]
3070
+ #* key=:flow_seqnotclosed msg="flow style sequence requires ']'."
3071
+ raise syntax_error(:flow_seqnotclosed)
3072
+ end
3073
+ getchar() if depth > 0
3074
+ return seq
3075
+ end
3076
+
3077
+ def parse_flow_seq_item(depth)
3078
+ return parse_flow(depth)
3079
+ end
3080
+
3081
+ def parse_flow_map(depth)
3082
+ assert current_char() == ?{ #}
3083
+ map = create_mapping() # {}
3084
+ ch = getchar()
3085
+ if ch != ?}
3086
+ linenum = current_linenum()
3087
+ key, value = parse_flow_map_item(depth + 1)
3088
+ #map[key] = value
3089
+ add_to_map(map, key, value, linenum)
3090
+ while (ch = current_char()) == ?,
3091
+ ch = getchar()
3092
+ if ch == ?}
3093
+ #* key=:flow_mapnoitem msg="mapping item required (or last comma is extra)."
3094
+ raise syntax_error(:flow_mapnoitem)
3095
+ end
3096
+ #break if ch == ?}
3097
+ linenum = current_linenum()
3098
+ key, value = parse_flow_map_item(depth + 1)
3099
+ #map[key] = value
3100
+ add_to_map(map, key, value, linenum)
3101
+ end
3102
+ end
3103
+ unless current_char() == ?}
3104
+ #* key=:flow_mapnotclosed msg="flow style mapping requires '}'."
3105
+ raise syntax_error(:flow_mapnotclosed)
3106
+ end
3107
+ getchar() if depth > 0
3108
+ return map
3109
+ end
3110
+
3111
+ def parse_flow_map_item(depth)
3112
+ key = parse_flow(depth)
3113
+ unless (ch = current_char()) == ?:
3114
+ $stderr.puts "*** debug: key=#{key.inspect}"
3115
+ s = ch ? "'#{ch.chr}'" : "EOF"
3116
+ #* key=:flow_nocolon msg="':' expected but got %s."
3117
+ raise syntax_error(:flow_nocolon, [s])
3118
+ end
3119
+ getchar()
3120
+ value = parse_flow(depth)
3121
+ return key, value
3122
+ end
3123
+
3124
+ def parse_flow_scalar(depth)
3125
+ case ch = current_char()
3126
+ when ?", ?' #"
3127
+ endch = ch
3128
+ s = ''
3129
+ while (ch = _getchar()) != nil && ch != endch
3130
+ if ch == ?\\
3131
+ ch = _getchar()
3132
+ if ch.nil?
3133
+ #* key=:flow_str_notclosed msg="%s: string not closed."
3134
+ raise syntax_error(:flow_str_notclosed, endch == ?" ? "'\"'" : '"\'"')
3135
+ end
3136
+ if endch == ?"
3137
+ case ch
3138
+ when ?\\ ; s << "\\"
3139
+ when ?" ; s << "\""
3140
+ when ?n ; s << "\n"
3141
+ when ?r ; s << "\r"
3142
+ when ?t ; s << "\t"
3143
+ when ?b ; s << "\b"
3144
+ else ; s << "\\" << ch.chr
3145
+ end
3146
+ elsif endch == ?'
3147
+ case ch
3148
+ when ?\\ ; s << '\\'
3149
+ when ?' ; s << '\''
3150
+ else ; s << '\\' << ch.chr
3151
+ end
3152
+ end
3153
+ else
3154
+ s << ch.chr
3155
+ end
3156
+ end
3157
+ getchar()
3158
+ scalar = s
3159
+ else
3160
+ s = ch.chr
3161
+ while (ch = _getchar()) != nil && ch != ?: && ch != ?, && ch != ?] && ch != ?}
3162
+ s << ch.chr
3163
+ end
3164
+ scalar = to_scalar(s.strip)
3165
+ end
3166
+ return create_scalar(scalar)
3167
+ end
3168
+
3169
+
3170
+ def parse_tag(column, value)
3171
+ assert value =~ /^!\S+/
3172
+ value =~ /^!(\S+)((\s+)(.*))?$/
3173
+ tag = $1
3174
+ space = $3
3175
+ value2 = $4
3176
+ if value2 && !value2.empty?
3177
+ value_start_column = column + 1 + tag.length + space.length
3178
+ data = parse_value(column, value2, value_start_column)
3179
+ else
3180
+ data = parse_child(column)
3181
+ end
3182
+ return data
3183
+ end
3184
+
3185
+
3186
+ def parse_anchor(column, value)
3187
+ assert value =~ /^\&([-\w]+)(( *)(.*))?$/
3188
+ label = $1
3189
+ space = $3
3190
+ value2 = $4
3191
+ if value2 && !value2.empty?
3192
+ #column2 = column + 1 + label.length + space.length
3193
+ #data = parse_value(column2, value2)
3194
+ value_start_column = column + 1 + label.length + space.length
3195
+ data = parse_value(column, value2, value_start_column)
3196
+ else
3197
+ #column2 = column + 1
3198
+ #data = parse_child(column2)
3199
+ data = parse_child(column)
3200
+ end
3201
+ register_anchor(label, data)
3202
+ return data
3203
+ end
3204
+
3205
+ def register_anchor(label, data)
3206
+ if @anchors[label]
3207
+ #* key=:anchor_duplicated msg="anchor '%s' is already used."
3208
+ raise syntax_error(:anchor_duplicated, [label])
3209
+ end
3210
+ @anchors[label] = data
3211
+ end
3212
+
3213
+ def parse_alias(column, value)
3214
+ assert value =~ /^\*([-\w]+)(( *)(.*))?$/
3215
+ label = $1
3216
+ space = $3
3217
+ value2 = $4
3218
+ if value2 && !value2.empty? && value2[0] != ?\#
3219
+ #* key=:alias_extradata msg="alias cannot take any data."
3220
+ raise syntax_error(:alias_extradata)
3221
+ end
3222
+ data = @anchors[label]
3223
+ unless data
3224
+ data = register_alias(label)
3225
+ #raise syntax_error("anchor '#{label}' not found (cannot refer to backward or child anchor).")
3226
+ end
3227
+ getline()
3228
+ return data
3229
+ end
3230
+
3231
+ def register_alias(label)
3232
+ @aliases[label] ||= 0
3233
+ @aliases[label] += 1
3234
+ return Alias.new(label, @linenum)
3235
+ end
3236
+
3237
+
3238
+ def resolve_aliases(data)
3239
+ @resolved ||= {}
3240
+ return if @resolved[data.__id__]
3241
+ @resolved[data.__id__] = data
3242
+ case data
3243
+ when Array
3244
+ seq = data
3245
+ seq.each_with_index do |val, i|
3246
+ if val.is_a?(Alias)
3247
+ anchor = val
3248
+ if @anchors.key?(anchor.label)
3249
+ #seq[i] = @anchors[anchor.label]
3250
+ set_seq_at(seq, i, @anchors[anchor.label], anchor.linenum)
3251
+ else
3252
+ #* key=:anchor_notfound msg="anchor '%s' not found"
3253
+ raise syntax_error(:anchor_notfound, [anchor.label], val.linenum)
3254
+ end
3255
+ elsif val.is_a?(Array) || val.is_a?(Hash)
3256
+ resolve_aliases(val)
3257
+ end
3258
+ end
3259
+ when Hash
3260
+ map = data
3261
+ map.each do |key, val|
3262
+ if val.is_a?(Alias)
3263
+ if @anchors.key?(val.label)
3264
+ anchor = val
3265
+ #map[key] = @anchors[anchor.label]
3266
+ set_map_with(map, key, @anchors[anchor.label], anchor.linenum)
3267
+ else
3268
+ ## :anchor_notfound is already defined on above
3269
+ raise syntax_error(:anchor_notfound, [val.label], val.linenum)
3270
+ end
3271
+ elsif val.is_a?(Array) || val.is_a?(Hash)
3272
+ resolve_aliases(val)
3273
+ end
3274
+ end
3275
+ else
3276
+ assert !data.is_a?(Alias)
3277
+ end
3278
+ end
3279
+
3280
+
3281
+ def parse_block_text(column, value)
3282
+ assert value =~ /^[>|\|]/
3283
+ value =~ /^([>|\|])([-+]?)(\d+)?\s*(.*)$/
3284
+ char = $1
3285
+ indicator = $2
3286
+ sep = char == "|" ? "\n" : " "
3287
+ margin = $3 && !$3.empty? ? $3.to_i : nil
3288
+ #text = $4.empty? ? '' : $4 + sep
3289
+ text = $4
3290
+ s = ''
3291
+ empty = ''
3292
+ min_indent = -1
3293
+ while line = _getline()
3294
+ line =~ /^( *)(.*)/
3295
+ indent = $1.length
3296
+ if $2.empty?
3297
+ empty << "\n"
3298
+ elsif indent < column
3299
+ break
3300
+ else
3301
+ min_indent = indent if min_indent < 0 || min_indent > indent
3302
+ s << empty << line
3303
+ empty = ''
3304
+ end
3305
+ end
3306
+ s << empty if indicator == '+' && char != '>'
3307
+ s[-1] = "" if indicator == '-'
3308
+ min_indent = column + margin - 1 if margin
3309
+ if min_indent > 0
3310
+ sp = ' ' * min_indent
3311
+ s.gsub!(/^#{sp}/, '')
3312
+ end
3313
+ if char == '>'
3314
+ s.gsub!(/([^\n])\n([^\n])/, '\1 \2')
3315
+ s.gsub!(/\n(\n+)/, '\1')
3316
+ s << empty if indicator == '+'
3317
+ end
3318
+ getline() if current_line() =~ /^\s*\#/
3319
+ return create_scalar(text + s)
3320
+ end
3321
+
3322
+
3323
+ def parse_sequence(column, value)
3324
+ assert value =~ /^-(( +)(.*))?$/
3325
+ seq = create_sequence() # []
3326
+ while true
3327
+ unless value =~ /^-(( +)(.*))?$/
3328
+ #* key=:sequence_noitem msg="sequence item is expected."
3329
+ raise syntax_error(:sequence_noitem)
3330
+ end
3331
+ value2 = $3
3332
+ space = $2
3333
+ column2 = column + 1
3334
+ linenum = current_linenum()
3335
+ #
3336
+ if !value2 || value2.empty?
3337
+ elem = parse_child(column2)
3338
+ else
3339
+ value_start_column = column2 + space.length
3340
+ elem = parse_value(column2, value2, value_start_column)
3341
+ end
3342
+ add_to_seq(seq, elem, linenum) #seq << elem
3343
+ #
3344
+ line = current_line()
3345
+ break unless line
3346
+ line =~ /^( *)(.*)/
3347
+ indent = $1.length
3348
+ if indent < column
3349
+ break
3350
+ elsif indent > column
3351
+ #* key=:sequence_badindent msg="illegal indent of sequence."
3352
+ raise syntax_error(:sequence_badindent)
3353
+ end
3354
+ value = $2
3355
+ end
3356
+ return seq
3357
+ end
3358
+
3359
+
3360
+ def parse_mapping(column, value)
3361
+ #assert value =~ /^(:?["']?[-.\w]+["']? *):(( +)(.*))?$/ #'
3362
+ assert value =~ /^((?::?[-.\w]+\*?|'.*?'|".*?"|=|<<) *):(( +)(.*))?$/
3363
+ map = create_mapping() # {}
3364
+ while true
3365
+ #unless value =~ /^(:?["']?[-.\w]+["']? *):(( +)(.*))?$/ #'
3366
+ unless value =~ /^((?::?[-.\w]+\*?|'.*?'|".*?"|=|<<) *):(( +)(.*))?$/
3367
+ #* key=:mapping_noitem msg="mapping item is expected."
3368
+ raise syntax_error(:mapping_noitem)
3369
+ end
3370
+ v = $1.strip
3371
+ key = to_scalar(v)
3372
+ value2 = $4
3373
+ column2 = column + 1
3374
+ linenum = current_linenum()
3375
+ #
3376
+ if !value2 || value2.empty?
3377
+ elem = parse_child(column2)
3378
+ else
3379
+ value_start_column = column2 + $1.length + $3.length
3380
+ elem = parse_value(column2, value2, value_start_column)
3381
+ end
3382
+ case v
3383
+ when '='
3384
+ set_default(map, elem, linenum)
3385
+ when '<<'
3386
+ merge_map(map, elem, linenum)
3387
+ else
3388
+ add_to_map(map, key, elem, linenum) # map[key] = elem
3389
+ end
3390
+ #
3391
+ line = current_line()
3392
+ break unless line
3393
+ line =~ /^( *)(.*)/
3394
+ indent = $1.length
3395
+ if indent < column
3396
+ break
3397
+ elsif indent > column
3398
+ #* key=:mapping_badindent msg="illegal indent of mapping."
3399
+ raise syntax_error(:mapping_badindent)
3400
+ end
3401
+ value = $2
3402
+ end
3403
+ return map
3404
+ end
3405
+
3406
+
3407
+ def parse_scalar(indent, value)
3408
+ data = create_scalar(to_scalar(value))
3409
+ getline()
3410
+ return data
3411
+ end
3412
+
3413
+
3414
+ def to_scalar(str)
3415
+ case str
3416
+ when /^"(.*)"([ \t]*\#.*$)?/ ; return $1
3417
+ when /^'(.*)'([ \t]*\#.*$)?/ ; return $1
3418
+ when /^(.*\S)[ \t]*\#/ ; str = $1
3419
+ end
3420
+
3421
+ case str
3422
+ when /^-?\d+$/ ; return str.to_i # integer
3423
+ when /^-?\d+\.\d+$/ ; return str.to_f # float
3424
+ when "true", "yes", "on" ; return true # true
3425
+ when "false", "no", "off" ; return false # false
3426
+ when "null", "~" ; return nil # nil
3427
+ #when /^"(.*)"$/ ; return $1 # "string"
3428
+ #when /^'(.*)'$/ ; return $1 # 'string'
3429
+ when /^:(\w+)$/ ; return $1.intern # :symbol
3430
+ when /^(\d\d\d\d)-(\d\d)-(\d\d)$/ # date
3431
+ year, month, day = $1.to_i, $2.to_i, $3.to_i
3432
+ return Date.new(year, month, day)
3433
+ when /^(\d\d\d\d)-(\d\d)-(\d\d)(?:[Tt]|[ \t]+)(\d\d?):(\d\d):(\d\d)(\.\d*)?(?:Z|[ \t]*([-+]\d\d?)(?::(\d\d))?)?$/
3434
+ year, mon, mday, hour, min, sec, usec, tzone_h, tzone_m = $1, $2, $3, $4, $5, $6, $7, $8, $9
3435
+ #Time.utc(sec, min, hour, mday, mon, year, wday, yday, isdst, zone)
3436
+ #t = Time.utc(sec, min, hour, mday, mon, year, nil, nil, nil, nil)
3437
+ #Time.utc(year[, mon[, day[, hour[, min[, sec[, usec]]]]]])
3438
+ time = Time.utc(year, mon, day, hour, min, sec, usec)
3439
+ if tzone_h
3440
+ diff_sec = tzone_h.to_i * 60 * 60
3441
+ if tzone_m
3442
+ if diff_sec > 0 ; diff_sec += tzone_m.to_i * 60
3443
+ else ; diff_sec -= tzone_m.to_i * 60
3444
+ end
3445
+ end
3446
+ p diff_sec
3447
+ time -= diff_sec
3448
+ end
3449
+ return time
3450
+ end
3451
+ return str
3452
+ end
3453
+
3454
+
3455
+ def assert(bool_expr)
3456
+ raise "*** assertion error" unless bool_expr
3457
+ end
3458
+
3459
+ end
3460
+
3461
+
3462
+
3463
+ ##
3464
+ ## (OBSOLETE) yaml parser
3465
+ ##
3466
+ ## this class has been obsoleted. use Kwalify::Yaml::Parser instead.
3467
+ ##
3468
+ ## ex.
3469
+ ## # load document with YamlParser
3470
+ ## str = ARGF.read()
3471
+ ## parser = Kwalify::YamlParser.new(str)
3472
+ ## document = parser.parse()
3473
+ ##
3474
+ ## # validate document
3475
+ ## schema = YAML.load(File.read('schema.yaml'))
3476
+ ## validator = Kwalify::Validator.new(schema)
3477
+ ## errors = validator.validate(document)
3478
+ ##
3479
+ ## # print validation result
3480
+ ## if errors && !errors.empty?
3481
+ ## parser.set_errors_linenum(errors)
3482
+ ## errors.sort.each do |error|
3483
+ ## print "line %d: path %s: %s" % [error.linenum, error.path, error.message]
3484
+ ## end
3485
+ ## end
3486
+ ##
3487
+ class YamlParser < PlainYamlParser
3488
+
3489
+ def initialize(*args)
3490
+ super
3491
+ @linenums_table = {} # object_id -> hash or array
3492
+ end
3493
+
3494
+ def parse()
3495
+ @doc = super()
3496
+ return @doc
3497
+ end
3498
+
3499
+ def path_linenum(path)
3500
+ return 1 if path.empty? || path == '/'
3501
+ elems = path.split('/')
3502
+ elems.shift if path[0] == ?/ # delete empty string on head
3503
+ last_elem = elems.pop
3504
+ c = @doc # collection
3505
+ elems.each do |elem|
3506
+ if c.is_a?(Array)
3507
+ c = c[elem.to_i]
3508
+ elsif c.is_a?(Hash)
3509
+ c = c[elem]
3510
+ else
3511
+ assert false
3512
+ end
3513
+ end
3514
+ linenums = @linenums_table[c.__id__]
3515
+ if c.is_a?(Array)
3516
+ linenum = linenums[last_elem.to_i]
3517
+ elsif c.is_a?(Hash)
3518
+ linenum = linenums[last_elem]
3519
+ end
3520
+ return linenum
3521
+ end
3522
+
3523
+ def set_errors_linenum(errors)
3524
+ errors.each do |error|
3525
+ error.linenum = path_linenum(error.path)
3526
+ end
3527
+ end
3528
+
3529
+ def set_error_linenums(errors)
3530
+ warn "*** Kwalify::YamlParser#set_error_linenums() is obsolete. You should use set_errors_linenum() instead."
3531
+ set_errors_linenum(errors)
3532
+ end
3533
+
3534
+ protected
3535
+
3536
+ def create_sequence(linenum=current_linenum())
3537
+ seq = []
3538
+ @linenums_table[seq.__id__] = []
3539
+ return seq
3540
+ end
3541
+
3542
+ def add_to_seq(seq, value, linenum)
3543
+ seq << value
3544
+ @linenums_table[seq.__id__] << linenum
3545
+ end
3546
+
3547
+ def set_seq_at(seq, i, value, linenum)
3548
+ seq[i] = value
3549
+ @linenums_table[seq.__id__][i] = linenum
3550
+ end
3551
+
3552
+ def create_mapping(linenum=current_linenum())
3553
+ map = {}
3554
+ @linenums_table[map.__id__] = {}
3555
+ return map
3556
+ end
3557
+
3558
+ def add_to_map(map, key, value, linenum)
3559
+ map[key] = value
3560
+ @linenums_table[map.__id__][key] = linenum
3561
+ end
3562
+
3563
+ def set_map_with(map, key, value, linenum)
3564
+ map[key] = value
3565
+ @linenums_table[map.__id__][key] = linenum
3566
+ end
3567
+
3568
+ def set_default(map, value, linenum)
3569
+ map.default = value
3570
+ @linenums_table[map.__id__][:'='] = linenum
3571
+ end
3572
+
3573
+ def merge_map(map, collection, linenum)
3574
+ t = @linenums_table[map.__id__]
3575
+ list = collection.is_a?(Array) ? collection : [ collection ]
3576
+ list.each do |m|
3577
+ t2 = @linenums_table[m.__id__]
3578
+ m.each do |key, val|
3579
+ unless map.key?(key)
3580
+ map[key] = val
3581
+ t[key] = t2[key]
3582
+ end
3583
+ end
3584
+ end
3585
+ end
3586
+
3587
+ def create_scalar(value, linenum=current_linenum())
3588
+ data = super(value)
3589
+ #return Scalar.new(data, linenum)
3590
+ return data
3591
+ end
3592
+
3593
+ end
3594
+
3595
+
3596
+ ## alias of YamlParser class
3597
+ class Parser < YamlParser
3598
+ def initialize(yaml_str)
3599
+ super(yaml_str)
3600
+ #warn "*** class Kwalify::Parser is obsolete. Please use Kwalify::YamlParser instead."
3601
+ end
3602
+ end
3603
+
3604
+
3605
+ end
3606
+ #--end of require 'kwalify/yaml-parser'
3607
+ #require 'kwalify/parser/base'
3608
+ #require 'kwalify/parser/yaml'
3609
+
3610
+
3611
+ module Kwalify
3612
+
3613
+ module Util
3614
+
3615
+ autoload :HashLike, 'kwalify/util/hashlike'
3616
+
3617
+ end
3618
+
3619
+ module Yaml
3620
+
3621
+ autoload :Parser, 'kwalify/parser/yaml'
3622
+
3623
+ ## read yaml_str, parse it, and return yaml document.
3624
+ ##
3625
+ ## opts:
3626
+ ## ::validator: Kwalify::Validator object
3627
+ ## ::preceding_aliass: allow preceding alias if true
3628
+ ## ::data_binding: enable data binding if true
3629
+ ## ::untabify: expand tab chars if true
3630
+ ## ::filename: filename
3631
+ def self.load(yaml_str, opts={})
3632
+ #require 'kwalify/parser/yaml'
3633
+ parser = Kwalify::Yaml::Parser.new(opts[:validator])
3634
+ parser.preceding_alias = true if opts[:preceding_alias]
3635
+ parser.data_binding = true if opts[:data_binding]
3636
+ yaml_str = Kwalify::Util.untabify(yaml_str) if opts[:untabify]
3637
+ ydoc = parser.parse(yaml_str, :filename=>opts[:filename])
3638
+ return ydoc
3639
+ end
3640
+
3641
+ ## read file, parse it, and return yaml document.
3642
+ ## see Kwalify::Yaml::Parser.load()
3643
+ def self.load_file(filename, opts={})
3644
+ opts[:filename] = filename
3645
+ return self.load(File.read(filename), opts)
3646
+ end
3647
+
3648
+ end
3649
+
3650
+ module Json
3651
+ end
3652
+
3653
+ end
3654
+ #--end of require 'kwalify'
3655
+ #--begin of require 'kwalify/main'
3656
+ ###
3657
+ ### $Rev: 95 $
3658
+ ### $Release: 0.7.1 $
3659
+ ### copyright(c) 2005-2008 kuwata-lab all rights reserved.
3660
+ ###
3661
+
3662
+ require 'yaml'
3663
+ require 'erb'
3664
+ #--already included require 'kwalify'
3665
+ #--already included require 'kwalify/util'
3666
+ #--begin of require 'kwalify/util/ordered-hash'
3667
+ ###
3668
+ ### $Rev: 84 $
3669
+ ### 0.7.1
3670
+ ### $COPYRIGHT$
3671
+ ###
3672
+
3673
+ module Kwalify
3674
+
3675
+ module Util
3676
+
3677
+ class OrderedHash < Hash
3678
+
3679
+ def initialize(*args, &block)
3680
+ super
3681
+ @_keys = []
3682
+ end
3683
+
3684
+ alias __set__ []=
3685
+
3686
+ def put(key, val)
3687
+ @_keys << key unless self.key?(key)
3688
+ __set__(key, val)
3689
+ end
3690
+
3691
+ def add(key, val)
3692
+ @_keys.delete_at(@_keys.index(key)) if self.key?(key)
3693
+ @_keys << key
3694
+ __set__(key, val)
3695
+ end
3696
+
3697
+ alias []= put
3698
+ #alias []= add
3699
+
3700
+ def keys
3701
+ return @_keys.dup
3702
+ end
3703
+
3704
+ def values
3705
+ return @_keys.collect {|key| self[key] }
3706
+ end
3707
+
3708
+ def delete(key)
3709
+ @_keys.delete_at(@_keys.index(key)) if self.key?(key)
3710
+ super
3711
+ end
3712
+
3713
+ def each
3714
+ @_keys.each do |key|
3715
+ yield key, self[key]
3716
+ end
3717
+ end
3718
+
3719
+ end
3720
+
3721
+ end
3722
+
3723
+ end
3724
+ #--end of require 'kwalify/util/ordered-hash'
3725
+
3726
+
3727
+ module Kwalify
3728
+
3729
+
3730
+ class CommandOptionError < KwalifyError
3731
+ def initialize(message, option, error_symbol)
3732
+ super(message)
3733
+ @option = option
3734
+ @error_symbol = error_symbol
3735
+ end
3736
+ attr_reader :option, :error_symbol
3737
+ end
3738
+
3739
+
3740
+ ##
3741
+ ## ex.
3742
+ ## command = File.basename($0)
3743
+ ## begin
3744
+ ## main = Kwalify::Main.new(command)
3745
+ ## s = main.execute
3746
+ ## print s if s
3747
+ ## rescue Kwalify::CommandOptionError => ex
3748
+ ## $stderr.puts "ERROR: #{ex.message}"
3749
+ ## exit 1
3750
+ ## rescue Kwalify::KwalifyError => ex
3751
+ ## $stderr.puts "ERROR: #{ex.message}"
3752
+ ## exit 1
3753
+ ## end
3754
+ ##
3755
+ class Main
3756
+
3757
+
3758
+ def initialize(command=nil)
3759
+ @command = command || File.basename($0)
3760
+ @options = {}
3761
+ @properties = {}
3762
+ @template_path = []
3763
+ $:.each do |path|
3764
+ tpath = "#{path}/kwalify/templates"
3765
+ @template_path << tpath if test(?d, tpath)
3766
+ end
3767
+ end
3768
+
3769
+
3770
+ def debug?
3771
+ @options[:debug]
3772
+ end
3773
+
3774
+
3775
+ def _inspect()
3776
+ sb = []
3777
+ sb << "command: #{@command}\n"
3778
+ sb << "options:\n"
3779
+ @options.keys.sort {|k1,k2| k1.to_s<=>k2.to_s }.each do |key|
3780
+ sb << " - #{key}: #{@options[key]}\n"
3781
+ end
3782
+ sb << "properties:\n"
3783
+ @properties.keys.sort_by {|k| k.to_s}.each do |key|
3784
+ sb << " - #{key}: #{@properties[key]}\n"
3785
+ end
3786
+ #sb << "template_path:\n"
3787
+ #@template_path.each do |path|
3788
+ # sb << " - #{path}\n"
3789
+ #end
3790
+ return sb.join
3791
+ end
3792
+
3793
+
3794
+ def execute(argv=ARGV)
3795
+ ## parse command-line options
3796
+ filenames = _parse_argv(argv)
3797
+
3798
+ ## help or version
3799
+ if @options[:help] || @options[:version]
3800
+ action = @options[:action]
3801
+ s = ''
3802
+ s << _version() << "\n" if @options[:version]
3803
+ s << _usage() if @options[:help] && !action
3804
+ s << _describe_properties(action) if @options[:help] && action
3805
+ puts s
3806
+ return
3807
+ end
3808
+
3809
+ # validation
3810
+ if @options[:meta2]
3811
+ validate_schemafiles2(filenames)
3812
+ elsif @options[:meta]
3813
+ validate_schemafiles(filenames)
3814
+ elsif @options[:action]
3815
+ unless @options[:schema]
3816
+ #* key=:command_option_actionnoschema msg="schema filename is not specified."
3817
+ raise option_error(:command_option_actionnoschema, @options[:action])
3818
+ end
3819
+ perform_action(@options[:action], @options[:schema])
3820
+ elsif @options[:schema]
3821
+ if @options[:debug]
3822
+ inspect_schema(@options[:schema])
3823
+ else
3824
+ validate_files(filenames, @options[:schema])
3825
+ end
3826
+ else
3827
+ #* key=:command_option_noaction msg="command-line option '-f' or '-m' required."
3828
+ raise option_error(:command_option_noaction, @command)
3829
+ end
3830
+ return
3831
+ end
3832
+
3833
+
3834
+ def self.main(command, argv=ARGV)
3835
+ begin
3836
+ main = Kwalify::Main.new(command)
3837
+ s = main.execute(argv)
3838
+ print s if s
3839
+ rescue Kwalify::CommandOptionError => ex
3840
+ raise ex if main.debug?
3841
+ $stderr.puts ex.message
3842
+ exit 1
3843
+ rescue Kwalify::KwalifyError => ex
3844
+ raise ex if main.debug?
3845
+ $stderr.puts "ERROR: #{ex.to_s}"
3846
+ exit 1
3847
+ #rescue => ex
3848
+ # if main.debug?
3849
+ # raise ex
3850
+ # else
3851
+ # $stderr.puts ex.message
3852
+ # exit 1
3853
+ # end
3854
+ end
3855
+ end
3856
+
3857
+
3858
+ private
3859
+
3860
+
3861
+ def option_error(error_symbol, arg)
3862
+ msg = Kwalify.msg(error_symbol) % arg
3863
+ return CommandOptionError.new(msg, arg, error_symbol)
3864
+ end
3865
+
3866
+
3867
+ def _find_template(action)
3868
+ template_filename = action + '.eruby'
3869
+ unless test(?f, template_filename)
3870
+ pathlist = []
3871
+ pathlist.concat(@options[:tpath].split(/,/)) if @options[:tpath]
3872
+ pathlist.concat(@template_path)
3873
+ tpath = pathlist.find {|path| test(?f, "#{path}/#{template_filename}") }
3874
+ #* key=:command_option_notemplate msg="%s: invalid action (template not found).\n"
3875
+ raise option_error(:command_option_notemplate, action) unless tpath
3876
+ template_filename = "#{tpath}/#{action}.eruby"
3877
+ end
3878
+ return template_filename
3879
+ end
3880
+
3881
+
3882
+ def apply_template(template_filename, hash)
3883
+ template = File.read(template_filename)
3884
+ trim_mode = 1
3885
+ erb = ERB.new(template, nil, trim_mode)
3886
+ context = Object.new
3887
+ hash.each do |key, val|
3888
+ context.instance_variable_set("@#{key}", val)
3889
+ end
3890
+ s = context.instance_eval(erb.src, template_filename)
3891
+ return s
3892
+ end
3893
+
3894
+
3895
+ def _describe_properties(action)
3896
+ template_filename = _find_template(action)
3897
+ s = apply_template(template_filename, :describe=>true)
3898
+ return s
3899
+ end
3900
+
3901
+
3902
+ def perform_action(action, schema_filename, describe=false)
3903
+ template_filename = _find_template(action)
3904
+ schema = _load_schemafile(schema_filename, ordered=true)
3905
+ validator = Kwalify::Validator.new(schema)
3906
+ @properties[:schema_filename] = schema_filename
3907
+ hash = { :validator=>validator, :schema=>schema, :properties=>@properties }
3908
+ s = apply_template(template_filename, hash)
3909
+ puts s if s && !s.empty?
3910
+ end
3911
+
3912
+
3913
+ def inspect_schema(schema_filename)
3914
+ schema = _load_schemafile(schema_filename)
3915
+ if schema.nil?
3916
+ puts "nil"
3917
+ else
3918
+ validator = Kwalify::Validator.new(schema) # error raised when schema is wrong
3919
+ puts validator._inspect()
3920
+ end
3921
+ end
3922
+
3923
+
3924
+ ## -f schemafile datafile
3925
+ def validate_files(filenames, schema_filename)
3926
+ schema = _load_schemafile(schema_filename)
3927
+ validator = Kwalify::Validator.new(schema)
3928
+ _validate_files(validator, filenames)
3929
+ end
3930
+
3931
+
3932
+ def _load_schemafile(schema_filename, ordered=false)
3933
+ str = File.read(schema_filename)
3934
+ if str.empty?
3935
+ #* key=:schema_empty msg="%s: empty schema.\n"
3936
+ msg = Kwalify.msg(:schema_emtpy) % filename
3937
+ raise CommandOptionError.new(msg)
3938
+ end
3939
+ str = Util.untabify(str) if @options[:untabify]
3940
+ parser = Kwalify::Yaml::Parser.new()
3941
+ parser.preceding_alias = true if @options[:preceding]
3942
+ parser.mapping_class = Kwalify::Util::OrderedHash if ordered
3943
+ schema = parser.parse(str, :filename=>schema_filename) # or YAML.load(str)
3944
+ return schema
3945
+ end
3946
+
3947
+
3948
+ ## -m schemafile
3949
+ def validate_schemafiles(schema_filenames)
3950
+ meta_validator = Kwalify::MetaValidator.instance()
3951
+ _validate_files(meta_validator, schema_filenames)
3952
+ end
3953
+
3954
+
3955
+ ## -M schemafile
3956
+ def validate_schemafiles2(schema_filenames)
3957
+ parser = Kwalify::Yaml::Parser.new()
3958
+ parser.preceding_alias = true if @options[:preceding]
3959
+ for schema_filename in schema_filenames
3960
+ str = File.read(schema_filename)
3961
+ str = Util.untabify(str) if @options[:untabify]
3962
+ schema = parser.parse(str, :filename=>schema_filename)
3963
+ Kwalify::Validator.new(schema) # exception raised when schema has errors
3964
+ end
3965
+ end
3966
+
3967
+
3968
+ def _validate_files(validator, filenames)
3969
+ ## parser
3970
+ if @options[:linenum] || @options[:preceding]
3971
+ parser = Kwalify::Yaml::Parser.new(validator)
3972
+ parser.preceding_alias = true if @options[:preceding]
3973
+ else
3974
+ parser = nil
3975
+ end
3976
+ ## filenames
3977
+ if filenames.empty?
3978
+ filenames = [ $stdin ]
3979
+ end
3980
+ for filename in filenames
3981
+ ## read input
3982
+ if filename.is_a?(IO)
3983
+ input = filename.read()
3984
+ filename = '(stdin)'
3985
+ else
3986
+ input = File.read(filename)
3987
+ end
3988
+ if input.empty?
3989
+ #* key=:validation_empty msg="%s#%d: empty."
3990
+ puts kwalify.msg(:validation_empty) % [filename, i]
3991
+ #puts "#{filename}##{i}: empty."
3992
+ next
3993
+ end
3994
+ input = Util.untabify(input) if @options[:untabify]
3995
+ ## parse input
3996
+ if parser
3997
+ #i = 0
3998
+ #ydoc = parser.parse(input, :filename=>filename)
3999
+ #_show_errors(filename, i, ydoc, parser.errors)
4000
+ #while parser.has_next?
4001
+ # i += 1
4002
+ # ydoc = parser.parse_next()
4003
+ # _show_errors(filename, i, ydoc, parser.errors)
4004
+ #end
4005
+ i = 0
4006
+ parser.parse_stream(input, :filename=>filename) do |ydoc|
4007
+ _show_errors(filename, i, ydoc, parser.errors)
4008
+ i += 1
4009
+ end
4010
+ else
4011
+ i = 0
4012
+ YAML.load_documents(input) do |ydoc|
4013
+ errors = validator.validate(ydoc)
4014
+ _show_errors(filename, i, ydoc, errors)
4015
+ i += 1
4016
+ end
4017
+ end
4018
+ end
4019
+ end
4020
+
4021
+
4022
+ def _show_errors(filename, i, ydoc, errors, ok_label="valid.", ng_label="INVALID")
4023
+ if errors && !errors.empty?
4024
+ puts "#{filename}##{i}: #{ng_label}"
4025
+ errors.sort!
4026
+ for error in errors
4027
+ e = error
4028
+ if @options[:emacs]
4029
+ raise unless @options[:linenum]
4030
+ puts "#{filename}:#{e.linenum}:#{e.column} [#{e.path}] #{e.message}\n"
4031
+ elsif @options[:linenum]
4032
+ puts " - (line #{e.linenum}) [#{e.path}] #{e.message}\n"
4033
+ else
4034
+ puts " - [#{e.path}] #{e.message}\n"
4035
+ end
4036
+ end
4037
+ elsif ydoc.nil?
4038
+ #* key=:validation_empty msg="%s#%d: empty.\n"
4039
+ puts kwalify.msg(:validation_empty) % [filename, i]
4040
+ else
4041
+ #* key=:validation_valid msg="%s#%d: valid."
4042
+ puts Kwalify.msg(:validation_valid) % [filename, i] unless @options[:quiet]
4043
+ #puts "#{filename}##{i} - #{ok_label}" unless @options[:quiet]
4044
+ end
4045
+ end
4046
+
4047
+
4048
+ def _usage()
4049
+ #msg = Kwalify.msg(:command_help) % [@command, @command, @command]
4050
+ msg = Kwalify.msg(:command_help)
4051
+ return msg
4052
+ end
4053
+
4054
+
4055
+ def _version()
4056
+ return RELEASE
4057
+ end
4058
+
4059
+
4060
+ def _to_value(str)
4061
+ case str
4062
+ when nil, "null", "nil" ; return nil
4063
+ when "true", "yes" ; return true
4064
+ when "false", "no" ; return false
4065
+ when /\A\d+\z/ ; return str.to_i
4066
+ when /\A\d+\.\d+\z/ ; return str.to_f
4067
+ when /\/(.*)\// ; return Regexp.new($1)
4068
+ when /\A'.*'\z/, /\A".*"\z/ ; return eval(str)
4069
+ else ; return str
4070
+ end
4071
+ end
4072
+
4073
+
4074
+ def _parse_argv(argv)
4075
+ option_table = {
4076
+ ?h => :help,
4077
+ ?v => :version,
4078
+ ?q => :quiet,
4079
+ ?s => :quiet,
4080
+ ?t => :untabify,
4081
+ #?z => :meta,
4082
+ ?m => :meta,
4083
+ ?M => :meta2,
4084
+ ?E => :emacs,
4085
+ ?l => :linenum,
4086
+ ?f => :schema,
4087
+ ?D => :debug,
4088
+ ?a => :action,
4089
+ ?I => :tpath,
4090
+ ?P => :preceding,
4091
+ }
4092
+
4093
+ errcode_table = {
4094
+ #* key=:command_option_schema_required msg="-%s: schema filename required."
4095
+ ?f => :command_option_schema_required,
4096
+ #* key=:command_option_action_required msg="-%s: action required."
4097
+ ?a => :command_option_action_required,
4098
+ #* key=:command_option_tpath_required msg="-%s: template path required."
4099
+ ?I => :command_option_tpath_required,
4100
+ }
4101
+
4102
+ while argv[0] && argv[0][0] == ?-
4103
+ optstr = argv.shift
4104
+ optstr = optstr[1, optstr.length-1]
4105
+ ## property
4106
+ if optstr[0] == ?-
4107
+ unless optstr =~ /\A\-([-\w]+)(?:=(.*))?\z/
4108
+ #* key=:command_property_invalid msg="%s: invalid property."
4109
+ raise option_error(:command_property_invalid, optstr)
4110
+ end
4111
+ prop_name = $1; prop_value = $2
4112
+ key = prop_name.gsub(/-/, '_').intern
4113
+ value = prop_value.nil? ? true : _to_value(prop_value)
4114
+ @properties[key] = value
4115
+ ## option
4116
+ else
4117
+ while optstr && !optstr.empty?
4118
+ optchar = optstr[0]
4119
+ optstr[0,1] = ""
4120
+ unless option_table.key?(optchar)
4121
+ #* key=:command_option_invalid msg="-%s: invalid command option."
4122
+ raise option_error(:command_option_invalid, optchar.chr)
4123
+ end
4124
+ optkey = option_table[optchar]
4125
+ case optchar
4126
+ when ?f, ?a, ?I
4127
+ arg = optstr.empty? ? argv.shift : optstr
4128
+ raise option_error(errcode_table[optchar], optchar.chr) unless arg
4129
+ @options[optkey] = arg
4130
+ optstr = ''
4131
+ else
4132
+ @options[optkey] = true
4133
+ end
4134
+ end
4135
+ end
4136
+ end # end of while
4137
+ #
4138
+ @options[:linenum] = true if @options[:emacs]
4139
+ @options[:help] = true if @properties[:help]
4140
+ @options[:version] = true if @properties[:version]
4141
+ filenames = argv
4142
+ return filenames
4143
+ end
4144
+
4145
+
4146
+ def _domain_type?(doc)
4147
+ klass = defined?(YAML::DomainType) ? YAML::DomainType : YAML::Syck::DomainType
4148
+ return doc.is_a?(klass)
4149
+ end
4150
+
4151
+
4152
+ end
4153
+
4154
+
4155
+ end
4156
+ #--end of require 'kwalify/main'
4157
+
4158
+ command = File.basename($0)
4159
+ Kwalify::Main.main(command, ARGV)