kwalify 0.5.1 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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)