Module: JCR

Defined in:
lib/jcr/parts.rb,
lib/jcr/jcr.rb,
lib/jcr/parser.rb,
lib/jcr/version.rb,
lib/jcr/find_roots.rb,
lib/jcr/check_groups.rb,
lib/jcr/evaluate_rules.rb,
lib/jcr/map_rule_names.rb,
lib/jcr/process_directives.rb,
lib/jcr/jcr_validator_error.rb,
lib/jcr/evaluate_array_rules.rb,
lib/jcr/evaluate_group_rules.rb,
lib/jcr/evaluate_value_rules.rb,
lib/jcr/evaluate_member_rules.rb,
lib/jcr/evaluate_object_rules.rb

Overview

Copyright © 2017 American Registry for Internet Numbers

Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.

THE SOFTWARE IS PROVIDED “AS IS” AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

Defined Under Namespace

Classes: ArrayBehavior, Context, EvalConditions, Evaluation, Failure, JcrParts, JcrValidatorError, ObjectBehavior, Parser, Root, Transformer

Constant Summary collapse

VERSION =
"0.8.3"

Class Method Summary collapse

Class Method Details

.annotations_to_s(annotations) ⇒ Object



524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
# File 'lib/jcr/evaluate_rules.rb', line 524

def self.annotations_to_s( annotations )
  retval = ""
  annotations.each do |a|
    case
      when a[:unordered_annotation]
        retval = retval + "@{unordered}"
      when a[:not_annotation]
        retval = retval + "@{not}"
      when a[:root_annotation]
        retval = retval + "@{root}"
      else
        retval = retval + "@{ ** unknown annotation ** }"
    end
  end if annotations
  retval = retval + " " if retval.length != 0
  return retval
end

.array_to_s(jcr, shallow = true) ⇒ Object



305
306
307
308
# File 'lib/jcr/evaluate_array_rules.rb', line 305

def self.array_to_s( jcr, shallow=true )
  rules, annotations = get_rules_and_annotations( jcr )
  return "#{annotations_to_s( annotations)}[ #{rules_to_s( rules, shallow )} ]"
end

.bad_value(jcr, rule_atom, expected, actual) ⇒ Object



404
405
406
# File 'lib/jcr/evaluate_value_rules.rb', line 404

def self.bad_value jcr, rule_atom, expected, actual
  Evaluation.new( false, "expected << #{expected} >> but got << #{actual} >> for #{raised_rule(jcr,rule_atom)}" )
end

.breakup_message(message, line_length) ⇒ Object



177
178
179
180
181
182
183
184
# File 'lib/jcr/jcr.rb', line 177

def self.breakup_message( message, line_length )
  line = message.gsub(/(.{1,#{line_length}})(\s+|\Z)/, "\\1\n")
  lines = []
  line.each_line do |l|
    lines << l.strip
  end
  return lines
end

.check_array_for_group(node, mapping) ⇒ Object



107
108
109
110
111
112
113
114
115
116
117
118
119
120
# File 'lib/jcr/check_groups.rb', line 107

def self.check_array_for_group node, mapping
  if node.is_a?( Array )
    node.each do |child_node|
      check_array_for_group( child_node, mapping )
    end
  elsif node.is_a? Hash
    if node[:target_rule_name]
      trule = get_name_mapping(node[:target_rule_name][:rule_name], mapping)
      disallowed_group_in_array?(trule, mapping)
    elsif node[:group_rule]
      disallowed_group_in_array?(node[:group_rule], mapping)
    end
  end
end

.check_groups(tree, mapping) ⇒ Object



21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# File 'lib/jcr/check_groups.rb', line 21

def self.check_groups( tree, mapping )
  if tree.is_a? Array
    tree.each do |node|
      check_groups( node, mapping )
    end
  else # is a hash
    if tree[:rule]
      check_groups( tree[:rule], mapping )
    elsif tree[:primitive_rule]
      check_value_for_group( tree[:primitive_rule], mapping )
    elsif tree[:member_rule]
      check_member_for_group( tree[:member_rule], mapping )
    elsif tree[:array_rule]
      check_array_for_group( tree[:array_rule], mapping )
    elsif tree[:object_rule]
      check_object_for_group( tree[:object_rule], mapping )
    end
  end
end

.check_member_for_group(node, mapping) ⇒ Object



72
73
74
75
76
77
78
79
80
81
82
# File 'lib/jcr/check_groups.rb', line 72

def self.check_member_for_group node, mapping
  if node.is_a? Array
    node = node[0]
  end
  if node[:target_rule_name]
    trule = get_name_mapping( node[:target_rule_name][:rule_name], mapping )
    disallowed_group_in_member?( trule, mapping )
  elsif node[:group_rule]
    disallowed_group_in_member?( node[:group_rule], mapping )
  end
end

.check_object_for_group(node, mapping) ⇒ Object



140
141
142
143
144
145
146
147
148
149
150
151
152
153
# File 'lib/jcr/check_groups.rb', line 140

def self.check_object_for_group node, mapping
  if node.is_a?( Array )
    node.each do |child_node|
      check_object_for_group( child_node, mapping )
    end
  elsif node.is_a? Hash
    if node[:target_rule_name]
      trule = get_name_mapping(node[:target_rule_name][:rule_name], mapping)
      disallowed_group_in_object?(trule, mapping)
    elsif node[:group_rule]
      disallowed_group_in_object?(node[:group_rule], mapping)
    end
  end
end

.check_rule_target_names(node, mapping) ⇒ Object



45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
# File 'lib/jcr/map_rule_names.rb', line 45

def self.check_rule_target_names( node, mapping )
  if node.is_a? Array
    node.each do |child_node|
      check_rule_target_names( child_node, mapping )
    end
  elsif node.is_a? Hash
    if node[:target_rule_name] && !mapping[ node[:target_rule_name][:rule_name].to_str ]
      raise_rule_name_missing node[:target_rule_name][:rule_name]
    else
      if node[:rule]
        check_rule_target_names( node[:rule], mapping )
      elsif node[:group_rule]
        check_rule_target_names( node[:group_rule], mapping )
      elsif node[:primitive_rule]
        check_rule_target_names( node[:primitive_rule], mapping )
      elsif node[:array_rule]
        check_rule_target_names( node[:array_rule], mapping )
      elsif node[:object_rule]
        check_rule_target_names( node[:object_rule], mapping )
      elsif node[:member_rule]
        check_rule_target_names( node[:member_rule], mapping )
      end
    end
  end
end

.check_value_for_group(node, mapping) ⇒ Object



41
42
43
44
45
# File 'lib/jcr/check_groups.rb', line 41

def self.check_value_for_group node, mapping
  if node.is_a?( Hash ) && node[:group_rule]
    disallowed_group_in_value?( node[:group_rule], mapping )
  end
end

.cli_eval(ctx, data, root_name, quiet) ⇒ Object



385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
# File 'lib/jcr/jcr.rb', line 385

def self.cli_eval ctx, data, root_name, quiet
  ec = 2
  e = ctx.evaluate( data, root_name )
  if e.success
    unless quiet
      puts "Success!"
    end
    ec = 0
  else
    unless quiet
      puts "Failure! Use -v for more information."
      ctx.failure_report.each do |line|
        puts line
      end
    end
    ec = 3
  end
  return ec
end

.disallowed_group_in_array?(node, mapping) ⇒ Boolean

Returns:

  • (Boolean)


122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
# File 'lib/jcr/check_groups.rb', line 122

def self.disallowed_group_in_array? node, mapping
  if node.is_a? Hash
    node = [ node ]
  end
  node.each do |groupee|
    if groupee[:group_rule]
      disallowed_group_in_array?( groupee[:group_rule], mapping )
    elsif groupee[:target_rule_name]
      trule = get_name_mapping( groupee[:target_rule_name][:rule_name], mapping )
      disallowed_group_in_array?( trule, mapping )
    elsif groupee[:member_rule]
      raise_group_error( "groups in array rules cannot have member rules", groupee[:member_rule] )
    else
      check_groups( groupee, mapping )
    end
  end
end

.disallowed_group_in_member?(node, mapping) ⇒ Boolean

Returns:

  • (Boolean)


84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
# File 'lib/jcr/check_groups.rb', line 84

def self.disallowed_group_in_member? node, mapping
  if node.is_a? Hash
    node = [ node ]
  end
  node.each do |groupee|
    if groupee[:sequence_combiner]
      raise_group_error( 'AND (comma) operation in group rule of member rule', groupee[:sequence_combiner] )
    end
    if groupee[:group_rule]
      disallowed_group_in_member?( groupee[:group_rule], mapping )
    elsif groupee[:target_rule_name]
      trule = get_name_mapping( groupee[:target_rule_name][:rule_name], mapping )
      if trule[:group_rule]
        disallowed_group_in_member?( trule[:group_rule], mapping )
      end
    elsif groupee[:member_rule]
      raise_group_error( "groups in member rules cannot have member rules", groupee[:member_rule] )
    else
      check_groups( groupee, mapping )
    end
  end
end

.disallowed_group_in_object?(node, mapping) ⇒ Boolean

Returns:

  • (Boolean)


155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
# File 'lib/jcr/check_groups.rb', line 155

def self.disallowed_group_in_object? node, mapping
  if node.is_a? Hash
    node = [ node ]
  end
  node.each do |groupee|
    if groupee[:group_rule]
      disallowed_group_in_object?( groupee[:group_rule], mapping )
    elsif groupee[:target_rule_name]
      trule = get_name_mapping( groupee[:target_rule_name][:rule_name], mapping )
      disallowed_group_in_object?( trule, mapping )
    elsif groupee[:array_rule]
      raise_group_error( "groups in object rules cannot have array rules", groupee[:member_rule] )
    elsif groupee[:object_rule]
      raise_group_error( "groups in object rules cannot have other object rules", groupee[:member_rule] )
    elsif groupee[:primitive_rule]
      raise_group_error( "groups in object rules cannot have value rules", groupee[:member_rule] )
    else
      check_groups( groupee, mapping )
    end
  end
end

.disallowed_group_in_value?(node, mapping) ⇒ Boolean

Returns:

  • (Boolean)


47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
# File 'lib/jcr/check_groups.rb', line 47

def self.disallowed_group_in_value? node, mapping
  if node.is_a? Hash
    node = [ node ]
  end
  node.each do |groupee|
    if groupee[:sequence_combiner]
      raise_group_error( 'AND (comma) operation in group rule of value rule', groupee[:sequence_combiner] )
    end
    if groupee[:group_rule]
      disallowed_group_in_value?( groupee[:group_rule], mapping )
    elsif groupee[:target_rule_name]
      trule = get_name_mapping( groupee[:target_rule_name][:rule_name], mapping )
      disallowed_group_in_value?( trule[:rule], mapping )
    elsif groupee[:member_rule]
      raise_group_error( "groups in value rules cannot have member rules", groupee[:member_rule] )
    elsif groupee[:object_rule]
      raise_group_error( "groups in value rules cannot have object rules", groupee[:member_rule] )
    elsif groupee[:array_rule]
      raise_group_error( "groups in value rules cannot have array rules", groupee[:member_rule] )
    elsif groupee[:primitive_rule]
      disallowed_group_in_value?( groupee[:primitive_rule], mapping )
    end
  end
end

.elide(s) ⇒ Object



317
318
319
320
321
322
323
# File 'lib/jcr/evaluate_rules.rb', line 317

def self.elide s
  if s.length > 45
    s = s[0..41]
    s = s + " ..."
  end
  return s
end

.evaluate_array(jcr, rule_atom, data, econs, behavior = nil, target_annotations = nil) ⇒ Object



69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
# File 'lib/jcr/evaluate_array_rules.rb', line 69

def self.evaluate_array jcr, rule_atom, data, econs, behavior = nil, target_annotations = nil

  rules, annotations = get_rules_and_annotations( jcr )

  ordered = true

  if behavior && behavior.is_a?( ArrayBehavior )
    ordered = behavior.ordered
  end

  annotations.each do |a|
    if a[:unordered_annotation]
      ordered = false
      break
    end
  end

  # if the data is not an array
  return evaluate_not( annotations,
    Evaluation.new( false, "#{data} is not an array #{raised_rule(jcr,rule_atom)}"),
                       econs, target_annotations ) unless data.is_a? Array

  # if the array is zero length and there are zero sub-rules (it is suppose to be empty)
  return evaluate_not( annotations,
    Evaluation.new( true, nil ), econs, target_annotations ) if rules.empty? && data.empty?

  # if the array is not empty and there are zero sub-rules (it is suppose to be empty)
  return evaluate_not( annotations,
    Evaluation.new( false, "Non-empty array for #{raised_rule(jcr,rule_atom)}" ),
                       econs, target_annotations ) if rules.empty? && data.length != 0

  if ordered
    return evaluate_not( annotations, evaluate_array_rule_ordered( rules, rule_atom, data, econs, behavior ),
                         econs, target_annotations )
  else
    return evaluate_not( annotations, evaluate_array_rule_unordered( rules, rule_atom, data, econs, behavior ),
                         econs, target_annotations )
  end
end

.evaluate_array_rule(jcr, rule_atom, data, econs, behavior = nil, target_annotations = nil) ⇒ Object



48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
# File 'lib/jcr/evaluate_array_rules.rb', line 48

def self.evaluate_array_rule jcr, rule_atom, data, econs, behavior = nil, target_annotations = nil

  push_trace_stack( econs, jcr )
  if behavior
    trace( econs, "Evaluating group in array rule starting at #{slice_to_s(jcr)} against", data )
    trace_def( econs, "array group", jcr, data )
  else
    trace( econs, "Evaluating array rule starting at #{slice_to_s(jcr)} against", data )
    trace_def( econs, "array", jcr, data )
  end
  retval = evaluate_array( jcr, rule_atom, data, econs, behavior, target_annotations )
  if behavior
    trace_eval( econs, "Array group", retval, jcr, data, "array" )
  else
    trace_eval( econs, "Array", retval, jcr, data, "array" )
  end
  pop_trace_stack( econs )
  return retval

end

.evaluate_array_rule_ordered(jcr, rule_atom, data, econs, behavior = nil) ⇒ Object



109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
# File 'lib/jcr/evaluate_array_rules.rb', line 109

def self.evaluate_array_rule_ordered jcr, rule_atom, data, econs, behavior = nil
  retval = nil

  behavior = ArrayBehavior.new unless behavior
  array_index = behavior.last_index


  jcr.each do |rule|

    # short circuit logic
    if rule[:choice_combiner] && retval && retval.success
      next
    elsif rule[:sequence_combiner] && retval && !retval.success
      break
    end

    repeat_min, repeat_max, repeat_step = get_repetitions( rule, econs )

    # group rules must be evaluated differently
    # groups require the effects of the evaluation to be discarded if they are false
    # groups must also be given the entire array

    grule, target_annotations = get_group( rule, econs )
    if grule

      if repeat_min == 0
        retval = Evaluation.new( true, nil )
      else
        for i in 1..repeat_min do
          if array_index == data.length
            return Evaluation.new( false, "array is not large enough for #{raised_rule(jcr,rule_atom)}" )
          else
            group_behavior = ArrayBehavior.new( behavior )
            group_behavior.last_index = array_index
            retval = evaluate_rule( grule, rule_atom, data, econs, group_behavior, target_annotations )
            if retval.success
              behavior.checked_hash.merge!( group_behavior.checked_hash )
              array_index = group_behavior.last_index
            else
              break;
            end
          end
        end
      end
      if !retval || retval.success
        for i in behavior.checked_hash.length..repeat_max-1 do
          break if array_index == data.length
          group_behavior = ArrayBehavior.new( behavior )
          group_behavior.last_index = array_index
          e = evaluate_rule( grule, rule_atom, data, econs, group_behavior, target_annotations )
          if e.success
            behavior.checked_hash.merge!( group_behavior.checked_hash )
            array_index = group_behavior.last_index
          else
            break;
          end
        end
      end

    else # else not grule (group)

      if repeat_min == 0
        retval = Evaluation.new( true, nil )
      else
        for i in 1..repeat_min do
          if array_index == data.length
            return Evaluation.new( false, "array is not large enough for #{raised_rule(jcr,rule_atom)}" )
          else
            retval = evaluate_rule( rule, rule_atom, data[ array_index ], econs, nil )
            break unless retval.success
            array_index = array_index + 1
            behavior.checked_hash[ i + behavior.last_index ] = retval.success
          end
        end
      end
      if !retval || retval.success
        for i in behavior.checked_hash.length..repeat_max-1 do
          break if array_index == data.length
          e = evaluate_rule( rule, rule_atom, data[ array_index ], econs, nil )
          break unless e.success
          array_index = array_index + 1
        end
      end

    end # end if grule else

    if repeat_step && ( array_index - repeat_min ) % repeat_step != 0
      retval = Evaluation.new( false, "Matches (#{array_index }) do not meat repetition step for #{repeat_max} % #{repeat_step}")
    end

  end

  behavior.last_index = array_index

  if data.length > array_index && behavior.extra_prohibited
    retval = Evaluation.new( false, "More items in array (#{data.length}) than specified (#{array_index}) for #{raised_rule(jcr,rule_atom)}" )
  end

  return retval

end

.evaluate_array_rule_unordered(jcr, rule_atom, data, econs, behavior = nil) ⇒ Object



211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
# File 'lib/jcr/evaluate_array_rules.rb', line 211

def self.evaluate_array_rule_unordered jcr, rule_atom, data, econs, behavior = nil

  retval = nil
  unless behavior
    behavior = ArrayBehavior.new
    behavior.ordered = false
  end
  highest_index = 0

  jcr.each do |rule|

    # short circuit logic
    if rule[:choice_combiner] && retval && retval.success
      next
    elsif rule[:sequence_combiner] && retval && !retval.success
      break
    end

    repeat_min, repeat_max, repeat_step = get_repetitions( rule, econs )

    # group rules must be evaluated differently
    # groups require the effects of the evaluation to be discarded if they are false
    # groups must also be given the entire array

    grule,target_annotations = get_group(rule, econs)
    if grule

      successes = 0
      for i in 0..repeat_max-1
        group_behavior = ArrayBehavior.new( behavior )
        group_behavior.last_index = highest_index
        group_behavior.ordered = false
        e = evaluate_rule( grule, rule_atom, data, econs, group_behavior, target_annotations )
        if e.success
          highest_index = group_behavior.last_index
          behavior.checked_hash.merge!( group_behavior.checked_hash )
          successes = successes + 1
        else
          break;
        end
      end

      if successes == 0 && repeat_min > 0
        retval = Evaluation.new( false, "array does not contain #{jcr_to_s(rule)} for #{raised_rule(jcr,rule_atom)}")
      elsif successes < repeat_min
        retval = Evaluation.new( false, "array does not have enough #{jcr_to_s(rule)} for #{raised_rule(jcr,rule_atom)}")
      elsif successes > repeat_max
        retval = Evaluation.new( false, "array has too many #{jcr_to_s(rule)} for #{raised_rule(jcr,rule_atom)}")
      elsif repeat_step && ( successes - repeat_min ) % repeat_step != 0
        retval = Evaluation.new( false, "array matches (#{successes}) do not meet repetition step of #{repeat_max} % #{repeat_step} with #{jcr_to_s(rule)} for #{raised_rule(jcr,rule_atom)}")
      else
        retval = Evaluation.new( true, nil )
      end

    else # else not group rule

      successes = 0
      for i in behavior.last_index..data.length-1
        break if successes == repeat_max
        unless behavior.checked_hash[ i ]
          e = evaluate_rule( rule, rule_atom, data[ i ], econs, nil )
          if e.success
            behavior.checked_hash[ i ] = e.success
            highest_index = i if i > highest_index
            successes = successes + 1
          end
        end
      end

      if successes == 0 && repeat_min > 0
        retval = Evaluation.new( false, "array does not contain #{jcr_to_s(rule)} for #{raised_rule(jcr,rule_atom)}")
      elsif successes < repeat_min
        retval = Evaluation.new( false, "array does not have enough #{jcr_to_s(rule)} for #{raised_rule(jcr,rule_atom)}")
      elsif successes > repeat_max
        retval = Evaluation.new( false, "array has too many #{jcr_to_s(rule)} for #{raised_rule(jcr,rule_atom)}")
      elsif repeat_step && ( successes - repeat_min ) % repeat_step != 0
        retval = Evaluation.new( false, "array matches (#{successes}) do not meet repetition step of #{repeat_max} % #{repeat_step} with #{jcr_to_s(rule)} for #{raised_rule(jcr,rule_atom)}")
      else
        retval = Evaluation.new( true, nil)
      end

    end # if grule else

  end

  behavior.last_index = highest_index

  if data.length > behavior.checked_hash.length && behavior.extra_prohibited
    retval = Evaluation.new( false, "More items in array #{data.length} than specified #{behavior.checked_hash.length} for #{raised_rule(jcr,rule_atom)}" )
  end

  return retval
end

.evaluate_callback(jcr, data, econs, callback, e) ⇒ Object



146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
# File 'lib/jcr/evaluate_rules.rb', line 146

def self.evaluate_callback jcr, data, econs, callback, e
  retval = e
  c = econs.callbacks[ callback ]
  if e.success
    retval = c.jcr_callback :rule_eval_true, jcr, data
  else
    retval = c.jcr_callback :rule_eval_false, jcr, data, e
  end
  if retval.is_a? TrueClass
    retval = Evaluation.new( true, nil )
  elsif retval.is_a? FalseClass
    retval = Evaluation.new( false, nil )
  elsif retval.is_a? String
    retval = Evaluation.new( false, retval )
  end
  trace( econs, "Callback #{callback} given evaluation of #{e.success} and returned #{retval}")
  return retval
end

.evaluate_group(jcr, rule_atom, data, econs, behavior = nil, target_annotations = nil) ⇒ Object



41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
# File 'lib/jcr/evaluate_group_rules.rb', line 41

def self.evaluate_group jcr, rule_atom, data, econs, behavior = nil, target_annotations = nil

  rules, annotations = get_rules_and_annotations( jcr )

  retval = nil

  rules.each do |rule|
    if rule[:choice_combiner] && retval && retval.success
      return evaluate_not( annotations, retval, econs, target_annotations ) # short circuit
    elsif rule[:sequence_combiner] && retval && !retval.success
      return evaluate_not( annotations, retval, econs, target_annotations ) # short circuit
    end
    retval = evaluate_rule( rule, rule_atom, data, econs, behavior )
  end

  return evaluate_not( annotations, retval, econs, target_annotations )
end

.evaluate_group_rule(jcr, rule_atom, data, econs, behavior = nil, target_annotations = nil) ⇒ Object



29
30
31
32
33
34
35
36
37
38
39
# File 'lib/jcr/evaluate_group_rules.rb', line 29

def self.evaluate_group_rule jcr, rule_atom, data, econs, behavior = nil, target_annotations = nil

  push_trace_stack( econs, jcr )
  trace( econs, "Evaluating group rule against ", data )
  trace_def( econs, "group", jcr, data )
  retval = evaluate_group( jcr, rule_atom, data, econs, behavior, target_annotations )
  trace_eval( econs, "Group", retval, jcr, data, "group" )
  pop_trace_stack( econs )
  return retval

end

.evaluate_member(jcr, rule_atom, data, econs, behavior, target_annotations) ⇒ Object



40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
# File 'lib/jcr/evaluate_member_rules.rb', line 40

def self.evaluate_member jcr, rule_atom, data, econs, behavior, target_annotations

  # unlike the other evaluate functions, here data is not just the json data.
  # it is an array, the first element being the member name or regex and the
  # second being the json data to be furthered on to other evaluation functions


  rules, annotations = get_rules_and_annotations( jcr )
  rule = merge_rules( rules )

  member_match = false

  if rule[:member_name]
    match_spec = rule[:member_name][:q_string].to_s
    if match_spec == data[ 0 ]
      member_match = true
    end
  else # must be regex
    regex = rule[:member_regex][:regex]
    if regex.is_a? Array
      match_spec = Regexp.new( "" )
      trace( econs, "Noting empty regular expression." )
    else
      match_spec = Regexp.new( rule[:member_regex][:regex].to_s )
    end
    if match_spec =~ data[ 0 ]
      member_match = true
    end
  end

  if member_match
    e = evaluate_rule( rule, rule_atom, data[ 1 ], econs, nil, target_annotations )
    e.member_found = true
    return evaluate_not( annotations, e, econs, target_annotations )
  end

  return evaluate_not( annotations,
     Evaluation.new( false, "#{match_spec} does not match #{data[0]} for #{raised_rule( jcr, rule_atom)}" ),
                       econs, target_annotations )

end

.evaluate_member_rule(jcr, rule_atom, data, econs, behavior, target_annotations) ⇒ Object



28
29
30
31
32
33
34
35
36
37
38
# File 'lib/jcr/evaluate_member_rules.rb', line 28

def self.evaluate_member_rule jcr, rule_atom, data, econs, behavior, target_annotations

  push_trace_stack( econs, jcr )
  trace( econs, "Evaluating member rule for key '#{data[0]}' starting at #{slice_to_s(jcr)} against ", data[1])
  trace_def( econs, "member", jcr, data )
  retval = evaluate_member( jcr, rule_atom, data, econs, behavior, target_annotations )
  trace_eval( econs, "Member", retval, jcr, data, "member" )
  pop_trace_stack( econs )
  return retval

end

.evaluate_not(annotations, evaluation, econs, target_annotations = nil) ⇒ Object



264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
# File 'lib/jcr/evaluate_rules.rb', line 264

def self.evaluate_not annotations, evaluation, econs, target_annotations = nil
  is_not = false

  target_annotations.each do |a|
    if a[:not_annotation]
      trace( econs, "Not annotation found on reference to rule.")
      is_not = !is_not
      break
    end
  end if target_annotations

  annotations.each do |a|
    if a[:not_annotation]
      is_not = !is_not
      break
    end
  end

  if is_not
    trace( econs, "Not annotation changing result from #{evaluation.success} to #{!evaluation.success}")
    evaluation.success = !evaluation.success
  end
  return evaluation
end

.evaluate_object(jcr, rule_atom, data, econs, behavior = nil, target_annotations = nil) ⇒ Object



51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
# File 'lib/jcr/evaluate_object_rules.rb', line 51

def self.evaluate_object jcr, rule_atom, data, econs, behavior = nil, target_annotations = nil

  rules, annotations = get_rules_and_annotations( jcr )

  # if the data is not an object (Hash)
  return evaluate_not( annotations,
    Evaluation.new( false, "#{data} is not an object for #{raised_rule(jcr,rule_atom)}"),
                    econs, target_annotations ) unless data.is_a? Hash

  # if the object has no members and there are zero sub-rules (it is suppose to be empty)
  return evaluate_not( annotations,
    Evaluation.new( true, nil ), econs, target_annotations ) if rules.empty? && data.length == 0

  # if the object has members and there are zero sub-rules (it is suppose to be empty)
  return evaluate_not( annotations,
    Evaluation.new( false, "Non-empty object for #{raised_rule(jcr,rule_atom)}" ),
                       econs, target_annotations ) if rules.empty? && data.length != 0

  retval = nil
  behavior = ObjectBehavior.new unless behavior

  rules.each do |rule|

    # short circuit logic
    if rule[:choice_combiner] && retval && retval.success
      next
    elsif rule[:sequence_combiner] && retval && !retval.success
      return evaluate_not( annotations, retval, econs, target_annotations ) # short circuit
    end

    repeat_min, repeat_max, repeat_step = get_repetitions( rule, econs )

    # Pay attention here:
    # Group rules need to be treated differently than other rules
    # Groups must be evaluated as if they are rules evaluated in
    # isolation until they evaluate as true.
    # Also, groups must be handed the entire object, not key/values
    # as member rules use.

    grule,gtarget_annotations = get_group(rule, econs)
    if grule

      successes = 0
      for i in 0..repeat_max-1
        group_behavior = ObjectBehavior.new
        group_behavior.checked_hash.merge!( behavior.checked_hash )
        e = evaluate_rule( grule, rule_atom, data, econs, group_behavior, gtarget_annotations )
        if e.success
          behavior.checked_hash.merge!( group_behavior.checked_hash )
          successes = successes + 1
        else
          break;
        end
      end

      if successes == 0 && repeat_min > 0
        retval = Evaluation.new( false, "object does not contain group #{jcr_to_s(rule)} for #{raised_rule(jcr,rule_atom)}")
      elsif successes < repeat_min
        retval = Evaluation.new( false, "object does not have contain necessary number of group #{jcr_to_s(rule)} for #{raised_rule(jcr,rule_atom)}")
      elsif repeat_step && ( successes - repeat_min ) % repeat_step != 0
        retval = Evaluation.new( false, "object matches (#{successes}) do not have contain repetition #{repeat_max} % #{repeat_step} of group #{jcr_to_s(rule)} for #{raised_rule(jcr,rule_atom)}")
      else
        retval = Evaluation.new( true, nil )
      end

    else # if not grule

      repeat_results = nil
      member_found = false

      # do a little lookahead for member rules defined by names
      # if defined by a name, and not a regex, just pluck it from the object
      # and short-circuit the enumeration

      lookahead, ltarget_annotations = get_leaf_rule( rule, econs )
      lrules, lannotations = get_rules_and_annotations( lookahead[:member_rule] )
      if lrules[0][:member_name]

        repeat_results = {}
        k = lrules[0][:member_name][:q_string].to_s
        v = data[k]
        if v
          unless behavior.checked_hash[k]
            e = evaluate_rule(rule, rule_atom, [k, v], econs, nil, nil)
            behavior.checked_hash[k] = e.success
            member_found = true if e.member_found
            repeat_results[ k ] = v if e.success
          end
        else
          trace( econs, "No member '#{k}' found in object.")
          e = evaluate_rule(rule, rule_atom, [nil, nil], econs, nil, nil)
          repeat_results[ nil ] = nil if e.success
        end

      else

        regex = lrules[0][:member_regex][:regex]
        trace( econs, "Scanning object for #{regex}.")
        i = 0
        found = false
        repeat_results = data.select do |k,v|
          unless behavior.checked_hash[k]
            if i < repeat_max
              e = evaluate_rule(rule, rule_atom, [k, v], econs, nil, nil)
              behavior.checked_hash[k] = e.success
              i = i + 1 if e.success
              found = true if e.member_found
              member_found = true if e.member_found
              e.success
            end
          end
        end
        unless found
          trace( econs, "No member matching #{regex} found in object.")
          e = evaluate_rule(rule, rule_atom, [nil, nil], econs, nil, nil)
          repeat_results[ nil ] = nil if e.success
        end

      end

      trace( econs, "Found #{repeat_results.length} matching members repetitions in object with min #{repeat_min} and max #{repeat_max}" )
      if repeat_results.length == 0 && repeat_min > 0
        retval = Evaluation.new( false, "object does not contain #{jcr_to_s(rule)} for #{raised_rule(jcr,rule_atom)}")
      elsif repeat_results.length < repeat_min
        retval = Evaluation.new( false, "object does not have enough #{jcr_to_s(rule)} for #{raised_rule(jcr,rule_atom)}")
      elsif repeat_results.length > repeat_max
        retval = Evaluation.new( false, "object has too many #{jcr_to_s(rule)} for #{raised_rule(jcr,rule_atom)}")
      elsif repeat_step && ( repeat_results.length - repeat_min ) % repeat_step != 0
        retval = Evaluation.new( false, "object matches (#{repeat_results.length}) does not match repetition step of #{repeat_max} & #{repeat_step} for #{jcr_to_s(rule)} for #{raised_rule(jcr,rule_atom)}")
      elsif member_found && repeat_results.length == 0 && repeat_max > 0
        retval = Evaluation.new( false, "object contains #{jcr_to_s(rule)} with member name though incorrect value for #{raised_rule(jcr,rule_atom)}")
      else
        retval = Evaluation.new( true, nil)
      end
    end

  end # end if grule else

  return evaluate_not( annotations, retval, econs, target_annotations )
end

.evaluate_object_rule(jcr, rule_atom, data, econs, behavior = nil, target_annotations = nil) ⇒ Object



30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
# File 'lib/jcr/evaluate_object_rules.rb', line 30

def self.evaluate_object_rule jcr, rule_atom, data, econs, behavior = nil, target_annotations = nil

  push_trace_stack( econs, jcr )
  if behavior
    trace( econs, "Evaluating group in object rule starting at #{slice_to_s(jcr)} against", data )
    trace_def( econs, "object group", jcr, data )
  else
    trace( econs, "Evaluating object rule starting at #{slice_to_s(jcr)} against", data )
    trace_def( econs, "object", jcr, data )
  end
  retval = evaluate_object( jcr, rule_atom, data, econs, behavior, target_annotations )
  if behavior
    trace_eval( econs, "Object group", retval, jcr, data, "object" )
  else
    trace_eval( econs, "Object", retval, jcr, data, "object" )
  end
  pop_trace_stack( econs )
  return retval

end

.evaluate_rule(jcr, rule_atom, data, econs, behavior = nil, target_annotations = nil) ⇒ Object



104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
# File 'lib/jcr/evaluate_rules.rb', line 104

def self.evaluate_rule jcr, rule_atom, data, econs, behavior = nil, target_annotations = nil
  trace( econs, "Dispatching rule for #{jcr_to_s(jcr)} with data: #{data}")
  if jcr.is_a?( Hash )
    if jcr[:rule_name]
      rn = slice_to_s( jcr[:rule_name] )
      trace( econs, "Named Rule: #{rn}" )
    end
  end

  retval = Evaluation.new( false, "failed to evaluate rule properly" )
  case
    when behavior.is_a?( ArrayBehavior )
      retval = evaluate_array_rule( jcr, rule_atom, data, econs, behavior, target_annotations )
    when behavior.is_a?( ObjectBehavior )
      retval = evaluate_object_rule( jcr, rule_atom, data, econs, behavior, target_annotations )
    when jcr[:rule]
      retval = evaluate_rule( jcr[:rule], rule_atom, data, econs, behavior, target_annotations)
    when jcr[:target_rule_name]
      target, target_annotations = get_target_rule( jcr, econs )
      retval = evaluate_rule( target, target, data, econs, behavior, target_annotations )
    when jcr[:primitive_rule]
      retval = evaluate_value_rule( jcr[:primitive_rule], rule_atom, data, econs, nil, target_annotations )
    when jcr[:group_rule]
      retval = evaluate_group_rule( jcr[:group_rule], rule_atom, data, econs, behavior, target_annotations)
    when jcr[:array_rule]
      retval = evaluate_array_rule( jcr[:array_rule], rule_atom, data, econs, behavior, target_annotations )
    when jcr[:object_rule]
      retval = evaluate_object_rule( jcr[:object_rule], rule_atom, data, econs, behavior, target_annotations)
    when jcr[:member_rule]
      retval = evaluate_member_rule( jcr[:member_rule], rule_atom, data, econs, nil, target_annotations)
    else
      retval = Evaluation.new( true, nil )
  end
  if jcr.is_a?( Hash ) && jcr[:rule_name]
    rn = jcr[:rule_name].to_s
    if econs.callbacks[ rn ]
      retval = evaluate_callback( jcr, data, econs, rn, retval )
    end
  end
  return retval
end

.evaluate_ruleset(data, ctx, root_name = nil) ⇒ Object

Raises:



119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
# File 'lib/jcr/jcr.rb', line 119

def self.evaluate_ruleset( data, ctx, root_name = nil )
  roots = []
  if root_name
    root_rule = ctx.mapping[root_name]
    raise JcrValidatorError, "No rule by the name of #{root_name} for a root rule has been found" unless root_rule
    root = JCR::Root.new( root_rule, root_name )
    roots << root
  else
    roots = ctx.roots
  end

  raise JcrValidatorError, "No root rule defined. Specify a root rule name" if roots.empty?

  retval = nil
  roots.each do |r|
    pp "Evaluating Root:", rule_to_s( r.rule, false ) if ctx.trace
    raise JcrValidatorError, "Root rules cannot be member rules" if r.rule[:member_rule]
    econs = EvalConditions.new( ctx.mapping, ctx.callbacks, ctx.trace )
    retval = JCR.evaluate_rule( r.rule, r.rule, data, econs )
    break if retval.success
    # else
    r.failures = econs.failures
    ctx.failed_roots << r
  end

  ctx.failure_report = failure_report( ctx )
  return retval
end

.evaluate_value_rule(jcr, rule_atom, data, econs, behavior, target_annotations) ⇒ Object



28
29
30
31
32
33
34
35
36
37
38
39
40
# File 'lib/jcr/evaluate_value_rules.rb', line 28

def self.evaluate_value_rule jcr, rule_atom, data, econs, behavior, target_annotations

  push_trace_stack( econs, jcr )
  trace( econs, "Evaluating value rule starting at #{slice_to_s(jcr)}" )
  trace_def( econs, "value", jcr, data )
  rules, annotations = get_rules_and_annotations( jcr )

  retval = evaluate_not( annotations, evaluate_values( rules[0], rule_atom, data, econs ),
                         econs, target_annotations )
  trace_eval( econs, "Value", retval, jcr, data, "value")
  pop_trace_stack( econs )
  return retval
end

.evaluate_values(jcr, rule_atom, data, econs) ⇒ Object



42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
# File 'lib/jcr/evaluate_value_rules.rb', line 42

def self.evaluate_values jcr, rule_atom, data, econs
  case

    #
    # any
    #

    when jcr[:any]
      return Evaluation.new( true, nil )

    #
    # integers
    #

    when jcr[:integer_v]
      si = jcr[:integer_v].to_s
      if si == "integer"
        return bad_value( jcr, rule_atom, "integer", data ) unless data.is_a?( Integer )
      end
    when jcr[:integer]
      i = jcr[:integer].to_s.to_i
      return bad_value( jcr, rule_atom, i, data ) unless data == i
    when jcr[:integer_min] != nil && jcr[:integer_max] == nil
      return bad_value( jcr, rule_atom, "integer", data ) unless data.is_a?( Integer )
      min = jcr[:integer_min].to_s.to_i
      return bad_value( jcr, rule_atom, min, data ) unless data >= min
    when jcr[:integer_min] == nil && jcr[:integer_max] != nil
      return bad_value( jcr, rule_atom, "integer", data ) unless data.is_a?( Integer )
      max = jcr[:integer_max].to_s.to_i
      return bad_value( jcr, rule_atom, max, data ) unless data <= max
    when jcr[:integer_min],jcr[:integer_max]
      return bad_value( jcr, rule_atom, "integer", data ) unless data.is_a?( Integer )
      min = jcr[:integer_min].to_s.to_i
      return bad_value( jcr, rule_atom, min, data ) unless data >= min
      max = jcr[:integer_max].to_s.to_i
      return bad_value( jcr, rule_atom, max, data ) unless data <= max
    when jcr[:sized_int_v]
      bits = jcr[:sized_int_v][:bits].to_i
      return bad_value( jcr, rule_atom, "int" + bits.to_s, data ) unless data.is_a?( Integer )
      min = -(2**(bits-1))
      return bad_value( jcr, rule_atom, min, data ) unless data >= min
      max = 2**(bits-1)-1
      return bad_value( jcr, rule_atom, max, data ) unless data <= max
    when jcr[:sized_uint_v]
      bits = jcr[:sized_uint_v][:bits].to_i
      return bad_value( jcr, rule_atom, "int" + bits.to_s, data ) unless data.is_a?( Integer )
      min = 0
      return bad_value( jcr, rule_atom, min, data ) unless data >= min
      max = 2**bits-1
      return bad_value( jcr, rule_atom, max, data ) unless data <= max

    #
    # floats
    #

    when jcr[:float_v]
      sf = jcr[:float_v].to_s
      if sf == "float"
        return bad_value( jcr, rule_atom, "float", data ) unless data.is_a?( Float )
      end
    when jcr[:float]
      f = jcr[:float].to_s.to_f
      return bad_value( jcr, rule_atom, f, data ) unless data == f
    when jcr[:float_min] != nil && jcr[:float_max] == nil
      return bad_value( jcr, rule_atom, "float", data ) unless data.is_a?( Float )
      min = jcr[:float_min].to_s.to_f
      return bad_value( jcr, rule_atom, min, data ) unless data >= min
    when jcr[:float_min] == nil && jcr[:float_max] != nil
      return bad_value( jcr, rule_atom, "float", data ) unless data.is_a?( Float )
      max = jcr[:float_max].to_s.to_f
      return bad_value( jcr, rule_atom, max, data ) unless data <= max
    when jcr[:float_min],jcr[:float_max]
      return bad_value( jcr, rule_atom, "float", data ) unless data.is_a?( Float )
      min = jcr[:float_min].to_s.to_f
      return bad_value( jcr, rule_atom, min, data ) unless data >= min
      max = jcr[:float_max].to_s.to_f
      return bad_value( jcr, rule_atom, max, data ) unless data <= max
    when jcr[:double_v]
      sf = jcr[:double_v].to_s
      if sf == "double"
        return bad_value( jcr, rule_atom, "double", data ) unless data.is_a?( Float )
      end

    #
    # boolean
    #

    when jcr[:true_v]
      return bad_value( jcr, rule_atom, "true", data ) unless data
    when jcr[:false_v]
      return bad_value( jcr, rule_atom, "false", data ) if data
    when jcr[:boolean_v]
      return bad_value( jcr, rule_atom, "boolean", data ) unless ( data.is_a?( TrueClass ) || data.is_a?( FalseClass ) )

    #
    # strings
    #

    when jcr[:string]
      return bad_value( jcr, rule_atom, "string", data ) unless data.is_a? String
    when jcr[:q_string]
      s = jcr[:q_string].to_s
      return bad_value( jcr, rule_atom, s, data ) unless data == s

    #
    # regex
    #

    when jcr[:regex]
      regex = Regexp.new( jcr[:regex].to_s )
      return bad_value( jcr, rule_atom, regex, data ) unless data.is_a? String
      return bad_value( jcr, rule_atom, regex, data ) unless data =~ regex

    #
    # ip addresses
    #

    when jcr[:ipv4]
      return bad_value( jcr, rule_atom, "IPv4 Address", data ) unless data.is_a? String
      begin
        ip = IPAddr.new( data )
      rescue IPAddr::InvalidAddressError
        return bad_value( jcr, rule_atom, "IPv4 Address", data )
      end
      return bad_value( jcr, rule_atom, "IPv4 Address", data ) unless ip.ipv4?
    when jcr[:ipv6]
      return bad_value( jcr, rule_atom, "IPv6 Address", data ) unless data.is_a? String
      begin
        ip = IPAddr.new( data )
      rescue IPAddr::InvalidAddressError
        return bad_value( jcr, rule_atom, "IPv6 Address", data )
      end
      return bad_value( jcr, rule_atom, "IPv6 Address", data ) unless ip.ipv6?
    when jcr[:ipaddr]
      return bad_value( jcr, rule_atom, "IP Address", data ) unless data.is_a? String
      begin
        ip = IPAddr.new( data )
      rescue IPAddr::InvalidAddressError
        return bad_value( jcr, rule_atom, "IP Address", data )
      end
      return bad_value( jcr, rule_atom, "IP Address", data ) unless ip.ipv6? || ip.ipv4?

    #
    # domain names
    #

    when jcr[:fqdn]
      return bad_value( jcr, rule_atom, "Fully Qualified Domain Name", data ) unless data.is_a? String
      return bad_value( jcr, rule_atom, "Fully Qualified Domain Name", data ) if data.empty?
      a = data.split( '.' )
      a.each do |label|
        return bad_value( jcr, rule_atom, "Fully Qualified Domain Name", data ) if label.start_with?( '-' )
        return bad_value( jcr, rule_atom, "Fully Qualified Domain Name", data ) if label.end_with?( '-' )
        label.each_char do |char|
          unless (char >= 'a' && char <= 'z') \
            || (char >= 'A' && char <= 'Z') \
            || (char >= '0' && char <='9') \
            || char == '-'
            return bad_value( jcr, rule_atom, "Fully Qualified Domain Name", data )
          end
        end
      end
    when jcr[:idn]
      return bad_value( jcr, rule_atom, "Internationalized Domain Name", data ) unless data.is_a? String
      return bad_value( jcr, rule_atom, "Internationalized Domain Name", data ) if data.empty?
      a = data.split( '.' )
      a.each do |label|
        return bad_value( jcr, rule_atom, "Internationalized Domain Name", data ) if label.start_with?( '-' )
        return bad_value( jcr, rule_atom, "Internationalized Domain Name", data ) if label.end_with?( '-' )
        label.each_char do |char|
          unless (char >= 'a' && char <= 'z') \
            || (char >= 'A' && char <= 'Z') \
            || (char >= '0' && char <='9') \
            || char == '-' \
            || char.ord > 127
            return bad_value( jcr, rule_atom, "Internationalized Domain Name", data )
          end
        end
      end

    #
    # uri and uri scheme
    #

    when jcr[:uri]
      if jcr[:uri].is_a? Hash
        t = jcr[:uri][:uri_scheme].to_s
        return bad_value( jcr, rule_atom, t, data ) unless data.is_a? String
        return bad_value( jcr, rule_atom, t, data ) unless data.start_with?( t )
      else
        return bad_value( jcr, rule_atom, "URI", data ) unless data.is_a?( String )
        uri = Addressable::URI.parse( data )
        return bad_value( jcr, rule_atom, "URI", data ) unless uri.is_a?( Addressable::URI )
      end

    #
    # phone and email value rules
    #

    when jcr[:email]
      return bad_value( jcr, rule_atom, "Email Address", data ) unless data.is_a? String
      return bad_value( jcr, rule_atom, "Email Address", data ) unless EmailAddressValidator.validate( data, true )

    when jcr[:phone]
      return bad_value( jcr, rule_atom, "Phone Number", data ) unless data.is_a? String
      p = BigPhoney::PhoneNumber.new( data )
      return bad_value( jcr, rule_atom, "Phone Number", data ) unless p.valid?

    #
    # hex values
    #

    when jcr[:hex]
      return bad_value( jcr, rule_atom, "Hex Data", data ) unless data.is_a? String
      return bad_value( jcr, rule_atom, "Hex Data", data ) unless data.length % 2 == 0
      pad_start = false
      data.each_char do |char|
        unless (char >= '0' && char <='9') \
            || (char >= 'A' && char <= 'F') \
            || (char >= 'a' && char <= 'f')
          return bad_value( jcr, rule_atom, "Hex Data", data )
        end
      end

    #
    # base32hex values
    #

    when jcr[:base32hex]
      return bad_value( jcr, rule_atom, "Base32hex Data", data ) unless data.is_a? String
      return bad_value( jcr, rule_atom, "Base32hex Data", data ) unless data.length % 8 == 0
      pad_start = false
      data.each_char do |char|
        if char == '='
          pad_start = true
        elsif pad_start && char != '='
          return bad_value( jcr, rule_atom, "Base32hex Data", data )
        else 
            unless (char >= '0' && char <='9') \
                || (char >= 'A' && char <= 'V') \
                || (char >= 'a' && char <= 'v')
              return bad_value( jcr, rule_atom, "Base32hex Data", data )
            end
        end
      end

    #
    # base32 values
    #

    when jcr[:base32]
      return bad_value( jcr, rule_atom, "Base 32 Data", data ) unless data.is_a? String
      return bad_value( jcr, rule_atom, "Base 32 Data", data ) unless data.length % 8 == 0
      pad_start = false
      data.each_char do |char|
        if char == '='
          pad_start = true
        elsif pad_start && char != '='
          return bad_value( jcr, rule_atom, "Base 32 Data", data )
        else 
            unless (char >= 'a' && char <= 'z') \
                || (char >= 'A' && char <= 'Z') \
                || (char >= '2' && char <='7')
              return bad_value( jcr, rule_atom, "Base 32 Data", data )
            end
        end
      end

    #
    # base64url values
    #

    when jcr[:base64url]
      return bad_value( jcr, rule_atom, "Base64url Data", data ) unless data.is_a? String
      return bad_value( jcr, rule_atom, "Base64url Data", data ) unless data.length % 4 == 0
      pad_start = false
      data.each_char do |char|
        if char == '='
          pad_start = true
        elsif pad_start && char != '='
          return bad_value( jcr, rule_atom, "Base64url Data", data )
        else 
            unless (char >= 'a' && char <= 'z') \
                || (char >= 'A' && char <= 'Z') \
                || (char >= '0' && char <='9') \
                || char == '-' || char == '_'
              return bad_value( jcr, rule_atom, "Base64url Data", data )
            end
        end
      end

    #
    # base64 values
    #

    when jcr[:base64]
      return bad_value( jcr, rule_atom, "Base 64 Data", data ) unless data.is_a? String
      return bad_value( jcr, rule_atom, "Base 64 Data", data ) unless data.length % 4 == 0
      pad_start = false
      data.each_char do |char|
        if char == '='
          pad_start = true
        elsif pad_start && char != '='
          return bad_value( jcr, rule_atom, "Base 64 Data", data )
        else 
            unless (char >= 'a' && char <= 'z') \
                || (char >= 'A' && char <= 'Z') \
                || (char >= '0' && char <='9') \
                || char == '+' || char == '/'
              return bad_value( jcr, rule_atom, "Base 64 Data", data )
            end
        end
      end

    #
    # time and date values
    #

    when jcr[:datetime]
      return bad_value( jcr, rule_atom, "Time and Date", data ) unless data.is_a? String
      begin
        Time.iso8601( data )
      rescue ArgumentError
        return bad_value( jcr, rule_atom, "Time and Date", data )
      end
    when jcr[:date]
      return bad_value( jcr, rule_atom, "Date", data ) unless data.is_a? String
      begin
        d = data + "T23:20:50.52Z"
        Time.iso8601( d )
      rescue ArgumentError
        return bad_value( jcr, rule_atom, "Date", data )
      end
    when jcr[:time]
      return bad_value( jcr, rule_atom, "Time", data ) unless data.is_a? String
      begin
        t = "1985-04-12T" + data + "Z"
        Time.iso8601( t )
      rescue ArgumentError
        return bad_value( jcr, rule_atom, "Time", data )
      end

    #
    # null
    #

    when jcr[:null]
      return bad_value( jcr, rule_atom, nil, data ) unless data == nil

    #
    # groups
    #

    when jcr[:group_rule]
      return evaluate_group_rule jcr[:group_rule], rule_atom, data, econs

    else
      raise "unknown value rule evaluation. this shouldn't happen"
  end
  return Evaluation.new( true, nil )
end

.failure_report(ctx) ⇒ Object



148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
# File 'lib/jcr/jcr.rb', line 148

def self.failure_report ctx
  report = []
  ctx.failed_roots.each do |failed_root|
    if failed_root.name
      report << "- Failures for root rule named '#{failed_root.name}'"
    else
      report << "- Failures for root rule at line #{failed_root.pos[0]}"
    end
    failed_root.failures.each_with_index do |failures,stack_level|
      if failures.length > 1
        report << "  - failure at rule level #{stack_level} caused by one of the following #{failures.length} reasons"
      else
        report << "  - failure at rule level #{stack_level} caused by"
      end
      failures.each_with_index do |failure, index|
        lines = breakup_message( "<< #{failure.json_elided} >> failed rule #{failure.definition}", ctx.failure_report_line_length - 5 )
        lines.each_with_index do |l,i|
          if i == 0
            report << "    - #{l}"
          else
            report << "      #{l}"
          end
        end
      end
    end
  end
  return report
end

.find_first_slice(slice) ⇒ Object



413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
# File 'lib/jcr/evaluate_rules.rb', line 413

def self.find_first_slice slice
  if slice.is_a? Parslet::Slice
    return slice
  elsif slice.is_a?( Hash ) && !slice.empty?
    s = nil
    slice.values.each do |v|
      s = find_first_slice( v )
      break if s
    end
    return s if s
  elsif slice.is_a?( Array ) && !slice.empty?
    s = nil
    slice.each do |i|
      s = find_first_slice( i )
      break if s
    end
    return s if s
  end
  #else
  return nil
end

.find_roots(tree) ⇒ Object



37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
# File 'lib/jcr/find_roots.rb', line 37

def self.find_roots( tree )
  roots = Array.new
  if tree.is_a? Hash
    tree = [ tree ]
  end
  tree.each do |node|
    if node[:rule]
      roots.concat( find_roots_in_named( node ) )
    elsif (top_rule = get_rule_by_type( node ))
      roots << Root.new( node, nil, true, true )
      roots.concat( find_roots_in_unnamed( top_rule ) )
    end
  end
  return roots
end

.find_roots_in_named(node) ⇒ Object



53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
# File 'lib/jcr/find_roots.rb', line 53

def self.find_roots_in_named( node )
  roots = Array.new
  rn = node[:rule][:rule_name].to_str
  rule = node[:rule]
  ruledef = get_rule_by_type( rule )
  new_root = nil
  # look to see if the root_annotation is in the name before assignment ( @{root} $r = ... )
  if rule[:annotations]
    if rule[:annotations].is_a? Array
      rule[:annotations].each do |annotation|
        if annotation[:root_annotation]
          new_root = Root.new(node, rn)
          roots << new_root
          # root is found, now look into subrule for unnamed roots
          subrule = get_rule_by_type( ruledef )
          roots.concat( find_roots_in_unnamed( subrule ) ) if subrule
        end
      end
    elsif rule[:annotations][:root_annotation]
      new_root = Root.new(node, rn)
      roots << new_root
      # root is found, now look into subrule for unnamed roots
      subrule = get_rule_by_type( ruledef )
      roots.concat( find_roots_in_unnamed( subrule ) ) if subrule
    end
  end
  if ruledef && !new_root
    if ruledef.is_a? Array
      ruledef.each do |rdi|
        # if it has a @{root} annotation in the rule definition
        if rdi[:root_annotation]
          roots << Root.new(node, rn)
          # else look into the definition further and examine subrules
        elsif (subrule = get_rule_by_type(rdi))
          roots.concat(find_roots_in_unnamed(subrule))
        end
      end
    elsif ruledef.is_a? Hash
      subrule = get_rule_by_type(ruledef)
      roots.concat(find_roots_in_unnamed(subrule)) if subrule
    end
  end
  return roots
end

.find_roots_in_unnamed(node) ⇒ Object



98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
# File 'lib/jcr/find_roots.rb', line 98

def self.find_roots_in_unnamed( node )
  roots = Array.new
  if node.is_a? Array
    node.each do |n|
      if n[:root_annotation]
        roots << Root.new( node )
      elsif (subrule = get_rule_by_type( n ) )
        roots.concat( find_roots_in_unnamed( subrule ) ) if subrule
      end
    end
  else
    subrule = get_rule_by_type( node )
    roots.concat( find_roots_in_unnamed( subrule ) ) if subrule
  end
  return roots
end

.get_group(rule, econs) ⇒ Object



289
290
291
292
293
294
295
296
297
298
# File 'lib/jcr/evaluate_rules.rb', line 289

def self.get_group rule, econs
  return rule[:group_rule], nil if rule[:group_rule]
  #else
  if rule[:target_rule_name]
    target, target_annotations = get_target_rule( rule, econs )
    return get_group( target, econs )[0], target_annotations
  end
  #else
  return false, nil
end

.get_leaf_rule(rule, econs) ⇒ Object



300
301
302
303
304
305
306
307
# File 'lib/jcr/evaluate_rules.rb', line 300

def self.get_leaf_rule rule, econs
  if rule[:target_rule_name ]
    target, target_annotations = get_target_rule( rule, econs )
    return target, target_annotations
  end
  #else
  return rule, nil
end

.get_name_mapping(rule_name, mapping) ⇒ Object



71
72
73
74
75
# File 'lib/jcr/map_rule_names.rb', line 71

def self.get_name_mapping rule_name, mapping
  trule = mapping[ rule_name.to_str ]
  raise_rule_name_missing( rule_name ) unless trule
  return trule
end

.get_repetitions(rule, econs) ⇒ Object



172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
# File 'lib/jcr/evaluate_rules.rb', line 172

def self.get_repetitions rule, econs

  repeat_min = 1
  repeat_max = 1
  repeat_step = nil
  if rule[:optional]
    repeat_min = 0
    repeat_max = 1
  elsif rule[:one_or_more]
    repeat_min = 1
    repeat_max = Float::INFINITY
    if rule[:repetition_step]
      repeat_step = rule[:repetition_step].to_s.to_i
      repeat_min = repeat_step
    end
  elsif rule[:zero_or_more]
    repeat_min = 0
    repeat_max = Float::INFINITY
    repeat_step = rule[:repetition_step].to_s.to_i if rule[:repetition_step]
  elsif rule[:specific_repetition] && rule[:specific_repetition].is_a?( Parslet::Slice )
    repeat_min = repeat_max = rule[:specific_repetition].to_s.to_i
  else
    o = rule[:repetition_interval]
    if o
      repeat_min = 0
      repeat_max = Float::INFINITY
    end
    o = rule[:repetition_min]
    if o
      if o.is_a?( Parslet::Slice )
        repeat_min = o.to_s.to_i
      end
    end
    o = rule[:repetition_max]
    if o
      if o.is_a?( Parslet::Slice )
        repeat_max = o.to_s.to_i
      end
    end
    o = rule[:repetition_step]
    if o
      if o.is_a?( Parslet::Slice )
        repeat_step = o.to_s.to_i
      end
    end
  end

  trace( econs, "rule repetition min = #{repeat_min} max = #{repeat_max} repetition step = #{repeat_step}" )
  return repeat_min, repeat_max, repeat_step
end

.get_rule_by_type(rule) ⇒ Object



115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
# File 'lib/jcr/find_roots.rb', line 115

def self.get_rule_by_type rule
  retval = nil
  return retval unless rule.is_a? Hash
  case
    when rule[:array_rule]
      retval = rule[:array_rule]
    when rule[:object_rule]
      retval = rule[:object_rule]
    when rule[:member_rule]
      retval = rule[:member_rule]
    when rule[:primitive_rule]
      retval = rule[:primitive_rule]
    when rule[:group_rule]
      retval = rule[:group_rule]
  end
  return retval
end

.get_rules_and_annotations(jcr) ⇒ Object



223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
# File 'lib/jcr/evaluate_rules.rb', line 223

def self.get_rules_and_annotations jcr
  rules = []
  annotations = []

  if jcr.is_a?( Hash )
    jcr = [ jcr ]
  end

  if jcr.is_a? Array
    i = 0
    jcr.each do |sub|
      case
        when sub[:unordered_annotation]
          annotations << sub
          i = i + 1
        when sub[:not_annotation]
          annotations << sub
          i = i + 1
        when sub[:root_annotation]
          annotations << sub
          i = i + 1
        when sub[:primitive_rule],sub[:object_rule],sub[:group_rule],sub[:array_rule],sub[:target_rule_name]
          break
      end
    end
    rules = jcr[i,jcr.length]
  end

  return rules, annotations
end

.get_target_rule(jcr, econs) ⇒ Object



165
166
167
168
169
170
# File 'lib/jcr/evaluate_rules.rb', line 165

def self.get_target_rule jcr, econs
  target = econs.mapping[ jcr[:target_rule_name][:rule_name].to_s ]
  raise "Target rule not in mapping. This should have been checked earlier." unless target
  trace( econs, "Referencing target rule #{slice_to_s(target)} from #{slice_to_s( jcr[:target_rule_name][:rule_name] )}" )
  return target,jcr[:target_rule_name][:annotations]
end

.group_to_s(jcr, shallow = true) ⇒ Object



59
60
61
62
# File 'lib/jcr/evaluate_group_rules.rb', line 59

def self.group_to_s( jcr, shallow=true)
  rules, annotations = get_rules_and_annotations( jcr )
  return "#{annotations_to_s( annotations)}( #{rules_to_s(rules,shallow)} )"
end

.ingest_ruleset(ruleset, existing_mapping = nil, ruleset_alias = nil) ⇒ Object



101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
# File 'lib/jcr/jcr.rb', line 101

def self.ingest_ruleset( ruleset, existing_mapping = nil, ruleset_alias=nil )
  tree = JCR.parse( ruleset )
  mapping = JCR.map_rule_names( tree, ruleset_alias )
  combined_mapping = {}
  combined_mapping.merge!( existing_mapping ) if existing_mapping
  combined_mapping.merge!( mapping )
  JCR.check_rule_target_names( tree, combined_mapping )
  JCR.check_groups( tree, combined_mapping )
  roots = JCR.find_roots( tree )
  ctx = Context.new
  ctx.tree = tree
  ctx.mapping = mapping
  ctx.callbacks = {}
  ctx.roots = roots
  JCR.process_directives( ctx )
  return ctx
end

.jcr_to_s(jcr, shallow = true) ⇒ Object



450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
# File 'lib/jcr/evaluate_rules.rb', line 450

def self.jcr_to_s( jcr, shallow=true )
  if jcr.is_a? Array
    retval = ""
    jcr.each_with_index do |item,idx|
      if idx > 1
        retval = retval + " , "
      end
      retval = retval + jcr_to_s( item, shallow )
    end
  elsif jcr.is_a? Parslet::Slice
    retval = slice_to_s( jcr )
  else
    if jcr[:q_string]
      retval = value_to_s( jcr )
    else
      retval = rule_to_s( jcr, shallow )
    end
  end
  return "<< " + retval + " >>"
end

.main(my_argv = nil) ⇒ Object



186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
# File 'lib/jcr/jcr.rb', line 186

def self.main my_argv=nil

  my_argv = ARGV unless my_argv

  options = {}

  opt_parser = OptionParser.new do |opt|
    opt.banner = "Usage: jcr [OPTIONS] [JSON_FILES]"
    opt.separator  ""
    opt.separator  "Evaluates JSON against JSON Content Rules (JCR)."
    opt.separator  ""
    opt.separator  "If -J is not specified, JSON_FILES is used."
    opt.separator  "If JSON_FILES is not specified, standard input (STDIN) is used."
    opt.separator  ""
    opt.separator  "Use -v to see results, otherwise check the exit code."
    opt.separator  ""
    opt.separator  "Options"

    opt.on("-r FILE","file containing ruleset") do |ruleset|
      if options[:ruleset]
        puts "A ruleset has already been specified. Use -h for help.", ""
        return 2
      end
      options[:ruleset] = File.open( ruleset ).read
    end

    opt.on("-R STRING","string containing ruleset. Should probably be quoted") do |ruleset|
      if options[:ruleset]
        puts "A ruleset has already been specified. Use -h for help.", ""
        return 2
      end
      options[:ruleset] = ruleset
    end

    opt.on("--test-jcr", "parse and test the JCR only") do |testjcr|
      options[:testjcr] = true
    end

    opt.on("--process-parts [DIRECTORY]", "creates smaller files for specification writing" ) do |directory|
      options[:process_parts] = true
      options[:process_parts_directory] = directory
    end

    opt.on("-S STRING","name of root rule. All roots will be tried if none is specified") do |root_name|
      if options[:root_name]
        puts "A root has already been specified. Use -h for help.", ""
        return 2
      end
      options[:root_name] = root_name
    end

    opt.on("-o FILE","file containing overide ruleset (option can be repeated)") do |ruleset|
      unless options[:overrides]
        options[:overrides] = Array.new
      end
      options[:overrides] << File.open( ruleset ).read
    end

    opt.on("-O STRING","string containing overide rule (option can be repeated)") do |rule|
      unless options[:overrides]
        options[:overrides] = Array.new
      end
      options[:overrides] << rule
    end

    opt.on("-J STRING","string containing JSON to evaluate. Should probably be quoted") do |json|
      if options[:json]
        puts "JSON has already been specified. Use -h for help.", ""
        return 2
      end
      options[:json] = json
    end

    opt.on("-v","verbose") do |verbose|
      options[:verbose] = true
    end

    opt.on("-q","quiet") do |quiet|
      options[:quiet] = true
    end

    opt.on("-h","display help") do |help|
      options[:help] = true
    end

    opt.separator  ""
    opt.separator  "Return codes:"
    opt.separator  " 0 = success"
    opt.separator  " 1 = bad JCR parsing or other bad condition"
    opt.separator  " 2 = invalid option or bad use of command"
    opt.separator  " 3 = unsuccessful evaluation of JSON"

    opt.separator  ""
    opt.separator  "JCR Version " + JCR::VERSION
  end

  begin
    opt_parser.parse! my_argv
  rescue OptionParser::InvalidOption => e
    puts "Unable to interpret command or options"
    puts e.message
    puts "", "Use -h for help"
    return 2
  end

  if options[:help]
    puts "HELP","----",""
    puts opt_parser
    return 2
  elsif !options[:ruleset]
    puts "No ruleset passed! Use -R or -r options.", ""
    puts "Use -h for help"
    return 2
  else

    begin

      ctx = Context.new( options[:ruleset], options[:verbose] )
      if options[:overrides]
        options[:overrides].each do |ov|
          ctx.override!( ov )
        end
      end

      if options[:verbose]
        pp "Ruleset Parse Tree", ctx.tree
        puts "Ruleset Map"
        ctx.mapping.each do |name,rule|
          puts "Parsed Rule: #{name}"
          puts rule_to_s( rule, false )
          puts "Parsed Rule Structure: #{name}"
          pp rule
        end
      end

      if options[:process_parts]
        parts = JCR::JcrParts.new
        parts.process_ruleset( options[:ruleset], options[:process_parts_directory] )
        if options[:overrides ]
          options[:overrides].each do |ov|
            parts = JCR::JcrParts.new
            parts.process_ruleset( ov, options[:process_parts_directory] )
          end
        end
      end

      if options[:testjcr]
        #we got this far which means the JCR was already parsed without
        #issue. therefore return 0
        return 0
      elsif options[:json]
        data = JSON.parse( options[:json] )
        ec = cli_eval( ctx, data, options[:root_name], options[:quiet] )
        return ec
      elsif $stdin.tty?
        ec = 0
        if my_argv.empty?
          ec = 2
        else
          my_argv.each do |fn|
            data = JSON.parse( File.open( fn ).read )
            tec = cli_eval( ctx, data, options[:root_name], options[:quiet] )
            ec = tec if tec != 0 #record error but don't let non-error overwrite error
          end
        end
        return ec
      else
        lines = ""
        ec = 0
        ARGF.each do |line|
          lines = lines + line
          if ARGF.eof?
            data = JSON.parse( lines )
            tec = cli_eval( ctx, data, options[:root_name], options[:quiet] )
            ec = tec if tec != 0 #record error but don't let non-error overwrite error
            lines = ""
          end
        end
        return ec
      end

    rescue JCR::JcrValidatorError => jcr_error
      puts jcr_error.message
      return 1
    rescue Parslet::ParseFailed => failure
      puts failure.parse_failure_cause.ascii_tree unless options[:quiet]
      return 1
    rescue JSON::ParserError => parser_error
      unless options[:quiet]
        puts "Unable to parse JSON"
        puts parser_error.message.inspect
      end
      return 3
    end

  end

end

.map_rule_names(tree, ruleset_alias = nil) ⇒ Object



20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
# File 'lib/jcr/map_rule_names.rb', line 20

def self.map_rule_names( tree, ruleset_alias = nil )
  prefix = ""
  if ruleset_alias
    prefix = ruleset_alias
    unless prefix.end_with?( "." )
      prefix = prefix + "."
    end
  end
  rule_name_maping = Hash.new
  if tree.is_a? Hash
    tree = [ tree ]
  end
  tree.each do |node|
    if node[:rule]
      rn = prefix + node[:rule][:rule_name].to_str
      if rule_name_maping[ rn ]
        raise JCR::JcrValidatorError, "Rule #{rn} already exists and is defined more than once"
      else
        rule_name_maping[ rn ] = node[:rule]
      end
    end
  end
  return rule_name_maping
end

.member_to_s(jcr, shallow = true) ⇒ Object



82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
# File 'lib/jcr/evaluate_member_rules.rb', line 82

def self.member_to_s( jcr, shallow=true )
  rules, annotations = get_rules_and_annotations( jcr )
  retval = ""
  rule = merge_rules( rules )
  case
    when rule[:member_name]
      retval = %Q|"#{rule[:member_name][:q_string].to_s}"|
    when rule[:member_regex]
      retval = "/#{rule[:member_regex][:regex].to_s}/"
    else
      retval = "** unknown member rule **"
  end
  retval = retval + " : " + rule_to_s( rule, shallow )
  return annotations_to_s( annotations ) + retval
end

.merge_rules(rules) ⇒ Object



254
255
256
257
258
259
260
261
262
# File 'lib/jcr/evaluate_rules.rb', line 254

def self.merge_rules rules
  new_rule = Hash.new
  rules.each do |rule|
    new_rule.merge!(rule) do |key,oldval,newval|
      raise "error: conflict in merge of #{rule} with #{new_rule}"
    end
  end
  return new_rule
end

.object_to_s(jcr, shallow = true) ⇒ Object



192
193
194
195
# File 'lib/jcr/evaluate_object_rules.rb', line 192

def self.object_to_s( jcr, shallow=true )
  rules, annotations = get_rules_and_annotations( jcr )
  return "#{annotations_to_s( annotations)}{ #{rules_to_s(rules,shallow)} }"
end

.parse(str) ⇒ Object



462
463
464
465
466
467
# File 'lib/jcr/parser.rb', line 462

def self.parse(str)

  parser = Parser.new
  parser.parse(str)

end

.parse_and_transform(str) ⇒ Object



469
470
471
472
473
474
475
476
477
478
479
# File 'lib/jcr/parser.rb', line 469

def self.parse_and_transform(str)
  # provided for the fun of it

  parser = Parser.new
  tree = parser.parse(str)
  pp tree

  transformer = Transformer.new
  transformer.apply( tree )

end

.pop_trace_stack(econs) ⇒ Object



313
314
315
# File 'lib/jcr/evaluate_rules.rb', line 313

def self.pop_trace_stack econs
  econs.trace_stack.pop
end


481
482
483
484
485
486
487
# File 'lib/jcr/parser.rb', line 481

def self.print_tree( tree )

  tree.each do |node|
    puts "named rule: " + node[:rule][:rule_name] if node[:rule]
  end

end

.process_directives(ctx) ⇒ Object



25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
# File 'lib/jcr/process_directives.rb', line 25

def self.process_directives( ctx )

  tree = ctx.tree
  if tree.is_a? Hash
    tree = [ tree ]
  end

  tree.each do |node|
    if node[:directive]
      d = node[:directive]
      case
        when d[:ruleset_id_d]
          process_ruleset_id( d[:ruleset_id_d], ctx )
        when d[:import_d]
          process_import( d[:import_d], ctx )
        when d[:jcr_version_d]
          process_jcrversion( d[:jcr_version_d], ctx )
      end
    end
  end
end

.process_import(directive, ctx) ⇒ Object



62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
# File 'lib/jcr/process_directives.rb', line 62

def self.process_import( directive, ctx )

  ruleset_id    = directive[:ruleset_id].to_str
  ruleset_alias = directive[:ruleset_id_alias].to_str
  u = ctx.map_ruleset_alias( ruleset_alias, ruleset_id )
  uri = URI.parse( u )
  ruleset = nil
  case uri.scheme
    when "http","https"
      response = Net::HTTP.get_response uri
      ruleset = response.body
    else
      ruleset = File.open( uri.path )
  end

  import_ctx = JCR.ingest_ruleset( ruleset, nil, ruleset_alias )
  ctx.mapping.merge!( import_ctx.mapping )
  ctx.roots.concat( import_ctx.roots )

end

.process_jcrversion(directive, ctx) ⇒ Object



51
52
53
54
55
56
57
58
59
60
# File 'lib/jcr/process_directives.rb', line 51

def self.process_jcrversion( directive, ctx )
  major = directive[:major_version].to_str.to_i
  minor = directive[:minor_version].to_str.to_i
  if major != 0
    raise "jcr version #{major}.#{minor} is incompatible with 0.7"
  end
  if minor != 7
    raise "jcr version #{major}.#{minor} is incompatible with 0.7"
  end
end

.process_ruleset_id(directive, ctx) ⇒ Object



47
48
49
# File 'lib/jcr/process_directives.rb', line 47

def self.process_ruleset_id( directive, ctx )
  ctx.id = directive[:ruleset_id].to_str
end

.push_trace_stack(econs, jcr) ⇒ Object



309
310
311
# File 'lib/jcr/evaluate_rules.rb', line 309

def self.push_trace_stack econs, jcr
  econs.trace_stack.push( find_first_slice( jcr ) )
end

.raise_group_error(str, node) ⇒ Object



177
178
179
180
181
182
183
184
185
# File 'lib/jcr/check_groups.rb', line 177

def self.raise_group_error str, node
  if node.is_a?( Parslet::Slice )
    pos = node.line_and_column
    name = node.to_str
    raise JCR::JcrValidatorError, "group rule error at line " + pos[0].to_s + " column " + pos[1].to_s + " name '" + name + "' :" + str
  else
    raise JCR::JcrValidatorError, "group rule error with '" + node.to_s + "' :" + str
  end
end

.raise_rule_name_missing(rule_name) ⇒ Object



77
78
79
80
81
82
# File 'lib/jcr/map_rule_names.rb', line 77

def self.raise_rule_name_missing rule_name
  pos = rule_name.line_and_column
  name = rule_name.to_str
  raise JCR::JcrValidatorError,
        "rule '" + name + "' at line " + pos[0].to_s + " column " + pos[1].to_s + " does not exist"
end

.raised_rule(jcr, rule_atom) ⇒ Object



446
447
448
# File 'lib/jcr/evaluate_rules.rb', line 446

def self.raised_rule jcr, rule_atom
  " rule at #{slice_to_s(jcr)} #{jcr_to_s(jcr)} from rule at #{slice_to_s(rule_atom)}"
end

.repetitions_to_s(rule) ⇒ Object



546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
# File 'lib/jcr/evaluate_rules.rb', line 546

def self.repetitions_to_s rule
  retval = ""
  if rule[:optional]
    retval = "?"
  elsif rule[:one_or_more]
    retval = "+"
    if rule[:repetition_step]
      retval = "%" + rule[:repetition_step].to_s
    end
  elsif rule[:zero_or_more]
    retval = "*"
    retval = retval + "%" + rule[:repetition_step].to_s if rule[:repetition_step]
  elsif rule[:specific_repetition] && rule[:specific_repetition].is_a?( Parslet::Slice )
    retval = "*" + rule[:specific_repetition].to_s
  else
    if rule[:repetition_interval]
      min = "0"
      max = "INF"
      o = rule[:repetition_min]
      if o
        if o.is_a?(Parslet::Slice)
          min = o.to_s
        end
      end
      o = rule[:repetition_max]
      if o
        if o.is_a?(Parslet::Slice)
          max = o.to_s
        end
      end
      retval = "*"+min+".."+max
    end
    o = rule[:repetition_step]
    if o
      if o.is_a?( Parslet::Slice )
        retval = retval + "%" + o.to_s
      end
    end
  end
  retval = " " + retval if retval.length != 0
  return retval
end

.rule_data(data = nil) ⇒ Object



325
326
327
328
329
330
331
332
333
334
335
336
# File 'lib/jcr/evaluate_rules.rb', line 325

def self.rule_data data=nil
  if data
    if data.is_a? String
      s = '"' + data + '"'
    else
      s = data.pretty_print_inspect
    end
    return elide(s)
  end
  #else
  return nil
end

.rule_def(type, jcr) ⇒ Object



354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
# File 'lib/jcr/evaluate_rules.rb', line 354

def self.rule_def type, jcr
  s = ""
  case type
    when "value"
      s = elide(value_to_s(jcr))
    when "member"
      s = elide(member_to_s(jcr))
    when "object"
      s = elide(object_to_s(jcr))
    when "object group"
      s = elide(group_to_s(jcr))
    when "array"
      s = elide(array_to_s(jcr))
    when "array group"
      s = elide(array_to_s(jcr))
    when "group"
      s = elide(group_to_s(jcr))
    else
      s = "** unknown rule **"
  end
  return "#{type} definition << #{s} >>"
end

.rule_to_s(rule, shallow = true) ⇒ Object



471
472
473
474
475
476
477
478
479
480
481
482
# File 'lib/jcr/evaluate_rules.rb', line 471

def self.rule_to_s( rule, shallow=true)
  if rule[:rule_name]
    if rule[:primitive_rule]
      retval = "$#{rule[:rule_name].to_s} =: #{ruletype_to_s( rule, shallow )}"
    else
      retval = "$#{rule[:rule_name].to_s} = #{ruletype_to_s( rule, shallow )}"
    end
  else
    retval = ruletype_to_s( rule, shallow )
  end
  return retval
end

.rules_to_s(rules, shallow = true) ⇒ Object



509
510
511
512
513
514
515
516
517
518
519
520
521
522
# File 'lib/jcr/evaluate_rules.rb', line 509

def self.rules_to_s( rules, shallow=true)
  retval = ""
  rules.each do |rule|
    if rule[:rule_name]
      next
    elsif rule[:choice_combiner]
      retval = retval + " | "
    elsif rule[:sequence_combiner]
      retval = retval + " , "
    end
    retval = retval + rule_to_s( rule, shallow ) + repetitions_to_s( rule )
  end
  return retval
end

.ruletype_to_s(rule, shallow = true) ⇒ Object



484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
# File 'lib/jcr/evaluate_rules.rb', line 484

def self.ruletype_to_s( rule, shallow=true )

  if rule[:primitive_rule]
    retval = value_to_s( rule[:primitive_rule] )
  elsif rule[:member_rule]
    retval = member_to_s( rule[:member_rule], shallow )
  elsif rule[:object_rule]
    retval = object_to_s( rule[:object_rule], shallow )
  elsif rule[:array_rule]
    retval = array_to_s( rule[:array_rule], shallow )
  elsif rule[:group_rule]
    retval = group_to_s( rule[:group_rule], shallow )
  elsif rule[:target_rule_name]
    retval = target_to_s( rule[:target_rule_name] )
  elsif rule[:rule_name]
    retval = "rule: #{rule[:rule_name].to_s}"
  elsif rule[:rule]
    retval = rule_to_s( rule[:rule], shallow )
  else
    retval = "** unknown rule definition ** #{rule}"
  end
  return retval

end

.slice_to_s(slice) ⇒ Object



435
436
437
438
439
440
441
442
443
444
# File 'lib/jcr/evaluate_rules.rb', line 435

def self.slice_to_s slice
  s = find_first_slice( slice )
  if s.is_a? Parslet::Slice
    pos = s.line_and_column
    retval = "'#{s.inspect}' ( line #{pos[0]} column #{pos[1]} )"
  else
    retval = slice.to_s
  end
  retval
end

.target_to_s(jcr) ⇒ Object



542
543
544
# File 'lib/jcr/evaluate_rules.rb', line 542

def self.target_to_s( jcr )
  return annotations_to_s( jcr[:annotations] ) + "$" + jcr[:rule_name].to_s
end

.trace(econs, message, data = nil) ⇒ Object



338
339
340
341
342
343
344
345
346
# File 'lib/jcr/evaluate_rules.rb', line 338

def self.trace econs, message, data = nil
  if econs.trace
    if data
      message = "#{message} data: #{rule_data( data )}"
    end
    last = econs.trace_stack.last
    puts "[ depth=#{econs.trace_stack.length}:#{trace_coord(econs)} ] #{message}"
  end
end

.trace_coord(econs) ⇒ Object



348
349
350
351
352
# File 'lib/jcr/evaluate_rules.rb', line 348

def self.trace_coord econs
  last = econs.trace_stack.last
  pos = "#{last.line_and_column}" if last
  return "pos=#{pos}"
end

.trace_def(econs, type, jcr, data) ⇒ Object



377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
# File 'lib/jcr/evaluate_rules.rb', line 377

def self.trace_def econs, type, jcr, data
  if econs.trace
    s = ""
    case type
      when "value"
        s = elide( value_to_s( jcr ) )
      when "member"
        s = elide( member_to_s( jcr  ) )
      when "object"
        s = elide( object_to_s( jcr ) )
      when "object group"
        s = elide( group_to_s( jcr ) )
      when "array"
        s = elide( array_to_s( jcr ) )
      when "array group"
        s = elide( array_to_s( jcr ) )
      when "group"
        s = elide( group_to_s( jcr ) )
      else
        s = "** unknown rule **"
    end
    trace( econs, rule_def( type, jcr ) )
  end
end

.trace_eval(econs, message, evaluation, jcr, data, type) ⇒ Object



402
403
404
405
406
407
408
409
410
411
# File 'lib/jcr/evaluate_rules.rb', line 402

def self.trace_eval econs, message, evaluation, jcr, data, type
  if evaluation.success
    econs.report_success
    trace( econs, "#{message} evaluation is true" )
  else
    failure = Failure.new( data, jcr, type, evaluation, econs.trace_stack.length )
    econs.report_failure( failure )
    trace( econs, "#{message} evaluation failed: #{evaluation.reason}")
  end
end

.value_to_s(jcr, shallow = true) ⇒ Object



408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
# File 'lib/jcr/evaluate_value_rules.rb', line 408

def self.value_to_s( jcr, shallow=true )

  rules, annotations = get_rules_and_annotations( jcr )

  rule = rules[ 0 ]
  retval = ""
  case

    when rule[:any]
      retval =  "any"

    when rule[:integer_v]
      retval =  rule[:integer_v].to_s
    when rule[:integer]
      retval =  rule[:integer].to_s.to_i
    when rule[:integer_min],rule[:integer_max]
      min = "-INF"
      max = "INF"
      min = rule[:integer_min].to_s.to_i if rule[:integer_min]
      max = rule[:integer_max].to_s.to_i if rule[:integer_max]
      retval =  "#{min}..#{max}"
    when rule[:sized_int_v]
      retval =  "int" + rule[:sized_int_v][:bits].to_s
    when rule[:sized_uint_v]
      retval =  "uint" + rule[:sized_uint_v][:bits].to_s

    when rule[:double_v]
      retval =  rule[:double_v].to_s
    when rule[:float_v]
      retval =  rule[:float_v].to_s
    when rule[:float]
      retval =  rule[:float].to_s.to_f
    when rule[:float_min],rule[:float_max]
      min = "-INF"
      max = "INF"
      min = rule[:float_min].to_s.to_f if rule[:float_min]
      max = rule[:float_max].to_s.to_f if rule[:float_max]
      retval =  "#{min}..#{max}"

    when rule[:true_v]
      retval =  "true"
    when rule[:false_v]
      retval =  "false"
    when rule[:boolean_v]
      retval =  "boolean"

    when rule[:string]
      retval =  "string"
    when rule[:q_string]
      retval =  %Q|"#{rule[:q_string].to_s}"|

    when rule[:regex]
      retval =  "/#{rule[:regex].to_s}/"

    when rule[:ipv4]
      retval =  "ipv4"
    when rule[:ipv6]
      retval =  "ipv6"

    when rule[:fqdn]
      retval =  "fqdn"
    when rule[:idn]
      retval =  "idn"

    when rule[:uri]
      if rule[:uri].is_a? Hash
        retval =  "uri..#{rule[:uri][:uri_scheme].to_s}"
      else
        retval =  "uri"
      end

    when rule[:email]
      retval =  "email"

    when rule[:phone]
      retval =  "phone"

    when rule[:hex]
      retval =  "hex"
    when rule[:base32hex]
      retval =  "base32hex"
    when rule[:base64url]
      retval =  "base64url"
    when rule[:base64]
      retval =  "base64"

    when rule[:datetime]
      retval =  "datetime"
    when rule[:date]
      retval =  "date"
    when rule[:time]
      retval =  "time"

    when rule[:null]
      retval =  "null"

    when rule[:group_rule]
      retval =  group_to_s( rule[:group_rule], shallow )

    else
      retval =  "** unknown value rule **"
  end
  return annotations_to_s( annotations ) + retval.to_s
end