From 6bb7c00782a68632bea7108494a166ba9ec32d72 Mon Sep 17 00:00:00 2001 From: fredkiefer Date: Wed, 21 Mar 2012 09:01:48 +0000 Subject: [PATCH] * Source/NSXMLDocument.m: Correct memory management. * Source/NSXMLElement.m, * Source/NSXMLNode.m: Add partial support for namespaces. Try to bracket more libxml2 functions with version checks. git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@34967 72102866-910b-0410-8b05-ffd578937521 --- ChangeLog | 7 ++ Source/NSXMLDocument.m | 11 +- Source/NSXMLElement.m | 233 +++++++++++++++++++++++++++++++---------- Source/NSXMLNode.m | 232 ++++++++++++++++++++++++++++++++-------- 4 files changed, 383 insertions(+), 100 deletions(-) diff --git a/ChangeLog b/ChangeLog index 11e10b1af..568768d36 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,10 @@ +2012-03-21 Fred Kiefer + + * Source/NSXMLDocument.m: Correct memory management. + * Source/NSXMLElement.m, + * Source/NSXMLNode.m: Add partial support for namespaces. + Try to bracket more libxml2 functions with version checks. + 2012-03-21 Richard Frith-Macdonald * Source/NSXMLDocument.m: diff --git a/Source/NSXMLDocument.m b/Source/NSXMLDocument.m index 01a62570f..86c7de8a6 100644 --- a/Source/NSXMLDocument.m +++ b/Source/NSXMLDocument.m @@ -220,6 +220,10 @@ GS_PRIVATE_INTERNAL(NSXMLDocument) - (void) setCharacterEncoding: (NSString*)encoding { + if (internal->node->encoding != NULL) + { + xmlFree((xmlChar *)internal->node->encoding); + } internal->node->encoding = XMLStringCopy(encoding); } @@ -264,6 +268,7 @@ GS_PRIVATE_INTERNAL(NSXMLDocument) // remove all sub nodes [self setChildren: nil]; + // FIXME: Should we use addChild: here? xmlDocSetRootElement(internal->node, [root _node]); // Do our subNode housekeeping... @@ -279,8 +284,12 @@ GS_PRIVATE_INTERNAL(NSXMLDocument) { if ([version isEqualToString: @"1.0"] || [version isEqualToString: @"1.1"]) { + if (internal->node->version != NULL) + { + xmlFree((xmlChar *)internal->node->version); + } internal->node->version = XMLStringCopy(version); - } + } else { [NSException raise: NSInvalidArgumentException diff --git a/Source/NSXMLElement.m b/Source/NSXMLElement.m index 8b1c37fb8..a99bd9749 100644 --- a/Source/NSXMLElement.m +++ b/Source/NSXMLElement.m @@ -37,15 +37,23 @@ GS_PRIVATE_INTERNAL(NSXMLElement) - (void) dealloc { - /* if (GS_EXISTS_INTERNAL && _internal != nil) { - while ([self childCount] > 0) - { - [self removeChildAtIndex: [self childCount] - 1]; - } + /* + NSArray *subNodes = [internal->subNodes copy]; + NSEnumerator *enumerator = [subNodes objectEnumerator]; + NSXMLNode *subNode; + + while ((subNode = [enumerator nextObject]) != nil) + { + if ([subNode kind] == NSXMLNamespaceKind) + { + [self removeNamespaceForPrefix: [subNode name]]; + } + } + */ } - */ + [super dealloc]; } @@ -132,33 +140,81 @@ GS_PRIVATE_INTERNAL(NSXMLElement) - (NSArray*) elementsForName: (NSString*)name { - NSMutableArray *results = [NSMutableArray arrayWithCapacity: 10]; - xmlNodePtr cur = NULL; + NSString *prefix = [[self class] prefixForName: name]; - for (cur = internal->node->children; cur != NULL; cur = cur->next) + if (nil != prefix) { - NSString *n = StringFromXMLStringPtr(cur->name); - if ([n isEqualToString: name]) - { - NSXMLNode *node = [NSXMLNode _objectForNode: cur]; - [results addObject: node]; - } + NSXMLNode *ns = [self namespaceForPrefix: prefix]; + + if (nil != ns) + { + NSString *localName = [[self class] localNameForName: name]; + + // Namespace nodes have the URI as their stringValue + return [self elementsForLocalName: localName URI: [ns stringValue]]; + } } + + { + NSMutableArray *results = [NSMutableArray arrayWithCapacity: 10]; + xmlNodePtr cur = NULL; + + for (cur = internal->node->children; cur != NULL; cur = cur->next) + { + if (cur->type == XML_ELEMENT_NODE) + { + NSString *n = StringFromXMLStringPtr(cur->name); + if ([n isEqualToString: name]) + { + NSXMLNode *node = [NSXMLNode _objectForNode: cur]; + [results addObject: node]; + } + } + } - return results; + return results; + } } - (NSArray*) elementsForLocalName: (NSString*)localName URI: (NSString*)URI { - [self notImplemented: _cmd]; - return nil; + NSMutableArray *results = [NSMutableArray arrayWithCapacity: 10]; + xmlNodePtr cur = NULL; + const xmlChar *href = XMLSTRING(URI); + xmlNsPtr parentNS = xmlSearchNsByHref(internal->node->doc, internal->node, href); + + for (cur = internal->node->children; cur != NULL; cur = cur->next) + { + if (cur->type == XML_ELEMENT_NODE) + { + xmlNsPtr childNS = parentNS; + + if (cur->nsDef != NULL) + { + childNS = xmlSearchNsByHref(internal->node->doc, cur, href); + } + + if (cur->ns == childNS) + { + NSString *n = StringFromXMLStringPtr(cur->name); + + if ([n isEqualToString: localName]) + { + NSXMLNode *node = [NSXMLNode _objectForNode: cur]; + [results addObject: node]; + } + } + } + } + + return results; } - (void) addAttribute: (NSXMLNode*)attribute { xmlNodePtr node = internal->node; xmlAttrPtr attr = (xmlAttrPtr)[attribute _node]; - xmlAttrPtr oldAttr = xmlHasProp(node, attr->name); + xmlAttrPtr oldAttr; if (nil != [attribute parent]) { @@ -166,6 +222,27 @@ GS_PRIVATE_INTERNAL(NSXMLElement) format: @"Tried to add attribute to multiple parents."]; } + if (attr->ns != NULL) + { + xmlNsPtr ns = attr->ns; + + if (ns->href == NULL) + { + xmlNsPtr newNs = xmlSearchNs(node->doc, node, ns->prefix); + + if (newNs != NULL) + { + ns = newNs; + } + } + + oldAttr = xmlHasNsProp(node, attr->name, ns->href); + } + else + { + oldAttr = xmlHasProp(node, attr->name); + } + if (NULL != oldAttr) { /* @@ -252,36 +329,65 @@ GS_PRIVATE_INTERNAL(NSXMLElement) - (NSXMLNode*) attributeForName: (NSString*)name { - NSXMLNode *result = nil; - xmlNodePtr node = internal->node; - xmlAttrPtr attributeNode = xmlHasProp(node, XMLSTRING(name)); + NSString *prefix = [[self class] prefixForName: name]; - if (NULL != attributeNode) + if (nil != prefix) { - result = [NSXMLNode _objectForNode: (xmlNodePtr)attributeNode]; + NSXMLNode *ns = [self namespaceForPrefix: prefix]; + + if (nil != ns) + { + NSString *localName = [[self class] localNameForName: name]; + + // Namespace nodes have the URI as their stringValue + return [self attributeForLocalName: localName URI: [ns stringValue]]; + } } - return result; + { + NSXMLNode *result = nil; + xmlNodePtr node = internal->node; + xmlAttrPtr attributeNode = xmlHasProp(node, XMLSTRING(name)); + + if (NULL != attributeNode) + { + result = [NSXMLNode _objectForNode: (xmlNodePtr)attributeNode]; + } + + return result; + } } - (NSXMLNode*) attributeForLocalName: (NSString*)localName URI: (NSString*)URI { - [self notImplemented: _cmd]; - return nil; + NSXMLNode *result = nil; + xmlNodePtr node = internal->node; + xmlAttrPtr attributeNode = xmlHasNsProp(node, XMLSTRING(localName), + XMLSTRING(URI)); + + if (NULL != attributeNode) + { + result = [NSXMLNode _objectForNode: (xmlNodePtr)attributeNode]; + } + + return result; } - (void) addNamespace: (NSXMLNode*)aNamespace { xmlNsPtr ns = (xmlNsPtr)[aNamespace _node]; + xmlNodePtr node = internal->node; - if (internal->node->nsDef == NULL) + if (node->nsDef == NULL) { - internal->node->nsDef = ns; + node->nsDef = ns; + [self _addSubNode: aNamespace]; } else { - xmlNsPtr cur = internal->node->nsDef; + xmlNsPtr cur = node->nsDef; + xmlNsPtr last = NULL; const xmlChar *prefix = ns->prefix; while (xmlStrcmp(prefix, cur->prefix) != 0) @@ -289,19 +395,43 @@ GS_PRIVATE_INTERNAL(NSXMLElement) if (cur->next == NULL) { cur->next = ns; + [self _addSubNode: aNamespace]; return; } + last = cur; cur = cur->next; } + + // Found the same prefix + if (cur->href == NULL) + { + // This was a fake namespace we added + if (node->ns == cur) + { + node->ns = ns; + } + if (last == NULL) + { + node->nsDef = ns; + } + else + { + last->next = ns; + } + ns->next = cur->next; + cur->next = NULL; + } } - [self _addSubNode: aNamespace]; + // FIXME: Need to replace fake namespaces in subnodes } - (void) removeNamespaceForPrefix: (NSString*)name { - if (internal->node->nsDef != NULL) + xmlNodePtr node = internal->node; + + if (node->nsDef != NULL) { - xmlNsPtr cur = internal->node->nsDef; + xmlNsPtr cur = node->nsDef; xmlNsPtr last = NULL; const xmlChar *prefix = XMLSTRING(name); @@ -318,6 +448,10 @@ GS_PRIVATE_INTERNAL(NSXMLElement) last->next = cur->next; } cur->next = NULL; + if (node->ns == cur) + { + node->ns = NULL; + } if (cur->_private != NULL) { [self _removeSubNode: (NSXMLNode *)cur->_private]; @@ -350,13 +484,14 @@ GS_PRIVATE_INTERNAL(NSXMLElement) - (NSArray*) namespaces { - // FIXME: Should use xmlGetNsList() + // FIXME: Should we use xmlGetNsList()? NSMutableArray *result = nil; xmlNsPtr ns = internal->node->nsDef; if (ns) { xmlNsPtr cur = NULL; + result = [NSMutableArray array]; for (cur = ns; cur != NULL; cur = cur->next) { @@ -369,20 +504,13 @@ GS_PRIVATE_INTERNAL(NSXMLElement) - (NSXMLNode*) namespaceForPrefix: (NSString*)name { - // FIXME: Should use xmlSearchNs() - xmlNsPtr ns = internal->node->nsDef; + const xmlChar *prefix = XMLSTRING(name); + xmlNodePtr node = internal->node; + xmlNsPtr ns = xmlSearchNs(node->doc, node, prefix); if (ns) { - const xmlChar *prefix = XMLSTRING(name); - xmlNsPtr cur = NULL; - for (cur = ns; cur != NULL; cur = cur->next) - { - if (xmlStrcmp(prefix, cur->prefix) == 0) - { - return [NSXMLNode _objectForNode: (xmlNodePtr)cur]; - } - } + return [NSXMLNode _objectForNode: (xmlNodePtr)ns]; } return nil; @@ -402,21 +530,12 @@ GS_PRIVATE_INTERNAL(NSXMLElement) - (NSString*) resolvePrefixForNamespaceURI: (NSString*)namespaceURI { - // FIXME Should use xmlSearchNsByHref() - xmlNsPtr ns = internal->node->nsDef; + const xmlChar *uri = XMLSTRING(namespaceURI); + xmlNsPtr ns = xmlSearchNsByHref(internal->node->doc, internal->node, uri); if (ns) { - const xmlChar *uri = XMLSTRING(namespaceURI); - xmlNsPtr cur; - - for (cur = ns; cur != NULL; cur = cur->next) - { - if (xmlStrcmp(uri, cur->href) == 0) - { - return StringFromXMLStringPtr(cur->prefix); - } - } + return StringFromXMLStringPtr(ns->prefix); } return nil; diff --git a/Source/NSXMLNode.m b/Source/NSXMLNode.m index d6a84ce89..e36fedefa 100644 --- a/Source/NSXMLNode.m +++ b/Source/NSXMLNode.m @@ -451,7 +451,28 @@ isEqualTree(xmlNodePtr nodeA, xmlNodePtr nodeB) xmlNodePtr curNode = [self _childNodeAtIndex: index]; BOOL mergeTextNodes = NO; // is there a defined option for this? - if (mergeTextNodes || childNode->type == XML_ATTRIBUTE_NODE) + if (childNode->type == XML_NAMESPACE_DECL) + { + // FIXME + return; + } + else + { +#if LIBXML_VERSION >= 20620 + xmlDocPtr tmp = childNode->doc; + + if (tmp) + { + xmlDOMWrapAdoptNode(NULL, childNode->doc, childNode, + parentNode->doc, parentNode, 0); + xmlFreeDoc(tmp); + } +#endif + } + + if (mergeTextNodes || + ((childNode->type != XML_TEXT_NODE) && + (parentNode->type != XML_TEXT_NODE))) { // this uses the built-in libxml functions which merge adjacent text nodes xmlNodePtr addedNode = NULL; @@ -466,33 +487,23 @@ isEqualTree(xmlNodePtr nodeA, xmlNodePtr nodeB) } if (addedNode != childNode) { - [child _setNode: NULL]; - child = [NSXMLNode _objectForNode: addedNode]; + if (addedNode != NULL) + { + // The node was freed while merging + [child _invalidate]; + } + // Don't add as subnode + return; } } - else if (childNode->type == XML_NAMESPACE_DECL) - { - // FIXME - } else { /* here we avoid merging adjacent text nodes by linking * the new node in "by hand" */ - xmlDocPtr tmp = childNode->doc; - - if (tmp) - { - xmlDOMWrapAdoptNode(NULL, childNode->doc, childNode, - parentNode->doc, parentNode, 0); - xmlFreeDoc(tmp); - } - else - { - xmlSetTreeDoc(childNode, parentNode->doc); - } - childNode->parent = parentNode; + xmlSetTreeDoc(childNode, parentNode->doc); + if (curNode) { // insert childNode before an existing node curNode @@ -532,6 +543,7 @@ isEqualTree(xmlNodePtr nodeA, xmlNodePtr nodeB) } [self _addSubNode: child]; + // FIXME: Need to replace fake namespaces in subnodes } - (void) _invalidate @@ -833,12 +845,20 @@ execute_xpath(NSXMLNode *xmlNode, NSString *xpath_exp, NSString *nmspaces) const xmlChar *xmlName = XMLSTRING(name); xmlChar *prefix = NULL; xmlChar *localName; + NSString *result = name; if (NULL == xmlName) return nil; localName = xmlSplitQName2(xmlName, &prefix); - return StringFromXMLStringPtr(localName); + if (NULL != localName) + { + result = StringFromXMLStringPtr(localName); + xmlFree(localName); + xmlFree(prefix); + } + + return result; } + (id) namespaceWithName: (NSString*)name @@ -887,21 +907,22 @@ execute_xpath(NSXMLNode *xmlNode, NSString *xpath_exp, NSString *nmspaces) + (NSString*) prefixForName: (NSString*)name { const xmlChar *xmlName = XMLSTRING(name); + xmlChar *localName; xmlChar *prefix = NULL; + NSString *result = @""; if (NULL == xmlName) return nil; - xmlSplitQName2(xmlName, &prefix); + localName = xmlSplitQName2(xmlName, &prefix); + if (NULL != prefix) + { + result = StringFromXMLStringPtr(prefix); + xmlFree(localName); + xmlFree(prefix); + } - if (NULL == prefix) - { - return @""; - } - else - { - return StringFromXMLStringPtr(prefix); - } + return result; } + (id) processingInstructionWithName: (NSString*)name @@ -1049,7 +1070,10 @@ execute_xpath(NSXMLNode *xmlNode, NSString *xpath_exp, NSString *nmspaces) if (node->type == XML_NAMESPACE_DECL) { ((xmlNsPtr)node)->_private = NULL; - xmlFreeNode(node); + // FIXME: Not sure when to free the node here, + // the same namespace node might be referenced + // from other places. + //xmlFreeNode(node); } else { @@ -1099,6 +1123,7 @@ execute_xpath(NSXMLNode *xmlNode, NSString *xpath_exp, NSString *nmspaces) } else { +#if LIBXML_VERSION >= 20620 if (node->doc) { /* Create a private document and move the node over. @@ -1112,6 +1137,7 @@ execute_xpath(NSXMLNode *xmlNode, NSString *xpath_exp, NSString *nmspaces) xmlDOMWrapAdoptNode(NULL, node->doc, node, tmp, NULL, 0); } else +#endif { // separate our node from its parent and siblings xmlUnlinkNode(node); @@ -1253,12 +1279,22 @@ execute_xpath(NSXMLNode *xmlNode, NSString *xpath_exp, NSString *nmspaces) } case NSXMLEntityDeclarationKind: - // FIXME ... xmlNewEntity doesn't exist in common version of libxml2 -/* +#if LIBXML_VERSION >= 20700 node = xmlNewEntity(NULL, (xmlChar *)"", 0, (xmlChar *)"", (xmlChar *)"", (xmlChar *)""); - */ - node = xmlNewNode(NULL, (xmlChar *)""); +#else + { + xmlEntityPtr ret; + ret = (xmlEntityPtr) xmlMalloc(sizeof(xmlEntity)); + memset(ret, 0, sizeof(xmlEntity)); + ret->type = XML_ENTITY_DECL; + ret->name = xmlStrdup((xmlChar *)""); + ret->ExternalID = xmlStrdup((xmlChar *)""); + ret->SystemID = xmlStrdup((xmlChar *)""); + ret->content = xmlStrdup((xmlChar *)""); + node = ret; + } +#endif break; case NSXMLNotationDeclarationKind: @@ -1333,11 +1369,6 @@ execute_xpath(NSXMLNode *xmlNode, NSString *xpath_exp, NSString *nmspaces) } - (NSString*) localName -{ - return [[self class] localNameForName: [self name]]; -} - -- (NSString*) name { xmlNodePtr node = internal->node; @@ -1356,6 +1387,23 @@ execute_xpath(NSXMLNode *xmlNode, NSString *xpath_exp, NSString *nmspaces) } } +- (NSString*) name +{ + NSString *localName = [self localName]; + + if (nil != localName) + { + NSString *prefix = [self prefix]; + + if ((nil != prefix) && [prefix length] > 0) + { + return [NSString stringWithFormat: @"%@:%@", prefix, localName]; + } + } + + return localName; +} + - (NSXMLNode*) _nodeFollowingInNaturalDirection: (BOOL)forward { NSXMLNode *ancestor = self; @@ -1440,7 +1488,26 @@ execute_xpath(NSXMLNode *xmlNode, NSString *xpath_exp, NSString *nmspaces) - (NSString*) prefix { - return [[self class] prefixForName: [self name]]; + xmlNodePtr node = internal->node; + + if (NULL == node) + { + return nil; + } + if (XML_NAMESPACE_DECL == node->type) + { + return nil; + } + if (XML_ELEMENT_NODE != node->type) + { + return @""; + } + if (node->ns == NULL) + { + return @""; + } + + return StringFromXMLStringPtr(node->ns->prefix); } - (NSXMLNode*) previousNode @@ -1510,7 +1577,76 @@ execute_xpath(NSXMLNode *xmlNode, NSString *xpath_exp, NSString *nmspaces) } else { - xmlNodeSetName(node, XMLSTRING(name)); + const xmlChar *xmlName = XMLSTRING(name); + xmlChar *prefix = NULL; + xmlChar *localName; + + if (NULL == xmlName) + { + xmlNodeSetName(node, (const xmlChar *)""); + return; + } + + localName = xmlSplitQName2(xmlName, &prefix); + if (localName != NULL) + { + // FIXME: Which other nodes get namespaces? + if ((node->type == XML_ATTRIBUTE_NODE) || + (node->type == XML_ELEMENT_NODE)) + { + xmlNsPtr ns; + + // Set namespace + if (node->doc == NULL) + { +#if LIBXML_VERSION >= 20620 + // Create a private document for this node + xmlDocPtr tmp = xmlNewDoc((xmlChar *)"1.0"); + + xmlDOMWrapAdoptNode(NULL, NULL, node, tmp, NULL, 0); +#endif + } + + ns = xmlSearchNs(node->doc, node, prefix); + if (ns) + { + xmlSetNs(node, ns); + } + else + { + // FIXME: Fake the name space and fix it later + // xmlReconciliateNs or xmlDOMWrapReconcileNamespaces ? + // This function is private, so re reimplemt it. + //ns = xmlDOMWrapStoreNs(node->doc, NULL, prefix); + xmlNsPtr oldNs = node->doc->oldNs; + + while (oldNs) + { + if (xmlStrEqual(oldNs->prefix, prefix)) + { + ns = oldNs; + break; + } + if (oldNs->next == NULL) + { + ns = xmlNewNs(NULL, NULL, prefix); + oldNs->next = ns; + break; + } + oldNs = oldNs->next; + } + xmlSetNs(node, ns); + } + } + + xmlNodeSetName(node, localName); + xmlFree(localName); + xmlFree(prefix); + } + else + { + xmlNodeSetName(node, xmlName); + } } } @@ -1610,9 +1746,21 @@ execute_xpath(NSXMLNode *xmlNode, NSString *xpath_exp, NSString *nmspaces) } #endif +#if LIBXML_VERSION >= 20623 ctxt = xmlSaveToBuffer(buffer, "utf-8", xmlOptions); xmlSaveTree(ctxt, internal->node); error = xmlSaveClose(ctxt); +#else + { + xmlDocPtr doc = NULL; + + if (internal->node->type != XML_NAMESPACE_DECL) + { + doc = internal->node->doc; + } + error = xmlNodeDump(buffer, doc, internal->node, 1, 1); + } +#endif if (-1 == error) { xmlBufferFree(buffer);