R/xmlOutputBuffer.R

xmlOutputBuffer <-
#
#
# Want to check with the DTD whether a tag is legitimate
#  attributes are valid, etc.
#
# Add an indentation level.
#
#  Need to escape characters via entities:
#      <-   => %sgets;
#      <    => %lt;
#      >    => %gt;
#   etc.
#
#
# Allow xmlEndTag with no argument to allow closing the current one.
#  (Maintain a stack)
#
# Allow addTag(tag, addTag(), addTag(),)
#
#
#  The handling of the connection (i.e. the buf argument) can
# be cleaned up using the OOP package from Omegahat. This will be done
# in the future.
#
#
# sapply(names(nameSpace), function(i, x){paste("xmlns:",i,"=\"",x[[i]],"\"", sep="")}, x=nameSpace)
#
#
#
function(dtd = NULL, nameSpace = NULL, buf = NULL, nsURI = NULL,
          header = "<?xml version=\"1.0\"?>")
{
    # If the user gave as an existing buffer and header is non-NULL,
    # we appendd it to the buffer. This can be used for adding things
    # like section breaks, etc.
    #
    # If the user did not give us a buffer, then we use the header.
    #
    # This is done immediately the function is called, rather than
    # in the calls to the functions of the closure after it is returned.
  if(is.null(buf))
    buf <- header
  else if(inherits(buf, "connection")) {
     if(!isOpen(buf)) {
       open(buf, rw = "w")
       on.exit(close(buf))
     }
    cat(header,"\n", sep="", file = buf)     
  } else if(!is.null(header))
    cat(header,"\n", sep="", file = buf)



  emittedDocType <- FALSE
  
  if(missing(nameSpace) && !is.null(nsURI) && !is.null(names(nsURI))) {
    nameSpace <- names(nsURI)[1]
  }

  openTags <- NULL  #list()
  lastTag <- 0


   # This is called from addTag() when the tag being 
   # emitted into the stream is left open by that call.
   # We store the tag name, its namespace and the URI of the
   # namespace if there is one in this call.
   # This triple is appended as the last row of the openTags
   # matrix and lastTag is incremented.
  addOpenTag <- function(tag, ns, xmlns) {
    lastTag <<- lastTag+1
    if( lastTag == 1 ) {
      rval <- matrix(c(tag, if(is.null(ns)) "" else ns, if(is.null(xmlns)) "" else xmlns), 
                     nrow = 1, dimnames=list(NULL, c("tagname","nsprefix", "nsURI")) )
    } else
      rval <- rbind(openTags, c(tag, ifelse(is.null(ns),openTags[lastTag-1,2], ns), ifelse(is.null(xmlns),"",xmlns)))
    openTags <<- rval
  }


  checkNamespace <- function(ns) {
    return(TRUE)

## Ignored
    if( (lastTag == 0) )
      stop(paste("Namespace `",ns, "' is not defined\n",sep=""))
    m <- match(ns, openTags$nsprefix, NULL)
    if( any(!is.null(openTags[m,"nsURI"])) )
      return(FALSE)
    stop(paste("Namespace:",ns, "is not defined\n",sep=" "))
  }


  openTag <- function(tag, ..., attrs = NULL, sep = "\n",
                       namespace = NULL, xmlns = NULL) {
    addTag(tag, ..., attrs = attrs, sep = sep, namespace = namespace, xmlns, close = FALSE)
  }

   # The namespace is the prefix for the tag name. 
   # For example, if the namespace is shelp and the tag is arg
   # the element is  shelp:tag.
   # In this function, we try to infer the ``current'' namespace
   # if the user doesn't specify it. We also have to ensure that 
   # the namespace has a definition before it is used.
   #
   # We also need to allow the user specify an empty namespace
   # so that tags 
  addTag <- function(tag, ..., attrs = NULL, sep = "\n", close = TRUE,
                       namespace = NULL, xmlns = NULL) {

    tmp <- ""

      # Flag indicating whether this is the very first, top-level tag.
      # should be shared across these functions and part of the state of 
      # the output buffer instance ?
    startingTag <- is.null(getOpenTag())

      # The user didn't specify a namespace, then we need to check about the xmlns.
      # If the user specified that, then there is an inconsistency.
      # Otherwise, no namespace and no xmlns. So need to get the 
      # current nameSpace.
    if(is.null(namespace)) {
      if( !is.null(xmlns) ) {
         # Really want to look this up in the set of "global" namespaces.
        if(is.null(names(xmlns)))
           stop("you must specify the namespace as well as xmlns")
        namespace <- names(xmlns)[1]
      }
      else {
         # so there is no xmlns. 
         # We need to determine what the currently active
         # namespace is.
       cur <- getOpenTag()
       if(is.null(cur)) {
          # Use the default namespace  given when the buffer waas constructed
         namespace <- nameSpace
#         xmlns <- nsURI
       } else {
         startingTag <- FALSE
         namespace <- cur[["nsprefix"]]
       }
      }
    }

      # if you remap prefixes this could be a problem
    if(!startingTag && !is.null(namespace) && namespace == nameSpace && is.null(xmlns) ) {
      tmp1 <- getOpenTag()

      if(is.null(tmp1) && !is.null(nsURI)) { # || tmp1[["nsURI"]] != nsURI) {
        xmlns <- nsURI[1]
      } # else  namespace <- NULL
    }

   
      #if xmlns is given but not the namespace, then
      # check this.
    if( !is.null(namespace) && is.null(xmlns) )
      checkNamespace(namespace)


    if( !is.null(namespace) && !is.null(xmlns) ) {
      if(!is.null(names(xmlns))) {
         tmpp <- xmlns
         names(tmpp) <- paste("xmlns", names(tmpp), sep=":")
         attrs <- c(attrs, tmpp)
      } else
        attrs[[paste("xmlns", namespace, sep=":")]] <-  xmlns
    }

    if(startingTag && !is.null(nsURI)) {
       tmpp <- nsURI
       names(tmpp) <- paste("xmlns", names(nsURI), sep=":")
       attrs <- c(attrs, tmpp)
    }

     # if the namespace is non-trivial (null or ""), then concatenate with the
     # tag name. Also handle the case that this is the starting tag
     # and so no namespaces are defined at this point.

#    !startingTag &&
    tagName <- if(!is.null(namespace) && namespace != "") paste(namespace,tag,sep=":") else tag


    if(!is.null(attrs)) {
      tmp <- paste(" ", paste(names(attrs),
                              paste("\"",attrs,"\"",  sep=""),sep="=",
                              collapse=" "),sep="")  
    }


    if(length(dtd) && !emittedDocType)  {
       add(paste("<!DOCTYPE", tag, "SYSTEM", ddQuote(dtd[1]), if(length(dtd) > 1) paste("PUBLIC", ddQuote(dtd[2])), ">"))
       emittedDocType <<- TRUE
    }
    
   
    add(paste("<", tagName, tmp, ">", sep=""))

    if(length(list(...)) > 0) {
      add(..., sep=sep)
    }

    if(close) 
      add(paste(if(sep == "\n")"" else"\n", "</",tagName, ">", "\n", sep=""), sep="")
    else 
      addOpenTag(tag, namespace, xmlns)

    NULL
  }

   closeTag <- function(name = NULL, namespace = nameSpace) {
    if(is.null(name)) {
      tmp <- getOpenTag() 
      name <- tmp[1] 
      if(length(tmp)>1)
        namespace <- tmp[2] 

      openTags <<- openTags[-lastTag, ,drop = FALSE]
      lastTag <<- lastTag-1
    } else if(is.numeric(name)) {
      for(i in 1:name)
        closeTag()
      return()
    } 

    add("</", ifelse(!is.null(namespace) && namespace != "", paste(namespace,name,sep=":"), name),">\n", sep="")
   }



   # This returns the last entry in the matrix openTags
   # which should contain the currently open tag, namespace and 
   # associated URI.
  getOpenTag <-  function() {
    if(lastTag > 0)
      openTags[lastTag, ]
    else 
      NULL
  }


  # 


  paste0 <- function(..., sep="", collapse="") paste(..., sep = sep, collapse=collapse)

  reset <-  function() {
     buf <<- header
     openTags <<- list()
     lastTag <<- 0
  }

  addComment <- function(..., sep="\n") {
    add("<!--", ..., "-->", sep=sep)
  }

  add <- function(..., sep="\n") {
   if(is.character(buf))
     buf <<- paste(buf, paste0(..., collapse=sep), sep=sep) 
   else
     cat(paste0(..., collapse=sep), sep, sep="", file=buf)
  }


  addCData <- function(text) {
    add("<![CDATA[", text, "]]>", sep="\n")
  }

  addPI <- function(name, text) {
    add("<?", name, " ", text, "?>\n", sep="")
  }  

  tagString <- function(tag, ..., attrs, close=FALSE) {

    tmp <- ""

    if(!missing(attrs)) {
     tmp <- paste(" ", paste(names(attrs), paste("\"",attrs,"\"", sep=""), sep="=", collapse=" "),sep="")
    }
    return(paste0("<", tag,tmp, ">",...,"</",tag,">"))
  }


  con <- list( value=function() {buf},
               addTag = addTag,
               openTag = openTag,
               closeTag = closeTag,
               addEndTag = closeTag,
               reset = reset,
               tagString = tagString,
               add = add,
               addComment = addComment,
               addPI = addPI,
               addCData = addCData,
               getOpenTag=getOpenTag,
               addOpenTag=addOpenTag
              ) 

  # class(con) <- c("XMLOutputBuffer", "XMLOutputStream")
  # con 
  ans = new("XMLOutputBuffer", con)
  names(ans) = names(con)
  ans
}

Try the XML package in your browser

Any scripts or data that you put into this service are public.

XML documentation built on Nov. 3, 2023, 1:14 a.m.