cqm-validators 0.1.0 → 2.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. checksums.yaml +5 -5
  2. data/.github/PULL_REQUEST_TEMPLATE.md +23 -0
  3. data/.gitignore +3 -1
  4. data/.overcommit.yml +18 -0
  5. data/.rubocop.yml +68 -0
  6. data/.simplecov +12 -0
  7. data/.travis.yml +20 -0
  8. data/Gemfile +11 -3
  9. data/README.md +20 -6
  10. data/Rakefile +8 -6
  11. data/config/mongoid.yml +6 -0
  12. data/cqm_validators.gemspec +23 -14
  13. data/lib/base_validator.rb +20 -20
  14. data/lib/cqm_validators.rb +4 -2
  15. data/lib/cqm_validators/version.rb +3 -1
  16. data/lib/data_validator.rb +74 -79
  17. data/lib/measure_validator.rb +104 -107
  18. data/lib/performance_rate_validator.rb +43 -63
  19. data/lib/qrda_qdm_template_validator.rb +164 -311
  20. data/lib/reported_result_extractor.rb +137 -139
  21. data/lib/schema_validator.rb +20 -19
  22. data/lib/schematron/c_processor.rb +19 -19
  23. data/lib/schematron/java_processor.rb +66 -68
  24. data/lib/schematron/qrda/{cat_1_r4/HL7 QRDA Category I STU 4.sch → cat_1_r5_1/HL7 QRDA Category I STU 5.1.sch } +1684 -2082
  25. data/lib/schematron/qrda/{cat_1_r3_1 → cat_1_r5_1}/voc.xml +1223 -1228
  26. data/lib/schematron_validator.rb +31 -32
  27. data/lib/validation_error.rb +11 -9
  28. data/lib/validators.rb +40 -106
  29. data/notice.md +9 -0
  30. metadata +97 -36
  31. data/lib/schematron/qrda/cat_1/HL7_CDAR2_QRDA_Category_I_2_12_16.sch +0 -4693
  32. data/lib/schematron/qrda/cat_1/voc.xml +0 -1177
  33. data/lib/schematron/qrda/cat_1_r2/QRDA Category I Release 2.sch +0 -4069
  34. data/lib/schematron/qrda/cat_1_r2/voc.xml +0 -1065
  35. data/lib/schematron/qrda/cat_1_r3_1/HL7 QRDA Category I STU 3.1.sch +0 -3573
  36. data/lib/schematron/qrda/cat_1_r3_1/HL7 QRDA Category III STU 1.1.sch +0 -464
  37. data/lib/schematron/qrda/cat_1_r3_1/QRDA Category I STU Release 3.1.sch +0 -5394
  38. data/lib/schematron/qrda/cat_1_r4/voc.xml +0 -1186
  39. data/lib/schematron/qrda/cat_3/QRDA Category III.sch +0 -675
  40. data/lib/schematron/qrda/cat_3/voc.xml +0 -21
  41. data/lib/schematron/qrda/cat_3_r1_1/HL7 QRDA Category III STU 1.1.sch +0 -528
  42. data/lib/schematron/qrda/cat_3_r1_1/voc.xml +0 -8
  43. data/lib/schematron/qrda/cat_3_r2/HL7 QRDA Category III STU 2.sch +0 -677
  44. data/lib/schematron/qrda/cat_3_r2/voc.xml +0 -1186
@@ -1,172 +1,170 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module CqmValidators
2
4
  module ReportedResultExtractor
5
+ # takes a document and a list of 1 or more id hashes, e.g.:
6
+ # [{measure_id:"8a4d92b2-36af-5758-0136-ea8c43244986", set_id:"03876d69-085b-415c-ae9d-9924171040c2", ipp:"D77106C4-8ED0-4C5D-B29E-13DBF255B9FF",
7
+ # den:"8B0FA80F-8FFE-494C-958A-191C1BB36DBF", num:"9363135E-A816-451F-8022-96CDA7E540DD"}]
8
+ # returns nil if nothing matching is found
9
+ # returns a hash with the values of the populations filled out along with the population_ids added to the result
3
10
 
4
- #takes a document and a list of 1 or more id hashes, e.g.:
5
- #[{measure_id:"8a4d92b2-36af-5758-0136-ea8c43244986", set_id:"03876d69-085b-415c-ae9d-9924171040c2", ipp:"D77106C4-8ED0-4C5D-B29E-13DBF255B9FF", den:"8B0FA80F-8FFE-494C-958A-191C1BB36DBF", num:"9363135E-A816-451F-8022-96CDA7E540DD"}]
6
- #returns nil if nothing matching is found
7
- # returns a hash with the values of the populations filled out along with the population_ids added to the result
8
-
9
-
10
- def extract_results_by_ids(measure_id, ids, doc)
11
- results = nil
12
- _ids = ids.dup
13
- stratification = _ids.delete("stratification")
14
- stratification ||= _ids.delete("STRAT")
15
- errors = []
16
- nodes = find_measure_node(measure_id, doc)
11
+ ALL_POPULATION_CODES = %w[IPP DENOM NUMER NUMEX DENEX DENEXCEP MSRPOPL MSRPOPLEX OBSERV].freeze
17
12
 
18
- if nodes.nil? || nodes.empty?
19
- # short circuit and return nil
20
- return {}
21
- end
13
+ def extract_results_by_ids(measure, poulation_set_id, doc, stratification_id = nil)
14
+ results = nil
15
+ nodes = find_measure_node(measure.hqmf_id, doc)
22
16
 
23
- nodes.each do |n|
24
- results = get_measure_components(n, _ids, stratification)
25
- break if (results != nil || (results != nil && !results.empty?))
26
- end
27
- return nil if results.nil?
28
- results[:population_ids] = ids.dup
29
- results
17
+ if nodes.nil? || nodes.empty?
18
+ # short circuit and return nil
19
+ return {}
30
20
  end
31
21
 
32
- def find_measure_node(id, doc)
33
- xpath_measures = %Q{/cda:ClinicalDocument/cda:component/cda:structuredBody/cda:component/cda:section
34
- /cda:entry/cda:organizer[ ./cda:templateId[@root = "2.16.840.1.113883.10.20.27.3.1"]
35
- and ./cda:reference/cda:externalDocument/cda:id[#{translate("@extension")}='#{id.upcase}' and #{translate("@root")}='2.16.840.1.113883.4.738']]}
36
- return doc.xpath(xpath_measures)
22
+ nodes.each do |n|
23
+ results = get_measure_components(n, measure.population_sets.where(population_set_id: poulation_set_id).first, stratification_id)
24
+ break if !results.nil? || (!results.nil? && !results.empty?)
37
25
  end
26
+ return nil if results.nil?
38
27
 
39
- def get_measure_components(n,ids, stratification)
40
- results = {:supplemental_data =>{}}
41
- ids.each_pair do |k,v|
42
- val = nil
43
- sup = nil
44
- if (k == 'OBSERV')
45
- msrpopl = ids['MSRPOPL']
46
- val, sup = extract_cv_value(n,v,msrpopl, stratification)
47
- else
48
- val,sup,pr =extract_component_value(n,k,v,stratification)
49
- end
50
- if !val.nil?
51
- results[k.to_s] = val
52
- results[:supplemental_data][k] = sup
53
- else
54
- # return nil
55
- end
56
- if !pr.nil?
57
- results["PR"] = pr
58
- end
59
- end
60
- results
61
- end
28
+ results
29
+ end
62
30
 
63
- def extract_cv_value(node, id, msrpopl, strata = nil)
64
- xpath_observation = %{ cda:component/cda:observation[./cda:value[@code = "MSRPOPL"] and ./cda:reference/cda:externalObservation/cda:id[#{translate("@root")}='#{msrpopl.upcase}']]}
65
- cv = node.at_xpath(xpath_observation)
66
- return nil unless cv
67
- val = nil
68
- if strata
69
- strata_path = %{ cda:entryRelationship[@typeCode="COMP"]/cda:observation[./cda:templateId[@root = "2.16.840.1.113883.10.20.27.3.4"] and ./cda:reference/cda:externalObservation/cda:id[#{translate("@root")}='#{strata.upcase}']]}
70
- n = cv.xpath(strata_path)
71
- val = get_cv_value(n,id)
72
- else
73
- val = get_cv_value(cv,id)
74
- end
75
- return val, (strata.nil? ? extract_supplemental_data(cv) : nil)
76
- end
31
+ def find_measure_node(id, doc)
32
+ xpath_measures = %(/cda:ClinicalDocument/cda:component/cda:structuredBody/cda:component/cda:section
33
+ /cda:entry/cda:organizer[ ./cda:templateId[@root = "2.16.840.1.113883.10.20.27.3.1"]
34
+ and ./cda:reference/cda:externalDocument/cda:id[#{translate('@extension')}='#{id.upcase}' and #{translate('@root')}='2.16.840.1.113883.4.738']])
35
+ doc.xpath(xpath_measures)
36
+ end
37
+
38
+ def get_measure_components(n, population_set, stratification_id)
39
+ results = { supplemental_data: {} }
40
+ stratification = stratification_id ? population_set.stratifications.where(stratification_id: stratification_id).first.hqmf_id : nil
41
+ ALL_POPULATION_CODES.each do |pop_code|
42
+ next unless population_set.populations[pop_code] || pop_code == 'OBSERV'
77
43
 
78
- def extract_component_value(node, code, id, strata = nil)
79
- xpath_observation = %{ cda:component/cda:observation[./cda:value[@code = "#{code}"] and ./cda:reference/cda:externalObservation/cda:id[#{translate("@root")}='#{id.upcase}']]}
80
- cv = node.at_xpath(xpath_observation)
81
- return nil unless cv
82
44
  val = nil
83
- if strata
84
- strata_path = %{ cda:entryRelationship[@typeCode="COMP"]/cda:observation[./cda:templateId[@root = "2.16.840.1.113883.10.20.27.3.4"] and ./cda:reference/cda:externalObservation/cda:id[#{translate("@root")}='#{strata.upcase}']]}
85
- n = cv.xpath(strata_path)
86
- val = get_aggregate_count(n) if n
45
+ sup = nil
46
+ if pop_code == 'OBSERV'
47
+ next unless population_set.populations['MSRPOPL']
48
+
49
+ msrpopl = population_set.populations['MSRPOPL']['hqmf_id']
50
+ val, sup = extract_cv_value(n, population_set.observations.first.hqmf_id, msrpopl, stratification)
87
51
  else
88
- val = get_aggregate_count(cv)
52
+ val, sup, pr = extract_component_value(n, pop_code, population_set.populations[pop_code]['hqmf_id'], stratification)
89
53
  end
90
- #Performance rate is only applicable for unstratified values
91
- if code == "NUMER" && strata == nil
92
- pref_rate_value = extract_performance_rate(node,code,id)
54
+ unless val.nil?
55
+ results[pop_code] = val
56
+ results[:supplemental_data][pop_code] = sup
93
57
  end
94
- return val,(strata.nil? ? extract_supplemental_data(cv) : nil),pref_rate_value
58
+ results['PR'] = pr unless pr.nil?
95
59
  end
60
+ results
61
+ end
96
62
 
97
- def extract_performance_rate(node,code,id)
98
- xpath_perf_rate = %{ cda:component/cda:observation[./cda:templateId[@root = "2.16.840.1.113883.10.20.27.3.14"] and ./cda:reference/cda:externalObservation/cda:id[#{translate("@root")}='#{id.upcase}']]/cda:value}
99
- perf_rate = node.at_xpath(xpath_perf_rate)
100
- pref_rate_value = {}
101
- if perf_rate != nil
102
- if perf_rate.at_xpath("./@nullFlavor")
103
- pref_rate_value["nullFlavor"] = "NA"
104
- return pref_rate_value
105
- else
106
- pref_rate_value["value"] = perf_rate.at_xpath("./@value").value
107
- return pref_rate_value
108
- end
109
- end
110
- return nil
63
+ def extract_cv_value(node, id, msrpopl, strata = nil)
64
+ xpath_observation = %( cda:component/cda:observation[./cda:value[@code = "MSRPOPL"] and ./cda:reference/cda:externalObservation/cda:id[#{translate('@root')}='#{msrpopl.upcase}']])
65
+ cv = node.at_xpath(xpath_observation)
66
+ return nil unless cv
67
+
68
+ val = nil
69
+ if strata
70
+ strata_path = %( cda:entryRelationship[@typeCode="COMP"]/cda:observation[./cda:templateId[@root = "2.16.840.1.113883.10.20.27.3.4"] and ./cda:reference/cda:externalObservation/cda:id[#{translate('@root')}='#{strata.upcase}']])
71
+ n = cv.xpath(strata_path)
72
+ val = get_cv_value(n, id)
73
+ else
74
+ val = get_cv_value(cv, id)
111
75
  end
112
- # convert numbers in value nodes to Int / Float as necessary TODO add more types other than 'REAL'
113
- def convert_value(value_node)
114
- if value_node.nil?
115
- return
116
- end
117
- if value_node['type'] == 'REAL' || value_node['value'].include?('.')
118
- return value_node['value'].to_f
76
+ [val, (strata.nil? ? extract_supplemental_data(cv) : nil)]
77
+ end
78
+
79
+ def extract_component_value(node, code, id, strata = nil)
80
+ code = 'IPOP' if code == 'IPP'
81
+ xpath_observation = %( cda:component/cda:observation[./cda:value[@code = "#{code}"] and ./cda:reference/cda:externalObservation/cda:id[#{translate('@root')}='#{id.upcase}']])
82
+ cv = node.at_xpath(xpath_observation)
83
+ return nil unless cv
84
+
85
+ val = nil
86
+ if strata
87
+ strata_path = %( cda:entryRelationship[@typeCode="COMP"]/cda:observation[./cda:templateId[@root = "2.16.840.1.113883.10.20.27.3.4"] and ./cda:reference/cda:externalObservation/cda:id[#{translate('@root')}='#{strata.upcase}']])
88
+ n = cv.xpath(strata_path)
89
+ val = get_aggregate_count(n) if n
90
+ else
91
+ val = get_aggregate_count(cv)
92
+ end
93
+ # Performance rate is only applicable for unstratified values
94
+ pref_rate_value = extract_performance_rate(node, code, id) if code == 'NUMER' && strata.nil?
95
+ [val, (strata.nil? ? extract_supplemental_data(cv) : nil), pref_rate_value]
96
+ end
97
+
98
+ def extract_performance_rate(node, _code, id)
99
+ xpath_perf_rate = %( cda:component/cda:observation[./cda:templateId[@root = "2.16.840.1.113883.10.20.27.3.14"] and ./cda:reference/cda:externalObservation/cda:id[#{translate('@root')}='#{id.upcase}']]/cda:value)
100
+ perf_rate = node.at_xpath(xpath_perf_rate)
101
+ pref_rate_value = {}
102
+ unless perf_rate.nil?
103
+ if perf_rate.at_xpath('./@nullFlavor')
104
+ pref_rate_value['nullFlavor'] = 'NA'
119
105
  else
120
- return value_node['value'].to_i
106
+ pref_rate_value['value'] = perf_rate.at_xpath('./@value').value
121
107
  end
108
+ return pref_rate_value
122
109
  end
110
+ nil
111
+ end
123
112
 
124
- #given an observation node with an aggregate count node, return the reported and expected value within the count node
125
- def get_cv_value(node, cv_id)
126
- xpath_value = %{cda:entryRelationship/cda:observation[./cda:templateId[@root="2.16.840.1.113883.10.20.27.3.2"] and ./cda:reference/cda:externalObservation/cda:id[#{translate("@root")}='#{cv_id.upcase}']]/cda:value}
113
+ # convert numbers in value nodes to Int / Float as necessary
114
+ # TODO: add more types other than 'REAL'
115
+ def convert_value(value_node)
116
+ return if value_node.nil?
117
+ return value_node['value'].to_f if value_node['type'] == 'REAL' || value_node['value'].include?('.')
127
118
 
128
- value_node = node.at_xpath(xpath_value)
129
- value = convert_value(value_node) if value_node
130
- value
131
- end
119
+ value_node['value'].to_i
120
+ end
132
121
 
133
- #given an observation node with an aggregate count node, return the reported and expected value within the count node
134
- def get_aggregate_count(node)
135
- xpath_value = 'cda:entryRelationship/cda:observation[./cda:templateId[@root="2.16.840.1.113883.10.20.27.3.3"]]/cda:value'
136
- value_node = node.at_xpath(xpath_value)
137
- value = convert_value(value_node) if value_node
138
- value
139
- end
122
+ # given an observation node with an aggregate count node, return the reported and expected value within the count node
123
+ def get_cv_value(node, cv_id)
124
+ xpath_value = %(cda:entryRelationship/cda:observation[./cda:templateId[@root="2.16.840.1.113883.10.20.27.3.2"] and ./cda:reference/cda:externalObservation/cda:id[#{translate('@root')}='#{cv_id.upcase}']]/cda:value)
125
+
126
+ value_node = node.at_xpath(xpath_value)
127
+ value = convert_value(value_node) if value_node
128
+ value
129
+ end
130
+
131
+ # given an observation node with an aggregate count node, return the reported and expected value within the count node
132
+ def get_aggregate_count(node)
133
+ xpath_value = 'cda:entryRelationship/cda:observation[./cda:templateId[@root="2.16.840.1.113883.10.20.27.3.3"]]/cda:value'
134
+ value_node = node.at_xpath(xpath_value)
135
+ value = convert_value(value_node) if value_node
136
+ value
137
+ end
140
138
 
141
- def extract_supplemental_data(cv)
142
- ret = {}
143
- supplemental_data_mapping = {"RACE"=> "2.16.840.1.113883.10.20.27.3.8",
144
- "ETHNICITY" => "2.16.840.1.113883.10.20.27.3.7",
145
- "SEX" => "2.16.840.1.113883.10.20.27.3.6",
146
- "PAYER" => "2.16.840.1.113883.10.20.27.3.9"}
147
- supplemental_data_mapping.each_pair do |supp, id|
148
- key_hash = {}
149
- xpath = "cda:entryRelationship/cda:observation[cda:templateId[@root='#{id}']]"
150
- (cv.xpath(xpath) || []).each do |node|
151
- value = node.at_xpath('cda:value')
152
- count = get_aggregate_count(node)
153
- if value.at_xpath("./@nullFlavor")
154
- if supp == "PAYER" && value['xsi:type'] == 'CD' && value['nullFlavor'] == 'OTH' && value.at_xpath("cda:translation") && value.at_xpath("cda:translation")['code']
155
- key_hash[value.at_xpath("cda:translation")['code']] = count
156
- else
157
- key_hash["UNK"] = count
158
- end
139
+ def extract_supplemental_data(cv)
140
+ ret = {}
141
+ supplemental_data_mapping = { 'RACE' => '2.16.840.1.113883.10.20.27.3.8',
142
+ 'ETHNICITY' => '2.16.840.1.113883.10.20.27.3.7',
143
+ 'SEX' => '2.16.840.1.113883.10.20.27.3.6',
144
+ 'PAYER' => '2.16.840.1.113883.10.20.27.3.9' }
145
+ supplemental_data_mapping.each_pair do |supp, id|
146
+ key_hash = {}
147
+ xpath = "cda:entryRelationship/cda:observation[cda:templateId[@root='#{id}']]"
148
+ (cv.xpath(xpath) || []).each do |node|
149
+ value = node.at_xpath('cda:value')
150
+ count = get_aggregate_count(node)
151
+ if value.at_xpath('./@nullFlavor')
152
+ if supp == 'PAYER' && value['xsi:type'] == 'CD' && value['nullFlavor'] == 'OTH' && value.at_xpath('cda:translation') && value.at_xpath('cda:translation')['code']
153
+ key_hash[value.at_xpath('cda:translation')['code']] = count
159
154
  else
160
- key_hash[value['code']] = count
155
+ key_hash['UNK'] = count
161
156
  end
157
+ else
158
+ key_hash[value['code']] = count
162
159
  end
163
- ret[supp.to_s] = key_hash
164
160
  end
165
- ret
161
+ ret[supp.to_s] = key_hash
166
162
  end
163
+ ret
164
+ end
167
165
 
168
- def translate(id)
169
- %{translate(#{id}, "abcdefghijklmnopqrstuvwxyz", "ABCDEFGHIJKLMNOPQRSTUVWXYZ")}
170
- end
166
+ def translate(id)
167
+ %{translate(#{id}, "abcdefghijklmnopqrstuvwxyz", "ABCDEFGHIJKLMNOPQRSTUVWXYZ")}
171
168
  end
169
+ end
172
170
  end
@@ -1,23 +1,24 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module CqmValidators
2
- module Schema
3
- class Validator
4
- include BaseValidator
5
-
6
- def initialize(name, schema_file)
7
- @name = name
8
- @schema_file = schema_file
9
- @xsd = Nokogiri::XML::Schema(File.new(@schema_file))
10
- end
11
-
12
- # Validate the document against the configured schema
13
- def validate(document,data={})
14
- @xsd.errors.clear
15
- doc = get_document(document)
16
- @xsd.validate(doc).map do |error|
17
- build_error(error.message, "/", data[:file_name])
18
- end
19
- end
4
+ module Schema
5
+ class Validator
6
+ include BaseValidator
7
+
8
+ def initialize(name, schema_file)
9
+ @name = name
10
+ @schema_file = schema_file
11
+ @xsd = Nokogiri::XML::Schema(File.new(@schema_file))
12
+ end
13
+
14
+ # Validate the document against the configured schema
15
+ def validate(document, data = {})
16
+ @xsd.errors.clear
17
+ doc = get_document(document)
18
+ @xsd.validate(doc).map do |error|
19
+ build_error(error.message, '/', data[:file_name])
20
20
  end
21
21
  end
22
+ end
23
+ end
22
24
  end
23
-
@@ -1,26 +1,26 @@
1
- module CqmValidators
2
- module Schematron
3
- module CProcessor
4
-
1
+ # frozen_string_literal: true
5
2
 
6
- def get_errors(document)
7
- document = get_document(document)
8
- processor.transform(document)
9
- end
3
+ module CqmValidators
4
+ module Schematron
5
+ module CProcessor
6
+ def get_errors(document)
7
+ document = get_document(document)
8
+ processor.transform(document)
9
+ end
10
10
 
11
- def processor
12
- return @processor if @processor
13
- doc = Nokogiri::XML(File.open(@schematron_file))
14
- doc.root["defaultPhase"] = ("errors")
11
+ def processor
12
+ return @processor if @processor
15
13
 
16
- xslt = Nokogiri::XSLT(File.open(ISO_SCHEMATRON))
14
+ doc = Nokogiri::XML(File.open(@schematron_file))
15
+ doc.root['defaultPhase'] = 'errors'
17
16
 
18
- result = xslt.transform(doc)
19
- #this is stupid but needs to be done to assocaite the xslt file with a dirctory
20
- result = Nokogiri::XML(result.to_s,@schematron_file)
21
- @processor = Nokogiri::XSLT::Stylesheet.parse_stylesheet_doc(result)
22
- end
17
+ xslt = Nokogiri::XSLT(File.open(ISO_SCHEMATRON))
23
18
 
24
- end
19
+ result = xslt.transform(doc)
20
+ # this needs to be done to associate the xslt file with a dirctory
21
+ result = Nokogiri::XML(result.to_s, @schematron_file)
22
+ @processor = Nokogiri::XSLT::Stylesheet.parse_stylesheet_doc(result)
25
23
  end
24
+ end
25
+ end
26
26
  end
@@ -1,92 +1,90 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'java'
2
4
  require_relative '../../../../java/saxon9he.jar'
3
5
 
4
- java_import "java.io.StringReader"
5
- java_import "java.io.StringWriter"
6
-
7
- java_import "javax.xml.parsers.DocumentBuilder"
8
- java_import "javax.xml.parsers.DocumentBuilderFactory"
6
+ java_import 'java.io.StringReader'
7
+ java_import 'java.io.StringWriter'
9
8
 
10
- java_import "javax.xml.transform.TransformerFactory"
11
- java_import "javax.xml.transform.Transformer"
12
- java_import "javax.xml.transform.dom.DOMSource"
13
- java_import "javax.xml.transform.stream.StreamSource"
14
- java_import "javax.xml.transform.stream.StreamResult"
9
+ java_import 'javax.xml.parsers.DocumentBuilder'
10
+ java_import 'javax.xml.parsers.DocumentBuilderFactory'
15
11
 
16
- java_import "org.w3c.dom.Document"
12
+ java_import 'javax.xml.transform.TransformerFactory'
13
+ java_import 'javax.xml.transform.Transformer'
14
+ java_import 'javax.xml.transform.dom.DOMSource'
15
+ java_import 'javax.xml.transform.stream.StreamSource'
16
+ java_import 'javax.xml.transform.stream.StreamResult'
17
17
 
18
+ java_import 'org.w3c.dom.Document'
18
19
 
19
- TRANSFORMER_FACTORY_IMPL = "net.sf.saxon.TransformerFactoryImpl"
20
+ TRANSFORMER_FACTORY_IMPL = 'net.sf.saxon.TransformerFactoryImpl'
20
21
 
21
22
  module CqmValidators
22
- module Schematron
23
- module JavaProcessor
23
+ module Schematron
24
+ module JavaProcessor
25
+ ISO_SCHEMATRON2 = File.join(DIR, 'lib/schematron/iso-schematron-xslt2/iso_svrl_for_xslt2.xsl')
24
26
 
25
- ISO_SCHEMATRON2 = File.join(DIR, 'lib/schematron/iso-schematron-xslt2/iso_svrl_for_xslt2.xsl')
27
+ class HdsUrlResolver
28
+ include javax.xml.transform.URIResolver
26
29
 
27
- class HdsUrlResolver
28
- include javax.xml.transform.URIResolver
30
+ def initialize(schematron)
31
+ @file = schematron
32
+ end
29
33
 
30
- def initialize(schematron)
31
- @file = schematron
32
- end
34
+ def resolve(href, _base)
35
+ path = File.join(File.dirname(@file), href)
36
+ StreamSource.new(java.io.File.new(path))
37
+ end
38
+ end
33
39
 
34
- def resolve(href, base)
35
- path = File.join(File.dirname(@file), href)
36
- return StreamSource.new(java.io.File.new(path))
37
- end
40
+ def get_errors(document)
41
+ document_j = get_document_j(document)
42
+ output = build_transformer(StringReader.new(processor), StreamSource.new(document_j), true)
43
+ Nokogiri::XML(output)
44
+ end
38
45
 
46
+ def get_document_j(doc)
47
+ case doc
48
+ when File
49
+ java.io.File.new(doc.path)
50
+ else
51
+ StringReader.new(doc.to_s)
39
52
  end
53
+ end
40
54
 
41
- def get_errors(document)
42
- document_j = get_document_j(document)
43
- output = build_transformer(StringReader.new(processor), StreamSource.new(document_j), true)
44
- Nokogiri::XML(output)
45
- end
55
+ def processor
56
+ @processor ||= build_transformer(java.io.File.new(ISO_SCHEMATRON2), schematron_file)
57
+ end
46
58
 
47
- def get_document_j(doc)
48
- case doc
49
- when File
50
- java.io.File.new(doc.path)
51
- else
52
- StringReader.new(doc.to_s)
53
- end
59
+ def schematron_file
60
+ # this allows us to run the validation utility app in jBoss/TorqueBox
61
+ # for some reason it breaks the first time you call DocumentBuilderFactory,
62
+ # so the solution is to catch the error and retry
63
+ # TODO: pull this out when the above is no longer the case.
64
+ begin
65
+ dbf = DocumentBuilderFactory.new_instance
66
+ rescue Exception
67
+ retry
54
68
  end
69
+ dbf.setIgnoringElementContentWhitespace(true)
70
+ db = dbf.new_document_builder
71
+ document = db.parse(java.io.File.new(@schematron_file))
55
72
 
56
- def processor
57
- @processor ||= build_transformer(java.io.File.new(ISO_SCHEMATRON2), schematron_file)
58
- end
73
+ root = document.document_element
74
+ root.set_attribute('defaultPhase', 'errors')
59
75
 
60
- def schematron_file
61
- byebug
62
- # this allows us to run the validation utility app in jBoss/TorqueBox
63
- # for some reason it breaks the first time you call DocumentBuilderFactory,
64
- # so the solution is to catch the error and retry
65
- # TODO: pull this out when the above is no longer the case.
66
- begin
67
- dbf = DocumentBuilderFactory.new_instance
68
- rescue Exception => ex
69
- retry
70
- end
71
- dbf.setIgnoringElementContentWhitespace(true);
72
- db = dbf.new_document_builder
73
- document = db.parse(java.io.File.new(@schematron_file))
74
-
75
- root = document.document_element
76
- phase = root.set_attribute("defaultPhase", "errors")
77
-
78
- DOMSource.new(root)
79
- end
76
+ DOMSource.new(root)
77
+ end
80
78
 
81
- def build_transformer(xslt, input_file, url=false)
82
- factory = TransformerFactory.newInstance(TRANSFORMER_FACTORY_IMPL, nil)
83
- factory.uri_resolver = HdsUrlResolver.new(@schematron_file) if url
84
- transformer = factory.new_transformer(StreamSource.new(xslt))
85
- sw = StringWriter.new
86
- output = StreamResult.new(sw)
87
- transformer.transform(input_file, output)
88
- sw.to_s
89
- end
79
+ def build_transformer(xslt, input_file, url = false)
80
+ factory = TransformerFactory.newInstance(TRANSFORMER_FACTORY_IMPL, nil)
81
+ factory.uri_resolver = HdsUrlResolver.new(@schematron_file) if url
82
+ transformer = factory.new_transformer(StreamSource.new(xslt))
83
+ sw = StringWriter.new
84
+ output = StreamResult.new(sw)
85
+ transformer.transform(input_file, output)
86
+ sw.to_s
90
87
  end
91
88
  end
92
89
  end
90
+ end