jcrvalidator 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,115 @@
1
+ # Copyright (c) 2015 American Registry for Internet Numbers
2
+ #
3
+ # Permission to use, copy, modify, and/or distribute this software for any
4
+ # purpose with or without fee is hereby granted, provided that the above
5
+ # copyright notice and this permission notice appear in all copies.
6
+ #
7
+ # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
8
+ # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
9
+ # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
10
+ # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
11
+ # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
12
+ # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
13
+ # IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
14
+
15
+ require 'jcr/parser'
16
+ require 'jcr/map_rule_names'
17
+ require 'jcr/check_groups'
18
+ require 'jcr/evaluate_rules'
19
+
20
+ module JCR
21
+
22
+ class ObjectBehavior
23
+ attr_accessor :checked_hash
24
+
25
+ def initialize
26
+ @checked_hash = {}
27
+ end
28
+ end
29
+
30
+ def self.evaluate_object_rule jcr, rule_atom, data, econs, behavior = nil
31
+
32
+ rules, annotations = get_rules_and_annotations( jcr )
33
+
34
+ # if the data is not an object (Hash)
35
+ return evaluate_reject( annotations,
36
+ Evaluation.new( false, "#{data} is not an object at #{jcr} from #{rule_atom}") ) unless data.is_a? Hash
37
+
38
+ # if the object has no members and there are zero sub-rules (it is suppose to be empty)
39
+ return evaluate_reject( annotations,
40
+ Evaluation.new( true, nil ) ) if rules.empty? && data.length == 0
41
+
42
+ # if the object has members and there are zero sub-rules (it is suppose to be empty)
43
+ return evaluate_reject( annotations,
44
+ Evaluation.new( false, "Non-empty object at #{jcr} from #{rule_atom}" ) ) if rules.empty? && data.length != 0
45
+
46
+ retval = nil
47
+ behavior = ObjectBehavior.new unless behavior
48
+
49
+ rules.each do |rule|
50
+
51
+ # short circuit logic
52
+ if rule[:choice_combiner] && retval && retval.success
53
+ next
54
+ elsif rule[:sequence_combiner] && retval && !retval.success
55
+ return evaluate_reject( annotations, retval ) # short circuit
56
+ end
57
+
58
+ repeat_min, repeat_max = get_repetitions( rule )
59
+
60
+ # Pay attention here:
61
+ # Group rules need to be treated differently than other rules
62
+ # Groups must be evaluated as if they are rules evaluated in
63
+ # isolation until they evaluate as true.
64
+ # Also, groups must be handed the entire object, not key/values
65
+ # as member rules use.
66
+
67
+ if (grule = get_group(rule, econs))
68
+
69
+ successes = 0
70
+ for i in 0..repeat_max
71
+ group_behavior = ObjectBehavior.new
72
+ e = evaluate_rule( grule, rule_atom, data, econs, group_behavior )
73
+ if e.success
74
+ behavior.checked_hash.merge!( group_behavior.checked_hash )
75
+ successes = successes + 1
76
+ else
77
+ break;
78
+ end
79
+ end
80
+
81
+ if successes == 0 && repeat_min > 0
82
+ retval = Evaluation.new( false, "object does not contain group #{rule} for #{jcr} from #{rule_atom}")
83
+ elsif successes < repeat_min
84
+ retval = Evaluation.new( false, "object does not have contain necessary number of group #{rule} for #{jcr} from #{rule_atom}")
85
+ else
86
+ retval = Evaluation.new( true, nil )
87
+ end
88
+
89
+ else # if not grule
90
+
91
+ repeat_results = data.select do |k,v|
92
+ unless behavior.checked_hash[k]
93
+ e = evaluate_rule(rule, rule_atom, [k, v], econs, nil)
94
+ behavior.checked_hash[k] = e.success
95
+ e.success
96
+ end
97
+ end
98
+
99
+ if repeat_results.length == 0 && repeat_min > 0
100
+ retval = Evaluation.new( false, "object does not contain #{rule} for #{jcr} from #{rule_atom}")
101
+ elsif repeat_results.length < repeat_min
102
+ retval = Evaluation.new( false, "object does not have enough #{rule} for #{jcr} from #{rule_atom}")
103
+ elsif repeat_results.length > repeat_max
104
+ retval = Evaluation.new( false, "object has too many #{rule} for #{jcr} from #{rule_atom}")
105
+ else
106
+ retval = Evaluation.new( true, nil)
107
+ end
108
+ end
109
+
110
+ end # end if grule else
111
+
112
+ return evaluate_reject( annotations, retval )
113
+ end
114
+
115
+ end
@@ -0,0 +1,211 @@
1
+ # Copyright (c) 2015 American Registry for Internet Numbers
2
+ #
3
+ # Permission to use, copy, modify, and/or distribute this software for any
4
+ # purpose with or without fee is hereby granted, provided that the above
5
+ # copyright notice and this permission notice appear in all copies.
6
+ #
7
+ # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
8
+ # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
9
+ # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
10
+ # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
11
+ # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
12
+ # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
13
+ # IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
14
+
15
+ require 'ipaddr'
16
+ require 'time'
17
+ require 'addressable/uri'
18
+ require 'addressable/template'
19
+ require 'email_address_validator'
20
+ require 'big-phoney'
21
+
22
+ require 'jcr/parser'
23
+ require 'jcr/map_rule_names'
24
+ require 'jcr/check_groups'
25
+ require 'jcr/evaluate_array_rules'
26
+ require 'jcr/evaluate_object_rules'
27
+ require 'jcr/evaluate_group_rules'
28
+ require 'jcr/evaluate_member_rules'
29
+ require 'jcr/evaluate_value_rules'
30
+
31
+
32
+ # Adapted from Matt Sears
33
+ class Proc
34
+ def jcr_callback(callable, *args)
35
+ self === Class.new do
36
+ method_name = callable.to_sym
37
+ define_method(method_name) { |&block| block.nil? ? true : block.call(*args) }
38
+ define_method("#{method_name}?") { true }
39
+ def method_missing(method_name, *args, &block) false; end
40
+ end.new
41
+ end
42
+ end
43
+
44
+ module JCR
45
+
46
+ class Evaluation
47
+ attr_accessor :success, :reason, :child_evaluation
48
+ def initialize success, reason
49
+ @success = success
50
+ @reason = reason
51
+ end
52
+ end
53
+
54
+ class EvalConditions
55
+ attr_accessor :mapping, :callbacks
56
+ def initialize mapping, callbacks
57
+ @mapping = mapping
58
+ if callbacks
59
+ @callbacks = callbacks
60
+ else
61
+ @callbacks = {}
62
+ end
63
+ end
64
+ end
65
+
66
+ def self.evaluate_rule jcr, rule_atom, data, econs, behavior = nil
67
+ retval = Evaluation.new( false, "failed to evaluate rule properly" )
68
+ case
69
+ when behavior.is_a?( ArrayBehavior )
70
+ retval = evaluate_array_rule( jcr, rule_atom, data, econs, behavior)
71
+ when behavior.is_a?( ObjectBehavior )
72
+ retval = evaluate_object_rule( jcr, rule_atom, data, econs, behavior)
73
+ when jcr[:rule]
74
+ retval = evaluate_rule( jcr[:rule], rule_atom, data, econs, behavior)
75
+ when jcr[:target_rule_name]
76
+ target = econs.mapping[ jcr[:target_rule_name][:rule_name].to_s ]
77
+ raise "Target rule not in mapping. This should have been checked earlier." unless target
78
+ retval = evaluate_rule( target, target, data, econs, behavior )
79
+ when jcr[:primitive_rule]
80
+ retval = evaluate_value_rule( jcr[:primitive_rule], rule_atom, data, econs)
81
+ when jcr[:group_rule]
82
+ retval = evaluate_group_rule( jcr[:group_rule], rule_atom, data, econs, behavior)
83
+ when jcr[:array_rule]
84
+ retval = evaluate_array_rule( jcr[:array_rule], rule_atom, data, econs, behavior)
85
+ when jcr[:object_rule]
86
+ retval = evaluate_object_rule( jcr[:object_rule], rule_atom, data, econs, behavior)
87
+ when jcr[:member_rule]
88
+ retval = evaluate_member_rule( jcr[:member_rule], rule_atom, data, econs)
89
+ else
90
+ retval = Evaluation.new( true, nil )
91
+ end
92
+ if jcr.is_a?( Hash ) && jcr[:rule_name]
93
+ rn = jcr[:rule_name].to_s
94
+ if econs.callbacks[ rn ]
95
+ retval = evaluate_callback( jcr, data, econs, rn, retval )
96
+ end
97
+ end
98
+ return retval
99
+ end
100
+
101
+ def self.evaluate_callback jcr, data, econs, callback, e
102
+ retval = e
103
+ c = econs.callbacks[ callback ]
104
+ if e.success
105
+ retval = c.jcr_callback :rule_eval_true, jcr, data
106
+ else
107
+ retval = c.jcr_callback :rule_eval_false, jcr, data, e
108
+ end
109
+ if retval.is_a? TrueClass
110
+ retval = Evaluation.new( true, nil )
111
+ elsif retval.is_a? FalseClass
112
+ retval = Evaluation.new( false, nil )
113
+ elsif retval.is_a? String
114
+ retval = Evaluation.new( false, retval )
115
+ end
116
+ return retval
117
+ end
118
+
119
+ def self.get_repetitions rule
120
+
121
+ repeat_min = 1
122
+ repeat_max = 1
123
+ if rule[:optional]
124
+ repeat_min = 0
125
+ repeat_max = 1
126
+ elsif rule[:one_or_more]
127
+ repeat_min = 1
128
+ repeat_max = Float::INFINITY
129
+ elsif rule[:specific_repetition] && rule[:specific_repetition].is_a?( Parslet::Slice )
130
+ repeat_min = repeat_max = rule[:specific_repetition].to_s.to_i
131
+ else
132
+ o = rule[:repetition_interval]
133
+ if o
134
+ repeat_min = 0
135
+ repeat_max = Float::INFINITY
136
+ end
137
+ o = rule[:repetition_min]
138
+ if o
139
+ if o.is_a?( Parslet::Slice )
140
+ repeat_min = o.to_s.to_i
141
+ end
142
+ end
143
+ o = rule[:repetition_max]
144
+ if o
145
+ if o.is_a?( Parslet::Slice )
146
+ repeat_max = o.to_s.to_i
147
+ end
148
+ end
149
+ end
150
+
151
+ return repeat_min, repeat_max
152
+ end
153
+
154
+ def self.get_rules_and_annotations jcr
155
+ rules = []
156
+ annotations = []
157
+
158
+ if jcr.is_a?( Hash )
159
+ jcr = [ jcr ]
160
+ end
161
+
162
+ if jcr.is_a? Array
163
+ i = 0
164
+ jcr.each do |sub|
165
+ case
166
+ when sub[:unordered_annotation]
167
+ annotations << sub
168
+ i = i + 1
169
+ when sub[:reject_annotation]
170
+ annotations << sub
171
+ i = i + 1
172
+ when sub[:root_annotation]
173
+ annotations << sub
174
+ i = i + 1
175
+ when sub[:primitive_rule],sub[:object_rule],sub[:group_rule],sub[:array_rule],sub[:target_rule_name]
176
+ break
177
+ end
178
+ end
179
+ rules = jcr[i,jcr.length]
180
+ end
181
+
182
+ return rules, annotations
183
+ end
184
+
185
+ def self.evaluate_reject annotations, evaluation
186
+ reject = false
187
+ annotations.each do |a|
188
+ if a[:reject_annotation]
189
+ reject = true
190
+ break
191
+ end
192
+ end
193
+
194
+ if reject
195
+ evaluation.success = !evaluation.success
196
+ end
197
+ return evaluation
198
+ end
199
+
200
+ def self.get_group rule, econs
201
+ return rule[:group_rule] if rule[:group_rule]
202
+ #else
203
+ if rule[:target_rule_name]
204
+ target = econs.mapping[ rule[:target_rule_name][:rule_name].to_s ]
205
+ raise "Target rule not in mapping. This should have been checked earlier." unless target
206
+ return get_group( target, econs )
207
+ end
208
+ #else
209
+ return false
210
+ end
211
+ end
@@ -0,0 +1,279 @@
1
+ # Copyright (c) 2015 American Registry for Internet Numbers
2
+ #
3
+ # Permission to use, copy, modify, and/or distribute this software for any
4
+ # purpose with or without fee is hereby granted, provided that the above
5
+ # copyright notice and this permission notice appear in all copies.
6
+ #
7
+ # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
8
+ # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
9
+ # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
10
+ # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
11
+ # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
12
+ # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
13
+ # IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
14
+
15
+ require 'ipaddr'
16
+ require 'time'
17
+ require 'addressable/uri'
18
+ require 'addressable/template'
19
+ require 'email_address_validator'
20
+ require 'big-phoney'
21
+
22
+ require 'jcr/parser'
23
+ require 'jcr/map_rule_names'
24
+ require 'jcr/check_groups'
25
+
26
+ module JCR
27
+
28
+ def self.evaluate_value_rule jcr, rule_atom, data, mapping
29
+ rules, annotations = get_rules_and_annotations( jcr )
30
+
31
+ return evaluate_reject( annotations, evaluate_values( rules[0], rule_atom, data, mapping ) )
32
+ end
33
+
34
+ def self.evaluate_values jcr, rule_atom, data, mapping
35
+ case
36
+
37
+ #
38
+ # any
39
+ #
40
+
41
+ when jcr[:any]
42
+ return Evaluation.new( true, nil )
43
+
44
+ #
45
+ # integers
46
+ #
47
+
48
+ when jcr[:integer_v]
49
+ si = jcr[:integer_v].to_s
50
+ if si == "integer"
51
+ return bad_value( jcr, rule_atom, "integer", data ) unless data.is_a?( Fixnum )
52
+ end
53
+ when jcr[:integer]
54
+ i = jcr[:integer].to_s.to_i
55
+ return bad_value( jcr, rule_atom, i, data ) unless data == i
56
+ when jcr[:integer_min],jcr[:integer_max]
57
+ return bad_value( jcr, rule_atom, "integer", data ) unless data.is_a?( Fixnum )
58
+ min = jcr[:integer_min].to_s.to_i
59
+ return bad_value( jcr, rule_atom, min, data ) unless data >= min
60
+ max = jcr[:integer_max].to_s.to_i
61
+ return bad_value( jcr, rule_atom, max, data ) unless data <= max
62
+
63
+ #
64
+ # floats
65
+ #
66
+
67
+ when jcr[:float_v]
68
+ sf = jcr[:float_v].to_s
69
+ if sf == "float"
70
+ return bad_value( jcr, rule_atom, "float", data ) unless data.is_a?( Float )
71
+ end
72
+ when jcr[:float]
73
+ f = jcr[:float].to_s.to_f
74
+ return bad_value( jcr, rule_atom, f, data ) unless data == f
75
+ when jcr[:float_min],jcr[:float_max]
76
+ return bad_value( jcr, rule_atom, "float", data ) unless data.is_a?( Float )
77
+ min = jcr[:float_min].to_s.to_f
78
+ return bad_value( jcr, rule_atom, min, data ) unless data >= min
79
+ max = jcr[:float_max].to_s.to_f
80
+ return bad_value( jcr, rule_atom, max, data ) unless data <= max
81
+
82
+ #
83
+ # boolean
84
+ #
85
+
86
+ when jcr[:true_v]
87
+ return bad_value( jcr, rule_atom, "true", data ) unless data
88
+ when jcr[:false_v]
89
+ return bad_value( jcr, rule_atom, "false", data ) if data
90
+ when jcr[:boolean_v]
91
+ return bad_value( jcr, rule_atom, "boolean", data ) unless ( data.is_a?( TrueClass ) || data.is_a?( FalseClass ) )
92
+
93
+ #
94
+ # strings
95
+ #
96
+
97
+ when jcr[:string]
98
+ return bad_value( jcr, rule_atom, "string", data ) unless data.is_a? String
99
+ when jcr[:q_string]
100
+ s = jcr[:q_string].to_s
101
+ return bad_value( jcr, rule_atom, s, data ) unless data == s
102
+
103
+ #
104
+ # regex
105
+ #
106
+
107
+ when jcr[:regex]
108
+ regex = Regexp.new( jcr[:regex].to_s )
109
+ return bad_value( jcr, rule_atom, regex, data ) unless data.is_a? String
110
+ return bad_value( jcr, rule_atom, regex, data ) unless data =~ regex
111
+
112
+ #
113
+ # ip addresses
114
+ #
115
+
116
+ when jcr[:ip4]
117
+ return bad_value( jcr, rule_atom, "IPv4 Address", data ) unless data.is_a? String
118
+ begin
119
+ ip = IPAddr.new( data )
120
+ rescue IPAddr::InvalidAddressError
121
+ return bad_value( jcr, rule_atom, "IPv4 Address", data )
122
+ end
123
+ return bad_value( jcr, rule_atom, "IPv4 Address", data ) unless ip.ipv4?
124
+ when jcr[:ip6]
125
+ return bad_value( jcr, rule_atom, "IPv6 Address", data ) unless data.is_a? String
126
+ begin
127
+ ip = IPAddr.new( data )
128
+ rescue IPAddr::InvalidAddressError
129
+ return bad_value( jcr, rule_atom, "IPv6 Address", data )
130
+ end
131
+ return bad_value( jcr, rule_atom, "IPv6 Address", data ) unless ip.ipv6?
132
+
133
+ #
134
+ # domain names
135
+ #
136
+
137
+ when jcr[:fqdn]
138
+ return bad_value( jcr, rule_atom, "Fully Qualified Domain Name", data ) unless data.is_a? String
139
+ return bad_value( jcr, rule_atom, "Fully Qualified Domain Name", data ) if data.empty?
140
+ a = data.split( '.' )
141
+ a.each do |label|
142
+ return bad_value( jcr, rule_atom, "Fully Qualified Domain Name", data ) if label.start_with?( '-' )
143
+ return bad_value( jcr, rule_atom, "Fully Qualified Domain Name", data ) if label.end_with?( '-' )
144
+ label.each_char do |char|
145
+ unless (char >= 'a' && char <= 'z') \
146
+ || (char >= 'A' && char <= 'Z') \
147
+ || (char >= '0' && char <='9') \
148
+ || char == '-'
149
+ return bad_value( jcr, rule_atom, "Fully Qualified Domain Name", data )
150
+ end
151
+ end
152
+ end
153
+ when jcr[:idn]
154
+ return bad_value( jcr, rule_atom, "Internationalized Domain Name", data ) unless data.is_a? String
155
+ return bad_value( jcr, rule_atom, "Internationalized Domain Name", data ) if data.empty?
156
+ a = data.split( '.' )
157
+ a.each do |label|
158
+ return bad_value( jcr, rule_atom, "Internationalized Domain Name", data ) if label.start_with?( '-' )
159
+ return bad_value( jcr, rule_atom, "Internationalized Domain Name", data ) if label.end_with?( '-' )
160
+ label.each_char do |char|
161
+ unless (char >= 'a' && char <= 'z') \
162
+ || (char >= 'A' && char <= 'Z') \
163
+ || (char >= '0' && char <='9') \
164
+ || char == '-' \
165
+ || char.ord > 127
166
+ return bad_value( jcr, rule_atom, "Internationalized Domain Name", data )
167
+ end
168
+ end
169
+ end
170
+
171
+ #
172
+ # uri and uri templates
173
+ #
174
+
175
+ when jcr[:uri]
176
+ return bad_value( jcr, rule_atom, "URI", data ) unless data.is_a?( String )
177
+ uri = Addressable::URI.parse( data )
178
+ return bad_value( jcr, rule_atom, "URI", data ) unless uri.is_a?( Addressable::URI )
179
+ when jcr[:uri_template]
180
+ t = jcr[:uri_template].to_s
181
+ return bad_value( jcr, rule_atom, t, data ) unless data.is_a? String
182
+ template = Addressable::Template.new( t )
183
+ e = template.extract( data )
184
+ if e == nil
185
+ return bad_value( jcr, rule_atom, t, data )
186
+ else
187
+ e.each do |k,v|
188
+ return bad_value( jcr, rule_atom, t, data ) unless v
189
+ end
190
+ end
191
+
192
+ #
193
+ # phone and email value rules
194
+ #
195
+
196
+ when jcr[:email]
197
+ return bad_value( jcr, rule_atom, "Email Address", data ) unless data.is_a? String
198
+ return bad_value( jcr, rule_atom, "Email Address", data ) unless EmailAddressValidator.validate( data, true )
199
+
200
+ when jcr[:phone]
201
+ return bad_value( jcr, rule_atom, "Phone Number", data ) unless data.is_a? String
202
+ p = BigPhoney::PhoneNumber.new( data )
203
+ return bad_value( jcr, rule_atom, "Phone Number", data ) unless p.valid?
204
+
205
+ #
206
+ # base64 values
207
+ #
208
+
209
+ when jcr[:base64]
210
+ return bad_value( jcr, rule_atom, "Base 64 Data", data ) unless data.is_a? String
211
+ return bad_value( jcr, rule_atom, "Base 64 Data", data ) if data.empty?
212
+ pad_start = false
213
+ data.each_char do |char|
214
+ if pad_start && char != '='
215
+ return bad_value( jcr, rule_atom, "Base 64 Data", data )
216
+ elsif char == '='
217
+ pad_start = true
218
+ end
219
+ unless (char >= 'a' && char <= 'z') \
220
+ || (char >= 'A' && char <= 'Z') \
221
+ || (char >= '0' && char <='9') \
222
+ || char == '=' || char == '+' || char == '/'
223
+ return bad_value( jcr, rule_atom, "Base 64 Data", data )
224
+ end
225
+ end
226
+
227
+ #
228
+ # time and date values
229
+ #
230
+
231
+ when jcr[:date_time]
232
+ return bad_value( jcr, rule_atom, "Time and Date", data ) unless data.is_a? String
233
+ begin
234
+ Time.iso8601( data )
235
+ rescue ArgumentError
236
+ return bad_value( jcr, rule_atom, "Time and Date", data )
237
+ end
238
+ when jcr[:full_date]
239
+ return bad_value( jcr, rule_atom, "Date", data ) unless data.is_a? String
240
+ begin
241
+ d = data + "T23:20:50.52Z"
242
+ Time.iso8601( d )
243
+ rescue ArgumentError
244
+ return bad_value( jcr, rule_atom, "Date", data )
245
+ end
246
+ when jcr[:full_time]
247
+ return bad_value( jcr, rule_atom, "Time", data ) unless data.is_a? String
248
+ begin
249
+ t = "1985-04-12T" + data + "Z"
250
+ Time.iso8601( t )
251
+ rescue ArgumentError
252
+ return bad_value( jcr, rule_atom, "Time", data )
253
+ end
254
+
255
+ #
256
+ # null
257
+ #
258
+
259
+ when jcr[:null]
260
+ return bad_value( jcr, rule_atom, nil, data ) unless data == nil
261
+
262
+ #
263
+ # groups
264
+ #
265
+
266
+ when jcr[:group_rule]
267
+ return evaluate_group_rule jcr[:group_rule], rule_atom, data, mapping
268
+
269
+ else
270
+ raise "unknown value rule evaluation. this shouldn't happen"
271
+ end
272
+ return Evaluation.new( true, nil )
273
+ end
274
+
275
+ def self.bad_value jcr, rule_atom, expected, actual
276
+ Evaluation.new( false, "expected #{expected} but got #{actual} at #{jcr} from #{rule_atom}" )
277
+ end
278
+
279
+ end
@@ -0,0 +1,106 @@
1
+ # Copyright (c) 2015 American Registry for Internet Numbers
2
+ #
3
+ # Permission to use, copy, modify, and/or distribute this software for any
4
+ # purpose with or without fee is hereby granted, provided that the above
5
+ # copyright notice and this permission notice appear in all copies.
6
+ #
7
+ # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
8
+ # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
9
+ # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
10
+ # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
11
+ # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
12
+ # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
13
+ # IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
14
+
15
+ require 'jcr/parser'
16
+ require 'jcr/evaluate_rules'
17
+
18
+ module JCR
19
+
20
+ class Root
21
+ attr_accessor :nameless, :name, :rule, :default
22
+
23
+ def initialize rule, name = nil, nameless = true, default = false
24
+ @rule = rule
25
+ @name = name
26
+ @nameless = nameless
27
+ if name
28
+ @nameless = false
29
+ end
30
+ @default = default
31
+ end
32
+ end
33
+
34
+ def self.find_roots( tree )
35
+ roots = Array.new
36
+ if tree.is_a? Hash
37
+ tree = [ tree ]
38
+ end
39
+ tree.each do |node|
40
+ if node[:rule]
41
+ roots.concat( find_roots_in_named( node ) )
42
+ elsif (top_rule = get_rule_by_type( node ))
43
+ roots << Root.new( node, nil, true, true )
44
+ roots.concat( find_roots_in_unnamed( top_rule ) )
45
+ end
46
+ end
47
+ return roots
48
+ end
49
+
50
+ def self.find_roots_in_named( node )
51
+ roots = Array.new
52
+ rn = node[:rule][:rule_name].to_str
53
+ rule = node[:rule]
54
+ ruledef = get_rule_by_type( rule )
55
+ if ruledef
56
+ if ruledef.is_a? Array
57
+ ruledef.each do |rdi|
58
+ if rdi[:root_annotation]
59
+ roots << Root.new( node, rn )
60
+ elsif (subrule = get_rule_by_type( rdi ))
61
+ roots.concat( find_roots_in_unnamed( subrule ) )
62
+ end
63
+ end
64
+ elsif ruledef.is_a? Hash
65
+ subrule = get_rule_by_type( ruledef )
66
+ roots.concat( find_roots_in_unnamed( subrule ) ) if subrule
67
+ end
68
+ end
69
+ return roots
70
+ end
71
+
72
+ def self.find_roots_in_unnamed( node )
73
+ roots = Array.new
74
+ if node.is_a? Array
75
+ node.each do |n|
76
+ if n[:root_annotation]
77
+ roots << Root.new( node )
78
+ elsif (subrule = get_rule_by_type( n ) )
79
+ roots.concat( find_roots_in_unnamed( subrule ) ) if subrule
80
+ end
81
+ end
82
+ else
83
+ subrule = get_rule_by_type( node )
84
+ roots.concat( find_roots_in_unnamed( subrule ) ) if subrule
85
+ end
86
+ return roots
87
+ end
88
+
89
+ def self.get_rule_by_type rule
90
+ retval = nil
91
+ case
92
+ when rule[:array_rule]
93
+ retval = rule[:array_rule]
94
+ when rule[:object_rule]
95
+ retval = rule[:object_rule]
96
+ when rule[:member_rule]
97
+ retval = rule[:member_rule]
98
+ when rule[:primitive_rule]
99
+ retval = rule[:primitive_rule]
100
+ when rule[:group_rule]
101
+ retval = rule[:group_rule]
102
+ end
103
+ return retval
104
+ end
105
+
106
+ end