/*************************************************************************** (c) 2012 Adrien Prokopowicz This program 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 2, or (at your option) any later version. This program 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 this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ***************************************************************************/ #include "node.h" #include "element.h" #include "textnode.h" #include "parser.h" #include "document.h" #include "CNode.h" #include "utils.h" #include #include bool Node_NoInstanciate = false;//If true, newly-created Gambas objects won't instanciate a new node void XMLNode_Init(Node *node, Node::Type nodeType) { memset(node, 0, sizeof(Node)); node->type = nodeType; } void XMLNode_Free(Node *&node)//TODO: Handle per-node type freeing { if(!node) return; if(node->userData) { GB.Unref(POINTER(&(node->userData))); node->userData = 0; } switch(node->type) { case Node::ElementNode: XMLElement_Free((Element*)node); break; case Node::DocumentNode: XMLDocument_Release((Document*)node); break; case Node::NodeText: case Node::CDATA: case Node::Comment: XMLTextNode_Free((TextNode*)node); break; default: return; break; } node = 0; } CNode* XMLNode_GetGBObject(Node *node) { if(!node->GBObject) { XMLNode_NewGBObject(node); } return node->GBObject; } void XMLNode_NewGBObject(Node *node) { Node_NoInstanciate = true; switch(node->type) { case Node::ElementNode: node->GBObject = (CNode*)GB.New(GB.FindClass("XmlElement"), 0, 0); break; case Node::CDATA: node->GBObject = (CNode*)GB.New(GB.FindClass("XmlCDataNode"), 0, 0); break; case Node::Comment: node->GBObject = (CNode*)GB.New(GB.FindClass("XmlCommentNode"), 0, 0); break; case Node::NodeText: node->GBObject = (CNode*)GB.New(GB.FindClass("XmlTextNode"), 0, 0); break; case Node::DocumentNode: node->GBObject = (CNode*)GB.New(GB.FindClass("XmlDocument"), 0, 0); break; case Node::AttributeNode: node->GBObject = (CNode*)GB.New(GB.FindClass("XmlNode"), 0, 0); break; default: fprintf(stderr, "FATAL : tried to create a Gambas object with invalid type."); exit(EXIT_FAILURE); break; } node->GBObject->node = node; Node_NoInstanciate = false; } void XMLNode_DestroyGBObject(Node *&node) { if((!node->parent) && (!node->parentDocument)) { XMLNode_Free(node); } else { node->GBObject = 0; } } void XMLNode_DestroyParent(Node *node) { if(!node->GBObject) { XMLNode_Free(node); } else { node->parent = 0; node->parentDocument = 0; } } /***** Node tree *****/ Document* XMLNode_GetOwnerDocument(Node *node) { if(node->type == Node::DocumentNode || node->type == Node::HTMLDocumentNode) { return (Document*) node; } while(node->parent && !node->parentDocument) node = (Node*)(node->parent); return node->parentDocument; } void XMLNode_getGBChildren(Node *node, GB_ARRAY *array) { GB.Array.New(array, GB.FindClass("XmlNode"), node->childCount); if(!(SUPPORT_CHILDREN(node))) return; int i = 0; for(Node *tNode = node->firstChild; tNode != 0; tNode = tNode->nextNode) { *(reinterpret_cast((GB.Array.Get(*array, i)))) = XMLNode_GetGBObject(tNode); GB.Ref(tNode->GBObject); ++i; } } /***** Node tree *****/ void XMLNode_appendChild(Node *node, Node *newChild) { (node->childCount)++; if(!(node->lastChild))//No child { node->firstChild = newChild; node->lastChild = newChild; node->lastChild->previousNode = 0; node->lastChild->nextNode = 0; newChild->parent = node; return; } newChild->previousNode = node->lastChild; node->lastChild->nextNode = newChild; node->lastChild = newChild; node->lastChild->nextNode = 0; newChild->parent = node; } void XMLNode_prependChild(Node *node, Node *newChild) { node->childCount++; if(!node->lastChild)//No child { node->firstChild = newChild; node->lastChild = node->firstChild; node->lastChild->previousNode = 0; node->lastChild->nextNode = 0; newChild->parent = node; return; } newChild->nextNode = node->firstChild; node->firstChild->previousNode = newChild; node->firstChild = newChild; node->firstChild->previousNode = 0; newChild->parent = node; } void XMLNode_removeKeepChild(Node *node, Node *child) { if(child == node->firstChild) node->firstChild = child->nextNode; if(child == node->lastChild) node->lastChild = child->previousNode; if(child->nextNode) child->nextNode->previousNode = child->previousNode; if(child->previousNode) child->previousNode->nextNode = child->nextNode; node->childCount--; } void XMLNode_removeChild(Node *node, Node *child) { XMLNode_removeKeepChild(node, child); XMLNode_DestroyParent(child); } GB_VALUE *aft_args; int aft_argsCount; void XMLNode_appendFromTextSubstCallback(int index, char* *str, int *len) { if(index < 1 || index > aft_argsCount) return; size_t nlen; XML_Format(&(aft_args[index - 1]), *str, nlen); *len = (int)nlen; } void XMLNode_substAppendFromText(Node *node, const char *data, const size_t lenData, GB_VALUE *args, int argsCount) { char *newData; size_t lenNewData; aft_args = args; aft_argsCount = argsCount; newData = GB.SubstString(data, lenData, XMLNode_appendFromTextSubstCallback); lenNewData = GB.StringLength(newData); XMLNode_appendFromText(node, newData, lenNewData); } void XMLNode_appendFromText(Node *node, const char *data, const size_t lenData) { size_t nodeCount = 0; Document *parentDoc = XMLNode_GetOwnerDocument(node); Node **nodes = parse(data, lenData, &nodeCount, parentDoc ? parentDoc->docType : XMLDocumentType); for(size_t i = 0; i < nodeCount; i++) { XMLNode_appendChild(node, nodes[i]); } free(nodes); } void XMLNode_addGBChildrenByTagName(Node *node, const char *compTagName, const size_t compLenTagName, GB_ARRAY *array, const int mode, const int depth) { if(depth == 0 || depth == 1) return; for(Node *tNode = node->firstChild; tNode != 0; tNode = tNode->nextNode) { if(tNode->type != Node::ElementNode) continue; if(GB_MatchString(((Element*)tNode)->tagName, ((Element*)tNode)->lenTagName, compTagName, compLenTagName, mode)) { *(reinterpret_cast((GB.Array.Add(*array)))) = XMLNode_GetGBObject(tNode); GB.Ref(tNode->GBObject); } XMLNode_addGBChildrenByTagName(tNode, compTagName, compLenTagName, array, mode, depth - 1); } } void XMLNode_addGBChildrenByNamespace(Node *node, const char *cnamespace, const size_t lenNamespace, GB_ARRAY *array, const int mode, const int depth) { if(depth == 0 || depth == 1) return; for(Node *tNode = node->firstChild; tNode != 0; tNode = tNode->nextNode) { if(tNode->type != Node::ElementNode) continue; if(GB_MatchString(((Element*)tNode)->tagName, ((Element*)tNode)->lenTagName, cnamespace, lenNamespace, mode)) { *(reinterpret_cast((GB.Array.Add(*array)))) = XMLNode_GetGBObject(tNode); GB.Ref(tNode->GBObject); } XMLNode_addGBChildrenByNamespace(tNode, cnamespace, lenNamespace, array, mode, depth - 1); } } void XMLNode_addGBAllChildren(Node *node, GB_ARRAY *array) { if(SUPPORT_CHILDREN(node)) { for(Node *tNode = node->firstChild; tNode != 0; tNode = tNode->nextNode) { *(reinterpret_cast((GB.Array.Add(*array)))) = XMLNode_GetGBObject(tNode); GB.Ref(tNode->GBObject); XMLNode_addGBAllChildren(tNode, array); } } } void XMLNode_getGBChildrenByTagName(Node *node, const char *ctagName, const size_t clenTagName, GB_ARRAY *array, const int mode, const int depth) { GB.Array.New(array, GB.FindClass("XmlElement"), 0); XMLNode_addGBChildrenByTagName(node, ctagName, clenTagName, array, mode, depth); } void XMLNode_getGBChildrenByNamespace(Node *node, const char *cnamespace, const size_t lenNamespace, GB_ARRAY *array, const int mode, const int depth) { GB.Array.New(array, GB.FindClass("XmlElement"), 0); XMLNode_addGBChildrenByNamespace(node, cnamespace, lenNamespace, array, mode, depth); } void XMLNode_getGBAllChildren(Node *node, GB_ARRAY *array) { GB.Array.New(array, GB.FindClass("XmlNode"), 0); XMLNode_addGBAllChildren(node, array); } void XMLNode_getGBChildrenByAttributeValue(Node *node, const char *attrName, const size_t lenAttrName, const char *attrValue, const size_t lenAttrValue, GB_ARRAY *array, const int mode, const int depth) { GB.Array.New(array, GB.FindClass("XmlElement"), 0); XMLNode_addGBChildrenByAttributeValue(node, attrName, lenAttrName, attrValue, lenAttrValue, array, mode, depth); } void XMLNode_addGBChildrenByAttributeValue(Node *node, const char *attrName, const size_t lenAttrName, const char *attrValue, const size_t lenAttrValue, GB_ARRAY *array, const int mode, const int depth) { if(depth == 0 || depth == 1) return; for(Node *tNode = node->firstChild; tNode != 0; tNode = tNode->nextNode) { if(tNode->type != Node::ElementNode) continue; Attribute *attr = XMLElement_GetAttribute((Element*)tNode, attrName, lenAttrName); if(attr) { if(GB_MatchString(attr->attrValue, attr->lenAttrValue, attrValue, lenAttrValue, mode)) { *(reinterpret_cast((GB.Array.Add(*array)))) = XMLNode_GetGBObject(tNode); GB.Ref(tNode->GBObject); } } XMLNode_addGBChildrenByAttributeValue(tNode, attrName, lenAttrName, attrValue, lenAttrValue, array, mode, depth - 1); } } void XMLNode_getGBChildElements(Node *node, GB_ARRAY *array) { GB.Array.New(array, GB.FindClass("XmlElement"), 0); for(Node *tNode = node->firstChild; tNode != 0; tNode = tNode->nextNode) { if(!SUPPORT_CHILDREN(tNode)) continue; *(reinterpret_cast((GB.Array.Add(*array)))) = XMLNode_GetGBObject(tNode); GB.Ref(tNode->GBObject); } } void XMLNode_addChildrenByTagName(Node *node, const char *compTagName, const size_t compLenTagName, Element** &array, size_t &lenArray, const int depth) { if(depth == 0) return; if(node->type == Node::ElementNode) { if(compLenTagName == ((Element*)node)->lenTagName) { if(memcmp(compTagName, ((Element*)node)->tagName, compLenTagName) == 0) { array = (Element**)realloc(array, sizeof(Element*) * (lenArray + 1)); array[lenArray] = ((Element*)node); ++lenArray; } } } if(depth == 1) return; if(SUPPORT_CHILDREN(node)) { for(Node *tNode = node->firstChild; tNode != 0; tNode = tNode->nextNode) { XMLNode_addChildrenByTagName(tNode, compTagName, compLenTagName, array, lenArray,depth - 1); } } } Element** XMLNode_getChildrenByTagName(Node *node, const char *ctagName, const size_t clenTagName, size_t &lenArray, const int depth) { lenArray = 0; Element **array = 0; XMLNode_addChildrenByTagName(node, ctagName, clenTagName, array, lenArray, depth); return array; } Element* XMLNode_getFirstChildByTagName(const Node *node, const char *ctagName, const size_t clenTagName, const int depth) { if(depth == 0) return 0; if(node->type == Node::ElementNode) { if(((Element*)node)->lenTagName == clenTagName) { if(!memcmp(((Element*)node)->tagName, ctagName, clenTagName)) return ((Element*)node); } } if(depth == 1) return 0; if(!SUPPORT_CHILDREN(node)) return 0; Element *elmt = 0; for(Node *it = node->firstChild; it != 0; it = it->nextNode) { if((it)->type == Node::ElementNode) { elmt = XMLNode_getFirstChildByTagName(it, ctagName, clenTagName, depth - 1); if(elmt) return elmt; } } return 0; } Element* XMLNode_firstChildElement(Node *node) { Node *child = node->firstChild; while(child != 0) { if(child->type == Node::ElementNode) return (Element*)child; child = child->nextNode; } return 0; } Element* XMLNode_lastChildElement(Node *node) { Node *child = node->lastChild; while(child != 0) { if(child->type == Node::ElementNode) return (Element*)child; child = child->previousNode; } return 0; } Element* XMLNode_nextElement(Node *node) { Node *child = node->nextNode; while(child != 0) { if(child->type == Node::ElementNode) return (Element*)child; child = child->nextNode; } return 0; } Element* XMLNode_previousElement(const Node *node) { Node *child = node->previousNode; while(child != 0) { if(child->type == Node::ElementNode) return (Element*)child; child = child->previousNode; } return 0; } bool XMLNode_insertAfter(Node *node, Node *child, Node *newChild) { if(child->parent != node) return false; newChild->nextNode = child->nextNode; newChild->previousNode = child; if(child->nextNode) { child->nextNode->previousNode = newChild; } if(child == node->lastChild) { node->lastChild = newChild; } child->nextNode = newChild; newChild->parent = node; node->childCount++; return true; } bool XMLNode_insertBefore(Node *node, Node *child, Node *newChild) { if(child->parent != node) return false; newChild->nextNode = child; newChild->previousNode = child->previousNode; if(child->previousNode) { child->previousNode->nextNode = newChild; } if(child == node->firstChild) { node->firstChild = newChild; } child->previousNode = newChild; newChild->parent = node; node->childCount++; return true; } void XMLNode_replaceChild(Node *node, Node *oldChild, Node *newChild) { if(XMLNode_insertBefore(node, oldChild, newChild)) XMLNode_removeChild(node, oldChild); } void XMLNode_appendText(Node *node, const char *data, const size_t lenData) { if(node->lastChild && node->lastChild->type == Node::NodeText) { TextNode *text = (TextNode*)node->lastChild; text->content = (char*)realloc(text->content, lenData + text->lenContent); memcpy(text->content + text->lenContent, data, lenData); text->lenContent += lenData; } else { TextNode *text = XMLTextNode_New(data, lenData); XMLNode_appendChild(node, text); } } void XMLNode_clearChildren(Node *node) { if(node->childCount == 0) return; register Node* prevChild = 0; register Node* child = 0; for(child = node->firstChild->nextNode; child != 0; child = child->nextNode) { prevChild = child->previousNode; prevChild->nextNode = 0; prevChild->previousNode = 0; XMLNode_DestroyParent(prevChild); } node->lastChild->nextNode = 0; node->lastChild->previousNode = 0; XMLNode_DestroyParent(node->lastChild); node->childCount = 0; node->lastChild = 0; node->firstChild = 0; } void XMLNode_setTextContent(Node *node, const char *content, const size_t lenContent) { switch(node->type) { case Node::ElementNode: XMLElement_SetTextContent((Element*)node, content, lenContent); break; case Node::AttributeNode: XMLAttribute_SetValue((Attribute*)node, content, lenContent); default: return; } } GB_VARIANT* XMLNode_getUserData(Node *node, const char *key, const size_t lenkey) { if(!node->userData) return 0; GB_VARIANT *srcValue = new GB_VARIANT; if (GB.Collection.Get(node->userData, key, lenkey, srcValue)) return 0; return srcValue; } void XMLNode_addUserData(Node *node, const char *key, const size_t lenkey, GB_VARIANT *value) { if(!node->userData) { GB.Collection.New(POINTER(&(node->userData)), GB_COMP_BINARY); } GB.Collection.Set(node->userData, key, lenkey, value); } bool XMLNode_NoInstanciate() { return Node_NoInstanciate; } void XML_ReturnNode(Node *node) { if(!node) { GB.ReturnNull(); return; } if(!(node->GBObject)) { XMLNode_NewGBObject(node); } GB.ReturnObject(node->GBObject); } Element *XMLNode_getFirstChildByAttributeValue(Node *node, const char *attrName, const size_t lenAttrName, const char *attrValue, const size_t lenAttrValue, const int mode, const int depth) { if(depth == 0) return 0; if(SUPPORT_CHILDREN(node)) { for(Node *child = node->firstChild; child != 0; child = child->nextNode) { if(child->type == Node::ElementNode) { Element *elmt; Attribute *attr; attr = XMLElement_GetAttribute((Element*)child, attrName, lenAttrName); if(attr) { if(GB_MatchString(attr->attrValue, attr->lenAttrValue, attrValue, lenAttrValue, mode)) { return (Element*)child; } } elmt = XMLNode_getFirstChildByAttributeValue(child, attrName, lenAttrName, attrValue, lenAttrValue, mode, depth - 1); if(elmt) return elmt; } } } return 0; }