gambas-source-code/gb.xml/node.cpp
2017-07-30 13:14:48 +02:00

698 lines
20 KiB
C++

/***************************************************************************
(c) 2012 Adrien Prokopowicz <prokopy@users.sourceforge.net>
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 <stdio.h>
#include <stdlib.h>
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<void **>((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) return;
if(node->type == Node::ElementNode)
{
if(mode == GB_STRCOMP_NOCASE || mode == GB_STRCOMP_LANG + GB_STRCOMP_NOCASE)
{
if(compLenTagName == ((Element*)node)->lenTagName)
{
if(strncasecmp(compTagName, ((Element*)node)->tagName, compLenTagName) == 0)
{
*(reinterpret_cast<void **>((GB.Array.Add(*array)))) = XMLNode_GetGBObject(node);
GB.Ref(node->GBObject);
}
}
}
else if(mode == GB_STRCOMP_LIKE)
{
if(GB.MatchString(compTagName, compLenTagName, ((Element*)node)->tagName, ((Element*)node)->lenTagName))
{
*(reinterpret_cast<void **>((GB.Array.Add(*array)))) = XMLNode_GetGBObject(node);
GB.Ref(node->GBObject);
}
}
else
{
if(compLenTagName == ((Element*)node)->lenTagName)
{
if(memcmp(compTagName, ((Element*)node)->tagName, compLenTagName) == 0)
{
*(reinterpret_cast<void **>((GB.Array.Add(*array)))) = XMLNode_GetGBObject(node);
GB.Ref(node->GBObject);
}
}
}
}
if(depth == 1) return;
for(Node *tNode = node->firstChild; tNode != 0; tNode = tNode->nextNode)
{
if(tNode->type == Node::ElementNode)
{
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) return;
if(node->type != Node::ElementNode)
{
if(mode == GB_STRCOMP_NOCASE || mode == GB_STRCOMP_LANG + GB_STRCOMP_NOCASE)
{
if(lenNamespace == ((Element*)node)->lenTagName)
{
if(strncasecmp(cnamespace, ((Element*)node)->tagName, lenNamespace) == 0)
{
*(reinterpret_cast<void **>((GB.Array.Add(*array)))) = XMLNode_GetGBObject(node);
GB.Ref(node->GBObject);
}
}
}
else if(mode == GB_STRCOMP_LIKE)
{
if(GB.MatchString(cnamespace, lenNamespace, ((Element*)node)->tagName, ((Element*)node)->lenTagName))
{
*(reinterpret_cast<void **>((GB.Array.Add(*array)))) = XMLNode_GetGBObject(node);
GB.Ref(node->GBObject);
}
}
else
{
if(lenNamespace == ((Element*)node)->lenTagName)
{
if(memcmp(cnamespace, ((Element*)node)->tagName, lenNamespace) == 0)
{
*(reinterpret_cast<void **>((GB.Array.Add(*array)))) = XMLNode_GetGBObject(node);
GB.Ref(node->GBObject);
}
}
}
}
if(depth == 1) return;
for(Node *tNode = node->firstChild; tNode != 0; tNode = node->nextNode)
{
if(tNode->type == Node::ElementNode)
{
XMLNode_addGBChildrenByTagName(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<void **>((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(node->type == Node::ElementNode)
{
Attribute *attr = XMLElement_GetAttribute((Element*)node, attrName, lenAttrName);
if(attr)
{
if(GB_MatchString(attr->attrValue, attr->lenAttrValue, attrValue, lenAttrValue, mode))
{
*(reinterpret_cast<void **>((GB.Array.Add(*array)))) = XMLNode_GetGBObject(node);
GB.Ref(node->GBObject);
}
}
}
if(depth == 1) return;
for(Node *tNode = node->firstChild; tNode != 0; tNode = tNode->nextNode)
{
if(tNode->type == Node::ElementNode)
{
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<void **>((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;
}