kwalify 0.5.1 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (109) hide show
  1. data/ChangeLog +24 -19
  2. data/README.txt +51 -51
  3. data/bin/kwalify +2 -2
  4. data/contrib/inline-require +151 -0
  5. data/contrib/kwalify +2850 -0
  6. data/doc-api/classes/CommandOptionError.html +184 -0
  7. data/doc-api/classes/CommandOptionParser.html +325 -0
  8. data/doc-api/classes/Kwalify.html +270 -0
  9. data/doc-api/classes/Kwalify/AssertionError.html +148 -0
  10. data/doc-api/classes/Kwalify/BaseError.html +296 -0
  11. data/doc-api/classes/Kwalify/CommandOptionError.html +168 -0
  12. data/doc-api/classes/Kwalify/ErrorHelper.html +218 -0
  13. data/doc-api/classes/Kwalify/HashInterface.html +240 -0
  14. data/doc-api/classes/Kwalify/KwalifyError.html +111 -0
  15. data/doc-api/classes/Kwalify/Main.html +336 -0
  16. data/doc-api/classes/Kwalify/MetaValidator.html +432 -0
  17. data/doc-api/classes/Kwalify/Parser.html +155 -0
  18. data/doc-api/classes/Kwalify/PlainYamlParser.html +520 -0
  19. data/doc-api/classes/Kwalify/PlainYamlParser/Alias.html +165 -0
  20. data/doc-api/classes/Kwalify/Rule.html +411 -0
  21. data/doc-api/classes/Kwalify/SchemaError.html +148 -0
  22. data/doc-api/classes/Kwalify/Types.html +301 -0
  23. data/doc-api/classes/Kwalify/ValidationError.html +148 -0
  24. data/doc-api/classes/Kwalify/Validator.html +311 -0
  25. data/doc-api/classes/Kwalify/YamlParser.html +535 -0
  26. data/doc-api/classes/Kwalify/YamlSyntaxError.html +168 -0
  27. data/doc-api/classes/Test.html +107 -0
  28. data/doc-api/classes/Test/Unit.html +101 -0
  29. data/doc-api/classes/YamlHelper.html +259 -0
  30. data/doc-api/created.rid +1 -0
  31. data/doc-api/files/__/README_txt.html +179 -0
  32. data/doc-api/files/kwalify/errors_rb.html +114 -0
  33. data/doc-api/files/kwalify/main_rb.html +117 -0
  34. data/doc-api/files/kwalify/messages_rb.html +107 -0
  35. data/doc-api/files/kwalify/meta-validator_rb.html +117 -0
  36. data/doc-api/files/kwalify/rule_rb.html +116 -0
  37. data/doc-api/files/kwalify/types_rb.html +114 -0
  38. data/doc-api/files/kwalify/util/assert-text-equal_rb.html +115 -0
  39. data/doc-api/files/kwalify/util/hash-interface_rb.html +107 -0
  40. data/doc-api/files/kwalify/util/option-parser_rb.html +107 -0
  41. data/doc-api/files/kwalify/util/testcase-helper_rb.html +115 -0
  42. data/doc-api/files/kwalify/util/yaml-helper_rb.html +114 -0
  43. data/doc-api/files/kwalify/validator_rb.html +117 -0
  44. data/doc-api/files/kwalify/yaml-parser_rb.html +117 -0
  45. data/doc-api/files/kwalify_rb.html +120 -0
  46. data/doc-api/fr_class_index.html +50 -0
  47. data/doc-api/fr_file_index.html +41 -0
  48. data/doc-api/fr_method_index.html +109 -0
  49. data/doc-api/index.html +24 -0
  50. data/doc-api/rdoc-style.css +208 -0
  51. data/doc/users-guide.html +693 -193
  52. data/examples/address-book/Makefile +5 -0
  53. data/examples/address-book/address-book.schema.yaml +2 -1
  54. data/examples/invoice/Makefile +5 -0
  55. data/examples/invoice/invoice.schema.yaml +3 -2
  56. data/examples/tapkit/Makefile +5 -0
  57. data/examples/tapkit/main.rb +7 -0
  58. data/examples/tapkit/tapkit.schema.yaml +6 -1
  59. data/lib/kwalify.rb +3 -3
  60. data/lib/kwalify/errors.rb +2 -2
  61. data/lib/kwalify/main.rb +161 -84
  62. data/lib/kwalify/messages.rb +17 -11
  63. data/lib/kwalify/meta-validator.rb +11 -2
  64. data/lib/kwalify/rule.rb +13 -3
  65. data/lib/kwalify/templates/genclass-java.eruby +195 -0
  66. data/lib/kwalify/templates/genclass-ruby.eruby +84 -0
  67. data/lib/kwalify/types.rb +18 -18
  68. data/lib/kwalify/util/assert-text-equal.rb +44 -0
  69. data/lib/kwalify/util/hash-interface.rb +37 -0
  70. data/lib/kwalify/util/option-parser.rb +2 -2
  71. data/lib/kwalify/util/testcase-helper.rb +112 -0
  72. data/lib/kwalify/util/yaml-helper.rb +2 -2
  73. data/lib/kwalify/validator.rb +2 -2
  74. data/lib/kwalify/yaml-parser.rb +12 -9
  75. data/test/test-main.rb +77 -78
  76. data/test/test-main.yaml +543 -769
  77. data/test/test-metavalidator.rb +27 -47
  78. data/test/test-metavalidator.yaml +21 -2
  79. data/test/test-rule.rb +6 -39
  80. data/test/test-rule.yaml +2 -2
  81. data/test/test-validator.rb +36 -869
  82. data/test/test-validator.yaml +28 -20
  83. data/test/test-yamlparser.rb +30 -1248
  84. data/test/test-yamlparser.yaml +138 -110
  85. data/test/test.rb +33 -13
  86. data/test/tmp.dir/Context.java +40 -0
  87. data/test/tmp.dir/Group.java +33 -0
  88. data/test/tmp.dir/User.java +43 -0
  89. data/test/tmp.dir/action1.document +18 -0
  90. data/test/tmp.dir/action1.schema +32 -0
  91. data/test/tmp.dir/action2.document +18 -0
  92. data/test/tmp.dir/action2.schema +32 -0
  93. data/test/tmp.dir/emacs.document +6 -0
  94. data/test/tmp.dir/emacs.schema +6 -0
  95. data/test/tmp.dir/meta1.document +0 -0
  96. data/test/tmp.dir/meta1.schema +3 -0
  97. data/test/tmp.dir/meta2.document +0 -0
  98. data/test/tmp.dir/meta2.schema +3 -0
  99. data/test/tmp.dir/silent1.document +3 -0
  100. data/test/tmp.dir/silent1.schema +3 -0
  101. data/test/tmp.dir/silent2.document +7 -0
  102. data/test/tmp.dir/silent2.schema +3 -0
  103. data/test/tmp.dir/stream.invalid +8 -0
  104. data/test/tmp.dir/stream.schema +3 -0
  105. data/test/tmp.dir/stream.valid +8 -0
  106. data/test/tmp.dir/untabify.document +5 -0
  107. data/test/tmp.dir/untabify.schema +10 -0
  108. metadata +98 -12
  109. data/lib/kwalify/util/assert-diff.rb +0 -44
data/ChangeLog CHANGED
@@ -1,42 +1,47 @@
1
1
  .=title: ChangeLog
2
- .?release: $Release: 0.5.1 $
3
- .?lastupdate: $Date: 2005-12-20 12:50:56 +0900 (Tue, 20 Dec 2005) $
4
- .?version: $Rev: 44 $
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
- .: 2005-12-20 (release 0.5.1)
8
- .* Enhances:
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
- .: 2005-12-17 (release 0.5.0)
12
- .* Enhances:
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
- .* Changes:
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
- .: 2005-10-26 (release 0.4.1)
23
- .* Bugfix:
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
- .: 2005-10-25 (release 0.4.0)
28
- .* Enhances:
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
- .: 2005-09-30 (release 0.3.0)
33
- .* Enhances:
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
- .: 2005-09-25 (release 0.2.0)
39
- .* Enhances:
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
- .* Changes:
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
- .: 2005-08-01 (release 0.1.0)
65
+ .: release 0.1.0 (2005-08-01)
61
66
  .- beta release
data/README.txt CHANGED
@@ -1,66 +1,66 @@
1
- .=title: README
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
- See doc/users-guide.html for details.
9
+ == Introduction
14
10
 
11
+ Kwalify is a tiny schema validator for YAML and JSON.
15
12
 
16
- .$ Installation
13
+ See doc/users-guide.html for details.
17
14
 
18
- .* Just type 'gem install --remote kwalify' if you have installed RubyGems.
19
- .====================
20
- $ sudo gem install --remote kwalify
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
- .* 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
-
32
- .* If you can't be root user, copy script and libraries to proper directory.
33
- .====================
34
- $ tar xjf kwalify_x.x.x.tar.bz2
35
- $ cd kwalify_x.x.x/
36
- $ mkdir -p $HOME/bin
37
- $ cp -a bin/* $HOME/bin
38
- $ export PATH=$PATH:$HOME/bin
39
- $ mkdir -p $HOME/lib/ruby
40
- $ cp -a lib/* $HOME/lib/ruby
41
- $ export RUBYLIB=$HOME/lib/ruby
42
- .====================
43
-
44
- .* Or use 'contrib/inline-require' script to concatenate all script and
45
- libraries into one file.
46
- .====================
47
- $ tar xjf kwalify_x.x.x.tar.bz2
48
- $ cd kwalify_x.x.x/
49
- $ unset RUBYLIB
50
- $ ruby contrib/inline-require -I ./lib bin/kwalify > kwalify
51
- $ mv kwalify $HOME/bin
52
- $ chmod a+x $HOME/bin/kwalify
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 contact me.
58
+ If you need looser license, please suggest to me.
59
+
60
+
61
61
 
62
+ == Author
62
63
 
63
- .$ Author
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/
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
3
  ###
4
- ### $Rev: 42 $
5
- ### $Release: 0.5.1 $
4
+ ### $Rev: 48 $
5
+ ### $Release: 0.6.0 $
6
6
  ### copyright(c) 2005 kuwata-lab all rights reserved.
7
7
  ###
8
8
 
@@ -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
@@ -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)