dcc 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (336) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +16 -0
  3. data/.rspec +3 -0
  4. data/.rubocop.yml +16 -0
  5. data/.rubocop_todo.yml +10 -0
  6. data/CHANGELOG.adoc +54 -0
  7. data/CONTRIBUTING.adoc +64 -0
  8. data/Gemfile +25 -0
  9. data/LICENSE +21 -0
  10. data/README.adoc +69 -0
  11. data/Rakefile +13 -0
  12. data/dcc.gemspec +41 -0
  13. data/docs/cli-reference.adoc +67 -0
  14. data/docs/getting-started.adoc +117 -0
  15. data/docs/schema-versions.adoc +65 -0
  16. data/examples/build_minimal_dcc.rb +23 -0
  17. data/examples/convert_all_formats.rb +19 -0
  18. data/examples/parse_and_inspect.rb +19 -0
  19. data/examples/quantity_math_demo.rb +32 -0
  20. data/examples/validate_full.rb +27 -0
  21. data/exe/dcc +7 -0
  22. data/lib/dcc/base/administrative_data.rb +43 -0
  23. data/lib/dcc/base/byte_data.rb +36 -0
  24. data/lib/dcc/base/calibration_laboratory.rb +29 -0
  25. data/lib/dcc/base/comment.rb +23 -0
  26. data/lib/dcc/base/condition.rb +34 -0
  27. data/lib/dcc/base/contact.rb +38 -0
  28. data/lib/dcc/base/contact_not_strict.rb +38 -0
  29. data/lib/dcc/base/core_data.rb +44 -0
  30. data/lib/dcc/base/data.rb +42 -0
  31. data/lib/dcc/base/digital_calibration_certificate.rb +32 -0
  32. data/lib/dcc/base/equipment_class.rb +24 -0
  33. data/lib/dcc/base/formula.rb +33 -0
  34. data/lib/dcc/base/hash_type.rb +34 -0
  35. data/lib/dcc/base/identification.rb +27 -0
  36. data/lib/dcc/base/identifications.rb +27 -0
  37. data/lib/dcc/base/influence_condition.rb +16 -0
  38. data/lib/dcc/base/influence_condition_list.rb +40 -0
  39. data/lib/dcc/base/item.rb +38 -0
  40. data/lib/dcc/base/item_list.rb +38 -0
  41. data/lib/dcc/base/list.rb +44 -0
  42. data/lib/dcc/base/location.rb +36 -0
  43. data/lib/dcc/base/measurement_meta_data.rb +15 -0
  44. data/lib/dcc/base/measurement_meta_data_list.rb +27 -0
  45. data/lib/dcc/base/measurement_result.rb +41 -0
  46. data/lib/dcc/base/measurement_result_list.rb +28 -0
  47. data/lib/dcc/base/measuring_equipment.rb +41 -0
  48. data/lib/dcc/base/measuring_equipment_list.rb +38 -0
  49. data/lib/dcc/base/quantity.rb +61 -0
  50. data/lib/dcc/base/ref_type_definition.rb +36 -0
  51. data/lib/dcc/base/ref_type_definition_list.rb +21 -0
  52. data/lib/dcc/base/resp_person.rb +38 -0
  53. data/lib/dcc/base/resp_person_list.rb +27 -0
  54. data/lib/dcc/base/result.rb +31 -0
  55. data/lib/dcc/base/result_list.rb +27 -0
  56. data/lib/dcc/base/rich_content.rb +34 -0
  57. data/lib/dcc/base/software.rb +35 -0
  58. data/lib/dcc/base/software_list.rb +27 -0
  59. data/lib/dcc/base/statement.rb +50 -0
  60. data/lib/dcc/base/statement_list.rb +27 -0
  61. data/lib/dcc/base/string_with_lang.rb +24 -0
  62. data/lib/dcc/base/text.rb +33 -0
  63. data/lib/dcc/base/used_method.rb +31 -0
  64. data/lib/dcc/base/used_method_list.rb +27 -0
  65. data/lib/dcc/base/xml_blob.rb +20 -0
  66. data/lib/dcc/base.rb +58 -0
  67. data/lib/dcc/builder/dsl.rb +10 -0
  68. data/lib/dcc/builder/session.rb +170 -0
  69. data/lib/dcc/builder.rb +34 -0
  70. data/lib/dcc/cli/cli.rb +149 -0
  71. data/lib/dcc/cli/formatters.rb +77 -0
  72. data/lib/dcc/cli.rb +7 -0
  73. data/lib/dcc/common_elements.rb +20 -0
  74. data/lib/dcc/context_configuration.rb +167 -0
  75. data/lib/dcc/context_options.rb +47 -0
  76. data/lib/dcc/convert/csv.rb +129 -0
  77. data/lib/dcc/convert/html.rb +127 -0
  78. data/lib/dcc/convert/json.rb +62 -0
  79. data/lib/dcc/convert/result.rb +51 -0
  80. data/lib/dcc/convert/yaml.rb +21 -0
  81. data/lib/dcc/convert.rb +14 -0
  82. data/lib/dcc/diff/change.rb +21 -0
  83. data/lib/dcc/diff/result.rb +58 -0
  84. data/lib/dcc/diff.rb +83 -0
  85. data/lib/dcc/error.rb +54 -0
  86. data/lib/dcc/extract/file.rb +143 -0
  87. data/lib/dcc/extract/ring.rb +28 -0
  88. data/lib/dcc/extract.rb +9 -0
  89. data/lib/dcc/i18n/text_lookup.rb +67 -0
  90. data/lib/dcc/i18n.rb +12 -0
  91. data/lib/dcc/inspect/report.rb +139 -0
  92. data/lib/dcc/inspect.rb +9 -0
  93. data/lib/dcc/migrate.rb +41 -0
  94. data/lib/dcc/namespace/dcc.rb +30 -0
  95. data/lib/dcc/namespace/dsig.rb +15 -0
  96. data/lib/dcc/namespace/math_ml.rb +14 -0
  97. data/lib/dcc/namespace/qudt.rb +15 -0
  98. data/lib/dcc/namespace/si.rb +26 -0
  99. data/lib/dcc/namespace.rb +18 -0
  100. data/lib/dcc/plugin/registry.rb +45 -0
  101. data/lib/dcc/plugin.rb +42 -0
  102. data/lib/dcc/quantity_format/formatter.rb +90 -0
  103. data/lib/dcc/quantity_format.rb +10 -0
  104. data/lib/dcc/quantity_math/quantity.rb +46 -0
  105. data/lib/dcc/quantity_math/real.rb +116 -0
  106. data/lib/dcc/quantity_math.rb +14 -0
  107. data/lib/dcc/schema/resources/dcc/dsi/v1.0.1.xsd +415 -0
  108. data/lib/dcc/schema/resources/dcc/dsi/v1.3.0.xsd +814 -0
  109. data/lib/dcc/schema/resources/dcc/dsi/v1.3.1.xsd +814 -0
  110. data/lib/dcc/schema/resources/dcc/dsi/v2.0.0.xsd +869 -0
  111. data/lib/dcc/schema/resources/dcc/dsi/v2.1.0.xsd +1031 -0
  112. data/lib/dcc/schema/resources/dcc/dsi/v2.2.1.xsd +1574 -0
  113. data/lib/dcc/schema/resources/dcc/qudt/v2.2.1.xsd +44 -0
  114. data/lib/dcc/schema/resources/dcc/v2.1.0/dcc.xsd +650 -0
  115. data/lib/dcc/schema/resources/dcc/v2.1.1/dcc.xsd +669 -0
  116. data/lib/dcc/schema/resources/dcc/v2.2.0/dcc.xsd +730 -0
  117. data/lib/dcc/schema/resources/dcc/v2.3.0/dcc.xsd +708 -0
  118. data/lib/dcc/schema/resources/dcc/v2.4.0/dcc.xsd +725 -0
  119. data/lib/dcc/schema/resources/dcc/v3.0.0/dcc.xsd +749 -0
  120. data/lib/dcc/schema/resources/dcc/v3.1.0/dcc.xsd +843 -0
  121. data/lib/dcc/schema/resources/dcc/v3.1.1/dcc.xsd +843 -0
  122. data/lib/dcc/schema/resources/dcc/v3.1.2/dcc.xsd +844 -0
  123. data/lib/dcc/schema/resources/dcc/v3.2.0/dcc.xsd +940 -0
  124. data/lib/dcc/schema/resources/dcc/v3.2.1/dcc.xsd +963 -0
  125. data/lib/dcc/schema/resources/dcc/v3.3.0/dcc.xsd +1008 -0
  126. data/lib/dcc/schema/resources/dcc/xmldsig-core-schema.xsd +312 -0
  127. data/lib/dcc/schema/resources/dsi/qudt/v2.2.1.xsd +44 -0
  128. data/lib/dcc/schema/resources/dsi/v1.0.1/SI_Format.xsd +415 -0
  129. data/lib/dcc/schema/resources/dsi/v1.3.0/SI_Format.xsd +814 -0
  130. data/lib/dcc/schema/resources/dsi/v1.3.1/SI_Format.xsd +814 -0
  131. data/lib/dcc/schema/resources/dsi/v2.0.0/SI_Format.xsd +869 -0
  132. data/lib/dcc/schema/resources/dsi/v2.1.0/SI_Format.xsd +1031 -0
  133. data/lib/dcc/schema/resources/dsi/v2.2.1/SI_Format.xsd +1574 -0
  134. data/lib/dcc/schema/resources/qudt/qudt.xsd +44 -0
  135. data/lib/dcc/schema/resources/schematron/dcc.sch +523 -0
  136. data/lib/dcc/schema/resources/schematron/dcc.svrl +2302 -0
  137. data/lib/dcc/schema/resources/xmldsig/xmldsig-core-schema.xsd +312 -0
  138. data/lib/dcc/schema/version.rb +94 -0
  139. data/lib/dcc/schema.rb +35 -0
  140. data/lib/dcc/server/app.rb +133 -0
  141. data/lib/dcc/server/storage/entry.rb +15 -0
  142. data/lib/dcc/server/storage/memory.rb +57 -0
  143. data/lib/dcc/server/storage.rb +13 -0
  144. data/lib/dcc/server.rb +33 -0
  145. data/lib/dcc/si/base/complex.rb +43 -0
  146. data/lib/dcc/si/base/complex_list_xml_list.rb +39 -0
  147. data/lib/dcc/si/base/constant.rb +39 -0
  148. data/lib/dcc/si/base/coverage_interval_mu.rb +31 -0
  149. data/lib/dcc/si/base/expanded_mu.rb +29 -0
  150. data/lib/dcc/si/base/expanded_unc_xml_list.rb +30 -0
  151. data/lib/dcc/si/base/hybrid.rb +42 -0
  152. data/lib/dcc/si/base/measurement_uncertainty_univariate.rb +28 -0
  153. data/lib/dcc/si/base/real.rb +48 -0
  154. data/lib/dcc/si/base/real_list_xml_list.rb +45 -0
  155. data/lib/dcc/si/base/si_list.rb +37 -0
  156. data/lib/dcc/si/base/standard_mu.rb +25 -0
  157. data/lib/dcc/si/base.rb +24 -0
  158. data/lib/dcc/si/common_elements.rb +13 -0
  159. data/lib/dcc/si/namespace.rb +7 -0
  160. data/lib/dcc/si/v1/common_elements.rb +13 -0
  161. data/lib/dcc/si/v1/complex.rb +8 -0
  162. data/lib/dcc/si/v1/complex_list_xml_list.rb +8 -0
  163. data/lib/dcc/si/v1/configuration.rb +13 -0
  164. data/lib/dcc/si/v1/constant.rb +8 -0
  165. data/lib/dcc/si/v1/coverage_interval_mu.rb +8 -0
  166. data/lib/dcc/si/v1/expanded_mu.rb +8 -0
  167. data/lib/dcc/si/v1/expanded_unc.rb +23 -0
  168. data/lib/dcc/si/v1/expanded_unc_xml_list.rb +8 -0
  169. data/lib/dcc/si/v1/hybrid.rb +8 -0
  170. data/lib/dcc/si/v1/measurement_uncertainty_univariate.rb +8 -0
  171. data/lib/dcc/si/v1/namespace.rb +9 -0
  172. data/lib/dcc/si/v1/real.rb +8 -0
  173. data/lib/dcc/si/v1/real_list_xml_list.rb +8 -0
  174. data/lib/dcc/si/v1/si_list.rb +8 -0
  175. data/lib/dcc/si/v1/standard_mu.rb +8 -0
  176. data/lib/dcc/si/v1.rb +46 -0
  177. data/lib/dcc/si/v2/common_elements.rb +13 -0
  178. data/lib/dcc/si/v2/complex.rb +8 -0
  179. data/lib/dcc/si/v2/complex_list_xml_list.rb +8 -0
  180. data/lib/dcc/si/v2/configuration.rb +13 -0
  181. data/lib/dcc/si/v2/constant.rb +8 -0
  182. data/lib/dcc/si/v2/coverage_interval_mu.rb +8 -0
  183. data/lib/dcc/si/v2/coverage_interval_mu_xml_list.rb +24 -0
  184. data/lib/dcc/si/v2/expanded_mu.rb +8 -0
  185. data/lib/dcc/si/v2/expanded_mu_xml_list.rb +22 -0
  186. data/lib/dcc/si/v2/expanded_unc_xml_list.rb +8 -0
  187. data/lib/dcc/si/v2/hybrid.rb +8 -0
  188. data/lib/dcc/si/v2/measurement_uncertainty_univariate.rb +8 -0
  189. data/lib/dcc/si/v2/namespace.rb +9 -0
  190. data/lib/dcc/si/v2/real.rb +8 -0
  191. data/lib/dcc/si/v2/real_list_xml_list.rb +8 -0
  192. data/lib/dcc/si/v2/si_list.rb +8 -0
  193. data/lib/dcc/si/v2/standard_mu.rb +8 -0
  194. data/lib/dcc/si/v2/standard_mu_xml_list.rb +18 -0
  195. data/lib/dcc/si/v2.rb +49 -0
  196. data/lib/dcc/si.rb +34 -0
  197. data/lib/dcc/signature/result.rb +43 -0
  198. data/lib/dcc/signature/signer.rb +36 -0
  199. data/lib/dcc/signature/verifier.rb +41 -0
  200. data/lib/dcc/signature.rb +13 -0
  201. data/lib/dcc/transform/result.rb +24 -0
  202. data/lib/dcc/transform/xslt.rb +72 -0
  203. data/lib/dcc/transform.rb +12 -0
  204. data/lib/dcc/type/base64_binary.rb +55 -0
  205. data/lib/dcc/type/decimal_xml_list.rb +47 -0
  206. data/lib/dcc/type/iso_country_code.rb +23 -0
  207. data/lib/dcc/type/iso_language_code.rb +23 -0
  208. data/lib/dcc/type/schema_version.rb +37 -0
  209. data/lib/dcc/type/si_unit.rb +26 -0
  210. data/lib/dcc/type.rb +16 -0
  211. data/lib/dcc/type_guards.rb +140 -0
  212. data/lib/dcc/v2/administrative_data.rb +8 -0
  213. data/lib/dcc/v2/byte_data.rb +8 -0
  214. data/lib/dcc/v2/calibration_laboratory.rb +8 -0
  215. data/lib/dcc/v2/comment.rb +8 -0
  216. data/lib/dcc/v2/common_elements.rb +13 -0
  217. data/lib/dcc/v2/condition.rb +8 -0
  218. data/lib/dcc/v2/configuration.rb +17 -0
  219. data/lib/dcc/v2/contact.rb +8 -0
  220. data/lib/dcc/v2/contact_not_strict.rb +8 -0
  221. data/lib/dcc/v2/core_data.rb +8 -0
  222. data/lib/dcc/v2/data.rb +8 -0
  223. data/lib/dcc/v2/digital_calibration_certificate.rb +8 -0
  224. data/lib/dcc/v2/equipment_class.rb +8 -0
  225. data/lib/dcc/v2/formula.rb +8 -0
  226. data/lib/dcc/v2/hash_type.rb +8 -0
  227. data/lib/dcc/v2/identification.rb +8 -0
  228. data/lib/dcc/v2/identifications.rb +8 -0
  229. data/lib/dcc/v2/influence_condition.rb +7 -0
  230. data/lib/dcc/v2/influence_condition_list.rb +8 -0
  231. data/lib/dcc/v2/item.rb +8 -0
  232. data/lib/dcc/v2/item_list.rb +8 -0
  233. data/lib/dcc/v2/list.rb +8 -0
  234. data/lib/dcc/v2/location.rb +8 -0
  235. data/lib/dcc/v2/measurement_meta_data.rb +8 -0
  236. data/lib/dcc/v2/measurement_meta_data_list.rb +8 -0
  237. data/lib/dcc/v2/measurement_result.rb +8 -0
  238. data/lib/dcc/v2/measurement_result_list.rb +8 -0
  239. data/lib/dcc/v2/measuring_equipment.rb +8 -0
  240. data/lib/dcc/v2/measuring_equipment_list.rb +8 -0
  241. data/lib/dcc/v2/namespace.rb +8 -0
  242. data/lib/dcc/v2/quantity.rb +49 -0
  243. data/lib/dcc/v2/ref_type_definition.rb +11 -0
  244. data/lib/dcc/v2/ref_type_definition_list.rb +15 -0
  245. data/lib/dcc/v2/resp_person.rb +8 -0
  246. data/lib/dcc/v2/resp_person_list.rb +8 -0
  247. data/lib/dcc/v2/result.rb +8 -0
  248. data/lib/dcc/v2/result_list.rb +8 -0
  249. data/lib/dcc/v2/rich_content.rb +12 -0
  250. data/lib/dcc/v2/software.rb +8 -0
  251. data/lib/dcc/v2/software_list.rb +8 -0
  252. data/lib/dcc/v2/statement.rb +8 -0
  253. data/lib/dcc/v2/statement_list.rb +8 -0
  254. data/lib/dcc/v2/string_with_lang.rb +8 -0
  255. data/lib/dcc/v2/text.rb +8 -0
  256. data/lib/dcc/v2/used_method.rb +8 -0
  257. data/lib/dcc/v2/used_method_list.rb +8 -0
  258. data/lib/dcc/v2/xml_blob.rb +8 -0
  259. data/lib/dcc/v2.rb +84 -0
  260. data/lib/dcc/v3/administrative_data.rb +8 -0
  261. data/lib/dcc/v3/byte_data.rb +8 -0
  262. data/lib/dcc/v3/calibration_laboratory.rb +8 -0
  263. data/lib/dcc/v3/comment.rb +8 -0
  264. data/lib/dcc/v3/common_elements.rb +11 -0
  265. data/lib/dcc/v3/condition.rb +8 -0
  266. data/lib/dcc/v3/configuration.rb +17 -0
  267. data/lib/dcc/v3/contact.rb +8 -0
  268. data/lib/dcc/v3/contact_not_strict.rb +8 -0
  269. data/lib/dcc/v3/core_data.rb +8 -0
  270. data/lib/dcc/v3/data.rb +8 -0
  271. data/lib/dcc/v3/digital_calibration_certificate.rb +8 -0
  272. data/lib/dcc/v3/equipment_class.rb +8 -0
  273. data/lib/dcc/v3/formula.rb +8 -0
  274. data/lib/dcc/v3/hash_type.rb +8 -0
  275. data/lib/dcc/v3/identification.rb +8 -0
  276. data/lib/dcc/v3/identifications.rb +8 -0
  277. data/lib/dcc/v3/influence_condition.rb +7 -0
  278. data/lib/dcc/v3/influence_condition_list.rb +8 -0
  279. data/lib/dcc/v3/item.rb +8 -0
  280. data/lib/dcc/v3/item_list.rb +8 -0
  281. data/lib/dcc/v3/list.rb +8 -0
  282. data/lib/dcc/v3/location.rb +8 -0
  283. data/lib/dcc/v3/measurement_meta_data.rb +8 -0
  284. data/lib/dcc/v3/measurement_meta_data_list.rb +8 -0
  285. data/lib/dcc/v3/measurement_result.rb +8 -0
  286. data/lib/dcc/v3/measurement_result_list.rb +8 -0
  287. data/lib/dcc/v3/measuring_equipment.rb +8 -0
  288. data/lib/dcc/v3/measuring_equipment_list.rb +8 -0
  289. data/lib/dcc/v3/namespace.rb +7 -0
  290. data/lib/dcc/v3/quantity.rb +49 -0
  291. data/lib/dcc/v3/ref_type_definition.rb +8 -0
  292. data/lib/dcc/v3/ref_type_definition_list.rb +8 -0
  293. data/lib/dcc/v3/resp_person.rb +8 -0
  294. data/lib/dcc/v3/resp_person_list.rb +8 -0
  295. data/lib/dcc/v3/result.rb +8 -0
  296. data/lib/dcc/v3/result_list.rb +8 -0
  297. data/lib/dcc/v3/rich_content.rb +8 -0
  298. data/lib/dcc/v3/software.rb +8 -0
  299. data/lib/dcc/v3/software_list.rb +8 -0
  300. data/lib/dcc/v3/statement.rb +8 -0
  301. data/lib/dcc/v3/statement_list.rb +8 -0
  302. data/lib/dcc/v3/string_with_lang.rb +8 -0
  303. data/lib/dcc/v3/text.rb +8 -0
  304. data/lib/dcc/v3/used_method.rb +8 -0
  305. data/lib/dcc/v3/used_method_list.rb +8 -0
  306. data/lib/dcc/v3/xml_blob.rb +8 -0
  307. data/lib/dcc/v3.rb +111 -0
  308. data/lib/dcc/validate/business_rules.rb +121 -0
  309. data/lib/dcc/validate/issue.rb +56 -0
  310. data/lib/dcc/validate/result.rb +105 -0
  311. data/lib/dcc/validate/schematron/profile.rb +64 -0
  312. data/lib/dcc/validate/schematron/rule.rb +49 -0
  313. data/lib/dcc/validate/schematron/rules/administrative_data_completeness.rb +42 -0
  314. data/lib/dcc/validate/schematron/rules/base.rb +15 -0
  315. data/lib/dcc/validate/schematron/rules/date_range_check.rb +41 -0
  316. data/lib/dcc/validate/schematron/rules/id_refid_linking.rb +61 -0
  317. data/lib/dcc/validate/schematron/rules/influence_conditions_placement.rb +58 -0
  318. data/lib/dcc/validate/schematron/rules/iso_code_validation.rb +53 -0
  319. data/lib/dcc/validate/schematron/rules/language_code_dedup.rb +39 -0
  320. data/lib/dcc/validate/schematron/rules/non_si_declaration.rb +82 -0
  321. data/lib/dcc/validate/schematron/rules/release_format_check.rb +44 -0
  322. data/lib/dcc/validate/schematron/rules/schema_version_check.rb +31 -0
  323. data/lib/dcc/validate/schematron/rules/uncertainty_consistency.rb +58 -0
  324. data/lib/dcc/validate/schematron/rules/unit_format_check.rb +74 -0
  325. data/lib/dcc/validate/schematron/rules/used_methods_placement.rb +65 -0
  326. data/lib/dcc/validate/schematron/rules/used_software_placement.rb +43 -0
  327. data/lib/dcc/validate/schematron/rules/xml_list_spacing.rb +42 -0
  328. data/lib/dcc/validate/schematron/rules.rb +30 -0
  329. data/lib/dcc/validate/schematron.rb +22 -0
  330. data/lib/dcc/validate/severity.rb +39 -0
  331. data/lib/dcc/validate/xsd.rb +97 -0
  332. data/lib/dcc/validate.rb +15 -0
  333. data/lib/dcc/version.rb +5 -0
  334. data/lib/dcc/versioned_parser.rb +48 -0
  335. data/lib/dcc.rb +141 -0
  336. metadata +425 -0
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+
4
+ module Dcc
5
+ module Validate
6
+ module Schematron
7
+ module Rules
8
+ # `dcc:usedMethods` must appear somewhere (global, list, or quantity
9
+ # level). Error if missing everywhere.
10
+ class UsedMethodsPlacement < Base
11
+ def check_on(dcc)
12
+ issues = []
13
+ return issues unless Dcc::TypeGuards.has_attribute?(dcc, :measurement_results)
14
+
15
+ mr_list = safe_attr(dcc.measurement_results, :measurement_result)
16
+ return issues if mr_list.empty?
17
+
18
+ any_used = mr_list.any? do |mr|
19
+ Dcc::TypeGuards.has_attribute?(mr, :used_methods) && !mr.used_methods.nil? ||
20
+ mr_has_descendant?(mr, :used_methods)
21
+ end
22
+
23
+ issues << issue(
24
+ severity: :error,
25
+ message: "dcc:usedMethods is missing in every measurementResult and its descendants",
26
+ ) unless any_used
27
+
28
+ issues
29
+ end
30
+
31
+ private
32
+
33
+ def mr_has_descendant?(mr, attr_name)
34
+ return false unless Dcc::TypeGuards.has_attribute?(mr, :results) && mr.results
35
+
36
+ result = safe_attr(mr.results, :result)
37
+ result.any? { |r| descendant_has?(r, attr_name) }
38
+ end
39
+
40
+ def descendant_has?(node, attr_name)
41
+ return false unless node.is_a?(::Lutaml::Model::Serializable)
42
+
43
+ if Dcc::TypeGuards.has_attribute?(node, attr_name) && !node.public_send(attr_name).nil?
44
+ return true
45
+ end
46
+
47
+ node.class.attributes.each_key do |attr|
48
+ value = node.public_send(attr)
49
+ next unless value.is_a?(::Array) || value.is_a?(::Lutaml::Model::Serializable)
50
+
51
+ Array(value).any? { |child| descendant_has?(child, attr_name) }
52
+ end
53
+ false
54
+ end
55
+
56
+ def safe_attr(parent, attr)
57
+ return [] unless parent && parent.is_a?(::Lutaml::Model::Serializable) && parent.class.attributes.key?(attr)
58
+
59
+ Array(parent.public_send(attr))
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+
4
+ module Dcc
5
+ module Validate
6
+ module Schematron
7
+ module Rules
8
+ # `dcc:usedSoftware` is recommended but only generates a warning.
9
+ class UsedSoftwarePlacement < Base
10
+ def severity
11
+ ::Dcc::Validate::Severity::WARNING
12
+ end
13
+
14
+ def check_on(dcc)
15
+ return [] unless Dcc::TypeGuards.has_attribute?(dcc, :measurement_results)
16
+
17
+ mr_list = safe_attr(dcc.measurement_results, :measurement_result)
18
+ return [] if mr_list.empty?
19
+
20
+ present = mr_list.any? do |mr|
21
+ Dcc::TypeGuards.has_attribute?(mr, :used_software) && !mr.used_software.nil?
22
+ end
23
+
24
+ present ? [] : [
25
+ issue(
26
+ severity: :warning,
27
+ message: "dcc:usedSoftware is missing on every measurementResult",
28
+ ),
29
+ ]
30
+ end
31
+
32
+ private
33
+
34
+ def safe_attr(parent, attr)
35
+ return [] unless parent && parent.is_a?(::Lutaml::Model::Serializable) && parent.class.attributes.key?(attr)
36
+
37
+ Array(parent.public_send(attr))
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+
4
+ module Dcc
5
+ module Validate
6
+ module Schematron
7
+ module Rules
8
+ # Validates spacing in `*XMLList` attributes and elements: single
9
+ # space between tokens, no leading/trailing space.
10
+ class XmlListSpacing < Base
11
+ def check_on(dcc)
12
+ issues = []
13
+ check_string_list_attribute(dcc, issues, :ref_type, "refType")
14
+ issues
15
+ end
16
+
17
+ private
18
+
19
+ def check_string_list_attribute(node, issues, attr, label)
20
+ return unless node.is_a?(::Lutaml::Model::Serializable)
21
+
22
+ if Dcc::TypeGuards.has_attribute?(node, attr)
23
+ value = node.public_send(attr)
24
+ value = value.to_s if value
25
+ if value && !value.empty? && (value.start_with?(" ") || value.end_with?(" ") || value.match?(/\s\s/))
26
+ issues << issue(
27
+ severity: :error,
28
+ message: "#{label} has invalid spacing: '#{value}'",
29
+ )
30
+ end
31
+ end
32
+
33
+ node.class.attributes.each_key do |a|
34
+ v = node.public_send(a)
35
+ Array(v).each { |child| check_string_list_attribute(child, issues, attr, label) }
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ # `Dcc::Validate::Schematron::Rules` hosts the 14 pure-Ruby PTB Schematron
4
+ # rule classes. Each rule subclasses `Base` and overrides `#check_on(dcc)`.
5
+ #
6
+ # The parent module autoloads every rule lazily — no `require_relative`
7
+ # is used anywhere in this directory (project rule).
8
+ module Dcc
9
+ module Validate
10
+ module Schematron
11
+ module Rules
12
+ autoload :Base, "dcc/validate/schematron/rules/base"
13
+ autoload :UsedMethodsPlacement, "dcc/validate/schematron/rules/used_methods_placement"
14
+ autoload :UsedSoftwarePlacement, "dcc/validate/schematron/rules/used_software_placement"
15
+ autoload :InfluenceConditionsPlacement, "dcc/validate/schematron/rules/influence_conditions_placement"
16
+ autoload :SchemaVersionCheck, "dcc/validate/schematron/rules/schema_version_check"
17
+ autoload :IdRefIdLinking, "dcc/validate/schematron/rules/id_refid_linking"
18
+ autoload :IsoCodeValidation, "dcc/validate/schematron/rules/iso_code_validation"
19
+ autoload :DateRangeCheck, "dcc/validate/schematron/rules/date_range_check"
20
+ autoload :ReleaseFormatCheck, "dcc/validate/schematron/rules/release_format_check"
21
+ autoload :UncertaintyConsistency, "dcc/validate/schematron/rules/uncertainty_consistency"
22
+ autoload :UnitFormatCheck, "dcc/validate/schematron/rules/unit_format_check"
23
+ autoload :NonSiDeclaration, "dcc/validate/schematron/rules/non_si_declaration"
24
+ autoload :LanguageCodeDedup, "dcc/validate/schematron/rules/language_code_dedup"
25
+ autoload :XmlListSpacing, "dcc/validate/schematron/rules/xml_list_spacing"
26
+ autoload :AdministrativeDataCompleteness, "dcc/validate/schematron/rules/administrative_data_completeness"
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ # `Dcc::Validate::Schematron` is the public Schematron validator. Calling
4
+ # `Dcc::Validate::Schematron.call(dcc)` runs the entire PTB rule profile
5
+ # and returns a `Dcc::Validate::Result`.
6
+ module Dcc
7
+ module Validate
8
+ module Schematron
9
+ autoload :Rule, "dcc/validate/schematron/rule"
10
+ autoload :Profile, "dcc/validate/schematron/profile"
11
+ autoload :Rules, "dcc/validate/schematron/rules"
12
+
13
+ class << self
14
+ # @param dcc [Lutaml::Model::Serializable]
15
+ # @return [Dcc::Validate::Result]
16
+ def call(dcc)
17
+ Profile.call(dcc)
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Dcc
4
+ module Validate
5
+ # Severity levels for validation issues. Matches PTB's Schematron roles
6
+ # plus the standard error/warning/info distinction.
7
+ module Severity
8
+ ERROR = :error
9
+ WARNING = :warning
10
+ INFO = :info
11
+ UNKNOWN = :unknown
12
+
13
+ ALL = [ERROR, WARNING, INFO, UNKNOWN].freeze
14
+
15
+ class << self
16
+ # Coerce a string/symbol to a canonical severity symbol.
17
+ # @return [Symbol]
18
+ def normalize(value)
19
+ return value if ALL.include?(value)
20
+ return UNKNOWN unless value
21
+
22
+ sym = value.to_s.downcase.sub(/\A(strict_)?/, "").to_sym
23
+ case sym
24
+ when :err then ERROR
25
+ when :warn then WARNING
26
+ when :information, :informational then INFO
27
+ else
28
+ ALL.include?(sym) ? sym : UNKNOWN
29
+ end
30
+ end
31
+
32
+ # @return [Boolean] whether issues at this severity fail validation.
33
+ def failing?(severity)
34
+ severity == ERROR
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,97 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "nokogiri"
4
+
5
+ module Dcc
6
+ module Validate
7
+ # `Dcc::Validate::Xsd` validates DCC XML against one of the 12 bundled DCC
8
+ # schema versions. Auto-detects the version from the `schemaVersion`
9
+ # attribute when `version:` is `:auto` or nil.
10
+ #
11
+ # @example
12
+ # result = Dcc::Validate::Xsd.call(File.read("certificate.xml"))
13
+ # result.ok? # => true
14
+ # result.errors # => []
15
+ #
16
+ # result = Dcc::Validate::Xsd.call(xml, version: "3.3.0")
17
+ # result = Dcc::Validate::Xsd.call(xml, version: :auto)
18
+ module Xsd
19
+ SCHEMA_CACHE = {} # rubocop:disable Style/MutableConstant -- populated at runtime
20
+
21
+ class << self
22
+ # @param xml [String, IO] DCC XML to validate.
23
+ # @param version [String, Symbol, nil] DCC version or `:auto` for
24
+ # auto-detection. Defaults to `:auto`.
25
+ # @return [Dcc::Validate::Result]
26
+ def call(xml, version: :auto)
27
+ resolved = ::Dcc::Schema::Version.resolve_dcc(version, xml: xml)
28
+ schema = schema_for(resolved)
29
+ doc = parse_xml(xml)
30
+ errors = schema.validate(doc).map { |e| issue_from_nokogiri(e, resolved) }
31
+
32
+ ::Dcc::Validate::Result.new(
33
+ issues: errors,
34
+ schema_version: resolved,
35
+ source: "xsd",
36
+ )
37
+ end
38
+
39
+ private
40
+
41
+ def schema_for(version)
42
+ SCHEMA_CACHE[version] ||= load_schema(version)
43
+ end
44
+
45
+ def load_schema(version)
46
+ xsd_path = ::Dcc::Schema.path("dcc/v#{version}/dcc.xsd")
47
+ resolved = resolve_imports(xsd_path)
48
+ ::Nokogiri::XML::Schema.new(resolved)
49
+ rescue ::Nokogiri::XML::SyntaxError => e
50
+ raise ::Dcc::SchemaError,
51
+ "Failed to load bundled DCC XSD v#{version}: #{e.message}",
52
+ e.backtrace
53
+ end
54
+
55
+ # Rewrite relative `schemaLocation` imports to absolute file:// URIs
56
+ # so libxml2 can resolve them regardless of the process CWD. Nokogiri
57
+ # does not expose a base-URI option for `Schema.new`.
58
+ def resolve_imports(xsd_path)
59
+ base_dir = ::File.dirname(xsd_path)
60
+ content = ::File.read(xsd_path)
61
+ content.gsub(/schemaLocation\s*=\s*"([^"]+)"/) do |_match|
62
+ relative = ::Regexp.last_match(1)
63
+ next %{schemaLocation="#{relative}"} if relative.start_with?("http", "file://")
64
+
65
+ absolute = ::File.expand_path(relative, base_dir)
66
+ %{schemaLocation="file://#{absolute}"}
67
+ end
68
+ end
69
+
70
+ def parse_xml(xml)
71
+ xml_string = ::Dcc.read_input(xml)
72
+ ::Nokogiri::XML(xml_string) { |c| c.strict }
73
+ end
74
+
75
+ def issue_from_nokogiri(nokogiri_error, version)
76
+ ::Dcc::Validate::Issue.build(
77
+ severity: classify(nokogiri_error),
78
+ message: nokogiri_error.message,
79
+ code: "dcc.xsd.#{version}",
80
+ line: nokogiri_error.line,
81
+ column: nokogiri_error.column,
82
+ source: "xsd",
83
+ )
84
+ end
85
+
86
+ def classify(nokogiri_error)
87
+ # libxml2 levels: 1 = WARNING, 2 = ERROR, 3 = FATAL.
88
+ case nokogiri_error.level
89
+ when 2, 3 then ::Dcc::Validate::Severity::ERROR
90
+ when 1 then ::Dcc::Validate::Severity::WARNING
91
+ else ::Dcc::Validate::Severity::INFO
92
+ end
93
+ end
94
+ end
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ # `Dcc::Validate` is the entry-point module for all validation services:
4
+ # XSD, Schematron, and Ruby-side business rules. Each service returns a
5
+ # `Dcc::Validate::Result` model that can be rendered as text, JSON, or YAML.
6
+ module Dcc
7
+ module Validate
8
+ autoload :Result, "dcc/validate/result"
9
+ autoload :Issue, "dcc/validate/issue"
10
+ autoload :Severity, "dcc/validate/severity"
11
+ autoload :Xsd, "dcc/validate/xsd"
12
+ autoload :Schematron, "dcc/validate/schematron"
13
+ autoload :BusinessRules, "dcc/validate/business_rules"
14
+ end
15
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Dcc
4
+ VERSION = "0.2.0"
5
+ end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ # `Dcc::VersionedParser` is mixed into each major-version module
4
+ # (`Dcc::V2`, `Dcc::V3`, `Dcc::Si::V1`, `Dcc::Si::V2`) to provide a uniform
5
+ # `.parse(input, context:, register:)` entry point that delegates to
6
+ # `Lutaml::Model::GlobalContext` for type resolution.
7
+ #
8
+ # The version module must:
9
+ # 1. `extend Dcc::VersionedParser`
10
+ # 2. Define `self::ROOT_ELEMENT_TAG` — the XML element name to resolve
11
+ # (e.g. `"digitalCalibrationCertificate"`).
12
+ # 3. Define `self::Configuration` — a module that extends
13
+ # `Dcc::ContextConfiguration` and declares `CONTEXT_ID`.
14
+ module Dcc
15
+ module VersionedParser
16
+ # Parse an XML document into the version's typed object tree.
17
+ #
18
+ # @param input [String, IO] XML string or readable IO.
19
+ # @param context [Symbol, String, nil] substitution context.
20
+ # @param register [Symbol, String, nil] deprecated alias for `context`.
21
+ # @return [Lutaml::Model::Serializable] parsed root element.
22
+ def parse(input, context: ::Dcc::UNSPECIFIED_CONTEXT, register: nil)
23
+ context_id = resolve_context_id(context, register)
24
+ root_class = Lutaml::Model::GlobalContext.resolve_type(
25
+ root_element_symbol,
26
+ context_id,
27
+ )
28
+
29
+ xml = ::Dcc.read_input(input)
30
+ root_class.from_xml(xml, register: context_id)
31
+ end
32
+
33
+ private
34
+
35
+ def resolve_context_id(context, register)
36
+ ::Dcc::ContextOptions.normalize_context_option(
37
+ context: context,
38
+ register: register,
39
+ default_context: self::Configuration.default_context_id,
40
+ warning_source: "#{name}.parse",
41
+ )
42
+ end
43
+
44
+ def root_element_symbol
45
+ self.const_get(:ROOT_ELEMENT_TAG).to_sym
46
+ end
47
+ end
48
+ end
data/lib/dcc.rb ADDED
@@ -0,0 +1,141 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "lutaml/model"
4
+ require "lutaml/xml"
5
+ require "mml"
6
+
7
+ Lutaml::Model::Config.xml_adapter_type = :nokogiri
8
+
9
+ # Top-level namespace for the dcc gem.
10
+ #
11
+ # Dcc provides a fully typed Ruby object model for PTB Digital Calibration
12
+ # Certificates (DCC) and D-SI quantity documents. See README.adoc for an
13
+ # overview and QUICKSTART.
14
+ module Dcc
15
+ SUPPORTED_VERSIONS = [2, 3].freeze
16
+
17
+ # Sentinel value used to distinguish "no context supplied" from "context
18
+ # supplied as nil". Defined eagerly so callers can reference it without
19
+ # triggering autoload.
20
+ UNSPECIFIED_CONTEXT = Object.new.freeze
21
+
22
+ autoload :VERSION, "dcc/version"
23
+ # Errors are eagerly required so `Dcc::ParseError` etc. are always
24
+ # available without triggering autoload on first reference.
25
+ require "dcc/error"
26
+
27
+ autoload :Namespace, "dcc/namespace"
28
+ autoload :Type, "dcc/type"
29
+ autoload :ContextConfiguration, "dcc/context_configuration"
30
+ autoload :ContextOptions, "dcc/context_options"
31
+ autoload :VersionedParser, "dcc/versioned_parser"
32
+ autoload :CommonElements, "dcc/common_elements"
33
+ autoload :Base, "dcc/base"
34
+ autoload :Schema, "dcc/schema"
35
+ autoload :Builder, "dcc/builder"
36
+ autoload :Convert, "dcc/convert"
37
+ autoload :Diff, "dcc/diff"
38
+ autoload :Extract, "dcc/extract"
39
+ autoload :Inspect, "dcc/inspect"
40
+ autoload :Migrate, "dcc/migrate"
41
+ autoload :QuantityFormat, "dcc/quantity_format"
42
+ autoload :QuantityMath, "dcc/quantity_math"
43
+ autoload :Signature, "dcc/signature"
44
+ autoload :Transform, "dcc/transform"
45
+ autoload :Validate, "dcc/validate"
46
+ autoload :Server, "dcc/server"
47
+ autoload :I18n, "dcc/i18n"
48
+ autoload :Plugin, "dcc/plugin"
49
+ autoload :TypeGuards, "dcc/type_guards"
50
+ autoload :Si, "dcc/si"
51
+
52
+ autoload :V2, "dcc/v2"
53
+ autoload :V3, "dcc/v3"
54
+
55
+ module Cli
56
+ autoload :Cli, "dcc/cli/cli"
57
+ end
58
+
59
+ class << self
60
+ # Parse a DCC XML document and return a typed object tree.
61
+ #
62
+ # @param input [String, IO] XML string or readable IO.
63
+ # @param version [Integer, nil] Major DCC version (2 or 3). Auto-detected
64
+ # from the `schemaVersion` attribute when nil.
65
+ # @param context [Symbol, nil] Substitution context (e.g. `:dcc_v3_4`).
66
+ # @param register [Symbol, nil] Alias for `context` (mml-compatible API).
67
+ # @return [Dcc::V2::DigitalCalibrationCertificate, Dcc::V3::DigitalCalibrationCertificate]
68
+ def parse(input, version: nil, context: nil, register: nil)
69
+ load_all!
70
+ resolved = version || detect_version(input)
71
+ parser_for(resolved).parse(input, context: context, register: register)
72
+ end
73
+
74
+ # Build a new DCC programmatically using the builder DSL.
75
+ # @yield [Dcc::Builder::Session] builder session
76
+ # @param version [Integer, nil] major DCC version (default: 3).
77
+ def build(version: 3, &block)
78
+ Builder.call(version: version, &block)
79
+ end
80
+
81
+ # Migrate a parsed DCC object from one schema version to another.
82
+ # @param dcc [Dcc::V2::DigitalCalibrationCertificate, Dcc::V3::DigitalCalibrationCertificate]
83
+ # @param from [String] source version, e.g. "2.3.0".
84
+ # @param to [String] target version, e.g. "3.3.0".
85
+ def migrate(dcc, from:, to:)
86
+ Migrate.call(dcc, from: from, to: to)
87
+ end
88
+
89
+ # Return the parser module for the given major version.
90
+ # @param version [Integer] 2 or 3.
91
+ # @return [Module] Dcc::V2 or Dcc::V3.
92
+ def parser_for(version)
93
+ case version.to_i
94
+ when 2 then V2
95
+ when 3 then V3
96
+ else
97
+ raise UnknownVersionError, "Unsupported DCC version: #{version.inspect} (expected 2 or 3)"
98
+ end
99
+ end
100
+
101
+ # Detect the DCC major version from an XML document's `schemaVersion` attribute.
102
+ # @param input [String, IO]
103
+ # @return [Integer] 2 or 3.
104
+ def detect_version(input)
105
+ str = read_input(input)
106
+ match = str.match(/schemaVersion\s*=\s*["'](\d+)\./)
107
+ major = match && match[1] ? match[1].to_i : 3
108
+ major == 2 ? 2 : 3
109
+ end
110
+
111
+ # Read an input that may be a String or an IO-like object.
112
+ # Uses explicit type checks per project rule (no `respond_to?`).
113
+ # @param input [String, IO, StringIO, File, Tempfile]
114
+ # @return [String]
115
+ def read_input(input)
116
+ return input.read if io_like?(input)
117
+
118
+ input.to_s
119
+ end
120
+
121
+ # @return [Boolean] whether the input is an IO-like stream we support.
122
+ def io_like?(input)
123
+ !!(input.is_a?(::IO) ||
124
+ input.is_a?(::StringIO) ||
125
+ input.is_a?(::File) ||
126
+ (defined?(::Tempfile) && input.is_a?(::Tempfile)))
127
+ end
128
+
129
+ # Eager-load every version context (DCC + D-SI) so symbol type
130
+ # references in the base modules resolve correctly. Called automatically
131
+ # on first `Dcc.parse`. Safe to call multiple times.
132
+ # @return [true]
133
+ def load_all!
134
+ V2.load_all!
135
+ V3.load_all!
136
+ Si::V1.load_all!
137
+ Si::V2.load_all!
138
+ true
139
+ end
140
+ end
141
+ end