dc-kwalify 0.7.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (209) hide show
  1. data/CHANGES.txt +252 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.txt +61 -0
  4. data/bin/kwalify +13 -0
  5. data/contrib/inline-require +179 -0
  6. data/contrib/kwalify +4160 -0
  7. data/doc/docstyle.css +188 -0
  8. data/doc/img/fig01.png +0 -0
  9. data/doc/users-guide.html +2050 -0
  10. data/doc-api/classes/CommandOptionError.html +184 -0
  11. data/doc-api/classes/CommandOptionParser.html +325 -0
  12. data/doc-api/classes/Kwalify/AssertionError.html +148 -0
  13. data/doc-api/classes/Kwalify/BaseError.html +297 -0
  14. data/doc-api/classes/Kwalify/BaseParser.html +461 -0
  15. data/doc-api/classes/Kwalify/CommandOptionError.html +168 -0
  16. data/doc-api/classes/Kwalify/ErrorHelper.html +223 -0
  17. data/doc-api/classes/Kwalify/HashInterface.html +118 -0
  18. data/doc-api/classes/Kwalify/Json.html +105 -0
  19. data/doc-api/classes/Kwalify/KwalifyError.html +111 -0
  20. data/doc-api/classes/Kwalify/Main.html +339 -0
  21. data/doc-api/classes/Kwalify/MetaValidator.html +448 -0
  22. data/doc-api/classes/Kwalify/Parser.html +155 -0
  23. data/doc-api/classes/Kwalify/PlainYamlParser/Alias.html +165 -0
  24. data/doc-api/classes/Kwalify/PlainYamlParser.html +523 -0
  25. data/doc-api/classes/Kwalify/Rule.html +433 -0
  26. data/doc-api/classes/Kwalify/SchemaError.html +148 -0
  27. data/doc-api/classes/Kwalify/SyntaxError.html +185 -0
  28. data/doc-api/classes/Kwalify/Types.html +302 -0
  29. data/doc-api/classes/Kwalify/Util/HashLike.html +246 -0
  30. data/doc-api/classes/Kwalify/Util/OrderedHash.html +330 -0
  31. data/doc-api/classes/Kwalify/Util.html +390 -0
  32. data/doc-api/classes/Kwalify/ValidationError.html +148 -0
  33. data/doc-api/classes/Kwalify/Validator.html +381 -0
  34. data/doc-api/classes/Kwalify/Yaml/Parser.html +1538 -0
  35. data/doc-api/classes/Kwalify/Yaml.html +194 -0
  36. data/doc-api/classes/Kwalify/YamlParser.html +542 -0
  37. data/doc-api/classes/Kwalify/YamlSyntaxError.html +119 -0
  38. data/doc-api/classes/Kwalify.html +292 -0
  39. data/doc-api/classes/Test/Unit.html +101 -0
  40. data/doc-api/classes/Test.html +107 -0
  41. data/doc-api/created.rid +1 -0
  42. data/doc-api/files/__/README_txt.html +172 -0
  43. data/doc-api/files/kwalify/errors_rb.html +114 -0
  44. data/doc-api/files/kwalify/main_rb.html +118 -0
  45. data/doc-api/files/kwalify/messages_rb.html +107 -0
  46. data/doc-api/files/kwalify/meta-validator_rb.html +117 -0
  47. data/doc-api/files/kwalify/parser/base_rb.html +116 -0
  48. data/doc-api/files/kwalify/parser/yaml_rb.html +117 -0
  49. data/doc-api/files/kwalify/rule_rb.html +116 -0
  50. data/doc-api/files/kwalify/types_rb.html +114 -0
  51. data/doc-api/files/kwalify/util/assert-text-equal_rb.html +115 -0
  52. data/doc-api/files/kwalify/util/hash-interface_rb.html +114 -0
  53. data/doc-api/files/kwalify/util/hashlike_rb.html +107 -0
  54. data/doc-api/files/kwalify/util/option-parser_rb.html +107 -0
  55. data/doc-api/files/kwalify/util/ordered-hash_rb.html +107 -0
  56. data/doc-api/files/kwalify/util/testcase-helper_rb.html +115 -0
  57. data/doc-api/files/kwalify/util_rb.html +107 -0
  58. data/doc-api/files/kwalify/validator_rb.html +117 -0
  59. data/doc-api/files/kwalify/yaml-parser_rb.html +117 -0
  60. data/doc-api/files/kwalify_rb.html +121 -0
  61. data/doc-api/fr_class_index.html +57 -0
  62. data/doc-api/fr_file_index.html +45 -0
  63. data/doc-api/fr_method_index.html +168 -0
  64. data/doc-api/index.html +24 -0
  65. data/doc-api/rdoc-style.css +208 -0
  66. data/examples/address-book/Makefile +10 -0
  67. data/examples/address-book/address-book.schema.yaml +45 -0
  68. data/examples/address-book/address-book.yaml +36 -0
  69. data/examples/data-binding/BABEL.data.yaml +63 -0
  70. data/examples/data-binding/BABEL.schema.yaml +31 -0
  71. data/examples/data-binding/Makefile +8 -0
  72. data/examples/data-binding/Rakefile +13 -0
  73. data/examples/data-binding/main.rb +27 -0
  74. data/examples/invoice/Makefile +9 -0
  75. data/examples/invoice/invoice.schema.yaml +43 -0
  76. data/examples/invoice/invoice.yaml +32 -0
  77. data/examples/tapkit/Makefile +10 -0
  78. data/examples/tapkit/main.rb +7 -0
  79. data/examples/tapkit/tapkit.schema.yaml +146 -0
  80. data/examples/tapkit/tapkit.yaml +85 -0
  81. data/lib/kwalify/errors.rb +127 -0
  82. data/lib/kwalify/kwalify.schema.yaml +58 -0
  83. data/lib/kwalify/main.rb +442 -0
  84. data/lib/kwalify/messages.rb +173 -0
  85. data/lib/kwalify/meta-validator.rb +275 -0
  86. data/lib/kwalify/parser/base.rb +127 -0
  87. data/lib/kwalify/parser/yaml.rb +841 -0
  88. data/lib/kwalify/rule.rb +559 -0
  89. data/lib/kwalify/templates/genclass-java.eruby +222 -0
  90. data/lib/kwalify/templates/genclass-php.eruby +104 -0
  91. data/lib/kwalify/templates/genclass-ruby.eruby +113 -0
  92. data/lib/kwalify/types.rb +156 -0
  93. data/lib/kwalify/util/assert-text-equal.rb +46 -0
  94. data/lib/kwalify/util/hash-interface.rb +18 -0
  95. data/lib/kwalify/util/hashlike.rb +51 -0
  96. data/lib/kwalify/util/option-parser.rb +220 -0
  97. data/lib/kwalify/util/ordered-hash.rb +57 -0
  98. data/lib/kwalify/util/testcase-helper.rb +112 -0
  99. data/lib/kwalify/util.rb +158 -0
  100. data/lib/kwalify/validator.rb +282 -0
  101. data/lib/kwalify/yaml-parser.rb +870 -0
  102. data/lib/kwalify.rb +67 -0
  103. data/setup.rb +1585 -0
  104. data/test/Rookbook.yaml +10 -0
  105. data/test/data/users-guide/AddressBook.java.expected +40 -0
  106. data/test/data/users-guide/BABEL.data.yaml +24 -0
  107. data/test/data/users-guide/BABEL.schema.yaml +30 -0
  108. data/test/data/users-guide/ExampleAddressBook.java +47 -0
  109. data/test/data/users-guide/Group.java.expected +24 -0
  110. data/test/data/users-guide/Person.java.expected +44 -0
  111. data/test/data/users-guide/address_book.rb +52 -0
  112. data/test/data/users-guide/address_book.schema.yaml +28 -0
  113. data/test/data/users-guide/address_book.yaml +27 -0
  114. data/test/data/users-guide/answers-schema.yaml +12 -0
  115. data/test/data/users-guide/answers-validator.rb +52 -0
  116. data/test/data/users-guide/babel_genclass.result +26 -0
  117. data/test/data/users-guide/config.schema.yaml +7 -0
  118. data/test/data/users-guide/config.yaml +4 -0
  119. data/test/data/users-guide/document01a.yaml +3 -0
  120. data/test/data/users-guide/document01b.yaml +3 -0
  121. data/test/data/users-guide/document02a.yaml +4 -0
  122. data/test/data/users-guide/document02b.yaml +4 -0
  123. data/test/data/users-guide/document03a.yaml +6 -0
  124. data/test/data/users-guide/document03b.yaml +6 -0
  125. data/test/data/users-guide/document04a.yaml +9 -0
  126. data/test/data/users-guide/document04b.yaml +9 -0
  127. data/test/data/users-guide/document05a.yaml +11 -0
  128. data/test/data/users-guide/document05b.yaml +12 -0
  129. data/test/data/users-guide/document06a.yaml +15 -0
  130. data/test/data/users-guide/document06b.yaml +16 -0
  131. data/test/data/users-guide/document07a.yaml +9 -0
  132. data/test/data/users-guide/document07b.yaml +7 -0
  133. data/test/data/users-guide/document12a.json +10 -0
  134. data/test/data/users-guide/document12b.json +6 -0
  135. data/test/data/users-guide/document13a.yaml +17 -0
  136. data/test/data/users-guide/document14a.yaml +3 -0
  137. data/test/data/users-guide/document14b.yaml +3 -0
  138. data/test/data/users-guide/document15a.yaml +6 -0
  139. data/test/data/users-guide/document15b.yaml +5 -0
  140. data/test/data/users-guide/example_address_book.rb +10 -0
  141. data/test/data/users-guide/example_address_book_java.result +32 -0
  142. data/test/data/users-guide/example_address_book_ruby.result +31 -0
  143. data/test/data/users-guide/genclass_java.result +4 -0
  144. data/test/data/users-guide/howto-validation-with-parsing.rb +28 -0
  145. data/test/data/users-guide/howto-validation.rb +25 -0
  146. data/test/data/users-guide/howto3.rb +6 -0
  147. data/test/data/users-guide/howto3.result +5 -0
  148. data/test/data/users-guide/howto3.yaml +8 -0
  149. data/test/data/users-guide/howto5_databinding.result +111 -0
  150. data/test/data/users-guide/invalid01.result +3 -0
  151. data/test/data/users-guide/invalid02.result +5 -0
  152. data/test/data/users-guide/invalid03.result +5 -0
  153. data/test/data/users-guide/invalid04.result +4 -0
  154. data/test/data/users-guide/invalid05.result +11 -0
  155. data/test/data/users-guide/invalid06.result +4 -0
  156. data/test/data/users-guide/invalid07.result +3 -0
  157. data/test/data/users-guide/invalid08.result +3 -0
  158. data/test/data/users-guide/invalid12.json +8 -0
  159. data/test/data/users-guide/invalid14.result +4 -0
  160. data/test/data/users-guide/invalid15.result +4 -0
  161. data/test/data/users-guide/loadbabel.rb +27 -0
  162. data/test/data/users-guide/loadconfig.rb +16 -0
  163. data/test/data/users-guide/loadconfig.result +6 -0
  164. data/test/data/users-guide/models.rb +22 -0
  165. data/test/data/users-guide/option_ha.result +6 -0
  166. data/test/data/users-guide/option_ha_genclass_java.result +7 -0
  167. data/test/data/users-guide/schema01.yaml +3 -0
  168. data/test/data/users-guide/schema02.yaml +12 -0
  169. data/test/data/users-guide/schema03.yaml +9 -0
  170. data/test/data/users-guide/schema04.yaml +20 -0
  171. data/test/data/users-guide/schema05.yaml +29 -0
  172. data/test/data/users-guide/schema06.yaml +11 -0
  173. data/test/data/users-guide/schema12.json +12 -0
  174. data/test/data/users-guide/schema13.yaml +13 -0
  175. data/test/data/users-guide/schema14.yaml +5 -0
  176. data/test/data/users-guide/schema15.yaml +21 -0
  177. data/test/data/users-guide/valid01.result +2 -0
  178. data/test/data/users-guide/valid02.result +2 -0
  179. data/test/data/users-guide/valid03.result +2 -0
  180. data/test/data/users-guide/valid04.result +2 -0
  181. data/test/data/users-guide/valid05.result +2 -0
  182. data/test/data/users-guide/valid06.result +2 -0
  183. data/test/data/users-guide/valid07.result +2 -0
  184. data/test/data/users-guide/valid08.result +2 -0
  185. data/test/data/users-guide/valid12.result +2 -0
  186. data/test/data/users-guide/valid13.result +2 -0
  187. data/test/data/users-guide/valid14.result +2 -0
  188. data/test/data/users-guide/valid15.result +2 -0
  189. data/test/data/users-guide/validate08.rb +37 -0
  190. data/test/test-action.rb +78 -0
  191. data/test/test-action.yaml +738 -0
  192. data/test/test-databinding.rb +83 -0
  193. data/test/test-databinding.yaml +339 -0
  194. data/test/test-main.rb +157 -0
  195. data/test/test-main.yaml +415 -0
  196. data/test/test-metavalidator.rb +80 -0
  197. data/test/test-metavalidator.yaml +1179 -0
  198. data/test/test-parser-yaml.rb +57 -0
  199. data/test/test-parser-yaml.yaml +1749 -0
  200. data/test/test-rule.rb +26 -0
  201. data/test/test-rule.yaml +317 -0
  202. data/test/test-users-guide.rb +75 -0
  203. data/test/test-util.rb +125 -0
  204. data/test/test-validator.rb +95 -0
  205. data/test/test-validator.yaml +986 -0
  206. data/test/test-yaml-parser.rb +47 -0
  207. data/test/test-yaml-parser.yaml +1226 -0
  208. data/test/test.rb +61 -0
  209. metadata +274 -0
@@ -0,0 +1,559 @@
1
+ ###
2
+ ### $Rev$
3
+ ### $Release: 0.7.2 $
4
+ ### copyright(c) 2005-2010 kuwata-lab all rights reserved.
5
+ ###
6
+
7
+ require 'kwalify/messages'
8
+ require 'kwalify/errors'
9
+ require 'kwalify/types'
10
+
11
+
12
+ module Kwalify
13
+
14
+
15
+ class Rule
16
+ include Kwalify::ErrorHelper
17
+
18
+ attr_accessor :parent
19
+ attr_reader :name
20
+ attr_reader :desc
21
+ attr_reader :enum
22
+ attr_reader :required
23
+ attr_reader :type
24
+ attr_reader :type_class
25
+ attr_reader :pattern
26
+ attr_reader :regexp
27
+ attr_reader :sequence
28
+ attr_reader :mapping
29
+ attr_reader :assert
30
+ attr_reader :assert_proc
31
+ attr_reader :range
32
+ attr_reader :length
33
+ attr_reader :ident
34
+ attr_reader :unique
35
+ attr_reader :default
36
+ attr_reader :classname
37
+ attr_reader :classobj
38
+
39
+
40
+ def initialize(hash=nil, parent=nil)
41
+ _init(hash, "", {}) if hash
42
+ @parent = parent
43
+ end
44
+
45
+
46
+ def _init(hash, path="", rule_table={})
47
+ unless hash.is_a?(Hash)
48
+ #* key=:schema_notmap msg="schema definition is not a mapping."
49
+ raise Kwalify.schema_error(:schema_notmap, nil, (!path || path.empty? ? "/" : path), nil)
50
+ end
51
+ rule = self
52
+ rule_table[hash.__id__] = rule
53
+ ## 'type:' entry
54
+ curr_path = "#{path}/type"
55
+ _init_type_value(hash['type'], rule, curr_path)
56
+ ## other entries
57
+ hash.each do |key, val|
58
+ curr_path = "#{path}/#{key}"
59
+ sym = key.intern
60
+ method = get_init_method(sym)
61
+ unless method
62
+ #* key=:key_unknown msg="unknown key."
63
+ raise schema_error(:key_unknown, rule, curr_path, "#{key}:")
64
+ end
65
+ if sym == :sequence || sym == :mapping
66
+ __send__(method, val, rule, curr_path, rule_table)
67
+ else
68
+ __send__(method, val, rule, curr_path)
69
+ end
70
+ end
71
+ _check_confliction(hash, rule, path)
72
+ return self
73
+ end
74
+
75
+
76
+ keys = %w[type name desc required pattern enum assert range length
77
+ ident unique default sequence mapping class]
78
+ #table = keys.inject({}) {|h, k| h[k.intern] = "_init_#{k}_value".intern; h }
79
+ table = {}; keys.each {|k| table[k.intern] = "_init_#{k}_value".intern }
80
+ @@dispatch_table = table
81
+
82
+
83
+ protected
84
+
85
+
86
+ def get_init_method(sym)
87
+ @_dispatch_table ||= @@dispatch_table
88
+ return @_dispatch_table[sym]
89
+ end
90
+
91
+
92
+ private
93
+
94
+
95
+ def _init_type_value(val, rule, path)
96
+ @type = val
97
+ @type = Types::DEFAULT_TYPE if @type.nil?
98
+ unless @type.is_a?(String)
99
+ #* key=:type_notstr msg="not a string."
100
+ raise schema_error(:type_notstr, rule, path, @type.to_s)
101
+ end
102
+ @type_class = Types.type_class(@type)
103
+ #if @type_class.nil?
104
+ # begin
105
+ # @type_class = Kernel.const_get(@type)
106
+ # rescue NameError
107
+ # end
108
+ #end
109
+ unless @type_class
110
+ #* key=:type_unknown msg="unknown type."
111
+ raise schema_error(:type_unknown, rule, path, @type.to_s)
112
+ end
113
+ end
114
+
115
+
116
+ def _init_class_value(val, rule, path)
117
+ @classname = val
118
+ unless @type == 'map'
119
+ #* key=:class_notmap msg="available only with map type."
120
+ raise schema_error(:class_notmap, rule, path, 'class:')
121
+ end
122
+ begin
123
+ @classobj = Util.get_class(val)
124
+ rescue NameError
125
+ @classobj = nil
126
+ end
127
+ end
128
+
129
+
130
+ def _init_name_value(val, rule, path)
131
+ @name = val
132
+ end
133
+
134
+
135
+ def _init_desc_value(val, rule, path)
136
+ @desc = val
137
+ end
138
+
139
+
140
+ def _init_required_value(val, rule, path)
141
+ @required = val
142
+ unless val.is_a?(Boolean) #|| val.nil?
143
+ #* key=:required_notbool msg="not a boolean."
144
+ raise schema_error(:required_notbool, rule, path, val)
145
+ end
146
+ end
147
+
148
+
149
+ def _init_pattern_value(val, rule, path)
150
+ @pattern = val
151
+ unless val.is_a?(String) || val.is_a?(Regexp)
152
+ #* key=:pattern_notstr msg="not a string (or regexp)"
153
+ raise schema_error(:pattern_notstr, rule, path, val)
154
+ end
155
+ unless val =~ /\A\/(.*)\/([mi]?[mi]?)\z/
156
+ #* key=:pattern_notmatch msg="should be '/..../'."
157
+ raise schema_error(:pattern_notmatch, rule, path, val)
158
+ end
159
+ pat = $1; opt = $2
160
+ flag = 0
161
+ flag += Regexp::IGNORECASE if opt.include?("i")
162
+ flag += Regexp::MULTILINE if opt.include?("m")
163
+ begin
164
+ @regexp = Regexp.compile(pat, flag)
165
+ rescue RegexpError => ex
166
+ #* key=:pattern_syntaxerr msg="has regexp error."
167
+ raise schema_error(:pattern_syntaxerr, rule, path, val)
168
+ end
169
+ end
170
+
171
+
172
+ def _init_enum_value(val, rule, path)
173
+ @enum = val
174
+ unless val.is_a?(Array)
175
+ #* key=:enum_notseq msg="not a sequence."
176
+ raise schema_error(:enum_notseq, rule, path, val)
177
+ end
178
+ if Types.collection_type?(@type) # unless Kwalify.scalar_class?(@type_class)
179
+ #* key=:enum_notscalar msg="not available with seq or map."
180
+ raise schema_error(:enum_notscalar, rule, File.dirname(path), 'enum:')
181
+ end
182
+ elem_table = {}
183
+ @enum.each do |elem|
184
+ unless elem.is_a?(@type_class)
185
+ #* key=:enum_type_unmatch msg="%s type expected."
186
+ raise schema_error(:enum_type_unmatch, rule, path, elem, [Kwalify.word(@type)])
187
+ end
188
+ if elem_table[elem]
189
+ #* key=:enum_duplicate msg="duplicated enum value."
190
+ raise schema_error(:enum_duplicate, rule, path, elem.to_s)
191
+ end
192
+ elem_table[elem] = true
193
+ end
194
+ end
195
+
196
+
197
+ def _init_assert_value(val, rule, path)
198
+ @assert = val
199
+ unless val.is_a?(String)
200
+ #* key=:assert_notstr msg="not a string."
201
+ raise schema_error(:assert_notstr, rule, path, val)
202
+ end
203
+ unless val =~ /\bval\b/
204
+ #* key=:assert_noval msg="'val' is not used."
205
+ raise schema_error(:assert_noval, rule, path, val)
206
+ end
207
+ begin
208
+ @assert_proc = eval "proc { |val| #{val} }"
209
+ rescue ::SyntaxError => ex
210
+ #* key=:assert_syntaxerr msg="expression syntax error."
211
+ raise schema_error(:assert_syntaxerr, rule, path, val)
212
+ end
213
+ end
214
+
215
+
216
+ def _init_range_value(val, rule, path)
217
+ @range = val
218
+ unless val.is_a?(Hash)
219
+ #* key=:range_notmap msg="not a mapping."
220
+ raise schema_error(:range_notmap, rule, path, val)
221
+ end
222
+ if Types.collection_type?(@type) || @type == 'bool'
223
+ #* key=:range_notscalar msg="is available only with scalar type."
224
+ raise schema_error(:range_notscalar, rule, File.dirname(path), 'range:')
225
+ end
226
+ val.each do |k, v|
227
+ case k
228
+ when 'max', 'min', 'max-ex', 'min-ex'
229
+ unless v.is_a?(@type_class)
230
+ typename = Kwalify.word(@type) || @type
231
+ #* key=:range_type_unmatch msg="not a %s."
232
+ raise schema_error(:range_type_unmatch, rule, "#{path}/#{k}", v, [typename])
233
+ end
234
+ else
235
+ #* key=:range_undefined msg="undefined key."
236
+ raise schema_error(:range_undefined, rule, "#{path}/#{k}", "#{k}:")
237
+ end
238
+ end
239
+ if val.key?('max') && val.key?('max-ex')
240
+ #* key=:range_twomax msg="both 'max' and 'max-ex' are not available at once."
241
+ raise schema_error(:range_twomax, rule, path, nil)
242
+ end
243
+ if val.key?('min') && val.key?('min-ex')
244
+ #* key=:range_twomin msg="both 'min' and 'min-ex' are not available at once."
245
+ raise schema_error(:range_twomin, rule, path, nil)
246
+ end
247
+ max, min, max_ex, min_ex = val['max'], val['min'], val['max-ex'], val['min-ex']
248
+ if max
249
+ if min && max < min
250
+ #* key=:range_maxltmin msg="max '%s' is less than min '%s'."
251
+ raise validate_error(:range_maxltmin, rule, path, nil, [max, min])
252
+ elsif min_ex && max <= min_ex
253
+ #* key=:range_maxleminex msg="max '%s' is less than or equal to min-ex '%s'."
254
+ raise validate_error(:range_maxleminex, rule, path, nil, [max, min_ex])
255
+ end
256
+ elsif max_ex
257
+ if min && max_ex <= min
258
+ #* key=:range_maxexlemin msg="max-ex '%s' is less than or equal to min '%s'."
259
+ raise validate_error(:range_maxexlemin, rule, path, nil, [max_ex, min])
260
+ elsif min_ex && max_ex <= min_ex
261
+ #* key=:range_maxexleminex msg="max-ex '%s' is less than or equal to min-ex '%s'."
262
+ raise validate_error(:range_maxexleminex, rule, path, nil, [max_ex, min_ex])
263
+ end
264
+ end
265
+ end
266
+
267
+
268
+ def _init_length_value(val, rule, path)
269
+ @length = val
270
+ unless val.is_a?(Hash)
271
+ #* key=:length_notmap msg="not a mapping."
272
+ raise schema_error(:length_notmap, rule, path, val)
273
+ end
274
+ unless @type == 'str' || @type == 'text'
275
+ #* key=:length_nottext msg="is available only with string or text."
276
+ raise schema_error(:length_nottext, rule, File.dirname(path), 'length:')
277
+ end
278
+ val.each do |k, v|
279
+ case k
280
+ when 'max', 'min', 'max-ex', 'min-ex'
281
+ unless v.is_a?(Integer)
282
+ #* key=:length_notint msg="not an integer."
283
+ raise schema_error(:length_notint, rule, "#{path}/#{k}", v)
284
+ end
285
+ else
286
+ #* key=:length_undefined msg="undefined key."
287
+ raise schema_error(:length_undefined, rule, "#{path}/#{k}", "#{k}:")
288
+ end
289
+ end
290
+ if val.key?('max') && val.key?('max-ex')
291
+ #* key=:length_twomax msg="both 'max' and 'max-ex' are not available at once."
292
+ raise schema_error(:length_twomax, rule, path, nil)
293
+ end
294
+ if val.key?('min') && val.key?('min-ex')
295
+ #* key=:length_twomin msg="both 'min' and 'min-ex' are not available at once."
296
+ raise schema_error(:length_twomin, rule, path, nil)
297
+ end
298
+ max, min, max_ex, min_ex = val['max'], val['min'], val['max-ex'], val['min-ex']
299
+ if max
300
+ if min && max < min
301
+ #* key=:length_maxltmin msg="max '%s' is less than min '%s'."
302
+ raise validate_error(:length_maxltmin, rule, path, nil, [max, min])
303
+ elsif min_ex && max <= min_ex
304
+ #* key=:length_maxleminex msg="max '%s' is less than or equal to min-ex '%s'."
305
+ raise validate_error(:length_maxleminex, rule, path, nil, [max, min_ex])
306
+ end
307
+ elsif max_ex
308
+ if min && max_ex <= min
309
+ #* key=:length_maxexlemin msg="max-ex '%s' is less than or equal to min '%s'."
310
+ raise validate_error(:length_maxexlemin, rule, path, nil, [max_ex, min])
311
+ elsif min_ex && max_ex <= min_ex
312
+ #* key=:length_maxexleminex msg="max-ex '%s' is less than or equal to min-ex '%s'."
313
+ raise validate_error(:length_maxexleminex, rule, path, nil, [max_ex, min_ex])
314
+ end
315
+ end
316
+ end
317
+
318
+
319
+ def _init_ident_value(val, rule, path)
320
+ @ident = val
321
+ @required = true
322
+ unless val.is_a?(Boolean)
323
+ #* key=:ident_notbool msg="not a boolean."
324
+ raise schema_error(:ident_notbool, rule, path, val)
325
+ end
326
+ if @type == 'map' || @type == 'seq'
327
+ #* key=:ident_notscalar msg="is available only with a scalar type."
328
+ raise schema_error(:ident_notscalar, rule, File.dirname(path), "ident:")
329
+ end
330
+ if File.dirname(path) == "/"
331
+ #* key=:ident_onroot msg="is not available on root element."
332
+ raise schema_error(:ident_onroot, rule, "/", "ident:")
333
+ end
334
+ unless @parent && @parent.type == 'map'
335
+ #* key=:ident_notmap msg="is available only with an element of mapping."
336
+ raise schema_error(:ident_notmap, rule, File.dirname(path), "ident:")
337
+ end
338
+ end
339
+
340
+
341
+ def _init_unique_value(val, rule, path)
342
+ @unique = val
343
+ unless val.is_a?(Boolean)
344
+ #* key=:unique_notbool msg="not a boolean."
345
+ raise schema_error(:unique_notbool, rule, path, val)
346
+ end
347
+ if @type == 'map' || @type == 'seq'
348
+ #* key=:unique_notscalar msg="is available only with a scalar type."
349
+ raise schema_error(:unique_notscalar, rule, File.dirname(path), "unique:")
350
+ end
351
+ if File.dirname(path) == "/"
352
+ #* key=:unique_onroot msg="is not available on root element."
353
+ raise schema_error(:unique_onroot, rule, "/", "unique:")
354
+ end
355
+ end
356
+
357
+
358
+ def _init_default_value(val, rule, path)
359
+ @default = val
360
+ unless Types.scalar?(val)
361
+ #* key=:default_nonscalarval msg="not a scalar."
362
+ raise schema_error(:default_nonscalarval, rule, path, val)
363
+ end
364
+ if @type == 'map' || @type == 'seq'
365
+ #* key=:default_notscalar msg="is available only with a scalar type."
366
+ raise schema_error(:default_notscalar, rule, File.dirname(path), "default:")
367
+ end
368
+ unless val.nil? || val.is_a?(@type_class)
369
+ #* key=:default_unmatch msg="not a %s."
370
+ raise schema_error(:default_unmatch, rule, path, val, [Kwalify.word(@type)])
371
+ end
372
+ end
373
+
374
+
375
+ def _init_sequence_value(val, rule, path, rule_table)
376
+ if !val.nil? && !val.is_a?(Array)
377
+ #* key=:sequence_notseq msg="not a sequence."
378
+ raise schema_error(:sequence_notseq, rule, path, val)
379
+ elsif val.nil? || val.empty?
380
+ #* key=:sequence_noelem msg="required one element."
381
+ raise schema_error(:sequence_noelem, rule, path, val)
382
+ elsif val.length > 1
383
+ #* key=:sequence_toomany msg="required just one element."
384
+ raise schema_error(:sequence_toomany, rule, path, val)
385
+ else
386
+ elem = val[0]
387
+ elem ||= {}
388
+ i = 0 # or 1? *index*
389
+ rule = rule_table[elem.__id__]
390
+ rule ||= Rule.new(nil, self)._init(elem, "#{path}/#{i}", rule_table)
391
+ @sequence = [ rule ]
392
+ end
393
+ end
394
+
395
+
396
+ def _init_mapping_value(val, rule, path, rule_table)
397
+ if !val.nil? && !val.is_a?(Hash)
398
+ #* key=:mapping_notmap msg="not a mapping."
399
+ raise schema_error(:mapping_notmap, rule, path, val)
400
+ elsif val.nil? || (val.empty? && !val.default)
401
+ #* key=:mapping_noelem msg="required at least one element."
402
+ raise schema_error(:mapping_noelem, rule, path, val)
403
+ else
404
+ @mapping = {}
405
+ if val.default
406
+ elem = val.default # hash
407
+ rule = rule_table[elem.__id__]
408
+ rule ||= Rule.new(nil, self)._init(elem, "#{path}/=", rule_table)
409
+ @mapping.default = rule
410
+ end
411
+ val.each do |k, v|
412
+ ##* key=:key_duplicate msg="key duplicated."
413
+ #raise schema_error(:key_duplicate, rule, path, key) if @mapping.key?(key)
414
+ v ||= {}
415
+ rule = rule_table[v.__id__]
416
+ rule ||= Rule.new(nil, self)._init(v, "#{path}/#{k}", rule_table)
417
+ if k == '='
418
+ @mapping.default = rule
419
+ else
420
+ @mapping[k] = rule
421
+ end
422
+ end if val
423
+ end
424
+ end
425
+
426
+
427
+ def _check_confliction(hash, rule, path)
428
+ if @type == 'seq'
429
+ #* key=:seq_nosequence msg="type 'seq' requires 'sequence:'."
430
+ raise schema_error(:seq_nosequence, rule, path, nil) unless hash.key?('sequence')
431
+ #* key=:seq_conflict msg="not available with sequence."
432
+ raise schema_error(:seq_conflict, rule, path, 'enum:') if @enum
433
+ raise schema_error(:seq_conflict, rule, path, 'pattern:') if @pattern
434
+ raise schema_error(:seq_conflict, rule, path, 'mapping:') if @mapping
435
+ raise schema_error(:seq_conflict, rule, path, 'range:') if @range
436
+ raise schema_error(:seq_conflict, rule, path, 'length:') if @length
437
+ elsif @type == 'map'
438
+ #* key=:map_nomapping msg="type 'map' requires 'mapping:'."
439
+ raise schema_error(:map_nomapping, rule, path, nil) unless hash.key?('mapping')
440
+ #* key=:map_conflict msg="not available with mapping."
441
+ raise schema_error(:map_conflict, rule, path, 'enum:') if @enum
442
+ raise schema_error(:map_conflict, rule, path, 'pattern:') if @pattern
443
+ raise schema_error(:map_conflict, rule, path, 'sequence:') if @sequence
444
+ raise schema_error(:map_conflict, rule, path, 'range:') if @range
445
+ raise schema_error(:map_conflict, rule, path, 'length:') if @length
446
+ else
447
+ #* key=:scalar_conflict msg="not available with scalar type."
448
+ raise schema_error(:scalar_conflict, rule, path, 'sequence:') if @sequence
449
+ raise schema_error(:scalar_conflict, rule, path, 'mapping:') if @mapping
450
+ if @enum
451
+ #* key=:enum_conflict msg="not available with 'enum:'."
452
+ raise schema_error(:enum_conflict, rule, path, 'range:') if @range
453
+ raise schema_error(:enum_conflict, rule, path, 'length:') if @length
454
+ raise schema_error(:enum_conflict, rule, path, 'pattern:') if @pattern
455
+ end
456
+ unless @default.nil?
457
+ #* key=:default_conflict msg="not available when 'required:' is true."
458
+ raise schema_error(:default_conflict, rule, path, 'default:') if @required
459
+ end
460
+ end
461
+ end
462
+
463
+ #def inspect()
464
+ # str = ""; level = 0; done = {}
465
+ # _inspect(str, level, done)
466
+ # return str
467
+ #end
468
+
469
+
470
+ protected
471
+
472
+
473
+ def _inspect(str="", level=0, done={})
474
+ done[self.__id__] = true
475
+ str << " " * level << "name: #{@name}\n" unless @name.nil?
476
+ str << " " * level << "desc: #{@desc}\n" unless @desc.nil?
477
+ str << " " * level << "type: #{@type}\n" unless @type.nil?
478
+ str << " " * level << "klass: #{@type_class.name}\n" unless @type_class.nil?
479
+ str << " " * level << "required: #{@required}\n" unless @required.nil?
480
+ str << " " * level << "pattern: #{@regexp.inspect}\n" unless @pattern.nil?
481
+ str << " " * level << "assert: #{@assert}\n" unless @assert.nil?
482
+ str << " " * level << "ident: #{@ident}\n" unless @ident.nil?
483
+ str << " " * level << "unique: #{@unique}\n" unless @unique.nil?
484
+ if !@enum.nil?
485
+ str << " " * level << "enum:\n"
486
+ @enum.each do |item|
487
+ str << " " * (level+1) << "- #{item}\n"
488
+ end
489
+ end
490
+ if !@range.nil?
491
+ str << " " * level
492
+ str << "range: { "
493
+ colon = ""
494
+ %w[max max-ex min min-ex].each do |key|
495
+ val = @range[key]
496
+ unless val.nil?
497
+ str << colon << "#{key}: #{val.inspect}"
498
+ colon = ", "
499
+ end
500
+ end
501
+ str << " }\n"
502
+ end
503
+ if !@length.nil?
504
+ str << " " * level
505
+ str << "length: { "
506
+ colon = ""
507
+ %w[max max-ex min min-ex].each do |key|
508
+ val = @length[key]
509
+ if !val.nil?
510
+ str << colon << "#{key}: #{val.inspect}"
511
+ colon = ", "
512
+ end
513
+ end
514
+ str << " }\n"
515
+ end
516
+ @sequence.each do |rule|
517
+ if done[rule.__id__]
518
+ str << " " * (level+1) << "- ...\n"
519
+ else
520
+ str << " " * (level+1) << "- \n"
521
+ rule._inspect(str, level+2, done)
522
+ end
523
+ end if @sequence
524
+ @mapping.each do |key, rule|
525
+ if done[rule.__id__]
526
+ str << ' ' * (level+1) << '"' << key << "\": ...\n"
527
+ else
528
+ str << ' ' * (level+1) << '"' << key << "\":\n"
529
+ rule._inspect(str, level+2, done)
530
+ end
531
+ end if @mapping
532
+ return str
533
+ end
534
+
535
+
536
+ public
537
+
538
+
539
+ def _uniqueness_check_table() # :nodoc:
540
+ uniq_table = nil
541
+ if @type == 'map'
542
+ @mapping.keys.each do |key|
543
+ rule = @mapping[key]
544
+ if rule.unique || rule.ident
545
+ uniq_table ||= {}
546
+ uniq_table[key] = {}
547
+ end
548
+ end
549
+ elsif @unique || @ident
550
+ uniq_table = {}
551
+ end
552
+ return uniq_table
553
+ end
554
+
555
+
556
+ end
557
+
558
+
559
+ end