# Re-derive Define-XML child order from the BUNDLED schemas. # # The version profiles in R/define_profile.R hard-code the xs:sequence of # every element artoo emits, because a data table is the only form the # emitter can check a builder against. A hard-coded copy of someone else's # schema goes stale silently, so this helper reads the sequences back out of # the XSDs that ship with the package and the test compares the two. # # Three schema facts make this more than an XPath: # # * Define-XML redefines the ODM foundation through xs:redefine, so an # element's real sequence is ODM's with the def: extension groups spliced # in at the position the ODM type refers to them. # * a redefined group opens with a self-reference to the ORIGINAL group, # which is empty in ODM; it is skipped rather than resolved. # * def: elements live in their own schema under a different complexType # naming convention, and refer to ODM children with an odm: prefix. .schema_dir <- function(version) { dir <- c("2.0" = "2.0.0", "2.1" = "2.1.0")[[version]] root <- system.file("extdata", dir, package = "artoo") if (!nzchar(root)) { root <- testthat::test_path("..", "..", "inst", "extdata", dir) } root } .xsd_kids <- function(node) { # Document order, elements and group references alike. hits <- xml2::xml_find_all( node, ".//*[local-name()='element' or local-name()='group']" ) vapply( hits, function(h) { ref <- xml2::xml_attr(h, "ref") nm <- xml2::xml_attr(h, "name") kind <- xml2::xml_name(h) paste0(kind, ":", if (is.na(ref)) nm else ref) }, character(1) ) } # XSD text with comments stripped. Deprecated attribute references are # COMMENTED OUT rather than removed (def:Label, def:DomainKeys, def:Rank in # 2.0), so a derivation that reads the raw text would report attributes no # document may use. .xsd_text <- function(path) { gsub("", "", paste(readLines(path, warn = FALSE), collapse = "\n")) } # The def:-namespaced attributes each element may carry, per version. Mirrors # the `def_attrs` table in R/define_profile.R. .dx_schema_def_attrs <- function(version) { dir <- .schema_dir(version) defdir <- file.path(dir, paste0("cdisc-define-", version)) out <- list() pull <- function(txt, pattern, prefix) { for (m in regmatches(txt, gregexpr(pattern, txt, perl = TRUE))[[1]]) { el <- sub(pattern, "\\1", m, perl = TRUE) # ATTRIBUTE references only. A bare ref="def:..." also matches the # element and group references these blocks are full of. refs <- regmatches( m, gregexpr('.*?', "" ) pull( .xsd_text(file.path(defdir, "define-ns.xsd")), '(?s).*?', "def:" ) # ARM declares its elements inline rather than as named complexTypes, and # arm:AnalysisDatasets is the one that borrows a def: attribute. pull( .xsd_text(file.path(dir, "cdisc-arm-1.0", "arm-ns.xsd")), '(?s).*?', "arm:" ) out } # The LOCAL (unprefixed) attributes each def: element declares. Used to pin # the claim that def:Origin/@Source is the only local attribute the two # versions disagree about -- the emitter's def: guard cannot see local ones. .dx_schema_local_attrs <- function(version) { dir <- .schema_dir(version) txt <- .xsd_text(file.path( dir, paste0("cdisc-define-", version), "define-ns.xsd" )) out <- list() pattern <- '(?s).*?' for (m in regmatches(txt, gregexpr(pattern, txt, perl = TRUE))[[1]]) { el <- sub(pattern, "\\1", m, perl = TRUE) nm <- regmatches(m, gregexpr('