# Copyright 2015-2017 Meik Michalke <meik.michalke@hhu.de>
#
# This file is part of the R package rkwarddev.
#
# rkwarddev is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# rkwarddev is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with rkwarddev. If not, see <http://www.gnu.org/licenses/>.
#' Generate script code from XML objects
#'
#' These methods try to translate plugin XML objects into \code{rkwarddev} function calls.
#'
#' They are intended to make it easier to translate previously manually maintained plugin code
#' into \code{rkwarddev} scripts. The generated output should not be used as-is, but restructured
#' properly into a useful script.
#'
#' You can either use a full XML document (read with \code{\link[XiMpLe:parseXMLTree]{parseXMLTree}})
#' or single (also nested) XiMpLe XML nodes. If you provide a character string, it is assumed to be
#' the full path to a document to be parsed with \code{parseXMLTree} and then analysed. Connections
#' are also accepted.
#'
#' @note The methods might fail, especially with highly complex plugins. Try to break these
#' into sensible chunks and try those speparately. Sometimes, slightly changing the input file
#' might also do the trick to get some usable results.
#'
#' @param obj Either a character vector (path to a plugin XML file to parse), a connection, an already
#' parsed XML tree (class \code{XiMpLe.doc}) or a single \code{XiMpLe.node} object.
#' @param prefix Character string, used as the prefix for the object names used in the script. Set to \code{""}
#' to disable the prefix.
#' @param indent Logical, whether the script code should be indented properly.
#' @param level Integer number, the starting leven of indentation.
#' @param drop.defaults Logical, whether to check for the default options in function calls. If the
#' parsed and translated XML code resulted in default options, they are omitted in the resulting script.
#' @param node.names Logical, whether the node names should become part of the generated R object names.
#' @param collapse Character string, used to collapse the parts of the generated R object names.
#' @param update A named list, previous result of a \code{plugin2script} call to be updated, e.\,.g., to add
#' the content of a help file to a previously scanned XML file.
#' @export
#' @docType methods
#' @return Either a character vector (if \code{obj} is a single XML node)
#' or a list of character vectors named \code{"logic"}, \code{"dialog"}, \code{"wizard"},
#' \code{"summary"}, \code{"usage"}, \code{"settings"}, \code{"related"}, \code{"technical"},
#' \code{"dependencies"}, \code{"dependency_check"}, \code{"about"}, \code{"require"},
#' \code{"components"}, and \code{"hierarchy"} (if \code{obj} is a full XML document).
#' @rdname XiMpLe-methods
#' @examples
#' \dontrun{
#' # full XML documents
#' rkwarddevScript <- plugin2script("~/RKWardPlugins/plugins/myPlugin.xml")
#' rkwarddevScript <- plugin2script("~/RKWardPlugins/plugins/myPlugin.rkh", update=rkwarddevScript)
#' rkwarddevScript <- plugin2script("~/RKWardPlugins/myPlugin.pluginmap", update=rkwarddevScript)
#' sapply(rkwarddevScript, cat)
#' }
#'
#' # single XML node
#' (test.checkboxes <- rk.XML.row(
#' rk.XML.col(
#' list(
#' rk.XML.cbox(label="foo", value="foo1", chk=TRUE),
#' rk.XML.cbox(label="bar", value="bar2")
#' )
#' )
#' ))
#' rkwarddevScript <- plugin2script(test.checkboxes)
#' # see the generated script code
#' cat(rkwarddevScript)
#'
#' # we can evaluate the generated code to check whether original
#' # XML and the newly generated one are identical
#' eval(parse(text=rkwarddevScript))
#' identical(row_clmndc1212, test.checkboxes)
setGeneric(
"plugin2script",
function(obj, prefix="", indent=TRUE, level=1, drop.defaults=TRUE, node.names=FALSE, collapse=".", update=NULL){
standardGeneric("plugin2script")
}
)
#' @export
#' @docType methods
#' @rdname XiMpLe-methods
#' @aliases plugin2script,XiMpLe.doc-method
#' @import XiMpLe
setMethod("plugin2script",
signature(obj="XiMpLe.doc"),
function(obj, prefix="", indent=TRUE, level=1, drop.defaults=TRUE, node.names=FALSE, collapse=".", update=NULL) {
objSections <- list(
## *.xml
logic=XMLScan(obj, "logic"),
dialog=XMLScan(obj, "dialog"),
wizard=XMLScan(obj, "wizard"),
## *.rkh
summary=XMLScan(obj, "summary"),
usage=XMLScan(obj, "usage"),
settings=XMLScan(obj, "settings"),
related=XMLScan(obj, "related"),
technical=XMLScan(obj, "technical"),
## *.pluginmap
dependencies=XMLScan(obj, "dependencies"),
dependency_check=XMLScan(obj, "dependency_check"),
about=XMLScan(obj, "about"),
require=XMLScan(obj, "require"),
components=XMLScan(obj, "components"),
hierarchy=XMLScan(obj, "hierarchy")
)
result <- lapply(
names(objSections),
function(thisSection){
currentSection <- objSections[[thisSection]]
previousSection <- update[[thisSection]]
if(is.null(currentSection)){
ifelse(
is.null(previousSection),
"",
previousSection
)
} else if(is.list(currentSection)){
paste0(
sapply(
currentSection,
p2s,
indent=indent,
level=level,
prefix=prefix,
drop.defaults=drop.defaults,
node.names=node.names,
collapse=collapse
),
collapse=paste0(paste0(rep(" ", level), collapse=""), "\n")
)
} else {
p2s(
node=currentSection,
indent=indent,
level=level,
prefix=prefix,
drop.defaults=drop.defaults,
node.names=node.names,
collapse=collapse
)
}
}
)
names(result) <- names(objSections)
return(result)
}
)
#' @export
#' @docType methods
#' @rdname XiMpLe-methods
#' @aliases plugin2script,XiMpLe.node-method
#' @import XiMpLe
setMethod("plugin2script",
signature(obj="XiMpLe.node"),
function(obj, prefix="", indent=TRUE, level=1, drop.defaults=TRUE, node.names=FALSE, collapse=".", update=NULL) {
return(p2s(node=obj, indent=indent, level=level, prefix=prefix, drop.defaults=drop.defaults, node.names=node.names, collapse=collapse))
}
)
#' @export
#' @docType methods
#' @rdname XiMpLe-methods
#' @aliases plugin2script,character-method
#' @import XiMpLe
setMethod("plugin2script",
signature(obj="character"),
function(obj, prefix="", indent=TRUE, level=1, drop.defaults=TRUE, node.names=FALSE, collapse=".", update=NULL) {
XML.tree <- parseXMLTree(obj)
return(
plugin2script(
obj=XML.tree,
prefix=prefix,
indent=indent,
level=level,
drop.defaults=drop.defaults,
node.names=node.names,
collapse=collapse,
update=update
)
)
}
)
# connection is a S3 class
setOldClass("connection")
#' @export
#' @docType methods
#' @rdname XiMpLe-methods
#' @aliases plugin2script,connection-method
#' @import XiMpLe
setMethod("plugin2script",
signature(obj="connection"),
function(obj, prefix="", indent=TRUE, level=1, drop.defaults=TRUE, node.names=FALSE, collapse=".", update=NULL) {
XML.tree <- parseXMLTree(obj)
return(
plugin2script(
obj=XML.tree,
prefix=prefix,
indent=indent,
level=level,
drop.defaults=drop.defaults,
node.names=node.names,
collapse=collapse,
update=update
)
)
}
)
## internal functions and objects
## function p2s.checkModifiers()
# takes an attribute value (character string) and checks whether it has a valid modifier suffix.
# returns a list with named elements:
# - has.mod: logical value, TRUE if a modifier was found
# - id: the actual ID value
# - mod: the appended modifier (omitting ".not" if check_not=TRUE)
# - not: logical value if ".not" was appended if check_not=TRUE
p2s.checkModifiers <- function(value, check_not=FALSE){
result <- list(has.mod=FALSE, id="", mod="", not="FALSE")
split.value <- unlist(strsplit(gsub("\"", "", value), "\\."))
if(length(split.value) > 3){
# hm, wouldn't know how this could happen, but let's just return it as-is
result[["has.mod"]] <- TRUE
result[["id"]] <- paste0("\"", split.value[1], "\"")
result[["mod"]] <- paste0("\"", paste0(split.value[-1], collapse="."), "\"")
warning(paste0("bogus modifier found: \"", value, "\"!"))
} else {
# check if the modifier is valid
modif.validity("all", paste0(split.value[-1], collapse="."))
if(length(split.value) == 3){
result[["has.mod"]] <- TRUE
result[["id"]] <- paste0("\"", split.value[1], "\"")
if(all(isTRUE(check_not), "not" %in% tolower(split.value[3]))){
result[["mod"]] <- paste0("\"", split.value[2], "\"")
result[["not"]] <- "TRUE"
} else {
result[["mod"]] <- paste0(split.value[-1], collapse=".")
}
} else if(length(split.value) == 2){
result[["has.mod"]] <- TRUE
result[["id"]] <- paste0("\"", split.value[1], "\"")
if(all(isTRUE(check_not), "not" %in% tolower(split.value[2]))){
result[["not"]] <- "TRUE"
} else {
result[["mod"]] <- paste0("\"", split.value[2], "\"")
}
} else {
result[["id"]] <- paste0("\"", value, "\"")
}
}
return(result)
} ## end function p2s.checkModifiers()
## function p2s.extractAttributes()
# translates node attributes into function options
# called by p2s()
p2s.extractAttributes <- function(nodeName, nodeAttrs, rkwdevAttributes, rkwdevLogical, rkwdevSplit, rkwdevNumeric){
rkwdevOptions <- unlist(lapply(
names(nodeAttrs),
function(thisAttr){
# consequently fail if the attribute is unknown
if(!thisAttr %in% rkwdevAttributes){
stop(simpleError(paste0("'", thisAttr, "' is an unknown attribute in node '", nodeName, "'!")))
} else {}
thisOption <- nodeAttrs[[thisAttr]]
# check for logical/character transitions
if(all(identical(nodeName, "spinbox"), identical(thisAttr, "type"))){
# special treatement of spinbox
thisOption <- ifelse(identical(tolower(thisOption), "real"), TRUE, FALSE)
} else if(thisAttr %in% rkwdevNumeric){
thisOption <- as.numeric(thisOption)
} else if(thisAttr %in% rkwdevLogical){
if(tolower(thisOption) %in% c("true", "t")){
thisOption <- TRUE
} else if(tolower(thisOption) %in% c("false", "f")){
thisOption <- FALSE
} else {
warning("unknown logical value!")
}
} else if(thisAttr %in% rkwdevSplit){
thisOption <- paste0("c(\"", paste0(unlist(strsplit(thisOption, "[[:space:]]")), collapse="\", \""), "\")")
} else {
thisOption <- paste0("\"", thisOption, "\"")
}
names(thisOption) <- names(rkwdevAttributes)[[which(rkwdevAttributes %in% thisAttr)]]
return(thisOption)
}
))
# possible modifiers in the attributes?
if(nodeName %in% "connect"){
modGovernor <- p2s.checkModifiers(rkwdevOptions["governor"], check_not=TRUE)
modClient <- p2s.checkModifiers(rkwdevOptions["client"])
if(isTRUE(modGovernor[["has.mod"]])){
rkwdevOptions["governor"] <- modGovernor[["id"]]
if(!modGovernor[["mod"]] %in% ""){
rkwdevOptions["get"] <- modGovernor[["mod"]]
} else {}
rkwdevOptions["not"] <- modGovernor[["not"]]
} else {}
if(isTRUE(modClient[["has.mod"]])){
rkwdevOptions["client"] <- modClient[["id"]]
rkwdevOptions["set"] <- modClient[["mod"]]
} else {}
} else {}
if(nodeName %in% "convert"){
# <convert id="lgc_foobarbr" sources="foo.string;bar.state" mode="notequals" standard="bar" />
# rk.XML.convert(list(string="foo", state="bar"), mode=c(notequals="bar"))
# we need to split the sources first, might be multiple
splitSources <- gsub("[[:space:]\"]", "", unlist(strsplit(rkwdevOptions["sources"], ";")))
splitSources <- sapply(
splitSources,
function(thisSource) {
modSrc <- p2s.checkModifiers(thisSource)
if(isTRUE(modSrc[["has.mod"]])){
return(paste0(gsub("\"", "", modSrc[["mod"]]), "=", modSrc[["id"]]))
} else {
return(modSrc[["id"]])
}
}
)
rkwdevOptions["sources"] <- paste0("list(", paste0(splitSources, collapse=", "), ")")
# now get the mode option right
if("standard" %in% names(rkwdevOptions)){
rkwdevOptions["mode"] <- paste0("c(", gsub("\"", "", rkwdevOptions["mode"]), "=", rkwdevOptions["standard"], ")")
rkwdevOptions <- rkwdevOptions[!names(rkwdevOptions) %in% "standard"]
}
} else {}
if(nodeName %in% "optioncolumn"){
modConnect <- p2s.checkModifiers(rkwdevOptions["connect"])
if(isTRUE(modConnect[["has.mod"]])){
rkwdevOptions["connect"] <- modConnect[["id"]]
rkwdevOptions["modifier"] <- modConnect[["mod"]]
} else {}
} else {}
if(nodeName %in% "set"){
modID <- p2s.checkModifiers(rkwdevOptions["id"])
if(isTRUE(modID[["has.mod"]])){
rkwdevOptions["id"] <- modID[["id"]]
rkwdevOptions["set"] <- modID[["mod"]]
} else {}
} else {}
if(nodeName %in% "switch"){
modCondition <- p2s.checkModifiers(rkwdevOptions["condition"])
if(isTRUE(modCondition[["has.mod"]])){
rkwdevOptions["condition"] <- modCondition[["id"]]
rkwdevOptions["modifier"] <- modCondition[["mod"]]
} else {}
} else {}
return(rkwdevOptions)
} ## end function p2s.extractAttributes()
## function p2s.checkOption
# takes a XiMpLe node and returns an <option> node as
# a character string, either showing a list() call.
# option nodes with an ID are automatically returned as
# rk.XML.option() calls istead of the usual list entries.
# called by p2s()
p2s.checkOption <- function(node, level=1, indent=TRUE){
nodeName <- XMLName(node)
nodeAttrs <- XMLAttrs(node)
if(identical(nodeName, "option")){
if("id" %in% names(nodeAttrs)){
# generate rk.XML.option()
thisEntry <- p2s(node=node, indent=indent, level=level)
} else {
# name the list entry after option label
thisEntry <- paste0("\"", nodeAttrs[["label"]], "\"=c(")
# set the value
thisEntry <- paste0(thisEntry, "val=\"", nodeAttrs[["value"]], "\"")
# check if option is checked
if("checked" %in% names(nodeAttrs)){
if(tolower(nodeAttrs[["checked"]]) %in% c("true", "t")){
thisEntry <- paste0(thisEntry, ", chk=TRUE")
} else if(tolower(nodeAttrs[["checked"]]) %in% c("false", "f")){
thisEntry <- paste0(thisEntry, ", chk=FALSE")
} else {
warning("unknown logical value!")
}
} else {}
thisEntry <- paste0(thisEntry, ")")
}
} else {
warning(paste0("'", nodeName, "' is not an option! returning NULL!"))
return(NULL)
}
return(thisEntry)
} ## end function p2s.checkOption()
## end function p2s.checkTabs
# analyses <tab> nodes and returns them for inclusion by p2s()
p2s.checkTabs <- function(node, level=0, indent=TRUE){
nodeName <- XMLName(node)
nodeAttrs <- XMLAttrs(node)
if(identical(nodeName, "tab")){
# name the list entry after tab label
thisEntry <- paste0(
"\"", nodeAttrs[["label"]], "\"=list(\n", paste0(rep(" ", level+1), collapse=""),
paste0(
sapply(
XMLChildren(node),
function(thisChild){
p2s(node=thisChild, level=level+2, indent=indent)
}
),
collapse=paste0(",\n", rep(" ", level+1), collapse=""))
)
thisEntry <- paste0(thisEntry, "\n", paste0(rep(" ", level), collapse=""), ")")
} else {
warning(paste0("'", nodeName, "' is not a tab! returning NULL!"))
thisEntry <- NULL
}
return(thisEntry)
} ## end function p2s.checkTabs
## function p2s.checkTabIDs()
# rk.XML.tabbook() expects IDs of tabbook and all tabs in one vector, check for existing IDs
p2s.checkTabIDs <- function(node){
nodeName <- XMLName(node)
nodeAttrs <- XMLAttrs(node)
if(identical(nodeName, "tab")){
thisID <- id(node, js=FALSE)
if(identical(thisID, "NULL")){
# tabs should have IDs; if not, auto-generate one
if(is.null(nodeAttrs[["label"]])){
warning(paste0("'", nodeName, "' does neither have an 'id' nor 'label'! returning 'dummy'!"), call.=FALSE)
thisID <- "dummy"
} else {
thisID <- auto.ids(nodeAttrs[["label"]], prefix=ID.prefix("tab", length=3))
}
} else {}
} else {
warning(paste0("'", nodeName, "' is not a tab! returning NULL!"), call.=FALSE)
thisID <- NULL
}
return(thisID)
} ## end function p2s.checkTabIDs()
## function p2s.FetchChildNodes()
# used e.g. to get <package> out of <dependencies>
p2s.FetchChildNodes <- function(node, childNode, allOptions, newOption=childNode,
indent=TRUE, level=1, prefix="", drop.defaults=TRUE, node.names=FALSE, collapse=".",
paste=TRUE){
nodesFound <- child.list(XMLScan(node, childNode))
if(length(nodesFound) > 0){
result <- sapply(
nodesFound,
function(thisChild){
return(p2s(
node=thisChild,
indent=indent,
level=level+2,
prefix=prefix,
drop.defaults=drop.defaults,
node.names=node.names,
collapse=collapse
))
}
)
if(isTRUE(paste)){
result <- paste0("list(\n", paste0(rep(" ", level+1), collapse=""),
paste0(result, collapse=paste0(",\n", paste0(rep(" ", level+1), collapse=""))),
"\n", paste0(rep(" ", level), collapse=""), ")")
} else {}
allOptions[[newOption]] <- result
} else {}
return(allOptions)
} ## end function p2s.FetchChildNodes()
## function p2s.MoveOptions()
# newOptions: the new optin to move all of 'replace' to
p2s.MoveOptions <- function(allOptions, replace, newOption, level=1){
replace <- replace[replace %in% names(allOptions)]
allOptions[[newOption]] <- paste0("list(\n", paste0(rep(" ", level+1), collapse=""),
paste0(
replace, "=",
allOptions[replace],
collapse=paste0(",\n", paste0(rep(" ", level+1), collapse=""))),
"\n", paste0(rep(" ", level), collapse=""), ")")
allOptions <- allOptions[!names(allOptions) %in% replace]
return(allOptions)
} ## end function p2s.MoveOptions()
## function p2s()
# this is the main work horse, going through nested XML nodes recursively
# called by the actual methods
p2s <- function(node, indent=TRUE, level=1, prefix="", drop.defaults=TRUE, node.names=FALSE, collapse="."){
nodeName <- XMLName(node)
nodeAttrs <- XMLAttrs(node)
# fail if we don't know this node type
if(!nodeName %in% names(FONA)){
stop(simpleError(paste0("'", nodeName, "' is an unknown node!")))
} else {}
rkwdevAttributes <-rkwdevLogical <- rkwdevNumeric <- rkwdevSplit <- c()
recursive <- checkText <- checkNoi18n <- FALSE
rkwdevFunction <- FONA[[nodeName]][["funct"]]
if("opt" %in% names(FONA[[nodeName]])){
rkwdevAttributes <- FONA[[nodeName]][["opt"]]
} else {}
if("logical" %in% names(FONA[[nodeName]])){
rkwdevLogical <- FONA[[nodeName]][["logical"]]
} else {}
if("numeric" %in% names(FONA[[nodeName]])){
rkwdevNumeric <- FONA[[nodeName]][["numeric"]]
} else {}
if("split" %in% names(FONA[[nodeName]])){
rkwdevSplit <- FONA[[nodeName]][["split"]]
} else {}
if("children" %in% names(FONA[[nodeName]])){
rkwdevChildren <- FONA[[nodeName]][["children"]]
recursive <- TRUE
} else {}
if("text" %in% names(FONA[[nodeName]])){
rkwdevText <- FONA[[nodeName]][["text"]]
checkText <- TRUE
} else {}
if("noi18n" %in% names(FONA[[nodeName]])){
rkwdevNoi18n <- FONA[[nodeName]][["noi18n"]]
checkNoi18n <- TRUE
} else {}
rkwdevOptions <- p2s.extractAttributes(
nodeName=nodeName,
nodeAttrs=nodeAttrs,
rkwdevAttributes=rkwdevAttributes,
rkwdevLogical=rkwdevLogical,
rkwdevSplit=rkwdevSplit,
rkwdevNumeric=rkwdevNumeric
)
# need to include text?
if(isTRUE(checkText)){
nodeChildren <- XMLChildren(node)
if(length(nodeChildren) > 0){
nodeChildren <- XMLValue(nodeChildren[[1]])
if(inherits(nodeChildren, "character")){
# do some escaping
nodeChildren <- gsub("<" , "<", nodeChildren)
nodeChildren <- gsub(">" , ">", nodeChildren)
nodeChildren <- gsub("([^\\\\])\"" , "\\1"", nodeChildren, perl=TRUE)
rkwdevOptions[[rkwdevText]] <- paste0("\"", nodeChildren, "\"", collapse=" ")
} else {}
} else {}
} else {}
# get the child nodes
if(isTRUE(recursive)){
nodeChildren <- XMLChildren(node)
if(length(nodeChildren) > 0){
# some nodes need special treatment, because they take option children as named lists
if(nodeName %in% c("dropdown", "radio", "select", "valueselector")){
rkwdevChildnodes <- sapply(
nodeChildren,
function(thisChild){
return(p2s.checkOption(node=thisChild, level=level+2, indent=indent))
}
)
rkwdevOptions[[rkwdevChildren]] <- paste0("list(\n", paste0(rep(" ", level+1), collapse=""),
paste0(rkwdevChildnodes, collapse=paste0(",\n", paste0(rep(" ", level+1), collapse=""))),
"\n", paste0(rep(" ", level), collapse=""), ")")
} else if(nodeName %in% c("about")){
rkwdevOptions <- p2s.FetchChildNodes(
node=node,
childNode="author",
allOptions=rkwdevOptions,
indent=indent,
level=level,
prefix=prefix,
drop.defaults=drop.defaults,
node.names=node.names,
collapse=collapse
)
# we also need to move several arguments to a list(), they wrongly appear on the top level
# because of the differences between the function formals and the XML structure
rkwdevOptions <- p2s.MoveOptions(
allOptions=rkwdevOptions,
replace=c("desc", "long.desc", "version", "date", "url", "license", "category"),
newOption="about",
level=level
)
} else if(nodeName %in% c("dependencies", "dependency_check")){
for(thisChildNode in c("package", "pluginmap")){
rkwdevOptions <- p2s.FetchChildNodes(
node=node,
childNode=thisChildNode,
allOptions=rkwdevOptions,
indent=indent,
level=level,
prefix=prefix,
drop.defaults=drop.defaults,
node.names=node.names,
collapse=collapse
)
}
# again, move arguments to a list() that wrongly appear on the top level
# because of the differences between the function formals and the XML structure
rkwdevOptions <- p2s.MoveOptions(
allOptions=rkwdevOptions,
replace=c("rkward.min", "rkward.max", "R.min", "R.max"),
newOption="dependencies",
level=level
)
} else if(nodeName %in% c("related")){
# probably need to get the <link> nodes out of a nested <ul>
rkwdevChildnodes <- sapply(
XMLScan(node, "link"),
function(thisChild){
return(p2s(
node=thisChild,
indent=indent,
level=level+1,
prefix=prefix,
drop.defaults=drop.defaults,
node.names=node.names,
collapse=collapse
))
}
)
rkwdevOptions[[rkwdevChildren]] <- paste0(rkwdevChildnodes,
collapse=paste0(",\n", paste0(rep(" ", level), collapse="")))
} else if(nodeName %in% c("switch")){
allCases <- sapply(
nodeChildren,
function(thisChild){
return(p2s(
node=thisChild,
indent=indent,
level=level+2,
prefix=prefix,
drop.defaults=drop.defaults,
node.names=node.names,
collapse=collapse
))
}
)
rkwdevOptions[["cases"]] <- paste0(
"list(",
paste0(
"\n", paste0(rep(" ", level+1), collapse=""),
sapply(nodeChildren, XMLName), "=", allCases,
collapse=","
),
"\n", paste0(rep(" ", level), collapse=""), ")"
)
} else if(nodeName %in% c("tabbook")){
rkwdevChildnodes <- sapply(
nodeChildren,
function(thisChild){
return(p2s.checkTabs(node=thisChild, level=level+1, indent=indent))
}
)
rkwdevOptions[[rkwdevChildren]] <- paste0("list(\n", paste0(rep(" ", level+1), collapse=""),
paste0(rkwdevChildnodes, paste0(rep(" ", level-1), collapse=""),
collapse=paste0(",\n", paste0(rep(" ", level+1), collapse=""))),
"\n", paste0(rep(" ", level), collapse=""), ")")
# some <tabbook>s don't have IDs, but their <tab>s need them
# try to solve this by checking for <tab> IDs and then setting
# the tabbook ID to "tabbook" as a workaround
rkwdevTabIDs <- sapply(
nodeChildren,
function(thisChild){
return(p2s.checkTabIDs(thisChild))
}
)
if(identical(length(rkwdevTabIDs), length(nodeChildren))){
if(!"id.name" %in% names(rkwdevOptions)){
rkwdevOptions[["id.name"]] <- "\"tabbook\""
} else {}
rkwdevOptions[["id.name"]] <- paste0("c(", rkwdevOptions[["id.name"]], ", \"", paste0(rkwdevTabIDs, collapse="\", \""), "\")")
} else {}
} else {
rkwdevChildnodes <- sapply(
nodeChildren,
function(thisChild){
return(p2s(
node=thisChild,
indent=indent,
level=level+1,
prefix=prefix,
drop.defaults=drop.defaults,
node.names=node.names,
collapse=collapse
))
}
)
rkwdevOptions[[rkwdevChildren]] <- paste0(rkwdevChildnodes,
collapse=paste0(",\n", paste0(rep(" ", level), collapse="")))
}
} else {}
} else {}
if(isTRUE(checkNoi18n)){
# clean up non-i18n options
if(rkwdevNoi18n %in% names(rkwdevOptions)){
rkwdevOptions[["i18n"]] <- "FALSE"
rkwdevOptions[[gsub("noi18n_", "", rkwdevNoi18n)]] <- rkwdevOptions[[rkwdevNoi18n]]
rkwdevOptions <- rkwdevOptions[!names(rkwdevOptions) %in% rkwdevNoi18n]
} else {}
} else {}
# deal with comment nodes
if("!--" %in% nodeName){
# in theory, the value can be both in the value slot and in a child node's value slot
txtNode1 <- XMLValue(XMLChildren(node)[[1]])
txtNode2 <- XMLValue(node)
if(length(txtNode2) > 0 & !identical(txtNode2, "")){
txtNode1 <- paste(txtNode1, txtNode2)
} else {}
# check if this is an i18n comment
if(grepl("^i18n:", txtNode1)){
rkwdevFunction <- "rk.i18n.comment"
txtNode1 <- gsub("^i18n:[[:space:]]*", "", txtNode1)
} else {}
rkwdevOptions[["text"]] <- paste0("\"", txtNode1, "\"", collapse=" ")
} else {}
# set link type
if(nodeName %in% c("link")){
# we need to correct the href values
if(grepl("rkward://rhelp/", rkwdevOptions[["href"]])){
rkwdevOptions[["href"]] <- gsub("rkward://rhelp/", "", rkwdevOptions[["href"]])
rkwdevOptions[["type"]] <- "\"R\""
} else if(grepl("rkward://component/", rkwdevOptions[["href"]])){
rkwdevOptions[["href"]] <- gsub("rkward://component/", "", rkwdevOptions[["href"]])
rkwdevOptions[["type"]] <- "\"RK\""
} else {
rkwdevOptions[["type"]] <- "\"url\""
}
} else {}
# fix person roles in <author> nodes
if(nodeName %in% c("author")){
authorRoles <- unlist(strsplit(gsub("\"|[[:space:]]", "", rkwdevOptions[["role"]]), ","))
rkwdevOptions[["role"]] <- paste0("c(\"", paste0(authorRoles, collapse="\", \""), "\")")
} else {}
# check for default values and drop them
if(isTRUE(drop.defaults)){
defaults <- formals(rkwdevFunction)
if(!is.null(defaults)){
# bring formals into same format as rkwdevOptions
defaults[sapply(defaults, is.character)] <- paste0("\"", defaults[sapply(defaults, is.character)], "\"")
defaults <- sapply(defaults, as.character)
for (thisOption in names(rkwdevOptions)){
if(identical(rkwdevOptions[[thisOption]], defaults[[thisOption]])){
rkwdevOptions <- rkwdevOptions[!names(rkwdevOptions) %in% thisOption]
}
}
} else {}
} else {}
# bring options in optimized order
rkwdevOptions <- rkwdevOptions[order(sapply(names(rkwdevOptions), function(thisOpt){which(names(rkwdevAttributes) %in% thisOpt)}))]
if(isTRUE(indent)){
ind.start <- ind.char <- paste0("\n", paste0(rep(" ", level), collapse=""))
ind.end <- paste0("\n", paste0(rep(" ", level-1), collapse=""))
} else {
ind.char <- " "
ind.start <- ind.end <- ""
}
# write into an object, but only if the node has an ID value,
# because otherwise we can assume that it is not referenced anywhere else
nodeID <- id(node, js=FALSE)
if(!identical(nodeID, "NULL")){
rkObject <- paste0(nodeID, " <- ")
if(isTRUE(node.names)){
rkObject <- paste0(nodeName, collapse, rkObject)
} else {}
if(!identical(prefix, "")){
rkObject <- paste0(prefix, collapse, rkObject)
} else {}
} else {
rkObject <- ""
}
if(length(rkwdevOptions) > 0){
result <- paste0(
rkObject, rkwdevFunction, "(",
ind.start, paste0(names(rkwdevOptions), "=", rkwdevOptions, collapse=paste0(",", ind.char)),
ind.end,
")"
)
# remove "...=", if present
result <- gsub("\\.\\.\\.=", "", result)
} else {
result <- paste0(rkwdevFunction, "()")
}
return(result)
} ## end function p2s()
## list object FONA
# this list describes which rkwarddev function belongs to which XML node,
# and which option handles which attribute
# the structure is:
# "<node name>"=list(
# funct="<function name>",
# opt=c(
# <option1 name>="<attribute1 name>",
# <option2 name>="<attribute2 name>",
# ...
# ),
# text="<does this node nest text?>",
# children="<option name for nested child nodes that need to be checked recursively?>",
# split=c(
# "<attribute names that need to be split into a character vector>"
# ),
# logical=c(
# "<attribute name that needs translation from character to logical>"
# ),
# numeric=c(
# "<attribute name that needs translation from character to numeric>"
# ),
# modifiers=c(
# "<attribute name that could contain a modifier>"
# )
# )
# the name stands for function/option/node/attribute
#
# unsolved functions (or parts of them):
# - rk.rkh.doc()
# - a lot of rk.XML.optionset()...
# - children in rk.XML.plugin()
# - children in rk.XML.pluginmap()
# - rk.XML.values()
# - rk.XML.vars()
FONA <- list(
"!--"=list(
funct="rk.comment",
opt=c(
text="text" # needs special treatment; also check for "i18n:" prefix for rk.i18n.comment()
)
),
"about"=list(
funct="rk.XML.about",
opt=c(
name="name",
desc="shortinfo",
long.desc="longinfo",
version="version",
date="releasedate",
url="url",
license="license",
category="category",
author="author",
about="about"
),
children=c("author")
),
"author"=list(
funct="person",
opt=c(
given="given",
family="family",
middle="middle",
email="email",
role="role",
comment="comment",
first="first",
last="last"
)
),
"caption"=list(
funct="rk.rkh.caption",
opt=c(
id="id",
title="title",
i18n="i18n_context"
)
),
"label"=list(
funct="rk.rkh.label",
opt=c(
id="id",
i18n="i18n_context"
)
),
"link"=list(
funct="rk.rkh.link",
opt=c(
href="href",
text="text",
type="type", # only here for sorting order
i18n="i18n_context"
),
text="text"
),
"related"=list(
funct="rk.rkh.related",
opt=c(
"..."="...",
# text="text", ## we ignore the text element for now!
i18n="i18n_context"
),
# text="text",
children="..."
),
"section"=list(
funct="rk.rkh.section",
opt=c(
title="title",
text="text",
short="short_title",
id.name="id",
i18n="i18n_context",
noi18n_title="noi18n_title"
),
noi18n="noi18n_title",
text="text"
),
"setting"=list(
funct="rk.rkh.setting",
opt=c(
id="id",
text="text",
title="title",
i18n="i18n_context",
noi18n_title="noi18n_title"
),
noi18n="noi18n_title",
text="text"
),
"settings"=list(
funct="rk.rkh.settings",
opt=c(
"..."="..."
),
children="..."
),
"summary"=list(
funct="rk.rkh.summary",
opt=c(
i18n="i18n_context"
),
text="text"
),
"technical"=list(
funct="rk.rkh.technical",
opt=c(
text="text",
i18n="i18n_context"
),
text="text"
),
"title"=list(
funct="rk.rkh.title",
opt=c(
text="text",
i18n="i18n_context"
),
text="text"
),
"usage"=list(
funct="rk.rkh.usage",
opt=c(
text="text",
i18n="i18n_context"
),
text="text"
),
"attribute"=list(
funct="rk.XML.attribute",
opt=c(
id="id",
value="value",
label="label",
i18n="i18n_context",
noi18n_label="noi18n_label"
),
noi18n="noi18n_label"
),
"browser"=list(
funct="rk.XML.browser",
opt=c(
label="label",
type="type",
initial="initial",
urls="allow_urls",
filter="filter",
required="required",
id.name="id",
i18n="i18n_context",
noi18n_label="noi18n_label"
),
noi18n="noi18n_label",
split="filter",
logical=c("required", "allow_urls")
),
"case"=list(
funct="list", # doesn't have a function of its own, is a child of rk.XML.switch()
opt=c(
standard="standard",
fixed_value="fixed_value",
dynamic_value="dynamic_value"
)
),
"checkbox"=list(
funct="rk.XML.cbox",
opt=c(
label="label",
value="value",
un.value="value_unchecked",
chk="checked",
id.name="id",
i18n="i18n_context",
noi18n_label="noi18n_label"
),
noi18n="noi18n_label",
logical=c("checked")
),
"code"=list(
funct="rk.XML.code",
opt=c(
file="file"
)
),
"column"=list(
funct="rk.XML.col",
opt=c(
"..."="...",
id.name="id"
),
children="..."
),
"component"=list(
funct="rk.XML.component",
opt=c(
label="label",
file="file",
id.name="id",
type="type",
dependencies="dependencies",
i18n="i18n_context",
noi18n_label="noi18n_label"
),
noi18n="noi18n_label",
children="dependencies"
),
"components"=list(
funct="rk.XML.components",
opt=c(
"..."="..."
),
children="..."
),
"connect"=list(
funct="rk.XML.connect",
opt=c(
governor="governor",
client="client",
get="get", # these three
set="set", # are needed
not="not", # for ordering purposes
reconcile="reconcile"
),
logical=c("reconcile"),
modifiers=c("governor", "client")
),
"context"=list(
funct="rk.XML.context",
opt=c(
"..."="...",
id="id"
),
children="..."
),
"convert"=list(
funct="rk.XML.convert",
opt=c(
sources="sources",
mode="mode",
required="required",
id.name="id",
standard="standard"
),
modifiers=c("sources"),
logical=c("required")
),
"copy"=list(
funct="rk.XML.copy",
opt=c(
id="id",
as="as"
)
),
"default"=list(
funct="list", # doesn't have a function of its own, is a child of rk.XML.switch()
opt=c(
standard="standard",
fixed_value="fixed_value",
dynamic_value="dynamic_value"
)
),
"dependencies"=list(
funct="rk.XML.dependencies",
opt=c(
dependencies="dependencies",
rkward.min="rkward_min_version",
rkward.max="rkward_max_version",
R.min="R_min_version",
R.max="R_max_version",
package="package",
pluginmap="pluginmap"
),
children=c("dependencies", "package", "pluginmap")
),
"dependency_check"=list(
funct="rk.XML.dependency_check",
opt=c(
id.name="id",
dependencies="dependencies",
rkward.min="rkward_min_version",
rkward.max="rkward_max_version",
R.min="R_min_version",
R.max="R_max_version",
package="package",
pluginmap="pluginmap"
),
children=c("dependencies", "package", "pluginmap")
),
"dialog"=list(
funct="rk.XML.dialog",
opt=c(
"..."="...",
label="label",
recommended="recommended",
i18n="i18n_context",
noi18n_label="noi18n_label"
),
noi18n="noi18n_label",
children="...",
logical=c("recommended")
),
"dropdown"=list(
funct="rk.XML.dropdown",
opt=c(
label="label",
options="options",
id.name="id",
i18n="i18n_context",
noi18n_label="noi18n_label"
),
noi18n="noi18n_label",
children="options"
),
"embed"=list(
funct="rk.XML.embed",
opt=c(
component="component",
button="as_button",
label="label",
id.name="id",
i18n="i18n_context",
noi18n_label="noi18n_label"
),
noi18n="noi18n_label",
logical=c("as_button")
),
"entry"=list(
funct="rk.XML.entry",
opt=c(
component="component",
index="index"
)
),
"external"=list(
funct="rk.XML.external",
opt=c(
id="id",
default="default"
)
),
"false"=list(
funct="list", # doesn't have a function of its own, is a child of rk.XML.switch()
opt=c(
fixed_value="fixed_value",
dynamic_value="dynamic_value"
)
),
"formula"=list(
funct="rk.XML.formula",
opt=c(
fixed="fixed_factors",
dependent="dependent",
id.name="id"
)
),
"frame"=list(
funct="rk.XML.frame",
opt=c(
"..."="...",
label="label",
checkable="checkable",
chk="checked",
id.name="id",
i18n="i18n_context",
noi18n_label="noi18n_label"
),
noi18n="noi18n_label",
children="...",
logical=c("checkable", "checked")
),
"help"=list(
funct="rk.XML.help",
opt=c(
file="file"
)
),
"hierarchy"=list(
funct="rk.XML.hierarchy",
opt=c(
"..."="..."
),
children="..."
),
"i18n"=list(
funct="rk.XML.i18n",
opt=c(
label="label",
id.name="id",
i18n="i18n_context",
noi18n_label="noi18n_label"
),
noi18n="noi18n_label"
),
"include"=list(
funct="rk.XML.include",
opt=c(
file="file"
)
),
"input"=list(
funct="rk.XML.input",
opt=c(
label="label",
initial="initial",
size="size",
required="required",
id.name="id",
i18n="i18n_context",
noi18n_label="noi18n_label"
),
noi18n="noi18n_label",
logical=c("required")
),
"insert"=list(
funct="rk.XML.insert",
opt=c(
snippet="snippet"
)
),
"logic"=list(
funct="rk.XML.logic",
opt=c(
"..."="..."
),
children="..."
),
"matrix"=list(
funct="rk.XML.matrix",
opt=c(
label="label",
mode="mode",
rows="rows",
columns="columns",
min="min",
max="max",
min_rows="min_rows",
min_columns="min_columns",
allow_missings="allow_missings",
allow_user_resize_columns="allow_user_resize_columns",
allow_user_resize_rows="allow_user_resize_rows",
fixed_width="fixed_width",
fixed_height="fixed_height",
horiz_headers="horiz_headers",
vert_headers="vert_headers",
id.name="id",
i18n="i18n_context",
noi18n_label="noi18n_label"
),
noi18n="noi18n_label",
logical=c("allow_missings", "allow_user_resize_columns", "allow_user_resize_rows", "fixed_width", "fixed_height"),
numeric=c("min", "max", "min_rows", "min_columns")
),
"menu"=list(
funct="rk.XML.menu",
opt=c(
label="label",
"..."="...",
index="index",
id.name="id",
i18n="i18n_context",
noi18n_label="noi18n_label"
),
noi18n="noi18n_label",
children="..."
),
"option"=list(
funct="rk.XML.option",
opt=c(
label="label",
val="value",
chk="checked",
id.name="id",
i18n="i18n_context",
noi18n_label="noi18n_label"
),
noi18n="noi18n_label",
logical=c("checked")
),
"optioncolumn"=list(
funct="rk.XML.optioncolumn",
opt=c(
connect="connect",
modifier="modifier",
label="label",
external="external",
default="default",
id.name="id",
i18n="i18n_context",
noi18n_label="noi18n_label"
),
noi18n="noi18n_label",
modifiers="connect",
logical="external"
),
"optiondisplay"=list(
funct="rk.XML.optiondisplay",
opt=c(
index="index",
id.name="id"
),
logical=c("index")
),
"optionset"=list(
funct="rk.XML.optionset",
opt=c(
# content,
# optioncolumn,
min_rows="min_rows",
min_rows_if_any="min_rows_if_any",
max_rows="max_rows",
keycolumn="keycolumn",
# logic=NULL,
# optiondisplay=TRUE,
id.name="id"
),
children="...",
numeric=c("min_rows", "min_rows_if_any", "max_rows")
),
"package"=list(
funct="c", # has no function of its own, used in rk.XML.dependencies()
opt=c(
name="name",
min="min",
max="max",
repository="repository"
)
),
"page"=list(
funct="rk.XML.page",
opt=c(
"..."="...",
id.name="id"
),
children="..."
),
"plugin"=list(
funct="rk.XML.plugin",
opt=c(
i18n="i18n_context",
noi18n_label="noi18n_label"
),
noi18n="noi18n_label",
children="..."
),
"pluginmap"=list(
funct="c", # has no function of its own, used in rk.XML.dependencies()
opt=c(
name="name",
url="url"
)
),
"preview"=list(
funct="rk.XML.preview",
opt=c(
label="label",
mode="mode",
placement="placement",
id.name="id",
i18n="i18n_context",
noi18n_label="noi18n_label"
),
noi18n="noi18n_label",
logical="active"
),
"radio"=list(
funct="rk.XML.radio",
opt=c(
label="label",
options="options",
id.name="id",
i18n="i18n_context",
noi18n_label="noi18n_label"
),
noi18n="noi18n_label",
children="options"
),
"require"=list(
funct="rk.XML.require",
opt=c(
file="file",
map="map"
)
),
"row"=list(
funct="rk.XML.row",
opt=c(
"..."="...",
id.name="id"
),
children="..."
),
"saveobject"=list(
funct="rk.XML.saveobj",
opt=c(
label="label",
chk="checked",
checkable="checkable",
initial="initial",
required="required",
id.name="id",
i18n="i18n_context",
noi18n_label="noi18n_label"
),
noi18n="noi18n_label",
logical=c("checked", "checkable", "required")
),
"select"=list(
funct="rk.XML.select",
opt=c(
label="label",
options="options",
id.name="id",
i18n="i18n_context",
noi18n_label="noi18n_label"
),
noi18n="noi18n_label",
children="options"
),
"set"=list(
funct="rk.XML.set",
opt=c(
id="id",
set="set",
to="to"
),
modifiers=c("set"),
logical=c("to")
),
"snippet"=list(
funct="rk.XML.snippet",
opt=c(
"..."="...",
id.name="id"
),
children="..."
),
"snippets"=list(
funct="rk.XML.snippets",
opt=c(
"..."="..."
),
children="..."
),
"spinbox"=list(
funct="rk.XML.spinbox",
opt=c(
label="label",
min="min",
max="max",
initial="initial",
real="type", ## need special treatment
precision="precision",
max.precision="max_precision",
id.name="id",
i18n="i18n_context",
noi18n_label="noi18n_label"
),
numeric=c("min", "max", "initial", "precision", "max_precision"),
noi18n="noi18n_label"
),
"stretch"=list(
funct="rk.XML.stretch"
),
"switch"=list(
funct="rk.XML.switch",
opt=c(
condition="condition",
cases="cases",
modifier="modifier",
id.name="id"
),
children=c("cases"),
modifiers=c("condition")
),
"tabbook"=list(
funct="rk.XML.tabbook",
opt=c(
label="label",
tabs="tabs",
id.name="id",
i18n="i18n_context",
noi18n_label="noi18n_label"
),
noi18n="noi18n_label",
children="tabs"
),
"text"=list(
funct="rk.XML.text",
opt=c(
text="text",
type="type",
id.name="id",
i18n="i18n_context"
),
text="text"
),
"true"=list(
funct="list", # doesn't have a function of its own, is a child of rk.XML.switch()
opt=c(
fixed_value="fixed_value",
dynamic_value="dynamic_value"
)
),
"values"=list(
funct="rk.XML.values",
opt=c(
label="label",
## values <- function(label, slot.text, options=list(label=c(val=NULL, chk=FALSE, i18n=NULL)),
## required=FALSE, multi=FALSE, duplicates=FALSE, min=1, any=1, max=0,
## horiz=TRUE, add.nodes=NULL, frame.label=NULL
id.name="id",
i18n="i18n_context",
noi18n_label="noi18n_label"
),
noi18n="noi18n_label",
# children="...",
logical=c()
),
"valueselector"=list(
funct="rk.XML.valueselector",
opt=c(
label="label",
options="options",
id.name="id",
i18n="i18n_context",
noi18n_label="noi18n_label"
),
noi18n="noi18n_label",
children="options",
logical=c()
),
"valueslot"=list(
funct="rk.XML.valueslot",
opt=c(
label="label",
source="source",
property="source_property",
required="required",
multi="multi",
duplicates="allow_duplicates",
min="min_vars",
any="min_vars_if_any",
max="max_vars",
id.name="id",
i18n="i18n_context",
noi18n_label="noi18n_label"
),
noi18n="noi18n_label",
logical=c("required", "multi", "allow_duplicates"),
numeric=c("min_vars", "min_vars_if_any", "max_vars")
),
"vars"=list(
funct="rk.XML.vars",
opt=c(
label="label",
# rk.XML.vars <- function(label, slot.text, required=FALSE, multi=FALSE, duplicates=FALSE, min=1, any=1, max=0,
# dim=0, min.len=0, max.len=NULL, classes=NULL, types=NULL, horiz=TRUE, add.nodes=NULL,
# frame.label=NULL, formula.dependent=NULL, dep.options=list(),
id.name="id"
),
# children="...",
logical=c()
),
"varselector"=list(
funct="rk.XML.varselector",
opt=c(
label="label",
id.name="id",
i18n="i18n_context",
noi18n_label="noi18n_label"
),
noi18n="noi18n_label"
),
"varslot"=list(
funct="rk.XML.varslot",
opt=c(
label="label",
source="source",
property="source_property",
required="required",
multi="multi",
duplicates="allow_duplicates",
min="min_vars",
any="min_vars_if_any",
max="max_vars",
dim="num_dimensions",
min.len="min_length",
max.len="max_length",
classes="classes",
types="types",
id.name="id",
i18n="i18n_context",
noi18n_label="noi18n_label"
),
noi18n="noi18n_label",
logical=c("required", "multi", "allow_duplicates"),
numeric=c("min_vars", "min_vars_if_any", "max_vars", "num_dimensions", "min_length", "max_length"),
split=c("classes", "types")
),
"wizard"=list(
funct="rk.XML.wizard",
opt=c(
"..."="...",
label="label",
recommended="recommended",
i18n="i18n_context",
noi18n_label="noi18n_label"
),
noi18n="noi18n_label",
children="...",
logical=c("recommended")
)
) ## end list object FONA
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.