diff --git a/R/options.R b/R/options.R index 4b1ad88..e26ccc9 100644 --- a/R/options.R +++ b/R/options.R @@ -173,7 +173,7 @@ analysisOptionsFromJASPfile <- function(file) { return(options) } -parsePreloadDataFromDescriptionQml <- function(analysisName) { +parseDescriptionQmlFromAnalysisName <- function(analysisName) { modulePath <- getModulePathFromRFunction(analysisName) if (isBinaryPackage(modulePath)) { @@ -188,23 +188,116 @@ parsePreloadDataFromDescriptionQml <- function(analysisName) { return(TRUE) } - # some more parsing of QML with regex, mostly to make Joris cry - raw <- trimws(readLines(pathToDescriptionQml)) - idx <- grep(sprintf("\\s*func\\s*:\\s*\"%s\"", analysisName), raw) + return(parseDescriptionQmlFromPath(pathToDescriptionQml)) +} - if (length(idx) == 0) { - warning("Analysis ", analysisName, " not found in Description.qml. Assuming the module preloads data.") - return(TRUE) +# some code to test the function below on all Description.qml files in jasp +# dirs <- list.dirs("~/github/jasp/jasp-desktop/Modules", recursive = FALSE) +# qmls <- file.path(dirs, "inst", "Description.qml") +# qmls <- Filter(file.exists, qmls) +# nms <- basename(dirname(dirname(qmls))) +# names(qmls) <- nms +# results <- vector("list", length(nms)) +# names(results) <- nms +# for (nm in nms) { +# cat(nm, "\n") +# results[[nm]] <- jaspTools:::parseDescriptionQmlFromPath(qmls[[nm]]) +# } +parseDescriptionQmlFromPath <- function(pathToDescriptionQml) { + + raw <- trimws(readLines(pathToDescriptionQml)) + raw <- raw[raw != ""] + # drop import statements + raw <- raw[!startsWith(raw, "import")] + # ensure that everything is on a newline + raw <- trimws(unlist(strsplit(raw, ";", fixed = TRUE), use.names = FALSE)) + # remove any whitespace + raw <- gsub("\\s", "", raw) + # transform "Description{}" to c("Description", "{", "}) + raw <- trimws(unlist(strsplit(raw, "(?=\\{)", perl = TRUE), use.names = FALSE)) + raw <- trimws(unlist(strsplit(raw, "(?=\\})", perl = TRUE), use.names = FALSE)) + # "qsTr(\"bla\")" -> "\"bla\"" + raw <- gsub('qsTr\\("(.*)"\\)', "\\1", raw) + + result <- list() + subResults <- NULL + subNames <- character(0L) + depth <- 0L + skipUntilClose <- FALSE + skipCount <- 0L + + groupsToskip <- c("GroupTitle", "Separator", "Timer") + for (i in seq_along(raw)) { + + if (!skipUntilClose) { + + hasColon <- grepl(":", raw[i], fixed = TRUE) + if (!hasColon && grepl("{", raw[i + 1], fixed = TRUE)) { + + if (any(vapply(groupsToskip, function(x) identical(raw[i], x), FUN.VALUE = logical(1L)))) { + skipUntilClose <- TRUE + next + } + + depth <- depth + 1L + subNames[[depth]] <- raw[i] + subResult <- list() + subResults[[depth]] <- list() + + } else if (hasColon) { + + match <- regexec("([^:]+):(.*)", raw[i]) + parts <- regmatches(raw[i], match)[[1]] + key <- parts[2] + value <- parts[3] + # remove quotes at the start and end of the string + value <- gsub("^[\"']|[\"']$", "", value) + + subResults[[depth]][[key]] <- switch( + value, + "false" = FALSE, + "true" = TRUE, + value + ) + + } else if (grepl("}", raw[i], fixed = TRUE)) { + + subName <- subNames[[depth]] + subResult <- subResults[[depth]] + if (!is.null(subResult) && length(subResult) > 0L) { + + # rather than "Analysis", use the name of the R function + if (subName == "Analysis") + subName <- subResult[["func"]] + + result[[subName]] <- subResult + + } + + result[[subName]] <- subResult + depth <- depth - 1L + } + + } else if (grepl("{" , raw[i], fixed = TRUE)) { + skipCount <- skipCount + 1L + } else if (grepl("}", raw[i], fixed = TRUE)) { + + skipCount <- skipCount - 1L + if (skipCount == 0) + skipUntilClose <- FALSE + + } } - previous_bracket <- grep("{", raw[1:idx], fixed = TRUE) - previous_bracket <- previous_bracket[length(previous_bracket)] - next_bracket <- grep("}", raw[idx:length(raw)], fixed = TRUE) - next_bracket <- next_bracket[1] + idx - idx_match <- grep("\\s*preloadData\\s*:\\s*(.*)[^;]{0,1}", raw[previous_bracket:next_bracket]) - result <- gsub("\\s*preloadData\\s*:\\s*(.*)[^;]{0,1}", "\\1", raw[previous_bracket:next_bracket][idx_match]) - preloadData <- identical(result, "true") + return(result) + +} + +parsePreloadDataFromDescriptionQml <- function(analysisName) { + + description <- parseDescriptionQmlFromAnalysisName(analysisName) + preloadData <- isTRUE(description[["preloadData"]]) || isTRUE(description[[analysisName]][["preloadData"]]) if (!preloadData) warning("Analysis ", analysisName, " does not preload data. Please update the code.")