cqm-validators 0.1.1 → 1.0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/.github/PULL_REQUEST_TEMPLATE.md +23 -0
  3. data/.gitignore +3 -1
  4. data/.overcommit.yml +18 -0
  5. data/.rubocop.yml +66 -0
  6. data/.simplecov +10 -0
  7. data/.travis.yml +21 -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 +21 -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 } +1559 -1964
  25. data/lib/schematron/qrda/{cat_1_r4 → cat_1_r5_1}/voc.xml +7 -2
  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 +86 -24
  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_r3_1/voc.xml +0 -1229
  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