Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed
/*This set of functions is designed to map an internalXML DOM tree to an xmlHashTree.An xmlHashTree() is an environment, and so is mutable.It contains the following elements:1) nodes each with a unique identifier, e.g. 1, 2, 3.which are used to index them in the elements thatspecify the structure.2) .children - an environment3) .parents - an environment4) top - the root nodeFunctions.addNode and .nodeIdGeneratorIf we are not going to be adding to this tree,we don't need these functions.As we move through the internal tree, we can constructa node and then the character vector of children forthat node and assign it to the .children environmentSuppose we have a tree likeA/ \B C/ | \D E F|GWe start giving the nodes names by number, i.e. 1, 2, 3,....To create A, we make the node object as a listwith name (A), attributes (NULL), namespace (""), [children],id (1) and env.The children are2 and 3The parents*/#include <libxml/tree.h>#include <Rdefines.h>#include "DocParse.h"#include "Utils.h"static const char * const nodeElementNames[] = {"name", "attributes", "namespace", "children", "id", "env"};/*Identifier for the node. Could be anything, so we use the pointer address to ensureuniqueness. Ignore the next definition in the comment!#define SET_NODE_NAME(x, id) sprintf(x, "%d", id)*/#define SET_NODE_NAME(x, id, node) sprintf(x, "%p", (void *)node)SEXPmakeHashNode(xmlNodePtr node, char *buf, SEXP env, R_XMLSettings *parserSettings){SEXP ans, names, tmp;int i = 0, numEls = sizeof(nodeElementNames)/sizeof(nodeElementNames[0]);DECL_ENCODING_FROM_NODE(node)int hasValue = node->type == XML_TEXT_NODE || node->type == XML_COMMENT_NODE|| node->type == XML_CDATA_SECTION_NODE || node->type == XML_PI_NODE;if(hasValue)numEls++;if(node->nsDef)numEls++;PROTECT(ans = NEW_LIST(numEls));PROTECT(tmp = mkString(node->name ? XMLCHAR_TO_CHAR(node->name) : ""));if(node->ns)SET_NAMES(tmp, mkString((const char *)node->ns->prefix));SET_VECTOR_ELT(ans, i++, tmp);UNPROTECT(1);SET_VECTOR_ELT(ans, i++, RS_XML(AttributeList)(node, parserSettings));SET_VECTOR_ELT(ans, i++, ScalarString(ENC_COPY_TO_USER_STRING(node->ns && node->ns->prefix ? node->ns->prefix: (const xmlChar *)"")));/* skip the children */i = 4;SET_VECTOR_ELT(ans, i++, mkString(buf));SET_VECTOR_ELT(ans, i++, env);if(hasValue)SET_VECTOR_ELT(ans, i++, mkString((const char *)node->content));if(node->nsDef)SET_VECTOR_ELT(ans, i++, processNamespaceDefinitions(node->nsDef, node, parserSettings));PROTECT(names = NEW_CHARACTER(numEls));for(i = 0; i < sizeof(nodeElementNames)/sizeof(nodeElementNames[0]); i++)SET_STRING_ELT(names, i, ENC_COPY_TO_USER_STRING(nodeElementNames[i]));if(hasValue)SET_STRING_ELT(names, i++, COPY_TO_USER_STRING("value"));if(node->nsDef)SET_STRING_ELT(names, i++, COPY_TO_USER_STRING("namespaceDefinitions"));SET_NAMES(ans, names);UNPROTECT(1);/* The class of the node */PROTECT(names = NEW_CHARACTER( node->type == XML_ELEMENT_NODE ? 2 : 3));i = 0;SET_STRING_ELT(names, i++, COPY_TO_USER_STRING("XMLHashTreeNode"));if(node->type == XML_TEXT_NODE)SET_STRING_ELT(names, i++, COPY_TO_USER_STRING("XMLTextNode"));else if(node->type == XML_COMMENT_NODE)SET_STRING_ELT(names, i++, COPY_TO_USER_STRING("XMLCommentNode"));else if(node->type == XML_CDATA_SECTION_NODE)SET_STRING_ELT(names, i++, COPY_TO_USER_STRING("XMLCDataNode"));else if(node->type == XML_PI_NODE)SET_STRING_ELT(names, i++, COPY_TO_USER_STRING("XMLPINode"));SET_STRING_ELT(names, i++, COPY_TO_USER_STRING("XMLNode"));SET_CLASS(ans, names);UNPROTECT(2);return(ans);}unsigned intcountChildNodes(xmlNodePtr root, unsigned int *ctr){xmlNodePtr node;for(node = root->children; node; node = node->next) {if(node->type == XML_XINCLUDE_START)countChildNodes(node, ctr);else if(node->type != XML_XINCLUDE_END)(*ctr)++;}return(*ctr);}voidcollectChildNodes(xmlNodePtr root, unsigned int *ctr, SEXP kids){xmlNodePtr node;for(node = root->children; node; node = node->next) {if(node->type == XML_XINCLUDE_END)continue;if(node->type == XML_XINCLUDE_START)collectChildNodes(node, ctr, kids);else {char buf[20];SET_NODE_NAME(buf, *ctr + 1, node);SET_STRING_ELT(kids, *ctr, mkChar(buf));(*ctr)++;}}}/*This is the recursive function that process a node and then its children.It builds the node (via makeHashNode) and then adds an entry for thechildrenandparentThese provide the structure for the tree.*/voidprocessNode(xmlNodePtr root, xmlNodePtr parent, unsigned int *ctr, int parentId, char *id, SEXP env, SEXP childrenEnv, SEXP parentEnv, R_XMLSettings *parserSettings){// int i;xmlNodePtr node;SEXP rnode, kids;unsigned int curId = *ctr;char buf[20];SET_NODE_NAME(id, curId, root);if(root->type != XML_XINCLUDE_START && root->type != XML_XINCLUDE_END) {rnode = PROTECT(makeHashNode(root, id, env, parserSettings));defineVar(Rf_install(id), rnode, env);UNPROTECT(1);if(root->parent && root->parent->type != XML_DOCUMENT_NODE && root->parent->type != XML_HTML_DOCUMENT_NODE) {/* Put an entry in the .parents environment for this current id with the single valuewhich is the value of the parentId as a string, equivalent ofassign(curId, parentId, parentEnv)*/SET_NODE_NAME(id, curId, root);SET_NODE_NAME(buf, parentId, parent);defineVar(Rf_install(id), PROTECT(mkString(buf)), parentEnv);UNPROTECT(1);}if(root->children) {/* We have to deal with */unsigned int i = 0;countChildNodes(root, &i);PROTECT(kids = NEW_CHARACTER(i));i = 0; collectChildNodes(root, &i, kids);defineVar(Rf_install(id), kids, childrenEnv);UNPROTECT(1);}(*ctr)++;}if(root->type != XML_XINCLUDE_END) {/* Discard XML_INCLUDE_END nodes, but for XML_INCLUDE_START, we need to specify a different parent,i.e., the parent of the XML_INCLUDE_START node so that it will act as the parent of theincluded nodes.*/xmlNodePtr parent;parent = root->type == XML_XINCLUDE_START ? root->parent : root;for(node = root->children; node; node = node->next)processNode(node, parent, ctr, curId, id, env, childrenEnv, parentEnv, parserSettings);}}/*This is the top-level C entry point for starting the conversion of the node.*/unsigned intconvertDOMToHashTree(xmlNodePtr root, SEXP env, SEXP childrenEnv, SEXP parentEnv, R_XMLSettings *parserSettings){// SEXP rnode;unsigned int ctr = 0;xmlNodePtr tmp;char id[20];memset(id, '\0', sizeof(id));for(tmp = root; tmp; tmp = tmp->next)processNode(tmp, (xmlNodePtr) NULL, &ctr, -1, id, env, childrenEnv, parentEnv, parserSettings);return(ctr);}/*This is the R entry point for the conversion of the node and its subnode.*/SEXPR_convertDOMToHashTree(SEXP rnode, SEXP env, SEXP childrenEnv, SEXP parentEnv){unsigned int ctr;xmlNodePtr node;R_XMLSettings parserSettings;parserSettings.addAttributeNamespaces = 0;parserSettings.converters = NULL_USER_OBJECT;node = (xmlNodePtr) R_ExternalPtrAddr(rnode);ctr = convertDOMToHashTree(node, env, childrenEnv, parentEnv, &parserSettings);return(ScalarInteger(ctr));}