kwalify 0.5.1 → 0.6.0
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.
- data/ChangeLog +24 -19
- data/README.txt +51 -51
- data/bin/kwalify +2 -2
- data/contrib/inline-require +151 -0
- data/contrib/kwalify +2850 -0
- data/doc-api/classes/CommandOptionError.html +184 -0
- data/doc-api/classes/CommandOptionParser.html +325 -0
- data/doc-api/classes/Kwalify.html +270 -0
- data/doc-api/classes/Kwalify/AssertionError.html +148 -0
- data/doc-api/classes/Kwalify/BaseError.html +296 -0
- data/doc-api/classes/Kwalify/CommandOptionError.html +168 -0
- data/doc-api/classes/Kwalify/ErrorHelper.html +218 -0
- data/doc-api/classes/Kwalify/HashInterface.html +240 -0
- data/doc-api/classes/Kwalify/KwalifyError.html +111 -0
- data/doc-api/classes/Kwalify/Main.html +336 -0
- data/doc-api/classes/Kwalify/MetaValidator.html +432 -0
- data/doc-api/classes/Kwalify/Parser.html +155 -0
- data/doc-api/classes/Kwalify/PlainYamlParser.html +520 -0
- data/doc-api/classes/Kwalify/PlainYamlParser/Alias.html +165 -0
- data/doc-api/classes/Kwalify/Rule.html +411 -0
- data/doc-api/classes/Kwalify/SchemaError.html +148 -0
- data/doc-api/classes/Kwalify/Types.html +301 -0
- data/doc-api/classes/Kwalify/ValidationError.html +148 -0
- data/doc-api/classes/Kwalify/Validator.html +311 -0
- data/doc-api/classes/Kwalify/YamlParser.html +535 -0
- data/doc-api/classes/Kwalify/YamlSyntaxError.html +168 -0
- data/doc-api/classes/Test.html +107 -0
- data/doc-api/classes/Test/Unit.html +101 -0
- data/doc-api/classes/YamlHelper.html +259 -0
- data/doc-api/created.rid +1 -0
- data/doc-api/files/__/README_txt.html +179 -0
- data/doc-api/files/kwalify/errors_rb.html +114 -0
- data/doc-api/files/kwalify/main_rb.html +117 -0
- data/doc-api/files/kwalify/messages_rb.html +107 -0
- data/doc-api/files/kwalify/meta-validator_rb.html +117 -0
- data/doc-api/files/kwalify/rule_rb.html +116 -0
- data/doc-api/files/kwalify/types_rb.html +114 -0
- data/doc-api/files/kwalify/util/assert-text-equal_rb.html +115 -0
- data/doc-api/files/kwalify/util/hash-interface_rb.html +107 -0
- data/doc-api/files/kwalify/util/option-parser_rb.html +107 -0
- data/doc-api/files/kwalify/util/testcase-helper_rb.html +115 -0
- data/doc-api/files/kwalify/util/yaml-helper_rb.html +114 -0
- data/doc-api/files/kwalify/validator_rb.html +117 -0
- data/doc-api/files/kwalify/yaml-parser_rb.html +117 -0
- data/doc-api/files/kwalify_rb.html +120 -0
- data/doc-api/fr_class_index.html +50 -0
- data/doc-api/fr_file_index.html +41 -0
- data/doc-api/fr_method_index.html +109 -0
- data/doc-api/index.html +24 -0
- data/doc-api/rdoc-style.css +208 -0
- data/doc/users-guide.html +693 -193
- data/examples/address-book/Makefile +5 -0
- data/examples/address-book/address-book.schema.yaml +2 -1
- data/examples/invoice/Makefile +5 -0
- data/examples/invoice/invoice.schema.yaml +3 -2
- data/examples/tapkit/Makefile +5 -0
- data/examples/tapkit/main.rb +7 -0
- data/examples/tapkit/tapkit.schema.yaml +6 -1
- data/lib/kwalify.rb +3 -3
- data/lib/kwalify/errors.rb +2 -2
- data/lib/kwalify/main.rb +161 -84
- data/lib/kwalify/messages.rb +17 -11
- data/lib/kwalify/meta-validator.rb +11 -2
- data/lib/kwalify/rule.rb +13 -3
- data/lib/kwalify/templates/genclass-java.eruby +195 -0
- data/lib/kwalify/templates/genclass-ruby.eruby +84 -0
- data/lib/kwalify/types.rb +18 -18
- data/lib/kwalify/util/assert-text-equal.rb +44 -0
- data/lib/kwalify/util/hash-interface.rb +37 -0
- data/lib/kwalify/util/option-parser.rb +2 -2
- data/lib/kwalify/util/testcase-helper.rb +112 -0
- data/lib/kwalify/util/yaml-helper.rb +2 -2
- data/lib/kwalify/validator.rb +2 -2
- data/lib/kwalify/yaml-parser.rb +12 -9
- data/test/test-main.rb +77 -78
- data/test/test-main.yaml +543 -769
- data/test/test-metavalidator.rb +27 -47
- data/test/test-metavalidator.yaml +21 -2
- data/test/test-rule.rb +6 -39
- data/test/test-rule.yaml +2 -2
- data/test/test-validator.rb +36 -869
- data/test/test-validator.yaml +28 -20
- data/test/test-yamlparser.rb +30 -1248
- data/test/test-yamlparser.yaml +138 -110
- data/test/test.rb +33 -13
- data/test/tmp.dir/Context.java +40 -0
- data/test/tmp.dir/Group.java +33 -0
- data/test/tmp.dir/User.java +43 -0
- data/test/tmp.dir/action1.document +18 -0
- data/test/tmp.dir/action1.schema +32 -0
- data/test/tmp.dir/action2.document +18 -0
- data/test/tmp.dir/action2.schema +32 -0
- data/test/tmp.dir/emacs.document +6 -0
- data/test/tmp.dir/emacs.schema +6 -0
- data/test/tmp.dir/meta1.document +0 -0
- data/test/tmp.dir/meta1.schema +3 -0
- data/test/tmp.dir/meta2.document +0 -0
- data/test/tmp.dir/meta2.schema +3 -0
- data/test/tmp.dir/silent1.document +3 -0
- data/test/tmp.dir/silent1.schema +3 -0
- data/test/tmp.dir/silent2.document +7 -0
- data/test/tmp.dir/silent2.schema +3 -0
- data/test/tmp.dir/stream.invalid +8 -0
- data/test/tmp.dir/stream.schema +3 -0
- data/test/tmp.dir/stream.valid +8 -0
- data/test/tmp.dir/untabify.document +5 -0
- data/test/tmp.dir/untabify.schema +10 -0
- metadata +98 -12
- data/lib/kwalify/util/assert-diff.rb +0 -44
data/ChangeLog
CHANGED
|
@@ -1,42 +1,47 @@
|
|
|
1
1
|
.=title: ChangeLog
|
|
2
|
-
.?release: $Release: 0.
|
|
3
|
-
.?lastupdate: $Date:
|
|
4
|
-
.?version: $Rev:
|
|
2
|
+
.?release: $Release: 0.6.0 $
|
|
3
|
+
.?lastupdate: $Date: 2006-05-30 17:16:57 +0900 (Tue, 30 May 2006) $
|
|
4
|
+
.?version: $Rev: 52 $
|
|
5
5
|
|
|
6
|
+
.: release 0.6.0 (2006-05-30)
|
|
7
|
+
.* enhances:
|
|
8
|
+
.- Class definition generation support.
|
|
9
|
+
New command-line option '-a genclass-ruby' or '-a genclass-java' generates
|
|
10
|
+
class definitions in Ruby or Java from schema file.
|
|
6
11
|
|
|
7
|
-
.:
|
|
8
|
-
.*
|
|
12
|
+
.: release 0.5.1 (2005-12-20)
|
|
13
|
+
.* enhances:
|
|
9
14
|
.- add new command-line option '-E' which show errors in emacs-compatible style.
|
|
10
15
|
|
|
11
|
-
.:
|
|
12
|
-
.*
|
|
16
|
+
.: release 0.5.0 (2005-12-17)
|
|
17
|
+
.* enhances:
|
|
13
18
|
.- Meta-validation check for 'max < min', 'max-ex <= min-ex', and so on.
|
|
14
19
|
.- Many test-cases are added
|
|
15
|
-
.*
|
|
20
|
+
.* changes:
|
|
16
21
|
.- 'Parser' class is renamed to 'YamlParser'
|
|
17
22
|
.- 'PlainParser' class is renamed to 'PlainYamlParser'
|
|
18
23
|
.- YamlParser#set_error_linenums() is renamed to set_errors_linenum()
|
|
19
24
|
.- ValidatorError#<=> added
|
|
20
25
|
.- ParseError class is renamed to YamlSyntaxError
|
|
21
26
|
|
|
22
|
-
.:
|
|
23
|
-
.*
|
|
27
|
+
.: release 0.4.1 (2005-10-26)
|
|
28
|
+
.* bugfix:
|
|
24
29
|
.- Support Ruby 1.8.3 (around YAML::Syck::DomainType)
|
|
25
30
|
.- Show correct error line number when key is undefined or unknown.
|
|
26
31
|
|
|
27
|
-
.:
|
|
28
|
-
.*
|
|
32
|
+
.: release 0.4.0 (2005-10-25)
|
|
33
|
+
.* enhances:
|
|
29
34
|
.- New command-line option '-l' prints error line numbers.
|
|
30
35
|
.- Supports default rule of mapping.
|
|
31
36
|
|
|
32
|
-
.:
|
|
33
|
-
.*
|
|
37
|
+
.: release 0.3.0 (2005-09-30)
|
|
38
|
+
.* enhances:
|
|
34
39
|
.- Support 'max-ex' and 'min-ex' (max/min exclusive) support with 'range:'
|
|
35
40
|
.- Support 'max-ex' and 'min-ex' (max/min exclusive) support with 'length:'
|
|
36
41
|
.- Support 'unique' constraint
|
|
37
42
|
|
|
38
|
-
.:
|
|
39
|
-
.*
|
|
43
|
+
.: release 0.2.0 (2005-09-25)
|
|
44
|
+
.* enhances:
|
|
40
45
|
.- New type 'scalar' and 'timestamp' added
|
|
41
46
|
.- Add new rule 'range:' which validates value range.
|
|
42
47
|
See users' guide for details.
|
|
@@ -49,13 +54,13 @@
|
|
|
49
54
|
See users' guide for details.
|
|
50
55
|
.- New class 'MetaValidator' added.
|
|
51
56
|
.- New test script 'test/test-metavalidator.rb' added.
|
|
52
|
-
.*
|
|
53
|
-
.- Type name changed to suite YAML data type:
|
|
57
|
+
.* changes:
|
|
58
|
+
.- Type name changed to suite YAML data type:
|
|
54
59
|
.= string -> str
|
|
55
60
|
.= integer -> int
|
|
56
61
|
.= boolean -> bool
|
|
57
62
|
.- Error index starts with 0 (before starts with 1).
|
|
58
63
|
.- Class 'Schema' is renamed to 'Rule'.
|
|
59
64
|
|
|
60
|
-
.:
|
|
65
|
+
.: release 0.1.0 (2005-08-01)
|
|
61
66
|
.- beta release
|
data/README.txt
CHANGED
|
@@ -1,66 +1,66 @@
|
|
|
1
|
-
|
|
2
|
-
.?version: $Rev: 41 $
|
|
3
|
-
.?lastupdate: $Date: 2005-11-22 03:24:54 +0900 (Tue, 22 Nov 2005) $
|
|
4
|
-
.?release: $Release: 0.5.1 $
|
|
1
|
+
= README
|
|
5
2
|
|
|
3
|
+
release:: $Release: 0.6.0 $
|
|
4
|
+
lastupdate:: $Date: 2006-05-30 13:29:34 +0900 (Tue, 30 May 2006) $
|
|
5
|
+
copyright:: copyright(c) 2005 kuwata-lab all rights reserved.
|
|
6
6
|
|
|
7
|
-
.$ About Kwalify
|
|
8
7
|
|
|
9
|
-
Kwalify is a tiny schema validator for YAML document.
|
|
10
|
-
It is in fact very poor compared to Relax NG or DTD.
|
|
11
|
-
I hope you extend/customize Kwalify for your own way.
|
|
12
8
|
|
|
13
|
-
|
|
9
|
+
== Introduction
|
|
14
10
|
|
|
11
|
+
Kwalify is a tiny schema validator for YAML and JSON.
|
|
15
12
|
|
|
16
|
-
|
|
13
|
+
See doc/users-guide.html for details.
|
|
17
14
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
== Installation
|
|
18
|
+
|
|
19
|
+
If you have installed RubyGems, just type 'gem install -r kwalify'
|
|
20
|
+
|
|
21
|
+
$ sudo gem install --remote kwalify
|
|
22
22
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
.$ License
|
|
23
|
+
Else if you can be root user, use 'setup.rb' as following:
|
|
24
|
+
|
|
25
|
+
$ tar xjf kwalify_x.x.x.tar.bz2
|
|
26
|
+
$ cd kwalify_x.x.x/
|
|
27
|
+
$ ruby setup.rb config
|
|
28
|
+
$ ruby setup.rb setup
|
|
29
|
+
$ sudo ruby setup.rb install
|
|
30
|
+
|
|
31
|
+
Else copy script and libraries to proper directory.
|
|
32
|
+
|
|
33
|
+
$ tar xjf kwalify_x.x.x.tar.bz2
|
|
34
|
+
$ cd kwalify_x.x.x/
|
|
35
|
+
$ mkdir -p $HOME/bin
|
|
36
|
+
$ cp -a bin/* $HOME/bin
|
|
37
|
+
$ export PATH=$PATH:$HOME/bin
|
|
38
|
+
$ mkdir -p $HOME/lib/ruby
|
|
39
|
+
$ cp -a lib/* $HOME/lib/ruby
|
|
40
|
+
$ export RUBYLIB=$HOME/lib/ruby
|
|
41
|
+
|
|
42
|
+
(Optional) 'contrib/inline-require' script to concatenate all script
|
|
43
|
+
and libraries into a file.
|
|
44
|
+
|
|
45
|
+
$ tar xjf kwalify_x.x.x.tar.bz2
|
|
46
|
+
$ cd kwalify_x.x.x/
|
|
47
|
+
$ unset RUBYLIB
|
|
48
|
+
$ ruby contrib/inline-require -I ./lib bin/kwalify > contrib/kwalify
|
|
49
|
+
$ chmod a+x contrib/kwalify
|
|
50
|
+
$ mv contrib/kwalify $HOME/bin
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
== License
|
|
57
55
|
|
|
58
56
|
GNU General Public License ver.2
|
|
59
57
|
|
|
60
|
-
If you need looser license, please
|
|
58
|
+
If you need looser license, please suggest to me.
|
|
59
|
+
|
|
60
|
+
|
|
61
61
|
|
|
62
|
+
== Author
|
|
62
63
|
|
|
63
|
-
|
|
64
|
+
makoto kuwata <kwa(at)kuwata-lab.com>
|
|
64
65
|
|
|
65
|
-
Makoto Kuwata <kwa(at)kuwata-lab.com> .^
|
|
66
66
|
http://www.kuwata-lab.com/
|
data/bin/kwalify
CHANGED
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
|
|
3
|
+
###
|
|
4
|
+
### inline-require - expand 'require "foo"' into inline code
|
|
5
|
+
###
|
|
6
|
+
### usage: inline-require [-h] [-I path[,path2,..]] script
|
|
7
|
+
###
|
|
8
|
+
### copyright(c) 2005 kuwata-lab all rights reserved.
|
|
9
|
+
### $Release: 0.6.0 $
|
|
10
|
+
### $Rev: 2 $
|
|
11
|
+
###
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class InlineRequire
|
|
15
|
+
|
|
16
|
+
def expand(filename)
|
|
17
|
+
sbuf = ''
|
|
18
|
+
inlined = []
|
|
19
|
+
level = 0
|
|
20
|
+
expand_require(filename, sbuf, inlined, level)
|
|
21
|
+
return sbuf
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
private
|
|
25
|
+
|
|
26
|
+
def expand_require(filename, sbuf, inlined, level)
|
|
27
|
+
raise "*** assertion error" if inlined.include?(filename)
|
|
28
|
+
inlined << filename
|
|
29
|
+
prog = File.read(filename)
|
|
30
|
+
n = 0
|
|
31
|
+
flag_if_file = false
|
|
32
|
+
prog.each_line do |line|
|
|
33
|
+
n += 1
|
|
34
|
+
|
|
35
|
+
## comment out from 'if __FILE__ == $0' to 'end'
|
|
36
|
+
if level > 0
|
|
37
|
+
if flag_if_file
|
|
38
|
+
sbuf << "#" << line
|
|
39
|
+
flag_if_file = false if line =~ /^end$/
|
|
40
|
+
next
|
|
41
|
+
end
|
|
42
|
+
if line =~ /^if\s+__FILE__\s*==\s*\$0(\s+then)?$/ || line =~ /^if\s+\$0\s*==\s*__FILE__(\s+then)?$/
|
|
43
|
+
flag_if_file = true
|
|
44
|
+
sbuf << "#" << line
|
|
45
|
+
next
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
## find 'require "foo"' and expand it to inline code
|
|
50
|
+
flag_inline = false
|
|
51
|
+
if line =~ /^\s*require ['"](.*)["']\s*$/
|
|
52
|
+
libname = $1
|
|
53
|
+
libpath = find_library(libname)
|
|
54
|
+
$stderr.puts "*** debug: libpath=#{libpath.inspect}" if $debug_mode
|
|
55
|
+
unless libpath
|
|
56
|
+
raise "file '#{filename}'(line #{n}): library '#{libname}' not found."
|
|
57
|
+
end
|
|
58
|
+
flag_inline = true if libpath =~ /\.rb$/ && local_library?(libpath)
|
|
59
|
+
end
|
|
60
|
+
if !flag_inline
|
|
61
|
+
sbuf << line
|
|
62
|
+
elsif inlined.include?(libpath)
|
|
63
|
+
sbuf << "#--already included #{line}"
|
|
64
|
+
else
|
|
65
|
+
sbuf << "#--begin of #{line}"
|
|
66
|
+
expand_require(libpath, sbuf, inlined, level+1)
|
|
67
|
+
sbuf << "#--end of #{line}"
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
#sbuf << "\n" if sbuf[-1] != ?\n
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def local_library?(libpath)
|
|
74
|
+
return libpath !~ /^\//
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def find_library(libname)
|
|
78
|
+
if libname =~ /^\.rb$/
|
|
79
|
+
libname_rb = libname
|
|
80
|
+
libname_so = nil
|
|
81
|
+
elsif libname =~ /^\.so$/
|
|
82
|
+
libname_rb = nil
|
|
83
|
+
libname_so = libname
|
|
84
|
+
else
|
|
85
|
+
libname_rb = libname + ".rb"
|
|
86
|
+
libname_so = libname + ".so"
|
|
87
|
+
end
|
|
88
|
+
$LOAD_PATH.each do |path|
|
|
89
|
+
if libname_rb
|
|
90
|
+
libpath = path + "/" + libname_rb
|
|
91
|
+
return libpath if test(?f, libpath)
|
|
92
|
+
end
|
|
93
|
+
if libname_so
|
|
94
|
+
libpath = path + "/" + libname_so
|
|
95
|
+
return libpath if test(?f, libpath)
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
return nil
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
if __FILE__ == $0
|
|
104
|
+
|
|
105
|
+
begin
|
|
106
|
+
rubylib_paths = []
|
|
107
|
+
flag_help = false
|
|
108
|
+
while ARGV[0] && ARGV[0][0] == ?-
|
|
109
|
+
opt = ARGV.shift
|
|
110
|
+
case opt
|
|
111
|
+
when '-h', '--help'
|
|
112
|
+
flag_help = true
|
|
113
|
+
when '-I'
|
|
114
|
+
arg = ARGV.shift
|
|
115
|
+
raise "-I: library path required." unless arg
|
|
116
|
+
rubylib_paths.concat(arg.split(/,/))
|
|
117
|
+
when '-D'
|
|
118
|
+
$debug_mode = true
|
|
119
|
+
else
|
|
120
|
+
raise "#{opt}: invalid option."
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
if flag_help
|
|
125
|
+
command = File.basename($0)
|
|
126
|
+
puts "Usage: #{command} [-h] [-I path[,path2,..]] script"
|
|
127
|
+
puts " -h : help"
|
|
128
|
+
puts " -I path[,path2,...] : ruby library path"
|
|
129
|
+
exit(0)
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
$stderr.puts "*** debug: rubylib_paths=#{rubylib_paths.inspect}" if $debug_mode
|
|
133
|
+
$LOAD_PATH.concat(rubylib_paths)
|
|
134
|
+
|
|
135
|
+
filenames = ARGV
|
|
136
|
+
|
|
137
|
+
inline_require = InlineRequire.new
|
|
138
|
+
filenames.each do |filename|
|
|
139
|
+
print inline_require.expand(filename)
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
rescue => ex
|
|
143
|
+
if $debug_mode
|
|
144
|
+
raise ex
|
|
145
|
+
else
|
|
146
|
+
$stderr.puts ex.message
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
end
|
data/contrib/kwalify
ADDED
|
@@ -0,0 +1,2850 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
|
|
3
|
+
###
|
|
4
|
+
### $Rev: 48 $
|
|
5
|
+
### $Release: 0.6.0 $
|
|
6
|
+
### copyright(c) 2005 kuwata-lab all rights reserved.
|
|
7
|
+
###
|
|
8
|
+
|
|
9
|
+
#--begin of require 'kwalify'
|
|
10
|
+
###
|
|
11
|
+
### $Rev: 48 $
|
|
12
|
+
### $Release: 0.6.0 $
|
|
13
|
+
### copyright(c) 2005 kuwata-lab all rights reserved.
|
|
14
|
+
###
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
module Kwalify
|
|
18
|
+
|
|
19
|
+
RELEASE = ("$Release: 0.6.0 $" =~ /[.\d]+/) && $&
|
|
20
|
+
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
#--begin of require 'kwalify/types'
|
|
24
|
+
###
|
|
25
|
+
### $Rev: 51 $
|
|
26
|
+
### $Release: 0.6.0 $
|
|
27
|
+
### copyright(c) 2005 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
|
+
end
|
|
164
|
+
|
|
165
|
+
def collection_type?(type)
|
|
166
|
+
return type == 'seq' || type == 'map'
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
def scalar_type?(type)
|
|
170
|
+
return type != 'seq' && type != 'map' && type == 'any'
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
module_function 'collection?', 'scalar?', 'collection_type?', 'scalar_type?'
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
extend Types
|
|
177
|
+
|
|
178
|
+
end
|
|
179
|
+
#--end of require 'kwalify/types'
|
|
180
|
+
#--begin of require 'kwalify/messages'
|
|
181
|
+
###
|
|
182
|
+
### $Rev: 51 $
|
|
183
|
+
### $Release: 0.6.0 $
|
|
184
|
+
### copyright(c) 2005 kuwata-lab all rights reserved.
|
|
185
|
+
###
|
|
186
|
+
|
|
187
|
+
module Kwalify
|
|
188
|
+
|
|
189
|
+
@@messages = {}
|
|
190
|
+
|
|
191
|
+
def self.msg(key)
|
|
192
|
+
return @@messages[key]
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
@@messages[:command_help] = <<END
|
|
198
|
+
kwalify - tiny schema validator for YAML and JSON
|
|
199
|
+
Usage1: kwalify [..options..] -f schema.yaml doc.yaml [doc2.yaml ...]
|
|
200
|
+
Usage2: kwalify [..options..] -m schema.yaml [schema2.yaml ...]
|
|
201
|
+
Usage3: kwalify [..options..] -a action -f schema.yaml [schema2.yaml ...]
|
|
202
|
+
-h, --help : help
|
|
203
|
+
-v : version
|
|
204
|
+
-s : silent
|
|
205
|
+
-f schema.yaml : schema definition file
|
|
206
|
+
-m : meta-validation mode
|
|
207
|
+
-t : expand tab characters
|
|
208
|
+
-l : show linenumber when errored (experimental)
|
|
209
|
+
-E : show errors in emacs-style (implies '-l')
|
|
210
|
+
-a action : action ('genclass-ruby' or 'genclass-java')
|
|
211
|
+
(use '-ha action' option to show action properites)
|
|
212
|
+
END
|
|
213
|
+
# -I path : path for template of action
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
##----- begin
|
|
218
|
+
# filename: lib/kwalify/main.rb
|
|
219
|
+
@@messages[:command_option_actionnoschema] = "schema filename is not specified."
|
|
220
|
+
@@messages[:command_option_noaction] = "command-line option '-f' or '-m' required."
|
|
221
|
+
@@messages[:command_option_notemplate] = "%s: invalid action (template not found).\n"
|
|
222
|
+
@@messages[:schema_empty] = "%s: empty schema.\n"
|
|
223
|
+
@@messages[:validation_empty] = "%s#%d: empty.\n"
|
|
224
|
+
@@messages[:validation_valid] = "%s#%d: valid.\n"
|
|
225
|
+
@@messages[:validation_invalid] = "%s#%d: INVALID\n"
|
|
226
|
+
@@messages[:command_option_noschema] = "-%s: schema filename required."
|
|
227
|
+
@@messages[:command_option_noaction] = "-%s: action required."
|
|
228
|
+
@@messages[:command_option_notpath] = "-%s: template path required."
|
|
229
|
+
@@messages[:command_property_invalid] = "%s: invalid property."
|
|
230
|
+
@@messages[:command_option_invalid] = "-%s: invalid command option."
|
|
231
|
+
# --
|
|
232
|
+
# filename: lib/kwalify/rule.rb
|
|
233
|
+
@@messages[:schema_notmap] = "schema definition is not a mapping."
|
|
234
|
+
@@messages[:key_unknown] = "unknown key."
|
|
235
|
+
@@messages[:type_notstr] = "not a string."
|
|
236
|
+
@@messages[:type_unknown] = "unknown type."
|
|
237
|
+
@@messages[:classname_notmap] = "available only with map type."
|
|
238
|
+
@@messages[:required_notbool] = "not a boolean."
|
|
239
|
+
@@messages[:pattern_notstr] = "not a string (or regexp)"
|
|
240
|
+
@@messages[:pattern_notmatch] = "should be '/..../'."
|
|
241
|
+
@@messages[:pattern_syntaxerr] = "has regexp error."
|
|
242
|
+
@@messages[:enum_notseq] = "not a sequence."
|
|
243
|
+
@@messages[:enum_notscalar] = "not available with seq or map."
|
|
244
|
+
@@messages[:enum_type_unmatch] = "%s type expected."
|
|
245
|
+
@@messages[:enum_duplicate] = "duplicated enum value."
|
|
246
|
+
@@messages[:assert_notstr] = "not a string."
|
|
247
|
+
@@messages[:assert_noval] = "'val' is not used."
|
|
248
|
+
@@messages[:assert_syntaxerr] = "expression syntax error."
|
|
249
|
+
@@messages[:range_notmap] = "not a mapping."
|
|
250
|
+
@@messages[:range_notscalar] = "is available only with scalar type."
|
|
251
|
+
@@messages[:range_type_unmatch] = "not a %s."
|
|
252
|
+
@@messages[:range_undefined] = "undefined key."
|
|
253
|
+
@@messages[:range_twomax] = "both 'max' and 'max-ex' are not available at once."
|
|
254
|
+
@@messages[:range_twomin] = "both 'min' and 'min-ex' are not available at once."
|
|
255
|
+
@@messages[:range_maxltmin] = "max '%s' is less than min '%s'."
|
|
256
|
+
@@messages[:range_maxleminex] = "max '%s' is less than or equal to min-ex '%s'."
|
|
257
|
+
@@messages[:range_maxexlemin] = "max-ex '%s' is less than or equal to min '%s'."
|
|
258
|
+
@@messages[:range_maxexleminex] = "max-ex '%s' is less than or equal to min-ex '%s'."
|
|
259
|
+
@@messages[:length_notmap] = "not a mapping."
|
|
260
|
+
@@messages[:length_nottext] = "is available only with string or text."
|
|
261
|
+
@@messages[:length_notint] = "not an integer."
|
|
262
|
+
@@messages[:length_undefined] = "undefined key."
|
|
263
|
+
@@messages[:length_twomax] = "both 'max' and 'max-ex' are not available at once."
|
|
264
|
+
@@messages[:length_twomin] = "both 'min' and 'min-ex' are not available at once."
|
|
265
|
+
@@messages[:length_maxltmin] = "max '%s' is less than min '%s'."
|
|
266
|
+
@@messages[:length_maxleminex] = "max '%s' is less than or equal to min-ex '%s'."
|
|
267
|
+
@@messages[:length_maxexlemin] = "max-ex '%s' is less than or equal to min '%s'."
|
|
268
|
+
@@messages[:length_maxexleminex] = "max-ex '%s' is less than or equal to min-ex '%s'."
|
|
269
|
+
@@messages[:ident_notbool] = "not a boolean."
|
|
270
|
+
@@messages[:ident_notscalar] = "is available only with a scalar type."
|
|
271
|
+
@@messages[:ident_onroot] = "is not available on root element."
|
|
272
|
+
@@messages[:ident_notmap] = "is available only with an element of mapping."
|
|
273
|
+
@@messages[:unique_notbool] = "not a boolean."
|
|
274
|
+
@@messages[:unique_notscalar] = "is available only with a scalar type."
|
|
275
|
+
@@messages[:unique_onroot] = "is not available on root element."
|
|
276
|
+
@@messages[:sequence_notseq] = "not a sequence."
|
|
277
|
+
@@messages[:sequence_noelem] = "required one element."
|
|
278
|
+
@@messages[:sequence_toomany] = "required just one element."
|
|
279
|
+
@@messages[:mapping_notmap] = "not a mapping."
|
|
280
|
+
@@messages[:mapping_noelem] = "required at least one element."
|
|
281
|
+
@@messages[:seq_nosequence] = "type 'seq' requires 'sequence:'."
|
|
282
|
+
@@messages[:seq_conflict] = "not available with sequence."
|
|
283
|
+
@@messages[:map_nomapping] = "type 'map' requires 'mapping:'."
|
|
284
|
+
@@messages[:map_conflict] = "not available with mapping."
|
|
285
|
+
@@messages[:scalar_conflict] = "not available with scalar type."
|
|
286
|
+
@@messages[:enum_conflict] = "not available with 'enum:'."
|
|
287
|
+
# --
|
|
288
|
+
# filename: lib/kwalify/validator.rb
|
|
289
|
+
@@messages[:required_novalue] = "value required but none."
|
|
290
|
+
@@messages[:type_unmatch] = "not a %s."
|
|
291
|
+
@@messages[:assert_failed] = "assertion expression failed (%s)."
|
|
292
|
+
@@messages[:enum_notexist] = "invalid %s value."
|
|
293
|
+
@@messages[:pattern_unmatch] = "not matched to pattern %s."
|
|
294
|
+
@@messages[:range_toolarge] = "too large (> max %s)."
|
|
295
|
+
@@messages[:range_toosmall] = "too small (< min %s)."
|
|
296
|
+
@@messages[:range_toolargeex] = "too large (>= max %s)."
|
|
297
|
+
@@messages[:range_toosmallex] = "too small (<= min %s)."
|
|
298
|
+
@@messages[:length_toolong] = "too long (length %d > max %d)."
|
|
299
|
+
@@messages[:length_tooshort] = "too short (length %d < min %d)."
|
|
300
|
+
@@messages[:length_toolongex] = "too long (length %d >= max %d)."
|
|
301
|
+
@@messages[:length_tooshortex] = "too short (length %d <= min %d)."
|
|
302
|
+
@@messages[:value_notunique] = "is already used at '%s'."
|
|
303
|
+
@@messages[:required_nokey] = "key '%s:' is required."
|
|
304
|
+
@@messages[:key_undefined] = "key '%s' is undefined."
|
|
305
|
+
# --
|
|
306
|
+
# filename: lib/kwalify/yaml-parser.rb
|
|
307
|
+
@@messages[:flow_hastail] = "flow style sequence is closed but got '%s'."
|
|
308
|
+
@@messages[:flow_eof] = "found EOF when parsing flow style."
|
|
309
|
+
@@messages[:flow_noseqitem] = "sequence item required (or last comma is extra)."
|
|
310
|
+
@@messages[:flow_seqnotclosed] = "flow style sequence requires ']'."
|
|
311
|
+
@@messages[:flow_mapnoitem] = "mapping item required (or last comma is extra)."
|
|
312
|
+
@@messages[:flow_mapnotclosed] = "flow style mapping requires '}'."
|
|
313
|
+
@@messages[:flow_nocolon] = "':' expected but got '%s'."
|
|
314
|
+
@@messages[:anchor_duplicated] = "anchor '%s' is already used."
|
|
315
|
+
@@messages[:alias_extradata] = "alias cannot take any data."
|
|
316
|
+
@@messages[:anchor_notfound] = "anchor '%s' not found"
|
|
317
|
+
@@messages[:sequence_noitem] = "sequence item is expected."
|
|
318
|
+
@@messages[:sequence_badindent] = "illegal indent of sequence."
|
|
319
|
+
@@messages[:mapping_noitem] = "mapping item is expected."
|
|
320
|
+
@@messages[:mapping_badindent] = "illegal indent of mapping."
|
|
321
|
+
# --
|
|
322
|
+
##----- end
|
|
323
|
+
|
|
324
|
+
|
|
325
|
+
|
|
326
|
+
|
|
327
|
+
@@words = {}
|
|
328
|
+
|
|
329
|
+
def self.word(key)
|
|
330
|
+
return @@words[key] || key
|
|
331
|
+
end
|
|
332
|
+
|
|
333
|
+
@@words['str'] = 'string'
|
|
334
|
+
@@words['int'] = 'integer'
|
|
335
|
+
@@words['bool'] = 'boolean'
|
|
336
|
+
@@words['seq'] = 'sequence'
|
|
337
|
+
@@words['map'] = 'mapping'
|
|
338
|
+
|
|
339
|
+
end
|
|
340
|
+
#--end of require 'kwalify/messages'
|
|
341
|
+
#--begin of require 'kwalify/errors'
|
|
342
|
+
###
|
|
343
|
+
### $Rev: 48 $
|
|
344
|
+
### $Release: 0.6.0 $
|
|
345
|
+
### copyright(c) 2005 kuwata-lab all rights reserved.
|
|
346
|
+
###
|
|
347
|
+
|
|
348
|
+
#--already included require 'kwalify/messages'
|
|
349
|
+
|
|
350
|
+
module Kwalify
|
|
351
|
+
|
|
352
|
+
class KwalifyError < StandardError
|
|
353
|
+
end
|
|
354
|
+
|
|
355
|
+
|
|
356
|
+
class AssertionError < KwalifyError
|
|
357
|
+
def initialize(msg)
|
|
358
|
+
super("*** assertion error: " + msg)
|
|
359
|
+
end
|
|
360
|
+
end
|
|
361
|
+
|
|
362
|
+
|
|
363
|
+
class BaseError < KwalifyError
|
|
364
|
+
def initialize(message="", path=nil, value=nil, rule=nil, error_symbol=nil)
|
|
365
|
+
super(message)
|
|
366
|
+
@path = path
|
|
367
|
+
@rule = rule
|
|
368
|
+
@value = value
|
|
369
|
+
@error_symbol = error_symbol
|
|
370
|
+
end
|
|
371
|
+
attr_reader :error_symbol, :rule, :path, :value
|
|
372
|
+
attr_accessor :linenum
|
|
373
|
+
|
|
374
|
+
def path
|
|
375
|
+
return @path == '' ? "/" : @path
|
|
376
|
+
end
|
|
377
|
+
|
|
378
|
+
alias :_to_s :to_s
|
|
379
|
+
|
|
380
|
+
def message
|
|
381
|
+
_to_s
|
|
382
|
+
end
|
|
383
|
+
|
|
384
|
+
def to_s
|
|
385
|
+
return "[#{path()}] #{message()}"
|
|
386
|
+
end
|
|
387
|
+
|
|
388
|
+
def <=>(ex)
|
|
389
|
+
return @linenum <=> ex.linenum
|
|
390
|
+
end
|
|
391
|
+
end
|
|
392
|
+
|
|
393
|
+
|
|
394
|
+
class SchemaError < BaseError
|
|
395
|
+
def initialize(message="", path=nil, rule=nil, value=nil, error_symbol=nil)
|
|
396
|
+
super(message, path, rule, value, error_symbol)
|
|
397
|
+
end
|
|
398
|
+
end
|
|
399
|
+
|
|
400
|
+
|
|
401
|
+
class ValidationError < BaseError
|
|
402
|
+
def initialize(message="", path=nil, rule=nil, value=nil, error_symbol=nil)
|
|
403
|
+
super(message, path, rule, value, error_symbol)
|
|
404
|
+
end
|
|
405
|
+
end
|
|
406
|
+
|
|
407
|
+
|
|
408
|
+
class YamlSyntaxError < KwalifyError
|
|
409
|
+
def initialize(msg, linenum, error_symbol)
|
|
410
|
+
super("line #{linenum}: #{msg}")
|
|
411
|
+
@linenum = linenum
|
|
412
|
+
@error_symbol
|
|
413
|
+
end
|
|
414
|
+
attr_accessor :linenum, :error_symbol
|
|
415
|
+
end
|
|
416
|
+
|
|
417
|
+
|
|
418
|
+
module ErrorHelper
|
|
419
|
+
|
|
420
|
+
def assert_error(message="")
|
|
421
|
+
raise AssertionError.new(message)
|
|
422
|
+
end
|
|
423
|
+
|
|
424
|
+
def validate_error(error_symbol, rule, path, val, args=nil)
|
|
425
|
+
msg = _build_message(error_symbol, val, args);
|
|
426
|
+
return ValidationError.new(msg, path, val, rule, error_symbol)
|
|
427
|
+
end
|
|
428
|
+
|
|
429
|
+
def schema_error(error_symbol, rule, path, val, args=nil)
|
|
430
|
+
msg = _build_message(error_symbol, val, args);
|
|
431
|
+
return SchemaError.new(msg, path, val, rule, error_symbol)
|
|
432
|
+
end
|
|
433
|
+
|
|
434
|
+
def _build_message(message_key, val, args)
|
|
435
|
+
msg = Kwalify.msg(message_key)
|
|
436
|
+
assert_error("message_key=#{message_key.inspect}") unless msg
|
|
437
|
+
msg = msg % args if args
|
|
438
|
+
msg = "'#{val.to_s.gsub(/\n/, '\n')}': #{msg}" if val != nil && Types.scalar?(val)
|
|
439
|
+
return msg;
|
|
440
|
+
end
|
|
441
|
+
|
|
442
|
+
end
|
|
443
|
+
|
|
444
|
+
extend ErrorHelper
|
|
445
|
+
|
|
446
|
+
end
|
|
447
|
+
#--end of require 'kwalify/errors'
|
|
448
|
+
#--begin of require 'kwalify/rule'
|
|
449
|
+
###
|
|
450
|
+
### $Rev: 51 $
|
|
451
|
+
### $Release: 0.6.0 $
|
|
452
|
+
### copyright(c) 2005 kuwata-lab all rights reserved.
|
|
453
|
+
###
|
|
454
|
+
|
|
455
|
+
#--already included require 'kwalify/messages'
|
|
456
|
+
#--already included require 'kwalify/errors'
|
|
457
|
+
#--already included require 'kwalify/types'
|
|
458
|
+
|
|
459
|
+
module Kwalify
|
|
460
|
+
|
|
461
|
+
class Rule
|
|
462
|
+
include Kwalify::ErrorHelper
|
|
463
|
+
|
|
464
|
+
def initialize(hash=nil, parent=nil)
|
|
465
|
+
init(hash, "", {}) if hash
|
|
466
|
+
@parent = parent
|
|
467
|
+
end
|
|
468
|
+
|
|
469
|
+
attr_accessor :parent
|
|
470
|
+
#attr_reader :id
|
|
471
|
+
attr_reader :name
|
|
472
|
+
attr_reader :desc
|
|
473
|
+
attr_reader :enum
|
|
474
|
+
attr_reader :required
|
|
475
|
+
attr_reader :type
|
|
476
|
+
attr_reader :type_class
|
|
477
|
+
attr_reader :pattern
|
|
478
|
+
attr_reader :regexp
|
|
479
|
+
attr_reader :sequence
|
|
480
|
+
attr_reader :mapping
|
|
481
|
+
attr_reader :assert
|
|
482
|
+
attr_reader :assert_proc
|
|
483
|
+
attr_reader :range
|
|
484
|
+
attr_reader :length
|
|
485
|
+
attr_reader :ident
|
|
486
|
+
attr_reader :unique
|
|
487
|
+
attr_reader :classname
|
|
488
|
+
|
|
489
|
+
def init(hash, path="", rule_table={})
|
|
490
|
+
unless hash.is_a?(Hash)
|
|
491
|
+
#* key=:schema_notmap msg="schema definition is not a mapping."
|
|
492
|
+
raise Kwalify.schema_error(:schema_notmap, nil, (!path || path.empty? ? "/" : path), nil)
|
|
493
|
+
end
|
|
494
|
+
rule = self
|
|
495
|
+
rule_table[hash.__id__] = rule
|
|
496
|
+
## 'type:' entry
|
|
497
|
+
_init_type_value(hash['type'], rule, path)
|
|
498
|
+
## other entries
|
|
499
|
+
hash.each do |key, val|
|
|
500
|
+
code = key.intern
|
|
501
|
+
curr_path = "#{path}/#{key}"
|
|
502
|
+
case code
|
|
503
|
+
#when "id"
|
|
504
|
+
# @id = val
|
|
505
|
+
when :type ; # done
|
|
506
|
+
when :name ; _init_name_value( val, rule, path)
|
|
507
|
+
when :desc ; _init_desc_value( val, rule, path)
|
|
508
|
+
when :required ; _init_required_value(val, rule, path)
|
|
509
|
+
when :pattern ; _init_pattern_value( val, rule, path)
|
|
510
|
+
when :enum ; _init_enum_value( val, rule, path)
|
|
511
|
+
when :assert ; _init_assert_value( val, rule, path)
|
|
512
|
+
when :range ; _init_range_value( val, rule, path)
|
|
513
|
+
when :length ; _init_length_value( val, rule, path)
|
|
514
|
+
when :ident ; _init_ident_value( val, rule, path)
|
|
515
|
+
when :unique ; _init_unique_value( val, rule, path)
|
|
516
|
+
when :sequence ; _init_sequence_value(val, rule, path, rule_table)
|
|
517
|
+
when :mapping ; _init_mapping_value( val, rule, path, rule_table)
|
|
518
|
+
when :classname ; _init_classname_value(val, rule, path)
|
|
519
|
+
else
|
|
520
|
+
#* key=:key_unknown msg="unknown key."
|
|
521
|
+
raise schema_error(:key_unknown, rule, curr_path, "#{key}:")
|
|
522
|
+
end
|
|
523
|
+
end
|
|
524
|
+
check_confliction(hash, rule, path)
|
|
525
|
+
return self
|
|
526
|
+
end # end of def init
|
|
527
|
+
|
|
528
|
+
|
|
529
|
+
private
|
|
530
|
+
|
|
531
|
+
|
|
532
|
+
def _init_type_value(val, rule, path)
|
|
533
|
+
@type = val
|
|
534
|
+
@type = Types::DEFAULT_TYPE if @type == nil
|
|
535
|
+
unless @type.is_a?(String)
|
|
536
|
+
#* key=:type_notstr msg="not a string."
|
|
537
|
+
raise schema_error(:type_notstr, rule, "#{path}/type", @type.to_s)
|
|
538
|
+
end
|
|
539
|
+
@type_class = Types.type_class(@type)
|
|
540
|
+
#if @type_class == nil
|
|
541
|
+
# begin
|
|
542
|
+
# @type_class = Kernel.const_get(@type)
|
|
543
|
+
# rescue NameError
|
|
544
|
+
# end
|
|
545
|
+
#end
|
|
546
|
+
unless @type_class
|
|
547
|
+
#* key=:type_unknown msg="unknown type."
|
|
548
|
+
raise schema_error(:type_unknown, rule, "#{path}/type", @type.to_s)
|
|
549
|
+
end
|
|
550
|
+
end
|
|
551
|
+
|
|
552
|
+
|
|
553
|
+
def _init_classname_value(val, rule, path)
|
|
554
|
+
@classname = val
|
|
555
|
+
unless @type == 'map'
|
|
556
|
+
#* key=:classname_notmap msg="available only with map type."
|
|
557
|
+
raise schema_error(:classname_notmap, rule, "#{path}/classname", 'classname:')
|
|
558
|
+
end
|
|
559
|
+
end
|
|
560
|
+
|
|
561
|
+
|
|
562
|
+
def _init_name_value(val, rule, path)
|
|
563
|
+
@name = val
|
|
564
|
+
end
|
|
565
|
+
|
|
566
|
+
|
|
567
|
+
def _init_desc_value(val, rule, path)
|
|
568
|
+
@desc = val
|
|
569
|
+
end
|
|
570
|
+
|
|
571
|
+
|
|
572
|
+
def _init_required_value(val, rule, path)
|
|
573
|
+
@required = val
|
|
574
|
+
unless val.is_a?(Boolean) #|| val == nil
|
|
575
|
+
#* key=:required_notbool msg="not a boolean."
|
|
576
|
+
raise schema_error(:required_notbool, rule, "#{path}/required", val)
|
|
577
|
+
end
|
|
578
|
+
end
|
|
579
|
+
|
|
580
|
+
def _init_pattern_value(val, rule, path)
|
|
581
|
+
@pattern = val
|
|
582
|
+
unless val.is_a?(String) || val.is_a?(Regexp)
|
|
583
|
+
#* key=:pattern_notstr msg="not a string (or regexp)"
|
|
584
|
+
raise schema_error(:pattern_notstr, rule, "#{path}/pattern", val)
|
|
585
|
+
end
|
|
586
|
+
unless val =~ /\A\/(.*)\/([mi]?[mi]?)\z/
|
|
587
|
+
#* key=:pattern_notmatch msg="should be '/..../'."
|
|
588
|
+
raise schema_error(:pattern_notmatch, rule, "#{path}/pattern", val)
|
|
589
|
+
end
|
|
590
|
+
pat = $1; opt = $2
|
|
591
|
+
flag = 0
|
|
592
|
+
flag += Regexp::IGNORECASE if opt.include?("i")
|
|
593
|
+
flag += Regexp::MULTILINE if opt.include?("m")
|
|
594
|
+
begin
|
|
595
|
+
@regexp = Regexp.compile(pat, flag)
|
|
596
|
+
rescue RegexpError => ex
|
|
597
|
+
#* key=:pattern_syntaxerr msg="has regexp error."
|
|
598
|
+
raise schema_error(:pattern_syntaxerr, rule, "#{path}/pattern", val)
|
|
599
|
+
end
|
|
600
|
+
end
|
|
601
|
+
|
|
602
|
+
|
|
603
|
+
def _init_enum_value(val, rule, path)
|
|
604
|
+
@enum = val
|
|
605
|
+
unless val.is_a?(Array)
|
|
606
|
+
#* key=:enum_notseq msg="not a sequence."
|
|
607
|
+
raise schema_error(:enum_notseq, rule, "#{path}/enum", val)
|
|
608
|
+
end
|
|
609
|
+
if Types.collection_type?(@type) # unless Kwalify.scalar_class?(@type_class)
|
|
610
|
+
#* key=:enum_notscalar msg="not available with seq or map."
|
|
611
|
+
raise schema_error(:enum_notscalar, rule, path, 'enum:')
|
|
612
|
+
end
|
|
613
|
+
elem_table = {}
|
|
614
|
+
@enum.each do |elem|
|
|
615
|
+
unless elem.is_a?(@type_class)
|
|
616
|
+
#* key=:enum_type_unmatch msg="%s type expected."
|
|
617
|
+
raise schema_error(:enum_type_unmatch, rule, "#{path}/enum", elem, [Kwalify.word(@type)])
|
|
618
|
+
end
|
|
619
|
+
if elem_table[elem]
|
|
620
|
+
#* key=:enum_duplicate msg="duplicated enum value."
|
|
621
|
+
raise schema_error(:enum_duplicate, rule, "#{path}/enum", elem.to_s)
|
|
622
|
+
end
|
|
623
|
+
elem_table[elem] = true
|
|
624
|
+
end
|
|
625
|
+
end
|
|
626
|
+
|
|
627
|
+
|
|
628
|
+
def _init_assert_value(val, rule, path)
|
|
629
|
+
@assert = val
|
|
630
|
+
unless val.is_a?(String)
|
|
631
|
+
#* key=:assert_notstr msg="not a string."
|
|
632
|
+
raise schema_error(:assert_notstr, rule, "#{path}/assert", val)
|
|
633
|
+
end
|
|
634
|
+
unless val =~ /\bval\b/
|
|
635
|
+
#* key=:assert_noval msg="'val' is not used."
|
|
636
|
+
raise schema_error(:assert_noval, rule, "#{path}/assert", val)
|
|
637
|
+
end
|
|
638
|
+
begin
|
|
639
|
+
@assert_proc = eval "proc { |val| #{val} }"
|
|
640
|
+
rescue SyntaxError => ex
|
|
641
|
+
#* key=:assert_syntaxerr msg="expression syntax error."
|
|
642
|
+
raise schema_error(:assert_syntaxerr, rule, "#{path}/assert", val)
|
|
643
|
+
end
|
|
644
|
+
end
|
|
645
|
+
|
|
646
|
+
|
|
647
|
+
def _init_range_value(val, rule, path)
|
|
648
|
+
@range = val
|
|
649
|
+
curr_path = "#{path}/range"
|
|
650
|
+
unless val.is_a?(Hash)
|
|
651
|
+
#* key=:range_notmap msg="not a mapping."
|
|
652
|
+
raise schema_error(:range_notmap, rule, curr_path, val)
|
|
653
|
+
end
|
|
654
|
+
if Types.collection_type?(@type) || @type == 'bool'
|
|
655
|
+
#* key=:range_notscalar msg="is available only with scalar type."
|
|
656
|
+
raise schema_error(:range_notscalar, rule, path, 'range:')
|
|
657
|
+
end
|
|
658
|
+
val.each do |k, v|
|
|
659
|
+
case k
|
|
660
|
+
when 'max', 'min', 'max-ex', 'min-ex'
|
|
661
|
+
unless v.is_a?(@type_class)
|
|
662
|
+
typename = Kwalify.word(@type) || @type
|
|
663
|
+
#* key=:range_type_unmatch msg="not a %s."
|
|
664
|
+
raise schema_error(:range_type_unmatch, rule, "#{curr_path}/#{k}", v, [typename])
|
|
665
|
+
end
|
|
666
|
+
else
|
|
667
|
+
#* key=:range_undefined msg="undefined key."
|
|
668
|
+
raise schema_error(:range_undefined, rule, "#{curr_path}/#{k}", "#{k}:")
|
|
669
|
+
end
|
|
670
|
+
end
|
|
671
|
+
if val.key?('max') && val.key?('max-ex')
|
|
672
|
+
#* key=:range_twomax msg="both 'max' and 'max-ex' are not available at once."
|
|
673
|
+
raise schema_error(:range_twomax, rule, curr_path, nil)
|
|
674
|
+
end
|
|
675
|
+
if val.key?('min') && val.key?('min-ex')
|
|
676
|
+
#* key=:range_twomin msg="both 'min' and 'min-ex' are not available at once."
|
|
677
|
+
raise schema_error(:range_twomin, rule, curr_path, nil)
|
|
678
|
+
end
|
|
679
|
+
max, min, max_ex, min_ex = val['max'], val['min'], val['max-ex'], val['min-ex']
|
|
680
|
+
if max
|
|
681
|
+
if min && max < min
|
|
682
|
+
#* key=:range_maxltmin msg="max '%s' is less than min '%s'."
|
|
683
|
+
raise validate_error(:range_maxltmin, rule, curr_path, nil, [max, min])
|
|
684
|
+
elsif min_ex && max <= min_ex
|
|
685
|
+
#* key=:range_maxleminex msg="max '%s' is less than or equal to min-ex '%s'."
|
|
686
|
+
raise validate_error(:range_maxleminex, rule, curr_path, nil, [max, min_ex])
|
|
687
|
+
end
|
|
688
|
+
elsif max_ex
|
|
689
|
+
if min && max_ex <= min
|
|
690
|
+
#* key=:range_maxexlemin msg="max-ex '%s' is less than or equal to min '%s'."
|
|
691
|
+
raise validate_error(:range_maxexlemin, rule, curr_path, nil, [max_ex, min])
|
|
692
|
+
elsif min_ex && max_ex <= min_ex
|
|
693
|
+
#* key=:range_maxexleminex msg="max-ex '%s' is less than or equal to min-ex '%s'."
|
|
694
|
+
raise validate_error(:range_maxexleminex, rule, curr_path, nil, [max_ex, min_ex])
|
|
695
|
+
end
|
|
696
|
+
end
|
|
697
|
+
end
|
|
698
|
+
|
|
699
|
+
|
|
700
|
+
def _init_length_value(val, rule, path)
|
|
701
|
+
@length = val
|
|
702
|
+
curr_path = "#{path}/length"
|
|
703
|
+
unless val.is_a?(Hash)
|
|
704
|
+
#* key=:length_notmap msg="not a mapping."
|
|
705
|
+
raise schema_error(:length_notmap, rule, curr_path, val)
|
|
706
|
+
end
|
|
707
|
+
unless @type == 'str' || @type == 'text'
|
|
708
|
+
#* key=:length_nottext msg="is available only with string or text."
|
|
709
|
+
raise schema_error(:length_nottext, rule, path, 'length:')
|
|
710
|
+
end
|
|
711
|
+
val.each do |k, v|
|
|
712
|
+
case k
|
|
713
|
+
when 'max', 'min', 'max-ex', 'min-ex'
|
|
714
|
+
unless v.is_a?(Integer)
|
|
715
|
+
#* key=:length_notint msg="not an integer."
|
|
716
|
+
raise schema_error(:length_notint, rule, "#{curr_path}/#{k}", v)
|
|
717
|
+
end
|
|
718
|
+
else
|
|
719
|
+
#* key=:length_undefined msg="undefined key."
|
|
720
|
+
raise schema_error(:length_undefined, rule, "#{curr_path}/#{k}", "#{k}:")
|
|
721
|
+
end
|
|
722
|
+
end
|
|
723
|
+
if val.key?('max') && val.key?('max-ex')
|
|
724
|
+
#* key=:length_twomax msg="both 'max' and 'max-ex' are not available at once."
|
|
725
|
+
raise schema_error(:length_twomax, rule, curr_path, nil)
|
|
726
|
+
end
|
|
727
|
+
if val.key?('min') && val.key?('min-ex')
|
|
728
|
+
#* key=:length_twomin msg="both 'min' and 'min-ex' are not available at once."
|
|
729
|
+
raise schema_error(:length_twomin, rule, curr_path, nil)
|
|
730
|
+
end
|
|
731
|
+
max, min, max_ex, min_ex = val['max'], val['min'], val['max-ex'], val['min-ex']
|
|
732
|
+
if max
|
|
733
|
+
if min && max < min
|
|
734
|
+
#* key=:length_maxltmin msg="max '%s' is less than min '%s'."
|
|
735
|
+
raise validate_error(:length_maxltmin, rule, curr_path, nil, [max, min])
|
|
736
|
+
elsif min_ex && max <= min_ex
|
|
737
|
+
#* key=:length_maxleminex msg="max '%s' is less than or equal to min-ex '%s'."
|
|
738
|
+
raise validate_error(:length_maxleminex, rule, curr_path, nil, [max, min_ex])
|
|
739
|
+
end
|
|
740
|
+
elsif max_ex
|
|
741
|
+
if min && max_ex <= min
|
|
742
|
+
#* key=:length_maxexlemin msg="max-ex '%s' is less than or equal to min '%s'."
|
|
743
|
+
raise validate_error(:length_maxexlemin, rule, curr_path, nil, [max_ex, min])
|
|
744
|
+
elsif min_ex && max_ex <= min_ex
|
|
745
|
+
#* key=:length_maxexleminex msg="max-ex '%s' is less than or equal to min-ex '%s'."
|
|
746
|
+
raise validate_error(:length_maxexleminex, rule, curr_path, nil, [max_ex, min_ex])
|
|
747
|
+
end
|
|
748
|
+
end
|
|
749
|
+
end
|
|
750
|
+
|
|
751
|
+
|
|
752
|
+
def _init_ident_value(val, rule, path)
|
|
753
|
+
@ident = val
|
|
754
|
+
@required = true
|
|
755
|
+
unless val.is_a?(Boolean)
|
|
756
|
+
#* key=:ident_notbool msg="not a boolean."
|
|
757
|
+
raise schema_error(:ident_notbool, rule, "#{path}/ident", val)
|
|
758
|
+
end
|
|
759
|
+
if @type == 'map' || @type == 'seq'
|
|
760
|
+
#* key=:ident_notscalar msg="is available only with a scalar type."
|
|
761
|
+
raise schema_error(:ident_notscalar, rule, path, "ident:")
|
|
762
|
+
end
|
|
763
|
+
if path.empty?
|
|
764
|
+
#* key=:ident_onroot msg="is not available on root element."
|
|
765
|
+
raise schema_error(:ident_onroot, rule, "/", "ident:")
|
|
766
|
+
end
|
|
767
|
+
unless @parent && @parent.type == 'map'
|
|
768
|
+
#* key=:ident_notmap msg="is available only with an element of mapping."
|
|
769
|
+
raise schema_error(:ident_notmap, rule, path, "ident:")
|
|
770
|
+
end
|
|
771
|
+
end
|
|
772
|
+
|
|
773
|
+
|
|
774
|
+
def _init_unique_value(val, rule, path)
|
|
775
|
+
@unique = val
|
|
776
|
+
unless val.is_a?(Boolean)
|
|
777
|
+
#* key=:unique_notbool msg="not a boolean."
|
|
778
|
+
raise schema_error(:unique_notbool, rule, "#{path}/unique", val)
|
|
779
|
+
end
|
|
780
|
+
if @type == 'map' || @type == 'seq'
|
|
781
|
+
#* key=:unique_notscalar msg="is available only with a scalar type."
|
|
782
|
+
raise schema_error(:unique_notscalar, rule, path, "unique:")
|
|
783
|
+
end
|
|
784
|
+
if path.empty?
|
|
785
|
+
#* key=:unique_onroot msg="is not available on root element."
|
|
786
|
+
raise schema_error(:unique_onroot, rule, "/", "unique:")
|
|
787
|
+
end
|
|
788
|
+
end
|
|
789
|
+
|
|
790
|
+
|
|
791
|
+
def _init_sequence_value(val, rule, path, rule_table)
|
|
792
|
+
if val != nil && !val.is_a?(Array)
|
|
793
|
+
#* key=:sequence_notseq msg="not a sequence."
|
|
794
|
+
raise schema_error(:sequence_notseq, rule, "#{path}/sequence", val)
|
|
795
|
+
elsif val == nil || val.empty?
|
|
796
|
+
#* key=:sequence_noelem msg="required one element."
|
|
797
|
+
raise schema_error(:sequence_noelem, rule, "#{path}/sequence", val)
|
|
798
|
+
elsif val.length > 1
|
|
799
|
+
#* key=:sequence_toomany msg="required just one element."
|
|
800
|
+
raise schema_error(:sequence_toomany, rule, "#{path}/sequence", val)
|
|
801
|
+
else
|
|
802
|
+
elem = val[0]
|
|
803
|
+
elem ||= {}
|
|
804
|
+
i = 0 # or 1? *index*
|
|
805
|
+
rule = rule_table[elem.__id__]
|
|
806
|
+
rule ||= Rule.new(nil, self).init(elem, "#{path}/sequence/#{i}", rule_table)
|
|
807
|
+
@sequence = [ rule ]
|
|
808
|
+
end
|
|
809
|
+
end
|
|
810
|
+
|
|
811
|
+
|
|
812
|
+
def _init_mapping_value(val, rule, path, rule_table)
|
|
813
|
+
if val != nil && !val.is_a?(Hash)
|
|
814
|
+
#* key=:mapping_notmap msg="not a mapping."
|
|
815
|
+
raise schema_error(:mapping_notmap, rule, "#{path}/mapping", val)
|
|
816
|
+
elsif val == nil || (val.empty? && !val.default)
|
|
817
|
+
#* key=:mapping_noelem msg="required at least one element."
|
|
818
|
+
raise schema_error(:mapping_noelem, rule, "#{path}/mapping", val)
|
|
819
|
+
else
|
|
820
|
+
@mapping = {}
|
|
821
|
+
if val.default
|
|
822
|
+
elem = val.default # hash
|
|
823
|
+
rule = rule_table[elem.__id__]
|
|
824
|
+
rule ||= Rule.new(nil, self).init(elem, "#{path}/mapping/=", rule_table)
|
|
825
|
+
@mapping.default = rule
|
|
826
|
+
end
|
|
827
|
+
val.each do |k, v|
|
|
828
|
+
##* key=:key_duplicate msg="key duplicated."
|
|
829
|
+
#raise schema_error(:key_duplicate, rule, "#{path}/mapping", key) if @mapping.key?(key)
|
|
830
|
+
v ||= {}
|
|
831
|
+
rule = rule_table[v.__id__]
|
|
832
|
+
rule ||= Rule.new(nil, self).init(v, "#{path}/mapping/#{k}", rule_table)
|
|
833
|
+
if k == '='
|
|
834
|
+
@mapping.default = rule
|
|
835
|
+
else
|
|
836
|
+
@mapping[k] = rule
|
|
837
|
+
end
|
|
838
|
+
end if val
|
|
839
|
+
end
|
|
840
|
+
end
|
|
841
|
+
|
|
842
|
+
|
|
843
|
+
def check_confliction(hash, rule, path)
|
|
844
|
+
if @type == 'seq'
|
|
845
|
+
#* key=:seq_nosequence msg="type 'seq' requires 'sequence:'."
|
|
846
|
+
raise schema_error(:seq_nosequence, rule, path, nil) unless hash.key?('sequence')
|
|
847
|
+
#* key=:seq_conflict msg="not available with sequence."
|
|
848
|
+
raise schema_error(:seq_conflict, rule, path, 'enum:') if @enum
|
|
849
|
+
raise schema_error(:seq_conflict, rule, path, 'pattern:') if @pattern
|
|
850
|
+
raise schema_error(:seq_conflict, rule, path, 'mapping:') if @mapping
|
|
851
|
+
raise schema_error(:seq_conflict, rule, path, 'range:') if @range
|
|
852
|
+
raise schema_error(:seq_conflict, rule, path, 'length:') if @length
|
|
853
|
+
elsif @type == 'map'
|
|
854
|
+
#* key=:map_nomapping msg="type 'map' requires 'mapping:'."
|
|
855
|
+
raise schema_error(:map_nomapping, rule, path, nil) unless hash.key?('mapping')
|
|
856
|
+
#* key=:map_conflict msg="not available with mapping."
|
|
857
|
+
raise schema_error(:map_conflict, rule, path, 'enum:') if @enum
|
|
858
|
+
raise schema_error(:map_conflict, rule, path, 'pattern:') if @pattern
|
|
859
|
+
raise schema_error(:map_conflict, rule, path, 'sequence:') if @sequence
|
|
860
|
+
raise schema_error(:map_conflict, rule, path, 'range:') if @range
|
|
861
|
+
raise schema_error(:map_conflict, rule, path, 'length:') if @length
|
|
862
|
+
else
|
|
863
|
+
#* key=:scalar_conflict msg="not available with scalar type."
|
|
864
|
+
raise schema_error(:scalar_conflict, rule, path, 'sequence:') if @sequence
|
|
865
|
+
raise schema_error(:scalar_conflict, rule, path, 'mapping:') if @mapping
|
|
866
|
+
if @enum
|
|
867
|
+
#* key=:enum_conflict msg="not available with 'enum:'."
|
|
868
|
+
raise schema_error(:enum_conflict, rule, path, 'range:') if @range
|
|
869
|
+
raise schema_error(:enum_conflict, rule, path, 'length:') if @length
|
|
870
|
+
raise schema_error(:enum_conflict, rule, path, 'pattern:') if @pattern
|
|
871
|
+
end
|
|
872
|
+
end
|
|
873
|
+
end
|
|
874
|
+
|
|
875
|
+
#def inspect()
|
|
876
|
+
# str = ""; level = 0; done = {}
|
|
877
|
+
# _inspect(str, level, done)
|
|
878
|
+
# return str
|
|
879
|
+
#end
|
|
880
|
+
|
|
881
|
+
|
|
882
|
+
protected
|
|
883
|
+
|
|
884
|
+
|
|
885
|
+
def _inspect(str="", level=0, done={})
|
|
886
|
+
done[self.__id__] = true
|
|
887
|
+
str << " " * level << "name: #{@name}\n" if @name != nil
|
|
888
|
+
str << " " * level << "desc: #{@desc}\n" if @desc != nil
|
|
889
|
+
str << " " * level << "type: #{@type}\n" if @type != nil
|
|
890
|
+
str << " " * level << "klass: #{@type_class.name}\n" if @type_class != nil
|
|
891
|
+
str << " " * level << "required: #{@required}\n" if @required != nil
|
|
892
|
+
str << " " * level << "pattern: #{@regexp.inspect}\n" if @pattern != nil
|
|
893
|
+
str << " " * level << "assert: #{@assert}\n" if @assert != nil
|
|
894
|
+
str << " " * level << "ident: #{@ident}\n" if @ident != nil
|
|
895
|
+
str << " " * level << "unique: #{@unique}\n" if @unique != nil
|
|
896
|
+
if @enum != nil
|
|
897
|
+
str << " " * level << "enum:\n"
|
|
898
|
+
@enum.each do |item|
|
|
899
|
+
str << " " * (level+1) << "- #{item}\n"
|
|
900
|
+
end
|
|
901
|
+
end
|
|
902
|
+
if @range != nil
|
|
903
|
+
str << " " * level
|
|
904
|
+
str << "range: { "
|
|
905
|
+
colon = ""
|
|
906
|
+
%w[max max-ex min min-ex].each do |key|
|
|
907
|
+
val = @range[key]
|
|
908
|
+
if val != nil
|
|
909
|
+
str << colon << "#{key}: #{val.inspect}"
|
|
910
|
+
colon = ", "
|
|
911
|
+
end
|
|
912
|
+
end
|
|
913
|
+
str << " }\n"
|
|
914
|
+
end
|
|
915
|
+
if @length != nil
|
|
916
|
+
str << " " * level
|
|
917
|
+
str << "length: { "
|
|
918
|
+
colon = ""
|
|
919
|
+
%w[max max-ex min min-ex].each do |key|
|
|
920
|
+
val = @length[key]
|
|
921
|
+
if val != nil
|
|
922
|
+
str << colon << "#{key}: #{val.inspect}"
|
|
923
|
+
colon = ", "
|
|
924
|
+
end
|
|
925
|
+
end
|
|
926
|
+
str << " }\n"
|
|
927
|
+
end
|
|
928
|
+
@sequence.each do |rule|
|
|
929
|
+
if done[rule.__id__]
|
|
930
|
+
str << " " * (level+1) << "- ...\n"
|
|
931
|
+
else
|
|
932
|
+
str << " " * (level+1) << "- \n"
|
|
933
|
+
rule._inspect(str, level+2, done)
|
|
934
|
+
end
|
|
935
|
+
end if @sequence
|
|
936
|
+
@mapping.each do |key, rule|
|
|
937
|
+
if done[rule.__id__]
|
|
938
|
+
str << ' ' * (level+1) << '"' << key << "\": ...\n"
|
|
939
|
+
else
|
|
940
|
+
str << ' ' * (level+1) << '"' << key << "\":\n"
|
|
941
|
+
rule._inspect(str, level+2, done)
|
|
942
|
+
end
|
|
943
|
+
end if @mapping
|
|
944
|
+
return str
|
|
945
|
+
end
|
|
946
|
+
|
|
947
|
+
end
|
|
948
|
+
|
|
949
|
+
end
|
|
950
|
+
#--end of require 'kwalify/rule'
|
|
951
|
+
#--begin of require 'kwalify/validator'
|
|
952
|
+
###
|
|
953
|
+
### $Rev: 48 $
|
|
954
|
+
### $Release: 0.6.0 $
|
|
955
|
+
### copyright(c) 2005 kuwata-lab all rights reserved.
|
|
956
|
+
###
|
|
957
|
+
|
|
958
|
+
#--already included require 'kwalify/messages'
|
|
959
|
+
#--already included require 'kwalify/errors'
|
|
960
|
+
#--already included require 'kwalify/types'
|
|
961
|
+
#--already included require 'kwalify/rule'
|
|
962
|
+
|
|
963
|
+
module Kwalify
|
|
964
|
+
|
|
965
|
+
##
|
|
966
|
+
## ex.
|
|
967
|
+
## schema = YAML.load_file('schema.yaml')
|
|
968
|
+
## validator = Kwalify::Validator.new(schema)
|
|
969
|
+
## document = YAML.load_file('document.yaml')
|
|
970
|
+
## error_list = validator.validate(document)
|
|
971
|
+
## unless error_list.empty?
|
|
972
|
+
## error_list.each do |error|
|
|
973
|
+
## puts "- [#{error.path}] #{error.message}"
|
|
974
|
+
## end
|
|
975
|
+
## end
|
|
976
|
+
##
|
|
977
|
+
class Validator
|
|
978
|
+
include Kwalify::ErrorHelper
|
|
979
|
+
|
|
980
|
+
def initialize(hash, &block)
|
|
981
|
+
@rule = Rule.new(hash)
|
|
982
|
+
@block = block
|
|
983
|
+
end
|
|
984
|
+
attr_reader :rule
|
|
985
|
+
|
|
986
|
+
|
|
987
|
+
def _inspect
|
|
988
|
+
@rule._inspect
|
|
989
|
+
end
|
|
990
|
+
|
|
991
|
+
|
|
992
|
+
def validate(value)
|
|
993
|
+
path = ""; errors = []; done = {}
|
|
994
|
+
_validate(value, @rule, path, errors, done)
|
|
995
|
+
return errors
|
|
996
|
+
end
|
|
997
|
+
|
|
998
|
+
|
|
999
|
+
protected
|
|
1000
|
+
|
|
1001
|
+
|
|
1002
|
+
def validate_hook(value, rule, path, errors)
|
|
1003
|
+
end
|
|
1004
|
+
|
|
1005
|
+
|
|
1006
|
+
def _validate(value, rule, path, errors, done)
|
|
1007
|
+
if Types.collection?(value)
|
|
1008
|
+
return if done[value.__id__] # avoid infinite loop
|
|
1009
|
+
done[value.__id__] = true
|
|
1010
|
+
end
|
|
1011
|
+
if rule.required && value == nil
|
|
1012
|
+
#* key=:required_novalue msg="value required but none."
|
|
1013
|
+
errors << validate_error(:required_novalue, rule, path, value)
|
|
1014
|
+
return
|
|
1015
|
+
end
|
|
1016
|
+
if rule.type_class && value != nil && !value.is_a?(rule.type_class)
|
|
1017
|
+
#* key=:type_unmatch msg="not a %s."
|
|
1018
|
+
errors << validate_error(:type_unmatch, rule, path, value, [Kwalify.word(rule.type)])
|
|
1019
|
+
return
|
|
1020
|
+
end
|
|
1021
|
+
#
|
|
1022
|
+
n = errors.length
|
|
1023
|
+
if rule.sequence
|
|
1024
|
+
_validate_sequence(value, rule, path, errors, done)
|
|
1025
|
+
elsif rule.mapping
|
|
1026
|
+
_validate_mapping(value, rule, path, errors, done)
|
|
1027
|
+
else
|
|
1028
|
+
_validate_scalar(value, rule, path, errors, done)
|
|
1029
|
+
end
|
|
1030
|
+
return unless errors.length == n
|
|
1031
|
+
#
|
|
1032
|
+
validate_hook(value, rule, path, errors)
|
|
1033
|
+
@block.call(value, rule, path, errors) if @block
|
|
1034
|
+
end
|
|
1035
|
+
|
|
1036
|
+
|
|
1037
|
+
private
|
|
1038
|
+
|
|
1039
|
+
|
|
1040
|
+
def _validate_scalar(value, rule, path, errors, done)
|
|
1041
|
+
assert_error("rule.sequence.class==#{rule.sequence.class.name} (expected NilClass)") if rule.sequence
|
|
1042
|
+
assert_error("rule.mapping.class==#{rule.mapping.class.name} (expected NilClass)") if rule.mapping
|
|
1043
|
+
if rule.assert_proc
|
|
1044
|
+
unless rule.assert_proc.call(value)
|
|
1045
|
+
#* key=:assert_failed msg="assertion expression failed (%s)."
|
|
1046
|
+
errors << validate_error(:assert_failed, rule, path, value, [rule.assert])
|
|
1047
|
+
end
|
|
1048
|
+
end
|
|
1049
|
+
if rule.enum
|
|
1050
|
+
unless rule.enum.include?(value)
|
|
1051
|
+
keyname = File.basename(path)
|
|
1052
|
+
keyname = 'enum' if keyname =~ /\A\d+\z/
|
|
1053
|
+
#* key=:enum_notexist msg="invalid %s value."
|
|
1054
|
+
errors << validate_error(:enum_notexist, rule, path, value, [keyname])
|
|
1055
|
+
end
|
|
1056
|
+
end
|
|
1057
|
+
#
|
|
1058
|
+
return if value == nil
|
|
1059
|
+
#
|
|
1060
|
+
if rule.pattern
|
|
1061
|
+
unless value.to_s =~ rule.regexp
|
|
1062
|
+
#* key=:pattern_unmatch msg="not matched to pattern %s."
|
|
1063
|
+
errors << validate_error(:pattern_unmatch, rule, path, value, [rule.pattern])
|
|
1064
|
+
end
|
|
1065
|
+
end
|
|
1066
|
+
if rule.range
|
|
1067
|
+
assert_error("value.class=#{value.class.name}") unless Types.scalar?(value)
|
|
1068
|
+
if rule.range['max'] && rule.range['max'] < value
|
|
1069
|
+
#* key=:range_toolarge msg="too large (> max %s)."
|
|
1070
|
+
errors << validate_error(:range_toolarge, rule, path, value, [rule.range['max'].to_s])
|
|
1071
|
+
end
|
|
1072
|
+
if rule.range['min'] && rule.range['min'] > value
|
|
1073
|
+
#* key=:range_toosmall msg="too small (< min %s)."
|
|
1074
|
+
errors << validate_error(:range_toosmall, rule, path, value, [rule.range['min'].to_s])
|
|
1075
|
+
end
|
|
1076
|
+
if rule.range['max-ex'] && rule.range['max-ex'] <= value
|
|
1077
|
+
#* key=:range_toolargeex msg="too large (>= max %s)."
|
|
1078
|
+
errors << validate_error(:range_toolargeex, rule, path, value, [rule.range['max-ex'].to_s])
|
|
1079
|
+
end
|
|
1080
|
+
if rule.range['min-ex'] && rule.range['min-ex'] >= value
|
|
1081
|
+
#* key=:range_toosmallex msg="too small (<= min %s)."
|
|
1082
|
+
errors << validate_error(:range_toosmallex, rule, path, value, [rule.range['min-ex'].to_s])
|
|
1083
|
+
end
|
|
1084
|
+
end
|
|
1085
|
+
if rule.length
|
|
1086
|
+
assert_error("value.class=#{value.class.name}") unless value.is_a?(String) || value.is_a?(Text)
|
|
1087
|
+
len = value.to_s.length
|
|
1088
|
+
if rule.length['max'] && rule.length['max'] < len
|
|
1089
|
+
#* key=:length_toolong msg="too long (length %d > max %d)."
|
|
1090
|
+
errors << validate_error(:length_toolong, rule, path, value, [len, rule.length['max']])
|
|
1091
|
+
end
|
|
1092
|
+
if rule.length['min'] && rule.length['min'] > len
|
|
1093
|
+
#* key=:length_tooshort msg="too short (length %d < min %d)."
|
|
1094
|
+
errors << validate_error(:length_tooshort, rule, path, value, [len, rule.length['min']])
|
|
1095
|
+
end
|
|
1096
|
+
if rule.length['max-ex'] && rule.length['max-ex'] <= len
|
|
1097
|
+
#* key=:length_toolongex msg="too long (length %d >= max %d)."
|
|
1098
|
+
errors << validate_error(:length_toolongex, rule, path, value, [len, rule.length['max-ex']])
|
|
1099
|
+
end
|
|
1100
|
+
if rule.length['min-ex'] && rule.length['min-ex'] >= len
|
|
1101
|
+
#* key=:length_tooshortex msg="too short (length %d <= min %d)."
|
|
1102
|
+
errors << validate_error(:length_tooshortex, rule, path, value, [len, rule.length['min-ex']])
|
|
1103
|
+
end
|
|
1104
|
+
end
|
|
1105
|
+
end
|
|
1106
|
+
|
|
1107
|
+
|
|
1108
|
+
def _validate_sequence(list, seq_rule, path, errors, done)
|
|
1109
|
+
assert_error("seq_rule.sequence.class==#{seq_rule.sequence.class.name} (expected Array)") unless seq_rule.sequence.is_a?(Array)
|
|
1110
|
+
assert_error("seq_rule.sequence.length==#{seq_rule.sequence.length} (expected 1)") unless seq_rule.sequence.length == 1
|
|
1111
|
+
return if list == nil
|
|
1112
|
+
rule = seq_rule.sequence[0]
|
|
1113
|
+
list.each_with_index do |val, i|
|
|
1114
|
+
_validate(val, rule, "#{path}/#{i}", errors, done) ## validate recursively
|
|
1115
|
+
end
|
|
1116
|
+
if rule.type == 'map'
|
|
1117
|
+
unique_keys = []
|
|
1118
|
+
rule.mapping.keys.each do |key|
|
|
1119
|
+
map_rule = rule.mapping[key]
|
|
1120
|
+
unique_keys << key if map_rule.unique || map_rule.ident
|
|
1121
|
+
end
|
|
1122
|
+
unique_keys.each do |key|
|
|
1123
|
+
table = {}
|
|
1124
|
+
list.each_with_index do |map, i|
|
|
1125
|
+
val = map[key]
|
|
1126
|
+
next if val == nil
|
|
1127
|
+
curr_path = "#{path}/#{i}/#{key}"
|
|
1128
|
+
if table[val]
|
|
1129
|
+
#* key=:value_notunique msg="is already used at '%s'."
|
|
1130
|
+
errors << validate_error(:value_notunique, rule, "#{path}/#{i}/#{key}", val, "#{path}/#{table[val]}/#{key}")
|
|
1131
|
+
else
|
|
1132
|
+
table[val] = i
|
|
1133
|
+
end
|
|
1134
|
+
end
|
|
1135
|
+
end if !unique_keys.empty?
|
|
1136
|
+
elsif rule.unique
|
|
1137
|
+
table = {}
|
|
1138
|
+
list.each_with_index do |val, i|
|
|
1139
|
+
next if val == nil
|
|
1140
|
+
if table[val]
|
|
1141
|
+
# #* key=:value_notunique msg="is already used at '%s'."
|
|
1142
|
+
errors << validate_error(:value_notunique, rule, "#{path}/#{i}", val, "#{path}/#{table[val]}")
|
|
1143
|
+
else
|
|
1144
|
+
table[val] = i
|
|
1145
|
+
end
|
|
1146
|
+
end
|
|
1147
|
+
end
|
|
1148
|
+
end
|
|
1149
|
+
|
|
1150
|
+
|
|
1151
|
+
def _validate_mapping(hash, map_rule, path, errors, done)
|
|
1152
|
+
assert_error("map_rule.mapping.class==#{map_rule.mapping.class.name} (expected Hash)") unless map_rule.mapping.is_a?(Hash)
|
|
1153
|
+
return if hash == nil
|
|
1154
|
+
map_rule.mapping.each do |key, rule|
|
|
1155
|
+
if rule.required && !hash.key?(key)
|
|
1156
|
+
#* key=:required_nokey msg="key '%s:' is required."
|
|
1157
|
+
errors << validate_error(:required_nokey, rule, path, hash, [key])
|
|
1158
|
+
end
|
|
1159
|
+
end
|
|
1160
|
+
hash.each do |key, val|
|
|
1161
|
+
rule = map_rule.mapping[key]
|
|
1162
|
+
unless rule
|
|
1163
|
+
#* key=:key_undefined msg="key '%s' is undefined."
|
|
1164
|
+
errors << validate_error(:key_undefined, rule, "#{path}/#{key}", hash, ["#{key}:"])
|
|
1165
|
+
##* key=:key_undefined msg="undefined key."
|
|
1166
|
+
#errors << validate_error(:key_undefined, rule, "#{path}/#{key}", "#{key}:")
|
|
1167
|
+
else
|
|
1168
|
+
_validate(val, rule, "#{path}/#{key}", errors, done) ## validate recursively
|
|
1169
|
+
end
|
|
1170
|
+
end
|
|
1171
|
+
end
|
|
1172
|
+
|
|
1173
|
+
end
|
|
1174
|
+
|
|
1175
|
+
end
|
|
1176
|
+
#--end of require 'kwalify/validator'
|
|
1177
|
+
#--begin of require 'kwalify/meta-validator'
|
|
1178
|
+
###
|
|
1179
|
+
### $Rev: 51 $
|
|
1180
|
+
### $Release: 0.6.0 $
|
|
1181
|
+
### copyright(c) 2005 kuwata-lab all rights reserved.
|
|
1182
|
+
###
|
|
1183
|
+
|
|
1184
|
+
#--already included require 'kwalify/errors'
|
|
1185
|
+
#--already included require 'kwalify/rule'
|
|
1186
|
+
#--already included require 'kwalify/validator'
|
|
1187
|
+
require 'yaml'
|
|
1188
|
+
|
|
1189
|
+
module Kwalify
|
|
1190
|
+
|
|
1191
|
+
|
|
1192
|
+
##
|
|
1193
|
+
## ex.
|
|
1194
|
+
## meta_validator = Kwalify::MetaValidator.instance()
|
|
1195
|
+
## schema = File.load_file('schema.yaml')
|
|
1196
|
+
## errors = meta_validator.validate(schema)
|
|
1197
|
+
## if !errors.empty?
|
|
1198
|
+
## errors.each do |error|
|
|
1199
|
+
## puts "[#{error.path}] #{error.message}"
|
|
1200
|
+
## end
|
|
1201
|
+
## end
|
|
1202
|
+
##
|
|
1203
|
+
class MetaValidator < Validator
|
|
1204
|
+
|
|
1205
|
+
META_SCHEMA = <<'END'
|
|
1206
|
+
name: MAIN
|
|
1207
|
+
type: map
|
|
1208
|
+
required: yes
|
|
1209
|
+
mapping: &main-rule
|
|
1210
|
+
"name":
|
|
1211
|
+
type: str
|
|
1212
|
+
"desc":
|
|
1213
|
+
type: str
|
|
1214
|
+
"classname":
|
|
1215
|
+
type: str
|
|
1216
|
+
"type":
|
|
1217
|
+
type: str
|
|
1218
|
+
#required: yes
|
|
1219
|
+
enum:
|
|
1220
|
+
- seq
|
|
1221
|
+
#- sequence
|
|
1222
|
+
#- list
|
|
1223
|
+
- map
|
|
1224
|
+
#- mapping
|
|
1225
|
+
#- hash
|
|
1226
|
+
- str
|
|
1227
|
+
#- string
|
|
1228
|
+
- int
|
|
1229
|
+
#- integer
|
|
1230
|
+
- float
|
|
1231
|
+
- number
|
|
1232
|
+
#- numeric
|
|
1233
|
+
- bool
|
|
1234
|
+
#- boolean
|
|
1235
|
+
- text
|
|
1236
|
+
- date
|
|
1237
|
+
- time
|
|
1238
|
+
- timestamp
|
|
1239
|
+
#- object
|
|
1240
|
+
- any
|
|
1241
|
+
- scalar
|
|
1242
|
+
#- collection
|
|
1243
|
+
"required":
|
|
1244
|
+
type: bool
|
|
1245
|
+
"enum":
|
|
1246
|
+
type: seq
|
|
1247
|
+
sequence:
|
|
1248
|
+
- type: scalar
|
|
1249
|
+
unique: yes
|
|
1250
|
+
"pattern":
|
|
1251
|
+
type: str
|
|
1252
|
+
"assert":
|
|
1253
|
+
type: str
|
|
1254
|
+
pattern: /\bval\b/
|
|
1255
|
+
"range":
|
|
1256
|
+
type: map
|
|
1257
|
+
mapping:
|
|
1258
|
+
"max":
|
|
1259
|
+
type: scalar
|
|
1260
|
+
"min":
|
|
1261
|
+
type: scalar
|
|
1262
|
+
"max-ex":
|
|
1263
|
+
type: scalar
|
|
1264
|
+
"min-ex":
|
|
1265
|
+
type: scalar
|
|
1266
|
+
"length":
|
|
1267
|
+
type: map
|
|
1268
|
+
mapping:
|
|
1269
|
+
"max":
|
|
1270
|
+
type: int
|
|
1271
|
+
"min":
|
|
1272
|
+
type: int
|
|
1273
|
+
"max-ex":
|
|
1274
|
+
type: int
|
|
1275
|
+
"min-ex":
|
|
1276
|
+
type: int
|
|
1277
|
+
"ident":
|
|
1278
|
+
type: bool
|
|
1279
|
+
"unique":
|
|
1280
|
+
type: bool
|
|
1281
|
+
"sequence":
|
|
1282
|
+
name: SEQUENCE
|
|
1283
|
+
type: seq
|
|
1284
|
+
sequence:
|
|
1285
|
+
- type: map
|
|
1286
|
+
mapping: *main-rule
|
|
1287
|
+
name: MAIN
|
|
1288
|
+
#required: yes
|
|
1289
|
+
"mapping":
|
|
1290
|
+
name: MAPPING
|
|
1291
|
+
type: map
|
|
1292
|
+
mapping:
|
|
1293
|
+
=:
|
|
1294
|
+
type: map
|
|
1295
|
+
mapping: *main-rule
|
|
1296
|
+
name: MAIN
|
|
1297
|
+
#required: yes
|
|
1298
|
+
END
|
|
1299
|
+
|
|
1300
|
+
|
|
1301
|
+
def initialize(schema, &block)
|
|
1302
|
+
super
|
|
1303
|
+
end
|
|
1304
|
+
|
|
1305
|
+
def validate_hook(value, rule, path, errors)
|
|
1306
|
+
return if value == nil ## realy?
|
|
1307
|
+
return unless rule.name == "MAIN"
|
|
1308
|
+
#
|
|
1309
|
+
hash = value
|
|
1310
|
+
type = hash['type']
|
|
1311
|
+
type = Types::DEFAULT_TYPE if type == nil
|
|
1312
|
+
klass = Types.type_class(type)
|
|
1313
|
+
#unless klass
|
|
1314
|
+
# errors << validate_error(:type_unknown, rule, "#{path}/type", type)
|
|
1315
|
+
#end
|
|
1316
|
+
#
|
|
1317
|
+
if hash.key?('classname')
|
|
1318
|
+
val = hash['classname']
|
|
1319
|
+
unless val.nil? || type == 'map'
|
|
1320
|
+
errors << validate_error(:classname_notmap, rule, "#{path}/classname", 'classname:')
|
|
1321
|
+
end
|
|
1322
|
+
end
|
|
1323
|
+
#
|
|
1324
|
+
if hash.key?('pattern')
|
|
1325
|
+
val = hash['pattern']
|
|
1326
|
+
pat = (val =~ /\A\/(.*)\/([mi]?[mi]?)\z/ ? $1 : val)
|
|
1327
|
+
begin
|
|
1328
|
+
Regexp.compile(pat)
|
|
1329
|
+
rescue RegexpError => ex
|
|
1330
|
+
errors << validate_error(:pattern_syntaxerr, rule, "#{path}/pattern", val)
|
|
1331
|
+
end
|
|
1332
|
+
end
|
|
1333
|
+
#
|
|
1334
|
+
if hash.key?('enum')
|
|
1335
|
+
if Types.collection_type?(type)
|
|
1336
|
+
errors << validate_error(:enum_notscalar, rule, path, 'enum:')
|
|
1337
|
+
else
|
|
1338
|
+
#elem_table = {}
|
|
1339
|
+
hash['enum'].each do |elem|
|
|
1340
|
+
#if elem_table[elem]
|
|
1341
|
+
# errors << validate_error(:enum_duplicate, rule, "#{path}/enum", elem.to_s)
|
|
1342
|
+
#end
|
|
1343
|
+
#elem_table[elem] = true
|
|
1344
|
+
unless elem.is_a?(klass)
|
|
1345
|
+
errors << validate_error(:enum_type_unmatch, rule, "#{path}/enum", elem, [Kwalify.word(type)])
|
|
1346
|
+
end
|
|
1347
|
+
end
|
|
1348
|
+
end
|
|
1349
|
+
end
|
|
1350
|
+
#
|
|
1351
|
+
if hash.key?('assert')
|
|
1352
|
+
val = hash['assert']
|
|
1353
|
+
#val =~ /\bval\b/ or errors << validate_error(:assert_noval, rule, "#{path}/assert", val)
|
|
1354
|
+
begin
|
|
1355
|
+
eval "proc { |val| #{val} }"
|
|
1356
|
+
rescue SyntaxError => ex
|
|
1357
|
+
errors << validate_error(:assert_syntaxerr, rule, "#{path}/assert", val)
|
|
1358
|
+
end
|
|
1359
|
+
end
|
|
1360
|
+
#
|
|
1361
|
+
if hash.key?('range')
|
|
1362
|
+
val = hash['range']
|
|
1363
|
+
curr_path = path + "/range"
|
|
1364
|
+
#if ! val.is_a?(Hash)
|
|
1365
|
+
# errors << validate_error(:range_notmap, rule, curr_path, val)
|
|
1366
|
+
#elsif ...
|
|
1367
|
+
if Types.collection_type?(type) || type == 'bool' || type == 'any'
|
|
1368
|
+
errors << validate_error(:range_notscalar, rule, path, 'range:')
|
|
1369
|
+
else
|
|
1370
|
+
val.each do |rkey, rval|
|
|
1371
|
+
#case rkey
|
|
1372
|
+
#when 'max', 'min', 'max-ex', 'min-ex'
|
|
1373
|
+
unless rval.is_a?(klass)
|
|
1374
|
+
typename = Kwalify.word(type) || type
|
|
1375
|
+
errors << validate_error(:range_type_unmatch, rule, "#{curr_path}/#{rkey}", rval, [typename])
|
|
1376
|
+
end
|
|
1377
|
+
#else
|
|
1378
|
+
# errors << validate_error(:range_undefined, rule, curr_path, "#{rkey}:")
|
|
1379
|
+
#end
|
|
1380
|
+
end
|
|
1381
|
+
end
|
|
1382
|
+
if val.key?('max') && val.key?('max-ex')
|
|
1383
|
+
errors << validate_error(:range_twomax, rule, curr_path, nil)
|
|
1384
|
+
end
|
|
1385
|
+
if val.key?('min') && val.key?('min-ex')
|
|
1386
|
+
errors << validate_error(:range_twomin, rule, curr_path, nil)
|
|
1387
|
+
end
|
|
1388
|
+
max, min, max_ex, min_ex = val['max'], val['min'], val['max-ex'], val['min-ex']
|
|
1389
|
+
if max
|
|
1390
|
+
if min && max < min
|
|
1391
|
+
errors << validate_error(:range_maxltmin, rule, curr_path, nil, [max, min])
|
|
1392
|
+
elsif min_ex && max <= min_ex
|
|
1393
|
+
errors << validate_error(:range_maxleminex, rule, curr_path, nil, [max, min_ex])
|
|
1394
|
+
end
|
|
1395
|
+
elsif max_ex
|
|
1396
|
+
if min && max_ex <= min
|
|
1397
|
+
errors << validate_error(:range_maxexlemin, rule, curr_path, nil, [max_ex, min])
|
|
1398
|
+
elsif min_ex && max_ex <= min_ex
|
|
1399
|
+
errors << validate_error(:range_maxexleminex, rule, curr_path, nil, [max_ex, min_ex])
|
|
1400
|
+
end
|
|
1401
|
+
end
|
|
1402
|
+
end
|
|
1403
|
+
#
|
|
1404
|
+
if hash.key?('length')
|
|
1405
|
+
val = hash['length']
|
|
1406
|
+
curr_path = path + "/length"
|
|
1407
|
+
#val.is_a?(Hash) or errors << validate_error(:length_notmap, rule, curr_path, val)
|
|
1408
|
+
unless type == 'str' || type == 'text'
|
|
1409
|
+
errors << validate_error(:length_nottext, rule, path, 'length:')
|
|
1410
|
+
end
|
|
1411
|
+
#val.each do |lkey, lval|
|
|
1412
|
+
# #case lkey
|
|
1413
|
+
# #when 'max', 'min', 'max-ex', 'min-ex'
|
|
1414
|
+
# unless lval.is_a?(Integer)
|
|
1415
|
+
# errors << validate_error(:length_notint, rule, "#{curr_path}/#{lkey}", lval)
|
|
1416
|
+
# end
|
|
1417
|
+
# #else
|
|
1418
|
+
# # errors << validate_error(:length_undefined, rule, curr_path, "#{lkey}:")
|
|
1419
|
+
# #end
|
|
1420
|
+
#end
|
|
1421
|
+
if val.key?('max') && val.key?('max-ex')
|
|
1422
|
+
errors << validate_error(:length_twomax, rule, curr_path, nil)
|
|
1423
|
+
end
|
|
1424
|
+
if val.key?('min') && val.key?('min-ex')
|
|
1425
|
+
errors << validate_error(:length_twomin, rule, curr_path, nil)
|
|
1426
|
+
end
|
|
1427
|
+
max, min, max_ex, min_ex = val['max'], val['min'], val['max-ex'], val['min-ex']
|
|
1428
|
+
if max
|
|
1429
|
+
if min && max < min
|
|
1430
|
+
errors << validate_error(:length_maxltmin, rule, curr_path, nil, [max, min])
|
|
1431
|
+
elsif min_ex && max <= min_ex
|
|
1432
|
+
errors << validate_error(:length_maxleminex, rule, curr_path, nil, [max, min_ex])
|
|
1433
|
+
end
|
|
1434
|
+
elsif max_ex
|
|
1435
|
+
if min && max_ex <= min
|
|
1436
|
+
errors << validate_error(:length_maxexlemin, rule, curr_path, nil, [max_ex, min])
|
|
1437
|
+
elsif min_ex && max_ex <= min_ex
|
|
1438
|
+
errors << validate_error(:length_maxexleminex, rule, curr_path, nil, [max_ex, min_ex])
|
|
1439
|
+
end
|
|
1440
|
+
end
|
|
1441
|
+
end
|
|
1442
|
+
#
|
|
1443
|
+
if hash.key?('unique')
|
|
1444
|
+
if hash['unique'] && Types.collection_type?(type)
|
|
1445
|
+
errors << validate_error(:unique_notscalar, rule, path, "unique:")
|
|
1446
|
+
end
|
|
1447
|
+
if path.empty?
|
|
1448
|
+
errors << validate_error(:unique_onroot, rule, "/", "unique:")
|
|
1449
|
+
end
|
|
1450
|
+
end
|
|
1451
|
+
#
|
|
1452
|
+
if hash.key?('ident')
|
|
1453
|
+
if hash['ident'] && Types.collection_type?(type)
|
|
1454
|
+
errors << validate_error(:ident_notscalar, rule, path, "ident:")
|
|
1455
|
+
end
|
|
1456
|
+
if path.empty?
|
|
1457
|
+
errors << validate_error(:ident_onroot, rule, "/", "ident:")
|
|
1458
|
+
end
|
|
1459
|
+
end
|
|
1460
|
+
#
|
|
1461
|
+
if hash.key?('sequence')
|
|
1462
|
+
val = hash['sequence']
|
|
1463
|
+
#if val != nil && !val.is_a?(Array)
|
|
1464
|
+
# errors << validate_error(:sequence_notseq, rule, "#{path}/sequence", val)
|
|
1465
|
+
#elsif ...
|
|
1466
|
+
if val == nil || val.empty?
|
|
1467
|
+
errors << validate_error(:sequence_noelem, rule, "#{path}/sequence", val)
|
|
1468
|
+
elsif val.length > 1
|
|
1469
|
+
errors << validate_error(:sequence_toomany, rule, "#{path}/sequence", val)
|
|
1470
|
+
else
|
|
1471
|
+
elem = val[0]
|
|
1472
|
+
assert_error("elem.class=#{elem.class}") unless elem.is_a?(Hash)
|
|
1473
|
+
if elem['ident'] && elem['type'] != 'map'
|
|
1474
|
+
errors << validate_error(:ident_notmap, nil, "#{path}/sequence/0", 'ident:')
|
|
1475
|
+
end
|
|
1476
|
+
end
|
|
1477
|
+
end
|
|
1478
|
+
#
|
|
1479
|
+
if hash.key?('mapping')
|
|
1480
|
+
val = hash['mapping']
|
|
1481
|
+
if val != nil && !val.is_a?(Hash)
|
|
1482
|
+
errors << validate_error(:mapping_notmap, rule, "#{path}/mapping", val)
|
|
1483
|
+
elsif val == nil || (val.empty? && !val.default)
|
|
1484
|
+
errors << validate_error(:mapping_noelem, rule, "#{path}/mapping", val)
|
|
1485
|
+
end
|
|
1486
|
+
end
|
|
1487
|
+
#
|
|
1488
|
+
if type == 'seq'
|
|
1489
|
+
errors << validate_error(:seq_nosequence, rule, path, nil) unless hash.key?('sequence')
|
|
1490
|
+
#errors << validate_error(:seq_conflict, rule, path, 'enum:') if hash.key?('enum')
|
|
1491
|
+
errors << validate_error(:seq_conflict, rule, path, 'pattern:') if hash.key?('pattern')
|
|
1492
|
+
errors << validate_error(:seq_conflict, rule, path, 'mapping:') if hash.key?('mapping')
|
|
1493
|
+
#errors << validate_error(:seq_conflict, rule, path, 'range:') if hash.key?('range')
|
|
1494
|
+
#errors << validate_error(:seq_conflict, rule, path, 'length:') if hash.key?('length')
|
|
1495
|
+
elsif type == 'map'
|
|
1496
|
+
errors << validate_error(:map_nomapping, rule, path, nil) unless hash.key?('mapping')
|
|
1497
|
+
#errors << validate_error(:map_conflict, rule, path, 'enum:') if hash.key?('enum')
|
|
1498
|
+
errors << validate_error(:map_conflict, rule, path, 'pattern:') if hash.key?('pattern')
|
|
1499
|
+
errors << validate_error(:map_conflict, rule, path, 'sequence:') if hash.key?('sequence')
|
|
1500
|
+
#errors << validate_error(:map_conflict, rule, path, 'range:') if hash.key?('range')
|
|
1501
|
+
#errors << validate_error(:map_conflict, rule, path, 'length:') if hash.key?('length')
|
|
1502
|
+
else
|
|
1503
|
+
errors << validate_error(:scalar_conflict, rule, path, 'sequence:') if hash.key?('sequence')
|
|
1504
|
+
errors << validate_error(:scalar_conflict, rule, path, 'mapping:') if hash.key?('mapping')
|
|
1505
|
+
if hash.key?('enum')
|
|
1506
|
+
errors << validate_error(:enum_conflict, rule, path, 'range:') if hash.key?('range')
|
|
1507
|
+
errors << validate_error(:enum_conflict, rule, path, 'length:') if hash.key?('length')
|
|
1508
|
+
errors << validate_error(:enum_conflict, rule, path, 'pattern:') if hash.key?('pattern')
|
|
1509
|
+
end
|
|
1510
|
+
end
|
|
1511
|
+
|
|
1512
|
+
end # end of def validate_hook()
|
|
1513
|
+
|
|
1514
|
+
|
|
1515
|
+
schema = YAML.load(META_SCHEMA)
|
|
1516
|
+
@instance = MetaValidator.new(schema)
|
|
1517
|
+
|
|
1518
|
+
def self.instance()
|
|
1519
|
+
return @instance
|
|
1520
|
+
end
|
|
1521
|
+
|
|
1522
|
+
|
|
1523
|
+
end # end of class MetaValidator
|
|
1524
|
+
|
|
1525
|
+
|
|
1526
|
+
META_VALIDATOR = MetaValidator.instance()
|
|
1527
|
+
|
|
1528
|
+
def self.meta_validator # obsolete
|
|
1529
|
+
return META_VALIDATOR
|
|
1530
|
+
end
|
|
1531
|
+
|
|
1532
|
+
end
|
|
1533
|
+
#--end of require 'kwalify/meta-validator'
|
|
1534
|
+
#--begin of require 'kwalify/yaml-parser'
|
|
1535
|
+
###
|
|
1536
|
+
### $Rev: 51 $
|
|
1537
|
+
### $Release: 0.6.0 $
|
|
1538
|
+
### copyright(c) 2005 kuwata-lab all rights reserved.
|
|
1539
|
+
###
|
|
1540
|
+
|
|
1541
|
+
#--already included require 'kwalify/messages'
|
|
1542
|
+
#--already included require 'kwalify/errors'
|
|
1543
|
+
#--already included require 'kwalify/types'
|
|
1544
|
+
|
|
1545
|
+
require 'date'
|
|
1546
|
+
|
|
1547
|
+
|
|
1548
|
+
module Kwalify
|
|
1549
|
+
|
|
1550
|
+
##
|
|
1551
|
+
## ex.
|
|
1552
|
+
## str = ARGF.read()
|
|
1553
|
+
## parser = Kwalify::PlainYamlParser.new(str)
|
|
1554
|
+
## doc = parser.parse()
|
|
1555
|
+
## p doc
|
|
1556
|
+
##
|
|
1557
|
+
class PlainYamlParser
|
|
1558
|
+
|
|
1559
|
+
class Alias
|
|
1560
|
+
def initialize(label, linenum)
|
|
1561
|
+
@label = label
|
|
1562
|
+
@linenum = linenum
|
|
1563
|
+
end
|
|
1564
|
+
attr_reader :label, :linenum
|
|
1565
|
+
end
|
|
1566
|
+
|
|
1567
|
+
|
|
1568
|
+
def initialize(yaml_str)
|
|
1569
|
+
@lines = yaml_str.to_a()
|
|
1570
|
+
@line = nil
|
|
1571
|
+
@linenum = 0
|
|
1572
|
+
@anchors = {}
|
|
1573
|
+
@aliases = {}
|
|
1574
|
+
end
|
|
1575
|
+
|
|
1576
|
+
|
|
1577
|
+
def parse()
|
|
1578
|
+
data = parse_child(0)
|
|
1579
|
+
if data == nil && @end_flag == '---'
|
|
1580
|
+
data = parse_child(0)
|
|
1581
|
+
end
|
|
1582
|
+
resolve_aliases(data) unless @aliases.empty?
|
|
1583
|
+
return data
|
|
1584
|
+
end
|
|
1585
|
+
|
|
1586
|
+
|
|
1587
|
+
def has_next?
|
|
1588
|
+
return @end_flag != 'EOF'
|
|
1589
|
+
end
|
|
1590
|
+
|
|
1591
|
+
|
|
1592
|
+
def parse_all
|
|
1593
|
+
list = []
|
|
1594
|
+
while has_next()
|
|
1595
|
+
doc = parse()
|
|
1596
|
+
list << doc
|
|
1597
|
+
end
|
|
1598
|
+
return list
|
|
1599
|
+
end
|
|
1600
|
+
|
|
1601
|
+
|
|
1602
|
+
protected
|
|
1603
|
+
|
|
1604
|
+
|
|
1605
|
+
def create_sequence(linenum=nil)
|
|
1606
|
+
return []
|
|
1607
|
+
end
|
|
1608
|
+
|
|
1609
|
+
def add_to_seq(seq, value, linenum)
|
|
1610
|
+
seq << value
|
|
1611
|
+
end
|
|
1612
|
+
|
|
1613
|
+
def set_seq_at(seq, i, value, linenum)
|
|
1614
|
+
seq[i] = value
|
|
1615
|
+
end
|
|
1616
|
+
|
|
1617
|
+
def create_mapping(linenum=nil)
|
|
1618
|
+
return {}
|
|
1619
|
+
end
|
|
1620
|
+
|
|
1621
|
+
def add_to_map(map, key, value, linenum)
|
|
1622
|
+
map[key] = value
|
|
1623
|
+
end
|
|
1624
|
+
|
|
1625
|
+
def set_map_with(map, key, value, linenum)
|
|
1626
|
+
map[key] = value
|
|
1627
|
+
end
|
|
1628
|
+
|
|
1629
|
+
def set_default(map, value, linenum)
|
|
1630
|
+
map.value = value
|
|
1631
|
+
end
|
|
1632
|
+
|
|
1633
|
+
def merge_map(map, map2, linenum)
|
|
1634
|
+
map2.each do |key, val|
|
|
1635
|
+
map[key] = value unless map.key?(key)
|
|
1636
|
+
end
|
|
1637
|
+
end
|
|
1638
|
+
|
|
1639
|
+
def create_scalar(value, linenum=nil)
|
|
1640
|
+
return value
|
|
1641
|
+
end
|
|
1642
|
+
|
|
1643
|
+
|
|
1644
|
+
def current_line
|
|
1645
|
+
return @line
|
|
1646
|
+
end
|
|
1647
|
+
|
|
1648
|
+
def current_linenum
|
|
1649
|
+
return @linenum
|
|
1650
|
+
end
|
|
1651
|
+
|
|
1652
|
+
|
|
1653
|
+
private
|
|
1654
|
+
|
|
1655
|
+
|
|
1656
|
+
def getline
|
|
1657
|
+
line = _getline()
|
|
1658
|
+
line = _getline() while line && line =~ /^\s*($|\#)/
|
|
1659
|
+
return line
|
|
1660
|
+
end
|
|
1661
|
+
|
|
1662
|
+
def _getline
|
|
1663
|
+
@line = @lines[@linenum]
|
|
1664
|
+
@linenum += 1
|
|
1665
|
+
case @line
|
|
1666
|
+
when nil ; @end_flag = 'EOF'
|
|
1667
|
+
when /^\.\.\.$/ ; @end_flag = '...'; @line = nil
|
|
1668
|
+
when /^---(\s+.*)?$/ ; @end_flag = '---'; @line = nil
|
|
1669
|
+
else ; @end_flag = nil
|
|
1670
|
+
end
|
|
1671
|
+
return @line
|
|
1672
|
+
end
|
|
1673
|
+
|
|
1674
|
+
|
|
1675
|
+
def reset_sbuf(str)
|
|
1676
|
+
@sbuf = str[-1] == ?\n ? str : str + "\n"
|
|
1677
|
+
@index = -1
|
|
1678
|
+
end
|
|
1679
|
+
|
|
1680
|
+
|
|
1681
|
+
def _getchar
|
|
1682
|
+
@index += 1
|
|
1683
|
+
ch = @sbuf[@index]
|
|
1684
|
+
while ch == nil
|
|
1685
|
+
break if (line = getline()) == nil
|
|
1686
|
+
reset_sbuf(line)
|
|
1687
|
+
@index += 1
|
|
1688
|
+
ch = @sbuf[@index]
|
|
1689
|
+
end
|
|
1690
|
+
return ch
|
|
1691
|
+
end
|
|
1692
|
+
|
|
1693
|
+
def getchar
|
|
1694
|
+
ch = _getchar()
|
|
1695
|
+
ch = _getchar() while ch && white?(ch)
|
|
1696
|
+
return ch
|
|
1697
|
+
end
|
|
1698
|
+
|
|
1699
|
+
def getchar_or_nl
|
|
1700
|
+
ch = _getchar()
|
|
1701
|
+
ch = _getchar() while ch && white?(ch) && ch != ?\n
|
|
1702
|
+
return ch
|
|
1703
|
+
end
|
|
1704
|
+
|
|
1705
|
+
def current_char
|
|
1706
|
+
return @sbuf[@index]
|
|
1707
|
+
end
|
|
1708
|
+
|
|
1709
|
+
|
|
1710
|
+
def syntax_error(error_symbol, linenum=@linenum)
|
|
1711
|
+
msg = Kwalify.msg(error_symbol) % [linenum]
|
|
1712
|
+
return Kwalify::YamlSyntaxError.new(msg, linenum,error_symbol)
|
|
1713
|
+
end
|
|
1714
|
+
|
|
1715
|
+
def parse_child(column)
|
|
1716
|
+
line = getline()
|
|
1717
|
+
return create_scalar(nil) if !line
|
|
1718
|
+
line =~ /^( *)(.*)/
|
|
1719
|
+
indent = $1.length
|
|
1720
|
+
return create_scalar(nil) if indent < column
|
|
1721
|
+
value = $2
|
|
1722
|
+
return parse_value(column, value, indent)
|
|
1723
|
+
end
|
|
1724
|
+
|
|
1725
|
+
|
|
1726
|
+
def parse_value(column, value, value_start_column)
|
|
1727
|
+
case value
|
|
1728
|
+
when /^-( |$)/
|
|
1729
|
+
data = parse_sequence(value_start_column, value)
|
|
1730
|
+
when /^(:?:?[-.\w]+\*?|'.*?'|".*?"|=|<<) *:( |$)/
|
|
1731
|
+
#when /^:?["']?[-.\w]+["']? *:( |$)/ #'
|
|
1732
|
+
data = parse_mapping(value_start_column, value)
|
|
1733
|
+
when /^\[/, /^\{/
|
|
1734
|
+
data = parse_flowstyle(column, value)
|
|
1735
|
+
when /^\&[-\w]+( |$)/
|
|
1736
|
+
data = parse_anchor(column, value)
|
|
1737
|
+
when /^\*[-\w]+( |$)/
|
|
1738
|
+
data = parse_alias(column, value)
|
|
1739
|
+
when /^[|>]/
|
|
1740
|
+
data = parse_block_text(column, value)
|
|
1741
|
+
when /^!/
|
|
1742
|
+
data = parse_tag(column, value)
|
|
1743
|
+
when /^\#/
|
|
1744
|
+
data = parse_child(column)
|
|
1745
|
+
else
|
|
1746
|
+
data = parse_scalar(column, value)
|
|
1747
|
+
end
|
|
1748
|
+
return data
|
|
1749
|
+
end
|
|
1750
|
+
|
|
1751
|
+
def white?(ch)
|
|
1752
|
+
return ch == ?\ || ch == ?\t || ch == ?\n || ch == ?\r
|
|
1753
|
+
end
|
|
1754
|
+
|
|
1755
|
+
|
|
1756
|
+
##
|
|
1757
|
+
## flowstyle ::= flow_seq | flow_map | flow_scalar
|
|
1758
|
+
##
|
|
1759
|
+
## flow_seq ::= '[' [ flow_seq_item { ',' sp flow_seq_item } ] ']'
|
|
1760
|
+
## flow_seq_item ::= flowstyle
|
|
1761
|
+
##
|
|
1762
|
+
## flow_map ::= '{' [ flow_map_item { ',' sp flow_map_item } ] '}'
|
|
1763
|
+
## flow_map_item ::= flowstyle ':' sp flowstyle
|
|
1764
|
+
##
|
|
1765
|
+
## flow_scalar ::= string | number | boolean | symbol | date
|
|
1766
|
+
##
|
|
1767
|
+
|
|
1768
|
+
def parse_flowstyle(column, value)
|
|
1769
|
+
reset_sbuf(value)
|
|
1770
|
+
getchar()
|
|
1771
|
+
data = parse_flow(0)
|
|
1772
|
+
ch = current_char
|
|
1773
|
+
assert ch == ?] || ch == ?}
|
|
1774
|
+
ch = getchar_or_nl()
|
|
1775
|
+
unless ch == ?\n || ch == ?# || ch == nil
|
|
1776
|
+
#* key=:flow_hastail msg="flow style sequence is closed but got '%s'."
|
|
1777
|
+
raise syntax_error(:flow_hastail, [ch.chr])
|
|
1778
|
+
end
|
|
1779
|
+
getline() if ch != nil
|
|
1780
|
+
return data
|
|
1781
|
+
end
|
|
1782
|
+
|
|
1783
|
+
def parse_flow(depth)
|
|
1784
|
+
ch = current_char()
|
|
1785
|
+
#ch = getchar()
|
|
1786
|
+
if ch == nil
|
|
1787
|
+
#* key=:flow_eof msg="found EOF when parsing flow style."
|
|
1788
|
+
rase syntax_error(:flow_eof)
|
|
1789
|
+
end
|
|
1790
|
+
if ch == ?[
|
|
1791
|
+
data = parse_flow_seq(depth)
|
|
1792
|
+
elsif ch == ?{
|
|
1793
|
+
data = parse_flow_map(depth)
|
|
1794
|
+
else
|
|
1795
|
+
data = parse_flow_scalar(depth)
|
|
1796
|
+
end
|
|
1797
|
+
return data
|
|
1798
|
+
end
|
|
1799
|
+
|
|
1800
|
+
def parse_flow_seq(depth)
|
|
1801
|
+
assert current_char() == ?[
|
|
1802
|
+
seq = create_sequence() # []
|
|
1803
|
+
ch = getchar()
|
|
1804
|
+
if ch != ?}
|
|
1805
|
+
linenum = current_linenum()
|
|
1806
|
+
#seq << parse_flow_seq_item(depth + 1)
|
|
1807
|
+
add_to_seq(seq, parse_flow_seq_item(depth + 1), linenum)
|
|
1808
|
+
while (ch = current_char()) == ?,
|
|
1809
|
+
ch = getchar()
|
|
1810
|
+
if ch == ?]
|
|
1811
|
+
#* key=:flow_noseqitem msg="sequence item required (or last comma is extra)."
|
|
1812
|
+
raise syntax_error(:flow_noseqitem)
|
|
1813
|
+
end
|
|
1814
|
+
#break if ch == ?]
|
|
1815
|
+
linenum = current_linenum()
|
|
1816
|
+
#seq << parse_flow_seq_item(depth + 1)
|
|
1817
|
+
add_to_seq(seq, parse_flow_seq_item(depth + 1), linenum)
|
|
1818
|
+
end
|
|
1819
|
+
end
|
|
1820
|
+
unless current_char() == ?]
|
|
1821
|
+
#* key=:flow_seqnotclosed msg="flow style sequence requires ']'."
|
|
1822
|
+
raise syntax_error(:flow_seqnotclosed)
|
|
1823
|
+
end
|
|
1824
|
+
getchar() if depth > 0
|
|
1825
|
+
return seq
|
|
1826
|
+
end
|
|
1827
|
+
|
|
1828
|
+
def parse_flow_seq_item(depth)
|
|
1829
|
+
return parse_flow(depth)
|
|
1830
|
+
end
|
|
1831
|
+
|
|
1832
|
+
def parse_flow_map(depth)
|
|
1833
|
+
assert current_char() == ?{ #}
|
|
1834
|
+
map = create_mapping() # {}
|
|
1835
|
+
ch = getchar()
|
|
1836
|
+
if ch != ?}
|
|
1837
|
+
linenum = current_linenum()
|
|
1838
|
+
key, value = parse_flow_map_item(depth + 1)
|
|
1839
|
+
#map[key] = value
|
|
1840
|
+
add_to_map(map, key, value, linenum)
|
|
1841
|
+
while (ch = current_char()) == ?,
|
|
1842
|
+
ch = getchar()
|
|
1843
|
+
if ch == ?}
|
|
1844
|
+
#* key=:flow_mapnoitem msg="mapping item required (or last comma is extra)."
|
|
1845
|
+
raise syntax_error(:flow_mapnoitem)
|
|
1846
|
+
end
|
|
1847
|
+
#break if ch == ?}
|
|
1848
|
+
linenum = current_linenum()
|
|
1849
|
+
key, value = parse_flow_map_item(depth + 1)
|
|
1850
|
+
#map[key] = value
|
|
1851
|
+
add_to_map(map, key, value, linenum)
|
|
1852
|
+
end
|
|
1853
|
+
end
|
|
1854
|
+
unless current_char() == ?}
|
|
1855
|
+
#* key=:flow_mapnotclosed msg="flow style mapping requires '}'."
|
|
1856
|
+
raise syntax_error(:flow_mapnotclosed)
|
|
1857
|
+
end
|
|
1858
|
+
getchar() if depth > 0
|
|
1859
|
+
return map
|
|
1860
|
+
end
|
|
1861
|
+
|
|
1862
|
+
def parse_flow_map_item(depth)
|
|
1863
|
+
key = parse_flow(depth)
|
|
1864
|
+
unless (ch = current_char()) == ?:
|
|
1865
|
+
s = ch ? "'#{ch.chr}'" : "EOF"
|
|
1866
|
+
#* key=:flow_nocolon msg="':' expected but got '%s'."
|
|
1867
|
+
raise syntax_error(:flow_nocolon)
|
|
1868
|
+
end
|
|
1869
|
+
getchar()
|
|
1870
|
+
value = parse_flow(depth)
|
|
1871
|
+
return key, value
|
|
1872
|
+
end
|
|
1873
|
+
|
|
1874
|
+
def parse_flow_scalar(depth)
|
|
1875
|
+
case ch = current_char()
|
|
1876
|
+
when ?", ?' #"
|
|
1877
|
+
endch = ch
|
|
1878
|
+
s = ''
|
|
1879
|
+
while (ch = _getchar()) != nil && ch != endch
|
|
1880
|
+
s << ch.chr
|
|
1881
|
+
end
|
|
1882
|
+
getchar()
|
|
1883
|
+
scalar = s
|
|
1884
|
+
else
|
|
1885
|
+
s = ch.chr
|
|
1886
|
+
while (ch = _getchar()) != nil && ch != ?: && ch != ?, && ch != ?] && ch != ?}
|
|
1887
|
+
s << ch.chr
|
|
1888
|
+
end
|
|
1889
|
+
scalar = to_scalar(s.strip)
|
|
1890
|
+
end
|
|
1891
|
+
return create_scalar(scalar)
|
|
1892
|
+
end
|
|
1893
|
+
|
|
1894
|
+
|
|
1895
|
+
def parse_tag(column, value)
|
|
1896
|
+
assert value =~ /^!\S+/
|
|
1897
|
+
value =~ /^!(\S+)((\s+)(.*))?$/
|
|
1898
|
+
tag = $1
|
|
1899
|
+
space = $3
|
|
1900
|
+
value2 = $4
|
|
1901
|
+
if value2 && !value2.empty?
|
|
1902
|
+
value_start_column = column + 1 + tag.length + space.length
|
|
1903
|
+
data = parse_value(column, value2, value_start_column)
|
|
1904
|
+
else
|
|
1905
|
+
data = parse_child(column)
|
|
1906
|
+
end
|
|
1907
|
+
return data
|
|
1908
|
+
end
|
|
1909
|
+
|
|
1910
|
+
|
|
1911
|
+
def parse_anchor(column, value)
|
|
1912
|
+
assert value =~ /^\&([-\w]+)(( *)(.*))?$/
|
|
1913
|
+
label = $1
|
|
1914
|
+
space = $3
|
|
1915
|
+
value2 = $4
|
|
1916
|
+
if value2 && !value2.empty?
|
|
1917
|
+
#column2 = column + 1 + label.length + space.length
|
|
1918
|
+
#data = parse_value(column2, value2)
|
|
1919
|
+
value_start_column = column + 1 + label.length + space.length
|
|
1920
|
+
data = parse_value(column, value2, value_start_column)
|
|
1921
|
+
else
|
|
1922
|
+
#column2 = column + 1
|
|
1923
|
+
#data = parse_child(column2)
|
|
1924
|
+
data = parse_child(column)
|
|
1925
|
+
end
|
|
1926
|
+
register_anchor(label, data)
|
|
1927
|
+
return data
|
|
1928
|
+
end
|
|
1929
|
+
|
|
1930
|
+
def register_anchor(label, data)
|
|
1931
|
+
if @anchors[label]
|
|
1932
|
+
#* key=:anchor_duplicated msg="anchor '%s' is already used."
|
|
1933
|
+
raise syntax_error(:anchor_duplicated, [label])
|
|
1934
|
+
end
|
|
1935
|
+
@anchors[label] = data
|
|
1936
|
+
end
|
|
1937
|
+
|
|
1938
|
+
def parse_alias(column, value)
|
|
1939
|
+
assert value =~ /^\*([-\w]+)(( *)(.*))?$/
|
|
1940
|
+
label = $1
|
|
1941
|
+
space = $3
|
|
1942
|
+
value2 = $4
|
|
1943
|
+
if value2 && !value2.empty? && value2[0] != ?\#
|
|
1944
|
+
#* key=:alias_extradata msg="alias cannot take any data."
|
|
1945
|
+
raise syntax_error(:alias_extradata)
|
|
1946
|
+
end
|
|
1947
|
+
data = @anchors[label]
|
|
1948
|
+
unless data
|
|
1949
|
+
data = register_alias(label)
|
|
1950
|
+
#raise syntax_error("anchor '#{label}' not found (cannot refer to backward or child anchor).")
|
|
1951
|
+
end
|
|
1952
|
+
getline()
|
|
1953
|
+
return data
|
|
1954
|
+
end
|
|
1955
|
+
|
|
1956
|
+
def register_alias(label)
|
|
1957
|
+
@aliases[label] ||= 0
|
|
1958
|
+
@aliases[label] += 1
|
|
1959
|
+
return Alias.new(label, @linenum)
|
|
1960
|
+
end
|
|
1961
|
+
|
|
1962
|
+
|
|
1963
|
+
def resolve_aliases(data)
|
|
1964
|
+
@resolved ||= {}
|
|
1965
|
+
return if @resolved[data.__id__]
|
|
1966
|
+
@resolved[data.__id__] = data
|
|
1967
|
+
case data
|
|
1968
|
+
when Array
|
|
1969
|
+
seq = data
|
|
1970
|
+
seq.each_with_index do |val, i|
|
|
1971
|
+
if val.is_a?(Alias)
|
|
1972
|
+
anchor = val
|
|
1973
|
+
if @anchors.key?(anchor.label)
|
|
1974
|
+
#seq[i] = @anchors[anchor.label]
|
|
1975
|
+
set_seq_at(seq, i, @anchors[anchor.label], anchor.linenum)
|
|
1976
|
+
else
|
|
1977
|
+
#* key=:anchor_notfound msg="anchor '%s' not found"
|
|
1978
|
+
raise syntax_error(:anchor_notfound, [val.linenum])
|
|
1979
|
+
end
|
|
1980
|
+
elsif val.is_a?(Array) || val.is_a?(Hash)
|
|
1981
|
+
resolve_aliases(val)
|
|
1982
|
+
end
|
|
1983
|
+
end
|
|
1984
|
+
when Hash
|
|
1985
|
+
map = data
|
|
1986
|
+
map.each do |key, val|
|
|
1987
|
+
if val.is_a?(Alias)
|
|
1988
|
+
if @anchors.key?(val.label)
|
|
1989
|
+
anchor = val
|
|
1990
|
+
#map[key] = @anchors[anchor.label]
|
|
1991
|
+
set_map_with(map, key, @anchors[anchor.label], anchor.linenum)
|
|
1992
|
+
else
|
|
1993
|
+
## :anchor_notfound is already defined on above
|
|
1994
|
+
raise syntax_error(:anchor_notfound, [val.linenum])
|
|
1995
|
+
end
|
|
1996
|
+
elsif val.is_a?(Array) || val.is_a?(Hash)
|
|
1997
|
+
resolve_aliases(val)
|
|
1998
|
+
end
|
|
1999
|
+
end
|
|
2000
|
+
else
|
|
2001
|
+
assert !data.is_a?(Alias)
|
|
2002
|
+
end
|
|
2003
|
+
end
|
|
2004
|
+
|
|
2005
|
+
|
|
2006
|
+
def parse_block_text(column, value)
|
|
2007
|
+
assert value =~ /^[>|\|]/
|
|
2008
|
+
value =~ /^([>|\|])([-+]?)(\d+)?\s*(.*)$/
|
|
2009
|
+
char = $1
|
|
2010
|
+
indicator = $2
|
|
2011
|
+
sep = char == "|" ? "\n" : " "
|
|
2012
|
+
margin = $3 && !$3.empty? ? $3.to_i : nil
|
|
2013
|
+
#text = $4.empty? ? '' : $4 + sep
|
|
2014
|
+
text = $4
|
|
2015
|
+
s = ''
|
|
2016
|
+
empty = ''
|
|
2017
|
+
min_indent = -1
|
|
2018
|
+
while line = _getline()
|
|
2019
|
+
line =~ /^( *)(.*)/
|
|
2020
|
+
indent = $1.length
|
|
2021
|
+
if $2.empty?
|
|
2022
|
+
empty << "\n"
|
|
2023
|
+
elsif indent < column
|
|
2024
|
+
break
|
|
2025
|
+
else
|
|
2026
|
+
min_indent = indent if min_indent < 0 || min_indent > indent
|
|
2027
|
+
s << empty << line
|
|
2028
|
+
empty = ''
|
|
2029
|
+
end
|
|
2030
|
+
end
|
|
2031
|
+
s << empty if indicator == '+' && char != '>'
|
|
2032
|
+
s[-1] = "" if indicator == '-'
|
|
2033
|
+
min_indent = column + margin - 1 if margin
|
|
2034
|
+
if min_indent > 0
|
|
2035
|
+
sp = ' ' * min_indent
|
|
2036
|
+
s.gsub!(/^#{sp}/, '')
|
|
2037
|
+
end
|
|
2038
|
+
if char == '>'
|
|
2039
|
+
s.gsub!(/([^\n])\n([^\n])/, '\1 \2')
|
|
2040
|
+
s.gsub!(/\n(\n+)/, '\1')
|
|
2041
|
+
s << empty if indicator == '+'
|
|
2042
|
+
end
|
|
2043
|
+
getline() if current_line() =~ /^\s*\#/
|
|
2044
|
+
return create_scalar(text + s)
|
|
2045
|
+
end
|
|
2046
|
+
|
|
2047
|
+
|
|
2048
|
+
def parse_sequence(column, value)
|
|
2049
|
+
assert value =~ /^-(( +)(.*))?$/
|
|
2050
|
+
seq = create_sequence() # []
|
|
2051
|
+
while true
|
|
2052
|
+
unless value =~ /^-(( +)(.*))?$/
|
|
2053
|
+
#* key=:sequence_noitem msg="sequence item is expected."
|
|
2054
|
+
raise syntax_error(:sequence_noitem)
|
|
2055
|
+
end
|
|
2056
|
+
value2 = $3
|
|
2057
|
+
space = $2
|
|
2058
|
+
column2 = column + 1
|
|
2059
|
+
linenum = current_linenum()
|
|
2060
|
+
#
|
|
2061
|
+
if !value2 || value2.empty?
|
|
2062
|
+
elem = parse_child(column2)
|
|
2063
|
+
else
|
|
2064
|
+
value_start_column = column2 + space.length
|
|
2065
|
+
elem = parse_value(column2, value2, value_start_column)
|
|
2066
|
+
end
|
|
2067
|
+
add_to_seq(seq, elem, linenum) #seq << elem
|
|
2068
|
+
#
|
|
2069
|
+
line = current_line()
|
|
2070
|
+
break unless line
|
|
2071
|
+
line =~ /^( *)(.*)/
|
|
2072
|
+
indent = $1.length
|
|
2073
|
+
if indent < column
|
|
2074
|
+
break
|
|
2075
|
+
elsif indent > column
|
|
2076
|
+
#* key=:sequence_badindent msg="illegal indent of sequence."
|
|
2077
|
+
raise syntax_error(:sequence_badindent)
|
|
2078
|
+
end
|
|
2079
|
+
value = $2
|
|
2080
|
+
end
|
|
2081
|
+
return seq
|
|
2082
|
+
end
|
|
2083
|
+
|
|
2084
|
+
|
|
2085
|
+
def parse_mapping(column, value)
|
|
2086
|
+
#assert value =~ /^(:?["']?[-.\w]+["']? *):(( +)(.*))?$/ #'
|
|
2087
|
+
assert value =~ /^((?::?[-.\w]+\*?|'.*?'|".*?"|=|<<) *):(( +)(.*))?$/
|
|
2088
|
+
map = create_mapping() # {}
|
|
2089
|
+
while true
|
|
2090
|
+
#unless value =~ /^(:?["']?[-.\w]+["']? *):(( +)(.*))?$/ #'
|
|
2091
|
+
unless value =~ /^((?::?[-.\w]+\*?|'.*?'|".*?"|=|<<) *):(( +)(.*))?$/
|
|
2092
|
+
#* key=:mapping_noitem msg="mapping item is expected."
|
|
2093
|
+
raise syntax_error(:mapping_noitem)
|
|
2094
|
+
end
|
|
2095
|
+
v = $1.strip
|
|
2096
|
+
key = to_scalar(v)
|
|
2097
|
+
value2 = $4
|
|
2098
|
+
column2 = column + 1
|
|
2099
|
+
linenum = current_linenum()
|
|
2100
|
+
#
|
|
2101
|
+
if !value2 || value2.empty?
|
|
2102
|
+
elem = parse_child(column2)
|
|
2103
|
+
else
|
|
2104
|
+
value_start_column = column2 + $1.length + $3.length
|
|
2105
|
+
elem = parse_value(column2, value2, value_start_column)
|
|
2106
|
+
end
|
|
2107
|
+
case v
|
|
2108
|
+
when '='
|
|
2109
|
+
set_default(map, elem, linenum)
|
|
2110
|
+
when '<<'
|
|
2111
|
+
merge_map(map, elem, linenum)
|
|
2112
|
+
else
|
|
2113
|
+
add_to_map(map, key, elem, linenum) # map[key] = elem
|
|
2114
|
+
end
|
|
2115
|
+
#
|
|
2116
|
+
line = current_line()
|
|
2117
|
+
break unless line
|
|
2118
|
+
line =~ /^( *)(.*)/
|
|
2119
|
+
indent = $1.length
|
|
2120
|
+
if indent < column
|
|
2121
|
+
break
|
|
2122
|
+
elsif indent > column
|
|
2123
|
+
#* key=:mapping_badindent msg="illegal indent of mapping."
|
|
2124
|
+
raise syntax_error(:mapping_badindent)
|
|
2125
|
+
end
|
|
2126
|
+
value = $2
|
|
2127
|
+
end
|
|
2128
|
+
return map
|
|
2129
|
+
end
|
|
2130
|
+
|
|
2131
|
+
|
|
2132
|
+
def parse_scalar(indent, value)
|
|
2133
|
+
data = create_scalar(to_scalar(value))
|
|
2134
|
+
getline()
|
|
2135
|
+
return data
|
|
2136
|
+
end
|
|
2137
|
+
|
|
2138
|
+
|
|
2139
|
+
def to_scalar(str)
|
|
2140
|
+
case str
|
|
2141
|
+
when /^"(.*)"([ \t]*\#.*$)?/ ; return $1
|
|
2142
|
+
when /^'(.*)'([ \t]*\#.*$)?/ ; return $1
|
|
2143
|
+
when /^(.*\S)[ \t]*\#/ ; str = $1
|
|
2144
|
+
end
|
|
2145
|
+
|
|
2146
|
+
case str
|
|
2147
|
+
when /^-?\d+$/ ; return str.to_i # integer
|
|
2148
|
+
when /^-?\d+\.\d+$/ ; return str.to_f # float
|
|
2149
|
+
when "true", "yes", "on" ; return true # true
|
|
2150
|
+
when "false", "no", "off" ; return false # false
|
|
2151
|
+
when "null", "~" ; return nil # nil
|
|
2152
|
+
#when /^"(.*)"$/ ; return $1 # "string"
|
|
2153
|
+
#when /^'(.*)'$/ ; return $1 # 'string'
|
|
2154
|
+
when /^:(\w+)$/ ; return $1.intern # :symbol
|
|
2155
|
+
when /^(\d\d\d\d)-(\d\d)-(\d\d)$/ # date
|
|
2156
|
+
year, month, day = $1.to_i, $2.to_i, $3.to_i
|
|
2157
|
+
return Date.new(year, month, day)
|
|
2158
|
+
when /^(\d\d\d\d)-(\d\d)-(\d\d)(?:[Tt]|[ \t]+)(\d\d?):(\d\d):(\d\d)(\.\d*)?(?:Z|[ \t]*([-+]\d\d?)(?::(\d\d))?)?$/
|
|
2159
|
+
year, mon, mday, hour, min, sec, usec, tzone_h, tzone_m = $1, $2, $3, $4, $5, $6, $7, $8, $9
|
|
2160
|
+
#Time.utc(sec, min, hour, mday, mon, year, wday, yday, isdst, zone)
|
|
2161
|
+
#t = Time.utc(sec, min, hour, mday, mon, year, nil, nil, nil, nil)
|
|
2162
|
+
#Time.utc(year[, mon[, day[, hour[, min[, sec[, usec]]]]]])
|
|
2163
|
+
time = Time.utc(year, mon, day, hour, min, sec, usec)
|
|
2164
|
+
if tzone_h
|
|
2165
|
+
diff_sec = tzone_h.to_i * 60 * 60
|
|
2166
|
+
if tzone_m
|
|
2167
|
+
if diff_sec > 0 ; diff_sec += tzone_m.to_i * 60
|
|
2168
|
+
else ; diff_sec -= tzone_m.to_i * 60
|
|
2169
|
+
end
|
|
2170
|
+
end
|
|
2171
|
+
p diff_sec
|
|
2172
|
+
time -= diff_sec
|
|
2173
|
+
end
|
|
2174
|
+
return time
|
|
2175
|
+
end
|
|
2176
|
+
return str
|
|
2177
|
+
end
|
|
2178
|
+
|
|
2179
|
+
|
|
2180
|
+
def assert(bool_expr)
|
|
2181
|
+
raise "*** assertion error" unless bool_expr
|
|
2182
|
+
end
|
|
2183
|
+
|
|
2184
|
+
end
|
|
2185
|
+
|
|
2186
|
+
|
|
2187
|
+
|
|
2188
|
+
##
|
|
2189
|
+
## ex.
|
|
2190
|
+
## # load document with YamlParser
|
|
2191
|
+
## str = ARGF.read()
|
|
2192
|
+
## parser = Kwalify::YamlParser.new(str)
|
|
2193
|
+
## document = parser.parse()
|
|
2194
|
+
##
|
|
2195
|
+
## # validate document
|
|
2196
|
+
## schema = YAML.load(File.read('schema.yaml'))
|
|
2197
|
+
## validator = Kwalify::Validator.new(schema)
|
|
2198
|
+
## errors = validator.validate(document)
|
|
2199
|
+
##
|
|
2200
|
+
## # print validation result
|
|
2201
|
+
## if errors && !errors.empty?
|
|
2202
|
+
## parser.set_errors_linenum(errors)
|
|
2203
|
+
## errors.sort.each do |error|
|
|
2204
|
+
## print "line %d: path %s: %s" % [error.linenum, error.path, error.message]
|
|
2205
|
+
## end
|
|
2206
|
+
## end
|
|
2207
|
+
##
|
|
2208
|
+
class YamlParser < PlainYamlParser
|
|
2209
|
+
|
|
2210
|
+
def initialize(*args)
|
|
2211
|
+
super
|
|
2212
|
+
@linenums_table = {} # object_id -> hash or array
|
|
2213
|
+
end
|
|
2214
|
+
|
|
2215
|
+
def parse()
|
|
2216
|
+
@doc = super()
|
|
2217
|
+
return @doc
|
|
2218
|
+
end
|
|
2219
|
+
|
|
2220
|
+
def path_linenum(path)
|
|
2221
|
+
return 1 if path.empty? || path == '/'
|
|
2222
|
+
elems = path.split('/')
|
|
2223
|
+
elems.shift if path[0] == ?/ # delete empty string on head
|
|
2224
|
+
last_elem = elems.pop
|
|
2225
|
+
c = @doc # collection
|
|
2226
|
+
elems.each do |elem|
|
|
2227
|
+
if c.is_a?(Array)
|
|
2228
|
+
c = c[elem.to_i]
|
|
2229
|
+
elsif c.is_a?(Hash)
|
|
2230
|
+
c = c[elem]
|
|
2231
|
+
else
|
|
2232
|
+
assert false
|
|
2233
|
+
end
|
|
2234
|
+
end
|
|
2235
|
+
linenums = @linenums_table[c.__id__]
|
|
2236
|
+
if c.is_a?(Array)
|
|
2237
|
+
linenum = linenums[last_elem.to_i]
|
|
2238
|
+
elsif c.is_a?(Hash)
|
|
2239
|
+
linenum = linenums[last_elem]
|
|
2240
|
+
end
|
|
2241
|
+
return linenum
|
|
2242
|
+
end
|
|
2243
|
+
|
|
2244
|
+
def set_errors_linenum(errors)
|
|
2245
|
+
errors.each do |error|
|
|
2246
|
+
error.linenum = path_linenum(error.path)
|
|
2247
|
+
end
|
|
2248
|
+
end
|
|
2249
|
+
|
|
2250
|
+
def set_error_linenums(errors)
|
|
2251
|
+
$stderr.puts "*** Kwalify::YamlParser#set_error_linenums() is obsolete. You should use set_errors_linenum() instead."
|
|
2252
|
+
set_errors_linenum(errors)
|
|
2253
|
+
end
|
|
2254
|
+
|
|
2255
|
+
protected
|
|
2256
|
+
|
|
2257
|
+
def create_sequence(linenum=current_linenum())
|
|
2258
|
+
seq = []
|
|
2259
|
+
@linenums_table[seq.__id__] = []
|
|
2260
|
+
return seq
|
|
2261
|
+
end
|
|
2262
|
+
|
|
2263
|
+
def add_to_seq(seq, value, linenum)
|
|
2264
|
+
seq << value
|
|
2265
|
+
@linenums_table[seq.__id__] << linenum
|
|
2266
|
+
end
|
|
2267
|
+
|
|
2268
|
+
def set_seq_at(seq, i, value, linenum)
|
|
2269
|
+
seq[i] = value
|
|
2270
|
+
@linenums_table[seq.__id__][i] = linenum
|
|
2271
|
+
end
|
|
2272
|
+
|
|
2273
|
+
def create_mapping(linenum=current_linenum())
|
|
2274
|
+
map = {}
|
|
2275
|
+
@linenums_table[map.__id__] = {}
|
|
2276
|
+
return map
|
|
2277
|
+
end
|
|
2278
|
+
|
|
2279
|
+
def add_to_map(map, key, value, linenum)
|
|
2280
|
+
map[key] = value
|
|
2281
|
+
@linenums_table[map.__id__][key] = linenum
|
|
2282
|
+
end
|
|
2283
|
+
|
|
2284
|
+
def set_map_with(map, key, value, linenum)
|
|
2285
|
+
map[key] = value
|
|
2286
|
+
@linenums_table[map.__id__][key] = linenum
|
|
2287
|
+
end
|
|
2288
|
+
|
|
2289
|
+
def set_default(map, value, linenum)
|
|
2290
|
+
map.default = value
|
|
2291
|
+
@linenums_table[map.__id__][:'='] = linenum
|
|
2292
|
+
end
|
|
2293
|
+
|
|
2294
|
+
def merge_map(map, collection, linenum)
|
|
2295
|
+
t = @linenums_table[map.__id__]
|
|
2296
|
+
list = collection.is_a?(Array) ? collection : [ collection ]
|
|
2297
|
+
list.each do |m|
|
|
2298
|
+
t2 = @linenums_table[m.__id__]
|
|
2299
|
+
m.each do |key, val|
|
|
2300
|
+
unless map.key?(key)
|
|
2301
|
+
map[key] = val
|
|
2302
|
+
t[key] = t2[key]
|
|
2303
|
+
end
|
|
2304
|
+
end
|
|
2305
|
+
end
|
|
2306
|
+
end
|
|
2307
|
+
|
|
2308
|
+
def create_scalar(value, linenum=current_linenum())
|
|
2309
|
+
data = super(value)
|
|
2310
|
+
#return Scalar.new(data, linenum)
|
|
2311
|
+
return data
|
|
2312
|
+
end
|
|
2313
|
+
|
|
2314
|
+
end
|
|
2315
|
+
|
|
2316
|
+
|
|
2317
|
+
## obsolete
|
|
2318
|
+
class Parser < YamlParser
|
|
2319
|
+
def initialize(yaml_str)
|
|
2320
|
+
super(yaml_str)
|
|
2321
|
+
$stderr.puts "*** class Kwalify::Parser is obsolete. Please use Kwalify::YamlParser instead."
|
|
2322
|
+
end
|
|
2323
|
+
end
|
|
2324
|
+
|
|
2325
|
+
end
|
|
2326
|
+
#--end of require 'kwalify/yaml-parser'
|
|
2327
|
+
#--end of require 'kwalify'
|
|
2328
|
+
#--begin of require 'kwalify/main'
|
|
2329
|
+
###
|
|
2330
|
+
### $Rev: 51 $
|
|
2331
|
+
### $Release: 0.6.0 $
|
|
2332
|
+
### copyright(c) 2005 kuwata-lab all rights reserved.
|
|
2333
|
+
###
|
|
2334
|
+
|
|
2335
|
+
require 'yaml'
|
|
2336
|
+
require 'erb'
|
|
2337
|
+
#--already included require 'kwalify'
|
|
2338
|
+
#--begin of require 'kwalify/util/yaml-helper'
|
|
2339
|
+
###
|
|
2340
|
+
### $Rev: 51 $
|
|
2341
|
+
### $Release: 0.6.0 $
|
|
2342
|
+
### copyright(c) 2005 kuwata-lab all rights reserved.
|
|
2343
|
+
###
|
|
2344
|
+
|
|
2345
|
+
require 'yaml'
|
|
2346
|
+
|
|
2347
|
+
module YamlHelper
|
|
2348
|
+
|
|
2349
|
+
##
|
|
2350
|
+
## expand tab character to spaces
|
|
2351
|
+
##
|
|
2352
|
+
## ex.
|
|
2353
|
+
## untabified_str = YamlHelper.untabify(tabbed_str)
|
|
2354
|
+
##
|
|
2355
|
+
## input:: String or IO
|
|
2356
|
+
##
|
|
2357
|
+
def self.untabify(input)
|
|
2358
|
+
s = ''
|
|
2359
|
+
input.each_line do |line|
|
|
2360
|
+
s << line.gsub(/([^\t]{8})|([^\t]*)\t/n) { [$+].pack("A8") }
|
|
2361
|
+
end
|
|
2362
|
+
return s
|
|
2363
|
+
end
|
|
2364
|
+
|
|
2365
|
+
|
|
2366
|
+
##
|
|
2367
|
+
## create a hash table from list of hash with primary key.
|
|
2368
|
+
##
|
|
2369
|
+
## ex.
|
|
2370
|
+
## hashlist = [
|
|
2371
|
+
## { "name"=>"Foo", "gender"=>"M", "age"=>20, },
|
|
2372
|
+
## { "name"=>"Bar", "gender"=>"F", "age"=>25, },
|
|
2373
|
+
## { "name"=>"Baz", "gender"=>"M", "age"=>30, },
|
|
2374
|
+
## ]
|
|
2375
|
+
## hashtable = YamlHelper.create_hashtable(hashlist, "name")
|
|
2376
|
+
## p hashtable
|
|
2377
|
+
## # => { "Foo" => { "name"=>"Foo", "gender"=>"M", "age"=>20, },
|
|
2378
|
+
## "Bar" => { "name"=>"Bar", "gender"=>"F", "age"=>25, },
|
|
2379
|
+
## "Baz" => { "name"=>"Baz", "gender"=>"M", "age"=>30, }, }
|
|
2380
|
+
##
|
|
2381
|
+
def self.create_hashtable(hashlist, primarykey, flag_duplicate_check=true)
|
|
2382
|
+
hashtable = {}
|
|
2383
|
+
hashlist.each do |hash|
|
|
2384
|
+
key = hash[primarykey]
|
|
2385
|
+
unless key
|
|
2386
|
+
riase "primary key '#{key}' not found."
|
|
2387
|
+
end
|
|
2388
|
+
if flag_duplicate_check && hashtable.key?(key)
|
|
2389
|
+
raise "primary key '#{key}' duplicated (value '#{hashtable[key]}')"
|
|
2390
|
+
end
|
|
2391
|
+
hashtable[key] = hash
|
|
2392
|
+
end if hashlist
|
|
2393
|
+
return hashtable
|
|
2394
|
+
end
|
|
2395
|
+
|
|
2396
|
+
|
|
2397
|
+
##
|
|
2398
|
+
## get nested value directly.
|
|
2399
|
+
##
|
|
2400
|
+
## ex.
|
|
2401
|
+
## val = YamlHelper.get_value(obj, ['aaa', 0, 'xxx'])
|
|
2402
|
+
##
|
|
2403
|
+
## This is equal to the following:
|
|
2404
|
+
## begin
|
|
2405
|
+
## val = obj['aaa'][0]['xxx']
|
|
2406
|
+
## rescue NameError
|
|
2407
|
+
## val = nil
|
|
2408
|
+
## end
|
|
2409
|
+
##
|
|
2410
|
+
def self.get_value(obj, path)
|
|
2411
|
+
val = obj
|
|
2412
|
+
path.each do |key|
|
|
2413
|
+
return nil unless val.is_a?(Hash) || val.is_a?(Array)
|
|
2414
|
+
val = val[key]
|
|
2415
|
+
end if path
|
|
2416
|
+
return val
|
|
2417
|
+
end
|
|
2418
|
+
|
|
2419
|
+
end
|
|
2420
|
+
|
|
2421
|
+
#--end of require 'kwalify/util/yaml-helper'
|
|
2422
|
+
#require 'kwalify/util/option-parser'
|
|
2423
|
+
|
|
2424
|
+
|
|
2425
|
+
module Kwalify
|
|
2426
|
+
|
|
2427
|
+
|
|
2428
|
+
class CommandOptionError < KwalifyError
|
|
2429
|
+
def initialize(message, option, error_symbol)
|
|
2430
|
+
super(message)
|
|
2431
|
+
@option = option
|
|
2432
|
+
@error_symbol = error_symbol
|
|
2433
|
+
end
|
|
2434
|
+
attr_reader :option, :error_symbol
|
|
2435
|
+
end
|
|
2436
|
+
|
|
2437
|
+
|
|
2438
|
+
##
|
|
2439
|
+
## ex.
|
|
2440
|
+
## command = File.basename($0)
|
|
2441
|
+
## begin
|
|
2442
|
+
## main = Kwalify::Main.new(command)
|
|
2443
|
+
## s = main.execute
|
|
2444
|
+
## print s if s
|
|
2445
|
+
## rescue Kwalify::CommandOptionError => ex
|
|
2446
|
+
## $stderr.puts "ERROR: #{ex.message}"
|
|
2447
|
+
## exit 1
|
|
2448
|
+
## rescue Kwalify::KwalifyError => ex
|
|
2449
|
+
## $stderr.puts "ERROR: #{ex.message}"
|
|
2450
|
+
## exit 1
|
|
2451
|
+
## end
|
|
2452
|
+
##
|
|
2453
|
+
class Main
|
|
2454
|
+
|
|
2455
|
+
|
|
2456
|
+
def initialize(command=nil)
|
|
2457
|
+
@command = command || File.basename($0)
|
|
2458
|
+
@options = {}
|
|
2459
|
+
@properties = {}
|
|
2460
|
+
@template_path = []
|
|
2461
|
+
$:.each do |path|
|
|
2462
|
+
tpath = "#{path}/kwalify/templates"
|
|
2463
|
+
@template_path << tpath if test(?d, tpath)
|
|
2464
|
+
end
|
|
2465
|
+
end
|
|
2466
|
+
|
|
2467
|
+
|
|
2468
|
+
def debug?
|
|
2469
|
+
@options[:debug]
|
|
2470
|
+
end
|
|
2471
|
+
|
|
2472
|
+
|
|
2473
|
+
def _inspect()
|
|
2474
|
+
sb = []
|
|
2475
|
+
sb << "command: #{@command}\n"
|
|
2476
|
+
sb << "options:\n"
|
|
2477
|
+
@options.keys.sort {|k1,k2| k1.to_s<=>k2.to_s }.each do |key|
|
|
2478
|
+
sb << " - #{key}: #{@options[key]}\n"
|
|
2479
|
+
end
|
|
2480
|
+
sb << "properties:\n"
|
|
2481
|
+
@properties.keys.sort_by {|k| k.to_s}.each do |key|
|
|
2482
|
+
sb << " - #{key}: #{@properties[key]}\n"
|
|
2483
|
+
end
|
|
2484
|
+
#sb << "template_path:\n"
|
|
2485
|
+
#@template_path.each do |path|
|
|
2486
|
+
# sb << " - #{path}\n"
|
|
2487
|
+
#end
|
|
2488
|
+
return sb.join
|
|
2489
|
+
end
|
|
2490
|
+
|
|
2491
|
+
|
|
2492
|
+
def execute(argv=ARGV)
|
|
2493
|
+
# parse command-line options
|
|
2494
|
+
filenames = _parse_argv(argv)
|
|
2495
|
+
|
|
2496
|
+
# help or version
|
|
2497
|
+
if @options[:help] || @options[:version]
|
|
2498
|
+
action = @options[:action]
|
|
2499
|
+
s = ''
|
|
2500
|
+
s << _version() << "\n" if @options[:version]
|
|
2501
|
+
s << _usage() if @options[:help] && !action
|
|
2502
|
+
s << _describe_properties(action) if @options[:help] && action
|
|
2503
|
+
return s
|
|
2504
|
+
end
|
|
2505
|
+
|
|
2506
|
+
# validation
|
|
2507
|
+
if @options[:meta2]
|
|
2508
|
+
s = _quick_meta_validate(filenames)
|
|
2509
|
+
elsif @options[:meta]
|
|
2510
|
+
s = _meta_validate(filenames)
|
|
2511
|
+
elsif @options[:action]
|
|
2512
|
+
if !@options[:schema]
|
|
2513
|
+
#* key=:command_option_actionnoschema msg="schema filename is not specified."
|
|
2514
|
+
raise option_error(:command_option_actionnoschema, @options[:action])
|
|
2515
|
+
end
|
|
2516
|
+
s = _perform_action(@options[:action], @options[:schema])
|
|
2517
|
+
elsif @options[:schema]
|
|
2518
|
+
if @options[:debug]
|
|
2519
|
+
s = _inspect_schema(@options[:schema])
|
|
2520
|
+
else
|
|
2521
|
+
s = _validate(filenames, @options[:schema])
|
|
2522
|
+
end
|
|
2523
|
+
else
|
|
2524
|
+
#* key=:command_option_noaction msg="command-line option '-f' or '-m' required."
|
|
2525
|
+
raise option_error(:command_option_noaction, @command)
|
|
2526
|
+
end
|
|
2527
|
+
return s # or return (s == nil || s.empty?) ? nil : s
|
|
2528
|
+
end
|
|
2529
|
+
|
|
2530
|
+
|
|
2531
|
+
def self.main(command, argv=ARGV)
|
|
2532
|
+
begin
|
|
2533
|
+
main = Kwalify::Main.new(command)
|
|
2534
|
+
s = main.execute(argv)
|
|
2535
|
+
print s if s
|
|
2536
|
+
rescue Kwalify::CommandOptionError => ex
|
|
2537
|
+
$stderr.puts ex.message
|
|
2538
|
+
exit 1
|
|
2539
|
+
rescue Kwalify::KwalifyError => ex
|
|
2540
|
+
$stderr.puts "ERROR: #{ex.message}"
|
|
2541
|
+
exit 1
|
|
2542
|
+
#rescue => ex
|
|
2543
|
+
# if main.debug?
|
|
2544
|
+
# raise ex
|
|
2545
|
+
# else
|
|
2546
|
+
# $stderr.puts ex.message
|
|
2547
|
+
# exit 1
|
|
2548
|
+
# end
|
|
2549
|
+
end
|
|
2550
|
+
end
|
|
2551
|
+
|
|
2552
|
+
|
|
2553
|
+
private
|
|
2554
|
+
|
|
2555
|
+
|
|
2556
|
+
def option_error(error_symbol, arg)
|
|
2557
|
+
msg = Kwalify.msg(error_symbol) % arg
|
|
2558
|
+
return CommandOptionError.new(msg, arg, error_symbol)
|
|
2559
|
+
end
|
|
2560
|
+
|
|
2561
|
+
|
|
2562
|
+
def _find_template(action)
|
|
2563
|
+
template_filename = action + '.eruby'
|
|
2564
|
+
unless test(?f, template_filename)
|
|
2565
|
+
tpath = @template_path.find { |path| test(?f, "#{path}/#{template_filename}") }
|
|
2566
|
+
#* key=:command_option_notemplate msg="%s: invalid action (template not found).\n"
|
|
2567
|
+
raise option_error(:command_option_notemplate, action) unless tpath
|
|
2568
|
+
template_filename = "#{tpath}/#{action}.eruby"
|
|
2569
|
+
end
|
|
2570
|
+
return template_filename
|
|
2571
|
+
end
|
|
2572
|
+
|
|
2573
|
+
|
|
2574
|
+
def _apply_template(template_filename, hash)
|
|
2575
|
+
template = File.read(template_filename)
|
|
2576
|
+
trim_mode = 1
|
|
2577
|
+
erb = ERB.new(template, $SAFE, trim_mode)
|
|
2578
|
+
context = Object.new
|
|
2579
|
+
hash.each do |key, val|
|
|
2580
|
+
context.instance_variable_set("@#{key}", val)
|
|
2581
|
+
end
|
|
2582
|
+
return context.instance_eval(erb.src, template_filename)
|
|
2583
|
+
end
|
|
2584
|
+
|
|
2585
|
+
|
|
2586
|
+
def _describe_properties(action)
|
|
2587
|
+
template_filename = _find_template(action)
|
|
2588
|
+
s = _apply_template(template_filename, :describe=>true)
|
|
2589
|
+
return s
|
|
2590
|
+
end
|
|
2591
|
+
|
|
2592
|
+
|
|
2593
|
+
def _perform_action(action, schema_filename, describe=false)
|
|
2594
|
+
template_filename = _find_template(action)
|
|
2595
|
+
schema = _load_schema_file(schema_filename)
|
|
2596
|
+
rule = Rule.new(schema)
|
|
2597
|
+
@properties[:schema_filename] = schema_filename
|
|
2598
|
+
s = _apply_template(template_filename, :rule=>rule, :properties=>@properties)
|
|
2599
|
+
return s
|
|
2600
|
+
end
|
|
2601
|
+
|
|
2602
|
+
|
|
2603
|
+
def _inspect_schema(schema_filename)
|
|
2604
|
+
filename = schema_filename
|
|
2605
|
+
str = File.open(schema_filename) { |f| f.read() }
|
|
2606
|
+
str = YamlUtil.untabify(str) if @options[:untabify]
|
|
2607
|
+
schema = YAML.load(str)
|
|
2608
|
+
return nil if schema == nil
|
|
2609
|
+
validator = Kwalify::Validator.new(schema) # error raised when schema is wrong
|
|
2610
|
+
s = validator._inspect()
|
|
2611
|
+
s << "\n" unless s[-1] == ?\n
|
|
2612
|
+
return s
|
|
2613
|
+
end
|
|
2614
|
+
|
|
2615
|
+
|
|
2616
|
+
#--
|
|
2617
|
+
#class QuickMetaValidator
|
|
2618
|
+
# def validate(schema)
|
|
2619
|
+
# errors = []
|
|
2620
|
+
# begin
|
|
2621
|
+
# validator = Kwalify::Validator.new(schema) # error raised when schema is wrong
|
|
2622
|
+
# rescue Kwalify::SchemaError => ex
|
|
2623
|
+
# errors << ex
|
|
2624
|
+
# end
|
|
2625
|
+
# return errors
|
|
2626
|
+
# end
|
|
2627
|
+
#end
|
|
2628
|
+
#++
|
|
2629
|
+
|
|
2630
|
+
|
|
2631
|
+
def _quick_meta_validate(filenames)
|
|
2632
|
+
meta_validator = Object.new
|
|
2633
|
+
def meta_validator.validate(schema)
|
|
2634
|
+
errors = []
|
|
2635
|
+
begin
|
|
2636
|
+
validator = Kwalify::Validator.new(schema) # error raised when schema is wrong
|
|
2637
|
+
rescue Kwalify::SchemaError => ex
|
|
2638
|
+
errors << ex
|
|
2639
|
+
end
|
|
2640
|
+
return errors
|
|
2641
|
+
end
|
|
2642
|
+
s = _validate_files(meta_validator, filenames)
|
|
2643
|
+
return s
|
|
2644
|
+
end
|
|
2645
|
+
|
|
2646
|
+
|
|
2647
|
+
def _load_schema_file(schema_filename)
|
|
2648
|
+
filename = schema_filename
|
|
2649
|
+
str = File.read(filename)
|
|
2650
|
+
str = YamlHelper.untabify(str) if @options[:untabify]
|
|
2651
|
+
schema = YAML.load(str)
|
|
2652
|
+
return schema
|
|
2653
|
+
end
|
|
2654
|
+
|
|
2655
|
+
|
|
2656
|
+
def _meta_validate(filenames)
|
|
2657
|
+
meta_validator = Kwalify::MetaValidator.instance()
|
|
2658
|
+
s = _validate_files(meta_validator, filenames)
|
|
2659
|
+
return s
|
|
2660
|
+
end
|
|
2661
|
+
|
|
2662
|
+
|
|
2663
|
+
def _validate(filenames, schema_filename)
|
|
2664
|
+
schema = _load_schema_file(schema_filename)
|
|
2665
|
+
if schema
|
|
2666
|
+
validator = Kwalify::Validator.new(schema)
|
|
2667
|
+
s = _validate_files(validator, filenames)
|
|
2668
|
+
else
|
|
2669
|
+
#* key=:schema_empty msg="%s: empty schema.\n"
|
|
2670
|
+
s = Kwalify.msg(:schema_emtpy) % filename
|
|
2671
|
+
end
|
|
2672
|
+
return s
|
|
2673
|
+
end
|
|
2674
|
+
|
|
2675
|
+
|
|
2676
|
+
def _validate_files(validator, filenames)
|
|
2677
|
+
s = ''
|
|
2678
|
+
filenames = [ nil ] if filenames.empty?
|
|
2679
|
+
filenames.each do |filename|
|
|
2680
|
+
if filename
|
|
2681
|
+
str = File.open(filename) { |f| f.read() } # or File.read(filename) in ruby1.8
|
|
2682
|
+
else
|
|
2683
|
+
str = $stdin.read()
|
|
2684
|
+
filename = '(stdin)'
|
|
2685
|
+
end
|
|
2686
|
+
str = YamlHelper.untabify(str) if @options[:untabify]
|
|
2687
|
+
if @options[:linenum]
|
|
2688
|
+
parser = Kwalify::YamlParser.new(str)
|
|
2689
|
+
i = 0
|
|
2690
|
+
while parser.has_next?
|
|
2691
|
+
doc = parser.parse()
|
|
2692
|
+
s << _validate_document(validator, doc, filename, i, parser)
|
|
2693
|
+
i += 1
|
|
2694
|
+
end
|
|
2695
|
+
else
|
|
2696
|
+
parser = nil
|
|
2697
|
+
i = 0
|
|
2698
|
+
YAML.load_documents(str) do |doc|
|
|
2699
|
+
s << _validate_document(validator, doc, filename, i, nil)
|
|
2700
|
+
i += 1
|
|
2701
|
+
end
|
|
2702
|
+
end
|
|
2703
|
+
end
|
|
2704
|
+
return s
|
|
2705
|
+
end
|
|
2706
|
+
|
|
2707
|
+
|
|
2708
|
+
def _validate_document(validator, doc, filename, i, parser=nil)
|
|
2709
|
+
s = ''
|
|
2710
|
+
if doc == nil
|
|
2711
|
+
#* key=:validation_empty msg="%s#%d: empty.\n"
|
|
2712
|
+
s << kwalify.msg(:validation_empty) % [filename, i]
|
|
2713
|
+
return s
|
|
2714
|
+
end
|
|
2715
|
+
errors = validator.validate(doc)
|
|
2716
|
+
if errors == nil || errors.empty?
|
|
2717
|
+
#* key=:validation_valid msg="%s#%d: valid.\n"
|
|
2718
|
+
s << Kwalify.msg(:validation_valid) % [filename, i] unless @options[:silent]
|
|
2719
|
+
else
|
|
2720
|
+
#* key=:validation_invalid msg="%s#%d: INVALID\n"
|
|
2721
|
+
s << Kwalify.msg(:validation_invalid) % [filename, i]
|
|
2722
|
+
if @options[:linenum]
|
|
2723
|
+
#assert parser != nil
|
|
2724
|
+
raise unless parser != nil
|
|
2725
|
+
parser.set_errors_linenum(errors)
|
|
2726
|
+
errors.sort!
|
|
2727
|
+
end
|
|
2728
|
+
errors.each do |error|
|
|
2729
|
+
if @options[:emacs]
|
|
2730
|
+
#assert @options[:linenum]
|
|
2731
|
+
raise unless @options[:linenum]
|
|
2732
|
+
s << "#{filename}:#{error.linenum}: [#{error.path}] #{error.message}\n"
|
|
2733
|
+
elsif @options[:linenum]
|
|
2734
|
+
s << " - (line #{error.linenum}) [#{error.path}] #{error.message}\n"
|
|
2735
|
+
else
|
|
2736
|
+
s << " - [#{error.path}] #{error.message}\n"
|
|
2737
|
+
end
|
|
2738
|
+
end
|
|
2739
|
+
end
|
|
2740
|
+
return s
|
|
2741
|
+
end
|
|
2742
|
+
|
|
2743
|
+
|
|
2744
|
+
def _usage()
|
|
2745
|
+
#msg = Kwalify.msg(:command_help) % [@command, @command, @command]
|
|
2746
|
+
msg = Kwalify.msg(:command_help)
|
|
2747
|
+
return msg
|
|
2748
|
+
end
|
|
2749
|
+
|
|
2750
|
+
|
|
2751
|
+
def _version()
|
|
2752
|
+
return RELEASE
|
|
2753
|
+
end
|
|
2754
|
+
|
|
2755
|
+
|
|
2756
|
+
def _to_value(str)
|
|
2757
|
+
case str
|
|
2758
|
+
when nil, "null", "nil" ; return nil
|
|
2759
|
+
when "true", "yes" ; return true
|
|
2760
|
+
when "false", "no" ; return false
|
|
2761
|
+
when /\A\d+\z/ ; return str.to_i
|
|
2762
|
+
when /\A\d+\.\d+\z/ ; return str.to_f
|
|
2763
|
+
when /\/(.*)\// ; return Regexp.new($1)
|
|
2764
|
+
when /\A'.*'\z/, /\A".*"\z/ ; return eval(str)
|
|
2765
|
+
else ; return str
|
|
2766
|
+
end
|
|
2767
|
+
end
|
|
2768
|
+
|
|
2769
|
+
|
|
2770
|
+
def _parse_argv(argv)
|
|
2771
|
+
option_table = {
|
|
2772
|
+
?h => :help,
|
|
2773
|
+
?v => :version,
|
|
2774
|
+
?s => :silent,
|
|
2775
|
+
?t => :untabify,
|
|
2776
|
+
?m => :meta,
|
|
2777
|
+
?M => :meta2,
|
|
2778
|
+
?E => :emacs,
|
|
2779
|
+
?l => :linenum,
|
|
2780
|
+
?f => :schema,
|
|
2781
|
+
?D => :debug,
|
|
2782
|
+
?a => :action,
|
|
2783
|
+
?I => :tpath,
|
|
2784
|
+
}
|
|
2785
|
+
|
|
2786
|
+
errcode_table = {
|
|
2787
|
+
#* key=:command_option_noschema msg="-%s: schema filename required."
|
|
2788
|
+
?f => :command_option_noschema,
|
|
2789
|
+
#* key=:command_option_noaction msg="-%s: action required."
|
|
2790
|
+
?a => :command_option_noaction,
|
|
2791
|
+
#* key=:command_option_notpath msg="-%s: template path required."
|
|
2792
|
+
?I => :command_option_notpath,
|
|
2793
|
+
}
|
|
2794
|
+
|
|
2795
|
+
while argv[0] && argv[0][0] == ?-
|
|
2796
|
+
optstr = argv.shift
|
|
2797
|
+
optstr = optstr[1, optstr.length-1]
|
|
2798
|
+
# property
|
|
2799
|
+
if optstr[0] == ?-
|
|
2800
|
+
unless optstr =~ /\A\-([-\w]+)(?:=(.*))?\z/
|
|
2801
|
+
#* key=:command_property_invalid msg="%s: invalid property."
|
|
2802
|
+
raise option_error(:command_property_invalid, optstr)
|
|
2803
|
+
end
|
|
2804
|
+
prop_name = $1; prop_value = $2
|
|
2805
|
+
key = prop_name.gsub(/-/, '_').intern
|
|
2806
|
+
value = prop_value == nil ? true : _to_value(prop_value)
|
|
2807
|
+
@properties[key] = value
|
|
2808
|
+
# option
|
|
2809
|
+
else
|
|
2810
|
+
while optstr && !optstr.empty?
|
|
2811
|
+
optchar = optstr[0]
|
|
2812
|
+
optstr[0,1] = ""
|
|
2813
|
+
unless option_table.key?(optchar)
|
|
2814
|
+
#* key=:command_option_invalid msg="-%s: invalid command option."
|
|
2815
|
+
raise option_error(:command_option_invalid, optchar.chr)
|
|
2816
|
+
end
|
|
2817
|
+
optkey = option_table[optchar]
|
|
2818
|
+
case optchar
|
|
2819
|
+
when ?f, ?a, ?I
|
|
2820
|
+
arg = optstr.empty? ? argv.shift : optstr
|
|
2821
|
+
raise option_error(errcode_table[optchar], optchar.chr) unless arg
|
|
2822
|
+
@options[optkey] = arg
|
|
2823
|
+
optstr = ''
|
|
2824
|
+
else
|
|
2825
|
+
@options[optkey] = true
|
|
2826
|
+
end
|
|
2827
|
+
end
|
|
2828
|
+
end
|
|
2829
|
+
end # end of while
|
|
2830
|
+
#
|
|
2831
|
+
@options[:linenum] = true if @options[:emacs]
|
|
2832
|
+
@options[:help] = true if @properties[:help]
|
|
2833
|
+
@options[:version] = true if @properties[:version]
|
|
2834
|
+
filenames = argv
|
|
2835
|
+
return filenames
|
|
2836
|
+
end
|
|
2837
|
+
|
|
2838
|
+
|
|
2839
|
+
def _domain_type?(doc)
|
|
2840
|
+
klass = defined?(YAML::DomainType) ? YAML::DomainType : YAML::Syck::DomainType
|
|
2841
|
+
return doc.is_a?(klass)
|
|
2842
|
+
end
|
|
2843
|
+
|
|
2844
|
+
end
|
|
2845
|
+
|
|
2846
|
+
end
|
|
2847
|
+
#--end of require 'kwalify/main'
|
|
2848
|
+
|
|
2849
|
+
command = File.basename($0)
|
|
2850
|
+
Kwalify::Main.main(command, ARGV)
|