2009-02-09 16:16:11 +00:00
|
|
|
/* Implementation for NSXMLNode for GNUStep
|
|
|
|
Copyright (C) 2008 Free Software Foundation, Inc.
|
|
|
|
|
|
|
|
Written by: Richard Frith-Macdonald <rfm@gnu.org>
|
|
|
|
Created: September 2008
|
|
|
|
|
|
|
|
This file is part of the GNUstep Base Library.
|
|
|
|
|
|
|
|
This library is free software; you can redistribute it and/or
|
|
|
|
modify it under the terms of the GNU Lesser General Public
|
|
|
|
License as published by the Free Software Foundation; either
|
|
|
|
version 3 of the License, or (at your option) any later version.
|
2011-10-01 18:43:29 +00:00
|
|
|
|
2009-02-09 16:16:11 +00:00
|
|
|
This library 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
|
|
|
|
Library General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU Lesser General Public
|
|
|
|
License along with this library; if not, write to the Free
|
|
|
|
Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
|
|
|
Boston, MA 02111 USA.
|
|
|
|
*/
|
|
|
|
|
2010-02-19 08:12:46 +00:00
|
|
|
#import "common.h"
|
|
|
|
|
2012-03-12 13:27:32 +00:00
|
|
|
#define GSInternal NSXMLNodeInternal
|
|
|
|
#define GS_XMLNODETYPE xmlNode
|
|
|
|
|
2012-01-05 20:40:12 +00:00
|
|
|
#import "NSXMLPrivate.h"
|
|
|
|
#import "GSInternal.h"
|
2012-01-04 09:20:32 +00:00
|
|
|
GS_PRIVATE_INTERNAL(NSXMLNode)
|
2011-10-01 18:43:29 +00:00
|
|
|
|
2012-02-20 03:40:15 +00:00
|
|
|
#if defined(HAVE_LIBXML)
|
|
|
|
|
2012-03-01 06:11:30 +00:00
|
|
|
static int
|
|
|
|
countAttributes(xmlNodePtr node)
|
2012-02-20 03:40:15 +00:00
|
|
|
{
|
|
|
|
int count = 0;
|
|
|
|
xmlAttrPtr attr = node->properties;
|
|
|
|
|
|
|
|
while (attr)
|
|
|
|
{
|
|
|
|
count++;
|
|
|
|
attr = attr->next;
|
|
|
|
}
|
|
|
|
|
|
|
|
return count;
|
|
|
|
}
|
|
|
|
|
2012-03-01 06:11:30 +00:00
|
|
|
static BOOL
|
|
|
|
isEqualAttr(const xmlAttrPtr attrA, const xmlAttrPtr attrB)
|
2012-02-20 03:40:15 +00:00
|
|
|
{
|
2012-03-01 06:11:30 +00:00
|
|
|
xmlChar *contentA;
|
|
|
|
xmlChar *contentB;
|
|
|
|
const xmlChar *nameA;
|
|
|
|
const xmlChar *nameB;
|
2012-02-20 03:40:15 +00:00
|
|
|
|
2012-03-01 06:11:30 +00:00
|
|
|
/* what has to be the same for two attributes to be equal --
|
|
|
|
* just their values??
|
|
|
|
*/
|
2012-02-21 08:21:12 +00:00
|
|
|
if (attrB == attrA)
|
2012-02-20 03:40:15 +00:00
|
|
|
{
|
|
|
|
return YES;
|
|
|
|
}
|
|
|
|
|
2012-02-21 08:21:12 +00:00
|
|
|
if (attrA == NULL || attrB == NULL)
|
2012-02-20 03:40:15 +00:00
|
|
|
{
|
|
|
|
return NO;
|
|
|
|
}
|
|
|
|
|
2012-02-21 08:21:12 +00:00
|
|
|
nameA = attrA->name;
|
|
|
|
nameB = attrB->name;
|
2012-02-20 03:40:15 +00:00
|
|
|
|
2012-02-21 08:21:12 +00:00
|
|
|
if (xmlStrcmp(nameA, nameB) == 0)
|
2012-02-20 03:40:15 +00:00
|
|
|
{
|
2012-02-21 08:21:12 +00:00
|
|
|
// get the content...
|
|
|
|
contentA = xmlNodeGetContent((const xmlNodePtr)attrA);
|
|
|
|
contentB = xmlNodeGetContent((const xmlNodePtr)attrB);
|
|
|
|
|
|
|
|
if (xmlStrcmp(contentA, contentB) == 0)
|
2012-02-20 03:40:15 +00:00
|
|
|
{
|
2012-02-21 08:21:12 +00:00
|
|
|
xmlFree(contentA);
|
|
|
|
xmlFree(contentB);
|
2012-02-20 03:40:15 +00:00
|
|
|
return YES;
|
|
|
|
}
|
2012-02-21 08:21:12 +00:00
|
|
|
xmlFree(contentA);
|
|
|
|
xmlFree(contentB);
|
2012-02-20 03:40:15 +00:00
|
|
|
return NO;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NO;
|
|
|
|
}
|
|
|
|
|
2012-03-01 06:11:30 +00:00
|
|
|
static xmlAttrPtr
|
|
|
|
findAttrWithName(xmlNodePtr node, const xmlChar* targetName)
|
2012-02-20 03:40:15 +00:00
|
|
|
{
|
|
|
|
xmlAttrPtr attr = node->properties;
|
|
|
|
|
|
|
|
// find an attr in node with the given name, and return it, else NULL
|
2012-02-21 08:21:12 +00:00
|
|
|
while ((attr != NULL) && xmlStrcmp(attr->name, targetName) != 0)
|
2012-02-20 03:40:15 +00:00
|
|
|
{
|
|
|
|
attr = attr->next;
|
|
|
|
}
|
|
|
|
|
|
|
|
return attr;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-03-01 06:11:30 +00:00
|
|
|
static BOOL
|
|
|
|
isEqualAttributes(xmlNodePtr nodeA, xmlNodePtr nodeB)
|
2012-02-20 03:40:15 +00:00
|
|
|
{
|
|
|
|
xmlAttrPtr attrA = NULL;
|
|
|
|
|
|
|
|
if (countAttributes(nodeA) != countAttributes(nodeB))
|
|
|
|
return NO;
|
|
|
|
|
|
|
|
attrA = nodeA->properties;
|
2012-02-21 08:21:12 +00:00
|
|
|
while (attrA)
|
2012-02-20 03:40:15 +00:00
|
|
|
{
|
2012-02-21 08:21:12 +00:00
|
|
|
xmlAttrPtr attrB = findAttrWithName(nodeB, attrA->name);
|
2012-02-20 03:40:15 +00:00
|
|
|
if (!isEqualAttr(attrA, attrB))
|
|
|
|
{
|
|
|
|
return NO;
|
|
|
|
}
|
|
|
|
attrA = attrA->next;
|
|
|
|
}
|
|
|
|
|
|
|
|
return YES;
|
|
|
|
}
|
|
|
|
|
2012-03-01 06:11:30 +00:00
|
|
|
static BOOL
|
|
|
|
isEqualNode(xmlNodePtr nodeA, xmlNodePtr nodeB)
|
2012-02-20 03:40:15 +00:00
|
|
|
{
|
|
|
|
if (nodeA == nodeB)
|
|
|
|
return YES;
|
|
|
|
|
|
|
|
if (nodeA->type != nodeB->type)
|
|
|
|
return NO;
|
|
|
|
|
2012-03-12 19:50:51 +00:00
|
|
|
if (nodeA->type == XML_NAMESPACE_DECL)
|
|
|
|
{
|
|
|
|
xmlNsPtr nsA = (xmlNsPtr)nodeA;
|
|
|
|
xmlNsPtr nsB = (xmlNsPtr)nodeB;
|
|
|
|
|
|
|
|
if (xmlStrcmp(nsA->href, nsB->href) != 0)
|
|
|
|
{
|
|
|
|
return NO;
|
|
|
|
}
|
|
|
|
if (xmlStrcmp(nsA->prefix, nsB->prefix) != 0)
|
|
|
|
{
|
|
|
|
return NO;
|
|
|
|
}
|
|
|
|
|
|
|
|
return YES;
|
|
|
|
}
|
|
|
|
|
2012-02-21 08:21:12 +00:00
|
|
|
if (xmlStrcmp(nodeA->name, nodeB->name) != 0)
|
2012-02-20 03:40:15 +00:00
|
|
|
return NO;
|
|
|
|
|
|
|
|
if (nodeA->type == XML_ELEMENT_NODE)
|
|
|
|
{
|
|
|
|
xmlChar *contentA = NULL;
|
|
|
|
xmlChar *contentB = NULL;
|
|
|
|
|
|
|
|
if (!isEqualAttributes(nodeA, nodeB))
|
|
|
|
{
|
|
|
|
return NO;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get the value of any text node underneath the current element.
|
|
|
|
contentA = xmlNodeGetContent((const xmlNodePtr)nodeA);
|
|
|
|
contentB = xmlNodeGetContent((const xmlNodePtr)nodeB);
|
2012-02-21 08:21:12 +00:00
|
|
|
if (xmlStrcmp(contentA, contentB) != 0)
|
2012-02-20 03:40:15 +00:00
|
|
|
{
|
2012-02-21 08:21:12 +00:00
|
|
|
xmlFree(contentA);
|
|
|
|
xmlFree(contentB);
|
2012-02-20 03:40:15 +00:00
|
|
|
return NO;
|
|
|
|
}
|
2012-02-21 08:21:12 +00:00
|
|
|
xmlFree(contentA);
|
|
|
|
xmlFree(contentB);
|
2012-02-20 03:40:15 +00:00
|
|
|
}
|
2012-03-01 08:41:20 +00:00
|
|
|
// FIXME: Handle more node types
|
2012-02-20 03:40:15 +00:00
|
|
|
|
|
|
|
return YES;
|
|
|
|
}
|
|
|
|
|
2012-03-01 06:11:30 +00:00
|
|
|
static BOOL
|
|
|
|
isEqualTree(xmlNodePtr nodeA, xmlNodePtr nodeB)
|
2012-02-20 03:40:15 +00:00
|
|
|
{
|
2012-02-26 19:58:25 +00:00
|
|
|
xmlNodePtr childA;
|
|
|
|
xmlNodePtr childB;
|
|
|
|
|
2012-02-21 08:21:12 +00:00
|
|
|
if (nodeA == nodeB)
|
2012-02-20 03:40:15 +00:00
|
|
|
{
|
|
|
|
return YES;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (nodeA == NULL || nodeB == NULL)
|
|
|
|
{
|
|
|
|
return NO;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!isEqualNode(nodeA, nodeB))
|
|
|
|
{
|
|
|
|
return NO;
|
|
|
|
}
|
|
|
|
|
2012-03-12 19:50:51 +00:00
|
|
|
if (nodeA->type == XML_NAMESPACE_DECL)
|
|
|
|
{
|
|
|
|
return YES;
|
|
|
|
}
|
|
|
|
|
2012-02-26 19:58:25 +00:00
|
|
|
// Check children
|
|
|
|
childA = nodeA->children;
|
|
|
|
childB = nodeB->children;
|
|
|
|
while (isEqualTree(childA, childB))
|
2012-02-20 03:40:15 +00:00
|
|
|
{
|
2012-02-26 19:58:25 +00:00
|
|
|
if (childA == NULL)
|
|
|
|
{
|
|
|
|
return YES;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
childA = childA->next;
|
|
|
|
childB = childB->next;
|
|
|
|
}
|
2012-02-20 03:40:15 +00:00
|
|
|
}
|
|
|
|
|
2012-02-26 19:58:25 +00:00
|
|
|
return NO;
|
2012-02-20 03:40:15 +00:00
|
|
|
}
|
|
|
|
|
2012-03-12 18:13:27 +00:00
|
|
|
/* FIXME ... the libxml2 data structure representing a namespace has a
|
|
|
|
* completely different layout from that of almost all other nodes, so
|
2012-03-12 19:50:51 +00:00
|
|
|
* the generic xmlNode code won't work and we need to check the type
|
|
|
|
* in every method we use!
|
2012-03-12 18:13:27 +00:00
|
|
|
*/
|
2012-02-20 03:40:15 +00:00
|
|
|
@implementation NSXMLNode (Private)
|
|
|
|
- (void *) _node
|
|
|
|
{
|
2012-03-12 13:27:32 +00:00
|
|
|
return internal->node;
|
2012-02-20 03:40:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
- (void) _setNode: (void *)_anode
|
|
|
|
{
|
2012-03-04 21:40:39 +00:00
|
|
|
DESTROY(internal->subNodes);
|
2012-03-12 18:13:27 +00:00
|
|
|
internal->node = _anode;
|
|
|
|
if (internal->node != NULL)
|
2012-03-04 21:40:39 +00:00
|
|
|
{
|
2012-03-12 18:13:27 +00:00
|
|
|
if (internal->node->type == XML_NAMESPACE_DECL)
|
|
|
|
{
|
|
|
|
((xmlNsPtr)(internal->node))->_private = self;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
internal->node->_private = self;
|
|
|
|
}
|
2012-03-04 21:40:39 +00:00
|
|
|
}
|
2012-02-20 03:40:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
+ (NSXMLNode *) _objectForNode: (xmlNodePtr)node
|
|
|
|
{
|
|
|
|
NSXMLNode *result = nil;
|
|
|
|
|
|
|
|
if (node)
|
|
|
|
{
|
2012-03-12 18:13:27 +00:00
|
|
|
if (node->type == XML_NAMESPACE_DECL)
|
|
|
|
{
|
|
|
|
result = ((xmlNs *)node)->_private;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
result = node->_private;
|
|
|
|
}
|
2012-02-22 10:55:12 +00:00
|
|
|
if (result == nil)
|
2012-02-20 03:40:15 +00:00
|
|
|
{
|
2012-02-27 12:53:25 +00:00
|
|
|
Class cls;
|
|
|
|
NSXMLNodeKind kind;
|
2012-02-22 10:55:12 +00:00
|
|
|
xmlElementType type = node->type;
|
2012-03-12 18:13:27 +00:00
|
|
|
xmlDoc *docNode;
|
|
|
|
NSXMLDocument *doc = nil;
|
2012-03-08 00:15:08 +00:00
|
|
|
|
2012-02-27 12:53:25 +00:00
|
|
|
switch (type)
|
2012-02-20 03:40:15 +00:00
|
|
|
{
|
2012-03-01 06:11:30 +00:00
|
|
|
case XML_DOCUMENT_NODE:
|
2012-03-08 00:15:08 +00:00
|
|
|
case XML_HTML_DOCUMENT_NODE:
|
2012-03-01 06:11:30 +00:00
|
|
|
cls = [NSXMLDocument class];
|
|
|
|
kind = NSXMLDocumentKind;
|
|
|
|
break;
|
|
|
|
case XML_ELEMENT_NODE:
|
|
|
|
cls = [NSXMLElement class];
|
|
|
|
kind = NSXMLElementKind;
|
|
|
|
break;
|
2012-03-09 10:08:48 +00:00
|
|
|
case XML_DTD_NODE:
|
|
|
|
cls = [NSXMLDTD class];
|
|
|
|
kind = NSXMLDTDKind;
|
2012-03-01 06:11:30 +00:00
|
|
|
break;
|
2012-03-09 10:08:48 +00:00
|
|
|
case XML_ATTRIBUTE_DECL:
|
|
|
|
cls = [NSXMLDTDNode class];
|
|
|
|
kind = NSXMLAttributeDeclarationKind;
|
2012-03-01 06:11:30 +00:00
|
|
|
break;
|
2012-03-09 10:08:48 +00:00
|
|
|
case XML_ELEMENT_DECL:
|
|
|
|
cls = [NSXMLDTDNode class];
|
|
|
|
kind = NSXMLElementDeclarationKind;
|
|
|
|
break;
|
|
|
|
case XML_ENTITY_DECL:
|
|
|
|
cls = [NSXMLDTDNode class];
|
|
|
|
kind = NSXMLEntityDeclarationKind;
|
|
|
|
break;
|
|
|
|
case XML_NOTATION_NODE:
|
|
|
|
cls = [NSXMLDTDNode class];
|
|
|
|
kind = NSXMLNotationDeclarationKind;
|
2012-03-01 06:11:30 +00:00
|
|
|
break;
|
|
|
|
case XML_ATTRIBUTE_NODE:
|
|
|
|
cls = [NSXMLNode class];
|
|
|
|
kind = NSXMLAttributeKind;
|
|
|
|
break;
|
2012-03-08 00:15:08 +00:00
|
|
|
case XML_CDATA_SECTION_NODE:
|
|
|
|
cls = [NSXMLNode class];
|
|
|
|
kind = NSXMLTextKind;
|
2012-03-09 10:08:48 +00:00
|
|
|
// FIXME: Should set option
|
2012-03-08 00:15:08 +00:00
|
|
|
break;
|
2012-03-09 10:08:48 +00:00
|
|
|
case XML_COMMENT_NODE:
|
2012-03-08 00:15:08 +00:00
|
|
|
cls = [NSXMLNode class];
|
2012-03-09 10:08:48 +00:00
|
|
|
kind = NSXMLCommentKind;
|
2012-03-08 00:15:08 +00:00
|
|
|
break;
|
2012-03-09 10:08:48 +00:00
|
|
|
case XML_NAMESPACE_DECL:
|
2012-03-08 00:15:08 +00:00
|
|
|
cls = [NSXMLNode class];
|
2012-03-09 10:08:48 +00:00
|
|
|
kind = NSXMLNamespaceKind;
|
2012-03-08 00:15:08 +00:00
|
|
|
break;
|
2012-03-09 10:08:48 +00:00
|
|
|
case XML_PI_NODE:
|
2012-03-08 00:15:08 +00:00
|
|
|
cls = [NSXMLNode class];
|
2012-03-09 10:08:48 +00:00
|
|
|
kind = NSXMLProcessingInstructionKind;
|
2012-03-08 00:15:08 +00:00
|
|
|
break;
|
2012-03-09 10:08:48 +00:00
|
|
|
case XML_TEXT_NODE:
|
2012-03-08 00:15:08 +00:00
|
|
|
cls = [NSXMLNode class];
|
2012-03-09 10:08:48 +00:00
|
|
|
kind = NSXMLTextKind;
|
2012-03-08 00:15:08 +00:00
|
|
|
break;
|
2012-03-01 06:11:30 +00:00
|
|
|
default:
|
|
|
|
NSLog(@"ERROR: _objectForNode: called with a node of type %d",
|
|
|
|
type);
|
|
|
|
return nil;
|
|
|
|
break;
|
2012-02-20 03:40:15 +00:00
|
|
|
}
|
2012-03-12 18:13:27 +00:00
|
|
|
if (node->type == XML_NAMESPACE_DECL)
|
|
|
|
{
|
2012-03-14 10:09:15 +00:00
|
|
|
docNode = NULL;
|
2012-03-12 18:13:27 +00:00
|
|
|
}
|
|
|
|
else
|
2012-02-27 12:53:25 +00:00
|
|
|
{
|
2012-03-12 18:13:27 +00:00
|
|
|
docNode = node->doc;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((docNode != NULL) && ((xmlNodePtr)docNode != node))
|
|
|
|
{
|
|
|
|
doc = (NSXMLDocument*)[self _objectForNode: (xmlNodePtr)docNode];
|
2012-02-27 12:53:25 +00:00
|
|
|
if (doc != nil)
|
|
|
|
{
|
|
|
|
cls = [[doc class] replacementClassForClass: cls];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
result = [[cls alloc] _initWithNode: node kind: kind];
|
2012-02-20 03:40:15 +00:00
|
|
|
AUTORELEASE(result);
|
2012-03-12 18:13:27 +00:00
|
|
|
if (node->type == XML_NAMESPACE_DECL)
|
2012-02-20 03:40:15 +00:00
|
|
|
{
|
2012-03-12 18:13:27 +00:00
|
|
|
[doc _addSubNode: result];
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (node->parent)
|
|
|
|
{
|
|
|
|
NSXMLNode *parent = [self _objectForNode: node->parent];
|
|
|
|
[parent _addSubNode: result];
|
|
|
|
}
|
2012-02-20 03:40:15 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2012-02-22 10:55:12 +00:00
|
|
|
- (void) _addSubNode: (NSXMLNode *)subNode
|
2012-02-20 03:40:15 +00:00
|
|
|
{
|
|
|
|
if (!internal->subNodes)
|
|
|
|
internal->subNodes = [[NSMutableArray alloc] init];
|
2012-02-23 17:57:50 +00:00
|
|
|
if ([internal->subNodes indexOfObjectIdenticalTo: subNode] == NSNotFound)
|
2012-02-20 03:40:15 +00:00
|
|
|
{
|
2012-02-23 17:57:50 +00:00
|
|
|
[internal->subNodes addObject: subNode];
|
2012-02-20 03:40:15 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-02-22 10:55:12 +00:00
|
|
|
- (void) _removeSubNode: (NSXMLNode *)subNode
|
2012-02-20 03:40:15 +00:00
|
|
|
{
|
2012-03-01 06:11:30 +00:00
|
|
|
// retain temporarily so we can safely remove from our subNodes list first
|
|
|
|
[subNode retain];
|
2012-02-23 17:57:50 +00:00
|
|
|
[internal->subNodes removeObjectIdenticalTo: subNode];
|
2012-03-04 21:40:39 +00:00
|
|
|
// release temporary hold. Apple seems to do an autorelease here.
|
|
|
|
[subNode autorelease];
|
2012-02-20 03:40:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
- (void) _createInternal
|
|
|
|
{
|
|
|
|
GS_CREATE_INTERNAL(NSXMLNode);
|
|
|
|
}
|
|
|
|
|
2012-02-22 10:55:12 +00:00
|
|
|
- (id) _initWithNode: (xmlNodePtr)node kind: (NSXMLNodeKind)kind
|
2012-02-20 03:40:15 +00:00
|
|
|
{
|
|
|
|
if ((self = [super init]))
|
|
|
|
{
|
|
|
|
[self _createInternal];
|
2012-02-22 10:55:12 +00:00
|
|
|
[self _setNode: node];
|
2012-02-20 03:40:15 +00:00
|
|
|
internal->kind = kind;
|
|
|
|
}
|
|
|
|
return self;
|
|
|
|
}
|
|
|
|
|
2012-02-22 10:55:12 +00:00
|
|
|
- (xmlNodePtr) _childNodeAtIndex: (NSUInteger)index
|
|
|
|
{
|
|
|
|
NSUInteger count = 0;
|
2012-03-12 13:27:32 +00:00
|
|
|
xmlNodePtr node = internal->node;
|
2012-03-12 18:13:27 +00:00
|
|
|
xmlNodePtr children;
|
2012-03-01 06:11:30 +00:00
|
|
|
|
2012-03-12 18:13:27 +00:00
|
|
|
if (node->type == XML_NAMESPACE_DECL)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
children = node->children;
|
2012-02-22 10:55:12 +00:00
|
|
|
if (!children)
|
|
|
|
return NULL; // the Cocoa docs say it returns nil if there are no children
|
|
|
|
|
2012-03-01 06:11:30 +00:00
|
|
|
while (children != NULL && count++ < index)
|
2012-02-22 10:55:12 +00:00
|
|
|
{
|
2012-03-01 06:11:30 +00:00
|
|
|
children = children->next;
|
2012-02-22 10:55:12 +00:00
|
|
|
}
|
|
|
|
|
2012-03-01 06:11:30 +00:00
|
|
|
if (count < index)
|
2012-02-22 10:55:12 +00:00
|
|
|
[NSException raise: NSRangeException format: @"child index too large"];
|
|
|
|
|
|
|
|
return children;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void) _insertChild: (NSXMLNode*)child atIndex: (NSUInteger)index
|
|
|
|
{
|
2012-03-01 06:11:30 +00:00
|
|
|
/* this private method provides the common insertion
|
|
|
|
* implementation used by NSXMLElement and NSXMLDocument
|
|
|
|
*/
|
2012-02-22 10:55:12 +00:00
|
|
|
|
|
|
|
// Get all of the nodes...
|
2012-03-12 13:27:32 +00:00
|
|
|
xmlNodePtr parentNode = internal->node; // we are the parent
|
2012-03-01 08:41:20 +00:00
|
|
|
xmlNodePtr childNode = (xmlNodePtr)[child _node];
|
2012-02-22 10:55:12 +00:00
|
|
|
xmlNodePtr curNode = [self _childNodeAtIndex: index];
|
|
|
|
BOOL mergeTextNodes = NO; // is there a defined option for this?
|
|
|
|
|
|
|
|
if (mergeTextNodes || childNode->type == XML_ATTRIBUTE_NODE)
|
|
|
|
{
|
|
|
|
// this uses the built-in libxml functions which merge adjacent text nodes
|
|
|
|
xmlNodePtr addedNode = NULL;
|
|
|
|
|
2012-03-01 08:41:20 +00:00
|
|
|
if (curNode == NULL)
|
2012-02-22 10:55:12 +00:00
|
|
|
{
|
|
|
|
addedNode = xmlAddChild(parentNode, childNode);
|
|
|
|
}
|
2012-03-01 08:41:20 +00:00
|
|
|
else
|
2012-02-22 10:55:12 +00:00
|
|
|
{
|
|
|
|
addedNode = xmlAddPrevSibling(curNode, childNode);
|
|
|
|
}
|
|
|
|
if (addedNode != childNode)
|
|
|
|
{
|
2012-02-23 17:57:50 +00:00
|
|
|
[child _setNode: NULL];
|
|
|
|
child = [NSXMLNode _objectForNode: addedNode];
|
2012-02-22 10:55:12 +00:00
|
|
|
}
|
|
|
|
}
|
2012-03-12 19:50:51 +00:00
|
|
|
else if (childNode->type == XML_NAMESPACE_DECL)
|
|
|
|
{
|
|
|
|
// FIXME
|
|
|
|
}
|
2012-02-22 10:55:12 +00:00
|
|
|
else
|
|
|
|
{
|
2012-03-01 06:11:30 +00:00
|
|
|
/* here we avoid merging adjacent text nodes by linking
|
|
|
|
* the new node in "by hand"
|
|
|
|
*/
|
2012-02-22 10:55:12 +00:00
|
|
|
childNode->parent = parentNode;
|
2012-03-08 19:59:12 +00:00
|
|
|
xmlSetTreeDoc(childNode, parentNode->doc);
|
2012-02-22 10:55:12 +00:00
|
|
|
if (curNode)
|
|
|
|
{
|
|
|
|
// insert childNode before an existing node curNode
|
|
|
|
xmlNodePtr prevNode = curNode->prev;
|
|
|
|
curNode->prev = childNode;
|
|
|
|
childNode->next = curNode;
|
|
|
|
if (prevNode)
|
|
|
|
{
|
|
|
|
childNode->prev = prevNode;
|
|
|
|
prevNode->next = childNode;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2012-03-01 06:11:30 +00:00
|
|
|
/* in this case, this is the new "first child",
|
|
|
|
* so update our parent to point to it
|
|
|
|
*/
|
2012-02-22 10:55:12 +00:00
|
|
|
parentNode->children = childNode;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// not inserting before an existing node... add as new "last child"
|
|
|
|
xmlNodePtr formerLastChild = parentNode->last;
|
|
|
|
if (formerLastChild)
|
|
|
|
{
|
|
|
|
formerLastChild->next = childNode;
|
|
|
|
childNode->prev = formerLastChild;
|
|
|
|
parentNode->last = childNode;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// no former children -- this is the first
|
|
|
|
parentNode->children = childNode;
|
|
|
|
parentNode->last = childNode;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-02-23 17:57:50 +00:00
|
|
|
[self _addSubNode: child];
|
2012-02-22 10:55:12 +00:00
|
|
|
}
|
|
|
|
|
2012-02-20 03:40:15 +00:00
|
|
|
- (void) _invalidate
|
|
|
|
{
|
|
|
|
internal->kind = NSXMLInvalidKind;
|
2012-02-22 10:55:12 +00:00
|
|
|
[self _setNode: NULL];
|
2012-02-20 03:40:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
@end
|
|
|
|
|
2012-03-01 06:11:30 +00:00
|
|
|
static void
|
|
|
|
clearPrivatePointers(xmlNodePtr aNode)
|
2012-02-20 03:40:15 +00:00
|
|
|
{
|
|
|
|
if (!aNode)
|
|
|
|
return;
|
2012-03-12 18:13:27 +00:00
|
|
|
|
|
|
|
if (aNode->type == XML_NAMESPACE_DECL)
|
|
|
|
{
|
|
|
|
xmlNsPtr ns = (xmlNsPtr)aNode;
|
|
|
|
|
|
|
|
ns->_private = NULL;
|
|
|
|
clearPrivatePointers((xmlNodePtr)ns->next);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2012-02-20 03:40:15 +00:00
|
|
|
aNode->_private = NULL;
|
|
|
|
clearPrivatePointers(aNode->children);
|
|
|
|
clearPrivatePointers(aNode->next);
|
|
|
|
if (aNode->type == XML_ELEMENT_NODE)
|
2012-03-12 18:13:27 +00:00
|
|
|
{
|
|
|
|
clearPrivatePointers((xmlNodePtr)(aNode->properties));
|
|
|
|
}
|
2012-03-01 08:41:20 +00:00
|
|
|
// FIXME: Handle more node types
|
2012-02-20 03:40:15 +00:00
|
|
|
}
|
|
|
|
|
2012-03-01 06:11:30 +00:00
|
|
|
static int
|
|
|
|
register_namespaces(xmlXPathContextPtr xpathCtx, const xmlChar* nsList)
|
2012-02-20 03:40:15 +00:00
|
|
|
{
|
|
|
|
xmlChar* nsListDup;
|
|
|
|
xmlChar* prefix;
|
|
|
|
xmlChar* href;
|
|
|
|
xmlChar* next;
|
|
|
|
|
|
|
|
assert(xpathCtx);
|
|
|
|
assert(nsList);
|
|
|
|
|
|
|
|
nsListDup = xmlStrdup(nsList);
|
2012-02-22 10:55:12 +00:00
|
|
|
if (nsListDup == NULL)
|
|
|
|
{
|
|
|
|
NSLog(@"Error: unable to strdup namespaces list");
|
|
|
|
return -1;
|
|
|
|
}
|
2012-02-20 03:40:15 +00:00
|
|
|
|
|
|
|
next = nsListDup;
|
2012-02-22 10:55:12 +00:00
|
|
|
while (next != NULL)
|
|
|
|
{
|
|
|
|
/* skip spaces */
|
2012-02-23 17:57:50 +00:00
|
|
|
while ((*next) == ' ') next++;
|
|
|
|
if ((*next) == '\0') break;
|
2012-02-22 10:55:12 +00:00
|
|
|
|
|
|
|
/* find prefix */
|
|
|
|
prefix = next;
|
|
|
|
next = (xmlChar*)xmlStrchr(next, '=');
|
|
|
|
if (next == NULL)
|
|
|
|
{
|
|
|
|
NSLog(@"Error: invalid namespaces list format");
|
|
|
|
xmlFree(nsListDup);
|
|
|
|
return -1;
|
|
|
|
}
|
2012-02-20 03:40:15 +00:00
|
|
|
*(next++) = '\0';
|
|
|
|
|
2012-02-22 10:55:12 +00:00
|
|
|
/* find href */
|
|
|
|
href = next;
|
|
|
|
next = (xmlChar*)xmlStrchr(next, ' ');
|
|
|
|
if (next != NULL)
|
|
|
|
{
|
|
|
|
*(next++) = '\0';
|
|
|
|
}
|
|
|
|
|
|
|
|
/* do register namespace */
|
|
|
|
if (xmlXPathRegisterNs(xpathCtx, prefix, href) != 0)
|
|
|
|
{
|
2012-03-01 06:11:30 +00:00
|
|
|
NSLog(@"Error: unable to register NS with prefix=\"%s\""
|
|
|
|
@" and href=\"%s\"", prefix, href);
|
2012-02-22 10:55:12 +00:00
|
|
|
xmlFree(nsListDup);
|
|
|
|
return -1;
|
|
|
|
}
|
2012-02-20 03:40:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
xmlFree(nsListDup);
|
2012-02-22 10:55:12 +00:00
|
|
|
return 0;
|
2012-02-20 03:40:15 +00:00
|
|
|
}
|
|
|
|
|
2012-03-01 06:11:30 +00:00
|
|
|
static NSArray *
|
|
|
|
execute_xpath(NSXMLNode *xmlNode, NSString *xpath_exp, NSString *nmspaces)
|
2012-02-20 03:40:15 +00:00
|
|
|
{
|
2012-02-26 20:45:00 +00:00
|
|
|
xmlNodePtr node = [xmlNode _node];
|
|
|
|
xmlDocPtr doc = node->doc;
|
2012-02-20 03:40:15 +00:00
|
|
|
NSMutableArray *result = [NSMutableArray arrayWithCapacity: 10];
|
|
|
|
xmlChar* xpathExpr = (xmlChar *)XMLSTRING(xpath_exp);
|
|
|
|
xmlChar* nsList = (xmlChar *)XMLSTRING(nmspaces);
|
|
|
|
xmlXPathContextPtr xpathCtx = NULL;
|
|
|
|
xmlXPathObjectPtr xpathObj = NULL;
|
|
|
|
xmlNodeSetPtr nodeset = NULL;
|
|
|
|
xmlNodePtr cur = NULL;
|
|
|
|
int i = 0;
|
|
|
|
|
|
|
|
assert(xpathExpr);
|
|
|
|
|
|
|
|
/* Create xpath evaluation context */
|
|
|
|
xpathCtx = xmlXPathNewContext(doc);
|
2012-02-22 10:55:12 +00:00
|
|
|
if (!xpathCtx)
|
2012-02-20 03:40:15 +00:00
|
|
|
{
|
|
|
|
NSLog(@"Error: unable to create new XPath context.");
|
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Register namespaces from list (if any) */
|
2012-02-22 10:55:12 +00:00
|
|
|
if ((nsList != NULL) && (register_namespaces(xpathCtx, nsList) < 0))
|
2012-02-20 03:40:15 +00:00
|
|
|
{
|
|
|
|
NSLog(@"Error: failed to register namespaces list \"%s\"", nsList);
|
|
|
|
xmlXPathFreeContext(xpathCtx);
|
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
|
2012-02-23 17:57:50 +00:00
|
|
|
if (![xpath_exp hasPrefix: @"/"])
|
2012-02-26 20:45:00 +00:00
|
|
|
{
|
|
|
|
// provide a context for relative paths
|
|
|
|
xpathCtx->node = node;
|
|
|
|
}
|
2012-02-20 03:40:15 +00:00
|
|
|
|
|
|
|
/* Evaluate xpath expression */
|
|
|
|
xpathObj = xmlXPathEvalExpression(xpathExpr, xpathCtx);
|
2012-02-22 10:55:12 +00:00
|
|
|
if (xpathObj == NULL)
|
2012-02-20 03:40:15 +00:00
|
|
|
{
|
|
|
|
NSLog(@"Error: unable to evaluate xpath expression \"%s\"", xpathExpr);
|
2012-02-29 21:13:13 +00:00
|
|
|
xmlXPathFreeContext(xpathCtx);
|
2012-02-20 03:40:15 +00:00
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* results */
|
|
|
|
nodeset = xpathObj->nodesetval;
|
|
|
|
/*
|
|
|
|
if (nodeset == NULL || nodeset->nodeNr == 0)
|
|
|
|
{
|
|
|
|
xpathObj = xmlXPathEval(xpathExpr, xpathCtx);
|
|
|
|
if (xpathObj != NULL)
|
|
|
|
nodeset = xpathObj->nodesetval;
|
|
|
|
if (nodeset)
|
|
|
|
NSLog(@"Succeeded in evaluating as a path, using xmlXPathEval");
|
|
|
|
}
|
|
|
|
*/
|
2012-02-22 10:55:12 +00:00
|
|
|
if (nodeset)
|
2012-02-20 03:40:15 +00:00
|
|
|
{
|
|
|
|
/* Collect results */
|
2012-02-22 10:55:12 +00:00
|
|
|
for (i = 0; i < nodeset->nodeNr; i++)
|
2012-02-20 03:40:15 +00:00
|
|
|
{
|
|
|
|
id obj = nil;
|
|
|
|
cur = nodeset->nodeTab[i];
|
|
|
|
obj = [NSXMLNode _objectForNode: cur];
|
2012-02-22 10:55:12 +00:00
|
|
|
if (obj)
|
2012-02-20 03:40:15 +00:00
|
|
|
{
|
|
|
|
[result addObject: obj];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Cleanup */
|
|
|
|
xmlXPathFreeObject(xpathObj);
|
|
|
|
xmlXPathFreeContext(xpathCtx);
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
2011-10-17 10:24:07 +00:00
|
|
|
|
2009-02-09 16:16:11 +00:00
|
|
|
@implementation NSXMLNode
|
|
|
|
|
2012-03-06 22:33:54 +00:00
|
|
|
+ (void) initialize
|
|
|
|
{
|
|
|
|
xmlCheckVersion(LIBXML_VERSION);
|
|
|
|
// Protect against libxml2 not being correctly set up on Windows.
|
|
|
|
// See: http://www.linuxquestions.org/questions/programming-9/%5Bsolved%5Dusing-libxml2-on-mingw-xmlfree-crashes-839802/
|
|
|
|
if (!xmlFree)
|
|
|
|
{
|
|
|
|
xmlMemGet(&xmlFree, &xmlMalloc, &xmlRealloc, NULL);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-02-09 16:16:11 +00:00
|
|
|
+ (id) attributeWithName: (NSString*)name
|
|
|
|
stringValue: (NSString*)stringValue
|
|
|
|
{
|
2012-02-22 10:55:12 +00:00
|
|
|
NSXMLNode *n;
|
2009-02-09 16:16:11 +00:00
|
|
|
|
|
|
|
n = [[[self alloc] initWithKind: NSXMLAttributeKind] autorelease];
|
|
|
|
[n setStringValue: stringValue];
|
2011-12-30 21:40:12 +00:00
|
|
|
[n setName: name];
|
2012-02-20 03:40:15 +00:00
|
|
|
|
2009-02-09 16:16:11 +00:00
|
|
|
return n;
|
|
|
|
}
|
|
|
|
|
|
|
|
+ (id) attributeWithName: (NSString*)name
|
|
|
|
URI: (NSString*)URI
|
|
|
|
stringValue: (NSString*)stringValue
|
|
|
|
{
|
2012-02-22 10:55:12 +00:00
|
|
|
NSXMLNode *n;
|
2012-02-20 03:40:15 +00:00
|
|
|
|
2009-02-09 16:16:11 +00:00
|
|
|
n = [[[self alloc] initWithKind: NSXMLAttributeKind] autorelease];
|
|
|
|
[n setURI: URI];
|
|
|
|
[n setStringValue: stringValue];
|
2011-12-30 21:40:12 +00:00
|
|
|
[n setName: name];
|
2012-02-20 03:40:15 +00:00
|
|
|
|
2009-02-09 16:16:11 +00:00
|
|
|
return n;
|
|
|
|
}
|
|
|
|
|
|
|
|
+ (id) commentWithStringValue: (NSString*)stringValue
|
|
|
|
{
|
2012-02-22 10:55:12 +00:00
|
|
|
NSXMLNode *n;
|
2009-02-09 16:16:11 +00:00
|
|
|
|
|
|
|
n = [[[self alloc] initWithKind: NSXMLCommentKind] autorelease];
|
|
|
|
[n setStringValue: stringValue];
|
2012-02-20 03:40:15 +00:00
|
|
|
|
2009-02-09 16:16:11 +00:00
|
|
|
return n;
|
|
|
|
}
|
|
|
|
|
|
|
|
+ (id) DTDNodeWithXMLString: (NSString*)string
|
|
|
|
{
|
2012-02-22 10:55:12 +00:00
|
|
|
NSXMLNode *n;
|
2009-02-09 16:16:11 +00:00
|
|
|
|
2012-02-22 10:55:12 +00:00
|
|
|
n = [[[NSXMLDTDNode alloc] initWithXMLString: string] autorelease];
|
2012-02-20 03:40:15 +00:00
|
|
|
|
2009-02-09 16:16:11 +00:00
|
|
|
return n;
|
|
|
|
}
|
|
|
|
|
|
|
|
+ (id) document
|
|
|
|
{
|
2012-02-22 10:55:12 +00:00
|
|
|
NSXMLNode *n;
|
2009-02-09 16:16:11 +00:00
|
|
|
|
2012-02-22 10:55:12 +00:00
|
|
|
n = [[[NSXMLDocument alloc] initWithKind: NSXMLDocumentKind] autorelease];
|
2009-02-09 16:16:11 +00:00
|
|
|
return n;
|
|
|
|
}
|
|
|
|
|
|
|
|
+ (id) documentWithRootElement: (NSXMLElement*)element
|
|
|
|
{
|
|
|
|
NSXMLDocument *d;
|
|
|
|
|
2012-02-22 10:55:12 +00:00
|
|
|
d = [[[NSXMLDocument alloc] initWithRootElement: element] autorelease];
|
2009-02-09 16:16:11 +00:00
|
|
|
return d;
|
|
|
|
}
|
|
|
|
|
|
|
|
+ (id) elementWithName: (NSString*)name
|
|
|
|
{
|
2012-02-22 10:55:12 +00:00
|
|
|
NSXMLNode *n;
|
2009-02-09 16:16:11 +00:00
|
|
|
|
|
|
|
n = [[[NSXMLElement alloc] initWithName: name] autorelease];
|
|
|
|
return n;
|
|
|
|
}
|
|
|
|
|
|
|
|
+ (id) elementWithName: (NSString*)name
|
|
|
|
children: (NSArray*)children
|
|
|
|
attributes: (NSArray*)attributes
|
|
|
|
{
|
|
|
|
NSXMLElement *e = [self elementWithName: name];
|
|
|
|
|
|
|
|
[e insertChildren: children atIndex: 0];
|
|
|
|
[e setAttributes: attributes];
|
|
|
|
return e;
|
|
|
|
}
|
|
|
|
|
|
|
|
+ (id) elementWithName: (NSString*)name
|
|
|
|
URI: (NSString*)URI
|
|
|
|
{
|
2012-02-22 10:55:12 +00:00
|
|
|
NSXMLNode *n;
|
2009-02-09 16:16:11 +00:00
|
|
|
|
|
|
|
n = [[[NSXMLElement alloc] initWithName: name URI: URI] autorelease];
|
|
|
|
return n;
|
|
|
|
}
|
|
|
|
|
|
|
|
+ (id) elementWithName: (NSString*)name
|
|
|
|
stringValue: (NSString*)string
|
|
|
|
{
|
2012-02-22 10:55:12 +00:00
|
|
|
NSXMLElement *e;
|
|
|
|
|
2012-02-26 19:58:25 +00:00
|
|
|
e = [[NSXMLElement alloc] initWithName: name stringValue: string];
|
2009-02-09 16:16:11 +00:00
|
|
|
return e;
|
|
|
|
}
|
|
|
|
|
|
|
|
+ (NSString*) localNameForName: (NSString*)name
|
|
|
|
{
|
2012-02-22 10:55:12 +00:00
|
|
|
const xmlChar *xmlName = XMLSTRING(name);
|
|
|
|
xmlChar *prefix = NULL;
|
|
|
|
xmlChar *localName;
|
|
|
|
|
|
|
|
if (NULL == xmlName)
|
|
|
|
return nil;
|
|
|
|
|
|
|
|
localName = xmlSplitQName2(xmlName, &prefix);
|
|
|
|
return StringFromXMLStringPtr(localName);
|
2009-02-09 16:16:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
+ (id) namespaceWithName: (NSString*)name
|
|
|
|
stringValue: (NSString*)stringValue
|
|
|
|
{
|
2012-02-22 10:55:12 +00:00
|
|
|
NSXMLNode *n;
|
2009-02-09 16:16:11 +00:00
|
|
|
|
|
|
|
n = [[[self alloc] initWithKind: NSXMLNamespaceKind] autorelease];
|
2012-02-22 10:55:12 +00:00
|
|
|
[n setName: name];
|
2009-02-09 16:16:11 +00:00
|
|
|
[n setStringValue: stringValue];
|
|
|
|
return n;
|
|
|
|
}
|
|
|
|
|
|
|
|
+ (NSXMLNode*) predefinedNamespaceForPrefix: (NSString*)name
|
|
|
|
{
|
2012-02-22 10:55:12 +00:00
|
|
|
// FIXME: We should cache these instances
|
|
|
|
if ([name isEqualToString: @"xml"])
|
|
|
|
{
|
|
|
|
return [self namespaceWithName: @"xml"
|
2012-03-01 06:11:30 +00:00
|
|
|
stringValue: @"http: //www.w3.org/XML/1998/namespace"];
|
2012-02-22 10:55:12 +00:00
|
|
|
}
|
|
|
|
if ([name isEqualToString: @"xs"])
|
|
|
|
{
|
|
|
|
return [self namespaceWithName: @"xs"
|
2012-03-01 06:11:30 +00:00
|
|
|
stringValue: @"http: //www.w3.org/2001/XMLSchema"];
|
2012-02-22 10:55:12 +00:00
|
|
|
}
|
|
|
|
if ([name isEqualToString: @"xsi"])
|
|
|
|
{
|
|
|
|
return [self namespaceWithName: @"xsi"
|
2012-03-01 06:11:30 +00:00
|
|
|
stringValue: @"http: //www.w3.org/2001/XMLSchema-instance"];
|
2012-02-22 10:55:12 +00:00
|
|
|
}
|
|
|
|
if ([name isEqualToString: @"fn"])
|
|
|
|
{
|
|
|
|
return [self namespaceWithName: @"fn"
|
2012-03-01 06:11:30 +00:00
|
|
|
stringValue: @"http: //www.w3.org/2003/11/xpath-functions"];
|
2012-02-22 10:55:12 +00:00
|
|
|
}
|
|
|
|
if ([name isEqualToString: @"local"])
|
|
|
|
{
|
|
|
|
return [self namespaceWithName: @"local"
|
2012-03-01 06:11:30 +00:00
|
|
|
stringValue: @"http: //www.w3.org/2003/11/xpath-local-functions"];
|
2012-02-22 10:55:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return nil;
|
2009-02-09 16:16:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
+ (NSString*) prefixForName: (NSString*)name
|
|
|
|
{
|
2012-02-22 10:55:12 +00:00
|
|
|
const xmlChar *xmlName = XMLSTRING(name);
|
|
|
|
xmlChar *prefix = NULL;
|
|
|
|
|
|
|
|
if (NULL == xmlName)
|
|
|
|
return nil;
|
|
|
|
|
|
|
|
xmlSplitQName2(xmlName, &prefix);
|
|
|
|
|
|
|
|
if (NULL == prefix)
|
|
|
|
{
|
|
|
|
return @"";
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return StringFromXMLStringPtr(prefix);
|
|
|
|
}
|
2009-02-09 16:16:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
+ (id) processingInstructionWithName: (NSString*)name
|
|
|
|
stringValue: (NSString*)stringValue
|
|
|
|
{
|
2012-02-22 10:55:12 +00:00
|
|
|
NSXMLNode *n;
|
2009-02-09 16:16:11 +00:00
|
|
|
|
|
|
|
n = [[[self alloc] initWithKind: NSXMLProcessingInstructionKind] autorelease];
|
|
|
|
[n setStringValue: stringValue];
|
2012-03-06 22:33:54 +00:00
|
|
|
[n setName: name];
|
2009-02-09 16:16:11 +00:00
|
|
|
return n;
|
|
|
|
}
|
|
|
|
|
|
|
|
+ (id) textWithStringValue: (NSString*)stringValue
|
|
|
|
{
|
2012-02-22 10:55:12 +00:00
|
|
|
NSXMLNode *n;
|
2009-02-09 16:16:11 +00:00
|
|
|
|
|
|
|
n = [[[self alloc] initWithKind: NSXMLTextKind] autorelease];
|
|
|
|
[n setStringValue: stringValue];
|
|
|
|
return n;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (NSString*) canonicalXMLStringPreservingComments: (BOOL)comments
|
|
|
|
{
|
2012-03-12 18:13:27 +00:00
|
|
|
// FIXME ... generate from libxml
|
2012-03-14 21:12:19 +00:00
|
|
|
return [self XMLStringWithOptions: NSXMLNodePreserveWhitespace];
|
2009-02-09 16:16:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
- (NSXMLNode*) childAtIndex: (NSUInteger)index
|
|
|
|
{
|
2012-02-23 17:57:50 +00:00
|
|
|
xmlNodePtr childNode = [self _childNodeAtIndex: index];
|
2012-02-22 10:55:12 +00:00
|
|
|
return [NSXMLNode _objectForNode: childNode];
|
2009-02-09 16:16:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
- (NSUInteger) childCount
|
|
|
|
{
|
2012-02-20 03:40:15 +00:00
|
|
|
NSUInteger count = 0;
|
|
|
|
xmlNodePtr children = NULL;
|
2012-03-12 13:27:32 +00:00
|
|
|
xmlNodePtr node = internal->node;
|
2012-02-20 03:40:15 +00:00
|
|
|
|
2012-03-01 08:41:20 +00:00
|
|
|
if (!node)
|
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-03-12 18:13:27 +00:00
|
|
|
if (node->type == XML_NAMESPACE_DECL)
|
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-02-20 03:40:15 +00:00
|
|
|
for (children = node->children; children; children = children->next)
|
|
|
|
{
|
|
|
|
count++;
|
|
|
|
}
|
|
|
|
|
|
|
|
return count;
|
2009-02-09 16:16:11 +00:00
|
|
|
}
|
|
|
|
|
2012-01-06 12:22:30 +00:00
|
|
|
- (NSArray*) children
|
2009-02-09 16:16:11 +00:00
|
|
|
{
|
2012-02-20 03:40:15 +00:00
|
|
|
NSMutableArray *childrenArray = nil;
|
2012-02-22 10:55:12 +00:00
|
|
|
|
|
|
|
if (NSXMLInvalidKind == internal->kind)
|
2012-02-20 03:40:15 +00:00
|
|
|
{
|
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
xmlNodePtr children = NULL;
|
2012-03-12 13:27:32 +00:00
|
|
|
xmlNodePtr node = internal->node;
|
2012-02-20 03:40:15 +00:00
|
|
|
|
2012-03-12 18:13:27 +00:00
|
|
|
if ((node == NULL) ||
|
|
|
|
(node->type == XML_NAMESPACE_DECL) ||
|
|
|
|
(node->children == NULL))
|
2012-02-20 03:40:15 +00:00
|
|
|
{
|
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
|
|
|
|
childrenArray = [NSMutableArray array];
|
|
|
|
for (children = node->children; children; children = children->next)
|
|
|
|
{
|
|
|
|
NSXMLNode *n = [NSXMLNode _objectForNode: children];
|
|
|
|
[childrenArray addObject: n];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return childrenArray;
|
|
|
|
}
|
|
|
|
|
2009-02-09 16:16:11 +00:00
|
|
|
- (id) copyWithZone: (NSZone*)zone
|
|
|
|
{
|
2012-03-05 22:48:18 +00:00
|
|
|
NSXMLNode *c = [[self class] allocWithZone: zone];
|
2012-03-01 08:41:20 +00:00
|
|
|
xmlNodePtr newNode = xmlCopyNode([self _node], 2); // make a deep copy
|
2012-02-20 03:40:15 +00:00
|
|
|
clearPrivatePointers(newNode);
|
|
|
|
|
2012-02-22 10:55:12 +00:00
|
|
|
c = [c _initWithNode: newNode kind: internal->kind];
|
2012-01-04 12:41:45 +00:00
|
|
|
|
2012-03-06 09:55:36 +00:00
|
|
|
GSIVar(c, options) = internal->options;
|
2012-03-09 10:08:48 +00:00
|
|
|
if (nil != internal->objectValue)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
Only copy the objectValue when externally set.
|
|
|
|
The problem here are nodes created by parsing XML.
|
|
|
|
There the stringValue may be set, but the objectValue isn't.
|
|
|
|
This should rather be solved by creating a suitable objectValue,
|
|
|
|
when the node gets instantiated.
|
|
|
|
*/
|
|
|
|
[c setObjectValue: internal->objectValue];
|
|
|
|
}
|
2012-03-01 08:41:20 +00:00
|
|
|
[c setURI: [self URI]];
|
2012-02-20 03:40:15 +00:00
|
|
|
// [c setName: [self name]];
|
|
|
|
// [c setStringValue: [self stringValue]];
|
2012-01-06 02:43:26 +00:00
|
|
|
|
2012-01-04 12:41:45 +00:00
|
|
|
return c;
|
2012-01-06 12:48:49 +00:00
|
|
|
}
|
2009-02-09 16:16:11 +00:00
|
|
|
|
2012-02-27 12:53:25 +00:00
|
|
|
- (NSString*) description
|
|
|
|
{
|
|
|
|
return [NSString stringWithFormat:@"<%@ %@ %d>%@\n",
|
2012-03-01 06:11:30 +00:00
|
|
|
NSStringFromClass([self class]),
|
2012-03-05 22:48:18 +00:00
|
|
|
[self name], [self kind], [self XMLString]];
|
2012-02-27 12:53:25 +00:00
|
|
|
}
|
|
|
|
|
2009-02-09 16:16:11 +00:00
|
|
|
- (void) dealloc
|
|
|
|
{
|
2012-01-04 12:41:45 +00:00
|
|
|
if (GS_EXISTS_INTERNAL)
|
|
|
|
{
|
2012-03-12 13:27:32 +00:00
|
|
|
xmlNodePtr node = internal->node;
|
2012-03-03 11:37:49 +00:00
|
|
|
NSArray *subNodes = [internal->subNodes copy];
|
|
|
|
NSEnumerator *enumerator = [subNodes objectEnumerator];
|
|
|
|
NSXMLNode *subNode;
|
|
|
|
|
|
|
|
while ((subNode = [enumerator nextObject]) != nil)
|
|
|
|
{
|
|
|
|
[subNode detach];
|
|
|
|
}
|
|
|
|
[subNodes release];
|
2012-03-01 08:41:20 +00:00
|
|
|
|
2012-03-14 21:12:19 +00:00
|
|
|
[internal->URI release];
|
2012-01-04 12:41:45 +00:00
|
|
|
[internal->objectValue release];
|
2012-02-20 03:40:15 +00:00
|
|
|
[internal->subNodes release];
|
|
|
|
if (node)
|
2012-03-12 18:13:27 +00:00
|
|
|
{
|
2012-03-12 19:50:51 +00:00
|
|
|
if (node->type == XML_NAMESPACE_DECL)
|
2012-03-12 18:13:27 +00:00
|
|
|
{
|
2012-03-12 19:50:51 +00:00
|
|
|
((xmlNsPtr)node)->_private = NULL;
|
2012-03-12 18:13:27 +00:00
|
|
|
xmlFreeNode(node);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
node->_private = NULL;
|
|
|
|
if (node->parent == NULL)
|
|
|
|
{
|
|
|
|
// the top level node frees the entire tree
|
|
|
|
if (node->type == XML_DOCUMENT_NODE)
|
|
|
|
xmlFreeDoc((xmlDocPtr)node);
|
|
|
|
else
|
|
|
|
xmlFreeNode(node);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2012-01-04 12:41:45 +00:00
|
|
|
GS_DESTROY_INTERNAL(NSXMLNode);
|
|
|
|
}
|
2009-02-09 16:16:11 +00:00
|
|
|
[super dealloc];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void) detach
|
|
|
|
{
|
2012-03-12 13:27:32 +00:00
|
|
|
xmlNodePtr node = internal->node;
|
2012-03-01 08:41:20 +00:00
|
|
|
|
2012-02-22 10:55:12 +00:00
|
|
|
if (node)
|
2012-02-20 03:40:15 +00:00
|
|
|
{
|
2012-03-12 19:50:51 +00:00
|
|
|
NSXMLNode *parent = [self parent];
|
2012-03-01 06:11:30 +00:00
|
|
|
|
2012-03-12 22:33:27 +00:00
|
|
|
if (node->type == XML_NAMESPACE_DECL)
|
|
|
|
{
|
|
|
|
// FIXME
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// separate our node from its parent and siblings
|
|
|
|
xmlUnlinkNode(node);
|
|
|
|
xmlSetTreeDoc(node, NULL);
|
|
|
|
}
|
|
|
|
|
2012-02-20 03:40:15 +00:00
|
|
|
if (parent)
|
|
|
|
{
|
2012-02-23 17:57:50 +00:00
|
|
|
[parent _removeSubNode: self];
|
2012-02-20 03:40:15 +00:00
|
|
|
}
|
|
|
|
}
|
2009-02-09 16:16:11 +00:00
|
|
|
}
|
|
|
|
|
2012-01-06 12:22:30 +00:00
|
|
|
- (NSUInteger) hash
|
|
|
|
{
|
2012-02-22 10:55:12 +00:00
|
|
|
return [[self name] hash];
|
2012-01-06 12:22:30 +00:00
|
|
|
}
|
|
|
|
|
2009-02-09 16:16:11 +00:00
|
|
|
- (NSUInteger) index
|
|
|
|
{
|
2012-03-12 13:27:32 +00:00
|
|
|
xmlNodePtr node = internal->node;
|
2012-02-20 03:40:15 +00:00
|
|
|
int count = 0;
|
2012-03-01 08:41:20 +00:00
|
|
|
|
2012-03-12 19:50:51 +00:00
|
|
|
if (node->type == XML_NAMESPACE_DECL)
|
2012-03-12 18:13:27 +00:00
|
|
|
{
|
|
|
|
// FIXME: Could try to go to document an loop over the namespaces
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-02-20 03:40:15 +00:00
|
|
|
while ((node = node->prev))
|
2012-01-07 13:08:03 +00:00
|
|
|
{
|
2012-02-20 03:40:15 +00:00
|
|
|
count++; // count our earlier sibling nodes
|
2012-01-07 13:08:03 +00:00
|
|
|
}
|
2012-02-20 03:40:15 +00:00
|
|
|
|
|
|
|
return count;
|
2009-02-09 16:16:11 +00:00
|
|
|
}
|
|
|
|
|
2011-10-01 18:43:29 +00:00
|
|
|
- (id) init
|
|
|
|
{
|
|
|
|
return [self initWithKind: NSXMLInvalidKind];
|
|
|
|
}
|
|
|
|
|
2012-02-22 10:55:12 +00:00
|
|
|
- (id) initWithKind: (NSXMLNodeKind) kind
|
2009-02-09 16:16:11 +00:00
|
|
|
{
|
2012-02-22 10:55:12 +00:00
|
|
|
return [self initWithKind: kind options: 0];
|
2009-02-09 16:16:11 +00:00
|
|
|
}
|
|
|
|
|
2011-10-01 18:43:29 +00:00
|
|
|
- (id) initWithKind: (NSXMLNodeKind)kind options: (NSUInteger)theOptions
|
2009-02-09 16:16:11 +00:00
|
|
|
{
|
2011-10-01 18:43:29 +00:00
|
|
|
Class theSubclass = [NSXMLNode class];
|
2012-02-20 03:40:15 +00:00
|
|
|
void *node = NULL;
|
2012-01-04 12:41:45 +00:00
|
|
|
|
2011-10-01 18:43:29 +00:00
|
|
|
/*
|
2012-02-23 17:57:50 +00:00
|
|
|
* We find the correct subclass for specific node kinds:
|
2011-10-01 18:43:29 +00:00
|
|
|
*/
|
|
|
|
switch (kind)
|
|
|
|
{
|
2012-03-11 15:04:36 +00:00
|
|
|
case NSXMLDocumentKind:
|
|
|
|
theSubclass = [NSXMLDocument class];
|
|
|
|
break;
|
|
|
|
|
|
|
|
case NSXMLInvalidKind:
|
|
|
|
theSubclass = [NSXMLNode class];
|
|
|
|
break;
|
|
|
|
|
|
|
|
case NSXMLElementKind:
|
|
|
|
theSubclass = [NSXMLElement class];
|
|
|
|
break;
|
2012-02-20 03:40:15 +00:00
|
|
|
|
2012-03-11 15:04:36 +00:00
|
|
|
case NSXMLDTDKind:
|
|
|
|
theSubclass = [NSXMLDTD class];
|
|
|
|
break;
|
|
|
|
|
|
|
|
case NSXMLElementDeclarationKind:
|
|
|
|
case NSXMLEntityDeclarationKind:
|
|
|
|
case NSXMLNotationDeclarationKind:
|
|
|
|
theSubclass = [NSXMLDTDNode class];
|
|
|
|
break;
|
|
|
|
|
|
|
|
case NSXMLNamespaceKind:
|
2012-03-12 19:50:51 +00:00
|
|
|
theSubclass = [NSXMLNode class];
|
2012-03-11 15:04:36 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case NSXMLAttributeKind:
|
|
|
|
case NSXMLCommentKind:
|
|
|
|
case NSXMLProcessingInstructionKind:
|
|
|
|
case NSXMLTextKind:
|
|
|
|
break;
|
|
|
|
|
|
|
|
case NSXMLAttributeDeclarationKind:
|
|
|
|
[self release];
|
|
|
|
return nil;
|
|
|
|
|
|
|
|
default:
|
|
|
|
kind = NSXMLInvalidKind;
|
|
|
|
theSubclass = [NSXMLNode class];
|
|
|
|
break;
|
2011-10-01 18:43:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Check whether we are already initializing an instance of the given
|
2012-03-01 06:11:30 +00:00
|
|
|
* subclass. If we are not, release ourselves and allocate a subclass
|
|
|
|
* instance instead.
|
2011-10-01 18:43:29 +00:00
|
|
|
*/
|
|
|
|
if (NO == [self isKindOfClass: theSubclass])
|
2012-01-01 07:38:53 +00:00
|
|
|
{
|
|
|
|
[self release];
|
|
|
|
return [[theSubclass alloc] initWithKind: kind
|
|
|
|
options: theOptions];
|
|
|
|
}
|
2011-10-01 18:43:29 +00:00
|
|
|
|
2012-03-01 08:41:20 +00:00
|
|
|
/* If we are initializing for the correct class, we can actually perform
|
|
|
|
* initializations:
|
|
|
|
*/
|
2012-02-20 03:40:15 +00:00
|
|
|
switch (kind)
|
|
|
|
{
|
2012-03-01 06:11:30 +00:00
|
|
|
case NSXMLDocumentKind:
|
|
|
|
node = xmlNewDoc((xmlChar *)"1.0");
|
|
|
|
break;
|
|
|
|
|
|
|
|
case NSXMLInvalidKind:
|
|
|
|
case NSXMLElementKind:
|
|
|
|
node = xmlNewNode(NULL,(xmlChar *)"");
|
|
|
|
break;
|
2012-02-20 03:40:15 +00:00
|
|
|
|
2012-03-01 06:11:30 +00:00
|
|
|
case NSXMLDTDKind:
|
|
|
|
node = xmlNewDtd(NULL, (xmlChar *)"", (xmlChar *)"",(xmlChar *)"");
|
|
|
|
break;
|
|
|
|
|
|
|
|
case NSXMLEntityDeclarationKind:
|
|
|
|
case NSXMLElementDeclarationKind:
|
|
|
|
case NSXMLNotationDeclarationKind:
|
|
|
|
node = xmlNewNode(NULL, (xmlChar *)"");
|
|
|
|
break;
|
|
|
|
|
|
|
|
case NSXMLProcessingInstructionKind:
|
|
|
|
node = xmlNewPI((xmlChar *)"", (xmlChar *)"");
|
|
|
|
break;
|
|
|
|
|
|
|
|
case NSXMLCommentKind:
|
|
|
|
node = xmlNewComment((xmlChar *)"");
|
|
|
|
break;
|
|
|
|
|
|
|
|
case NSXMLTextKind:
|
|
|
|
node = xmlNewText((xmlChar *)"");
|
|
|
|
break;
|
|
|
|
|
|
|
|
case NSXMLNamespaceKind:
|
|
|
|
node = xmlNewNs(NULL,(xmlChar *)"",(xmlChar *)"");
|
|
|
|
break;
|
|
|
|
|
|
|
|
case NSXMLAttributeKind:
|
|
|
|
node = xmlNewProp(NULL,(xmlChar *)"",(xmlChar *)"");
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
break;
|
2012-02-20 03:40:15 +00:00
|
|
|
}
|
|
|
|
|
2012-03-01 08:41:20 +00:00
|
|
|
if (nil == (self = [self _initWithNode: node kind: kind]))
|
|
|
|
{
|
|
|
|
return nil;
|
|
|
|
}
|
2011-10-01 18:43:29 +00:00
|
|
|
|
2012-01-04 09:20:32 +00:00
|
|
|
internal->options = theOptions;
|
2009-02-09 16:16:11 +00:00
|
|
|
return self;
|
|
|
|
}
|
|
|
|
|
2012-01-06 12:22:30 +00:00
|
|
|
- (BOOL) isEqual: (id)other
|
|
|
|
{
|
2012-02-22 10:55:12 +00:00
|
|
|
if ([self kind] != [other kind])
|
2012-01-06 12:22:30 +00:00
|
|
|
{
|
|
|
|
return NO;
|
|
|
|
}
|
2012-03-06 09:55:36 +00:00
|
|
|
/*
|
|
|
|
NSLog(@"self %@ other %@", self, other);
|
|
|
|
NSLog(@"s sV '%@' oV '%@', other sV '%@' oV '%@'", [self stringValue], [self objectValue],
|
|
|
|
[other stringValue], [other objectValue]);
|
|
|
|
*/
|
2012-03-12 13:27:32 +00:00
|
|
|
return isEqualTree(internal->node, (xmlNodePtr)[other _node]);
|
2012-01-06 12:22:30 +00:00
|
|
|
}
|
|
|
|
|
2009-02-09 16:16:11 +00:00
|
|
|
- (NSXMLNodeKind) kind
|
|
|
|
{
|
2012-01-04 12:41:45 +00:00
|
|
|
return internal->kind;
|
2009-02-09 16:16:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
- (NSUInteger) level
|
|
|
|
{
|
2012-03-12 19:50:51 +00:00
|
|
|
NSXMLNode *parent = [self parent];
|
|
|
|
|
|
|
|
if (nil == parent)
|
2012-03-12 18:13:27 +00:00
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
2012-03-12 19:50:51 +00:00
|
|
|
else
|
2009-02-09 16:16:11 +00:00
|
|
|
{
|
2012-03-12 19:50:51 +00:00
|
|
|
return [parent level] + 1;
|
2009-02-09 16:16:11 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
- (NSString*) localName
|
|
|
|
{
|
2012-02-22 10:55:12 +00:00
|
|
|
return [[self class] localNameForName: [self name]];
|
2009-02-09 16:16:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
- (NSString*) name
|
|
|
|
{
|
2012-03-12 19:50:51 +00:00
|
|
|
xmlNodePtr node = internal->node;
|
|
|
|
|
2012-02-22 10:55:12 +00:00
|
|
|
if (NSXMLInvalidKind == internal->kind)
|
2012-02-20 03:40:15 +00:00
|
|
|
{
|
|
|
|
return nil;
|
|
|
|
}
|
2012-03-12 19:50:51 +00:00
|
|
|
|
|
|
|
if (node->type == XML_NAMESPACE_DECL)
|
|
|
|
{
|
|
|
|
return StringFromXMLStringPtr(((xmlNs *)node)->prefix);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return StringFromXMLStringPtr(node->name);
|
|
|
|
}
|
2009-02-09 16:16:11 +00:00
|
|
|
}
|
|
|
|
|
2011-10-01 18:43:29 +00:00
|
|
|
- (NSXMLNode*) _nodeFollowingInNaturalDirection: (BOOL)forward
|
|
|
|
{
|
2012-02-22 22:52:31 +00:00
|
|
|
NSXMLNode *ancestor = self;
|
2011-10-01 18:43:29 +00:00
|
|
|
NSXMLNode *candidate = nil;
|
2012-02-22 22:52:31 +00:00
|
|
|
NSXMLNodeKind kind;
|
2011-10-01 18:43:29 +00:00
|
|
|
|
|
|
|
/* Node walking is a depth-first thingy. Hence, we consider children first: */
|
2012-02-20 03:40:15 +00:00
|
|
|
if (0 != [self childCount])
|
2011-10-01 18:43:29 +00:00
|
|
|
{
|
2012-01-01 07:38:53 +00:00
|
|
|
NSUInteger theIndex = 0;
|
|
|
|
if (NO == forward)
|
|
|
|
{
|
2012-02-22 10:55:12 +00:00
|
|
|
theIndex = [self childCount] - 1;
|
2012-01-01 07:38:53 +00:00
|
|
|
}
|
2012-02-20 03:40:15 +00:00
|
|
|
candidate = [[self children] objectAtIndex: theIndex];
|
2011-10-01 18:43:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* If there are no children, we move on to siblings: */
|
|
|
|
/* If there are no siblings left for the receiver, we recurse down to the root
|
|
|
|
* of the tree until we find an ancestor with further siblings: */
|
|
|
|
while ((nil == candidate) && (nil != ancestor))
|
|
|
|
{
|
2012-01-01 07:38:53 +00:00
|
|
|
if (forward)
|
|
|
|
{
|
|
|
|
candidate = [ancestor nextSibling];
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
candidate = [ancestor previousSibling];
|
|
|
|
}
|
2012-02-22 22:52:31 +00:00
|
|
|
ancestor = [ancestor parent];
|
2011-10-01 18:43:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* No children, no next siblings, no next siblings for any ancestor: We are
|
|
|
|
* the last node */
|
|
|
|
if (nil == candidate)
|
2012-01-01 07:38:53 +00:00
|
|
|
{
|
|
|
|
return nil;
|
|
|
|
}
|
2011-10-01 18:43:29 +00:00
|
|
|
|
|
|
|
/* Sanity check: Namespace and attribute nodes are skipped: */
|
2012-02-22 22:52:31 +00:00
|
|
|
kind = [candidate kind];
|
|
|
|
if ((NSXMLAttributeKind == kind) || (NSXMLNamespaceKind == kind))
|
2012-01-01 07:38:53 +00:00
|
|
|
{
|
|
|
|
return [candidate _nodeFollowingInNaturalDirection: forward];
|
|
|
|
}
|
2011-10-01 18:43:29 +00:00
|
|
|
return candidate;
|
|
|
|
}
|
|
|
|
|
2009-02-09 16:16:11 +00:00
|
|
|
- (NSXMLNode*) nextNode
|
|
|
|
{
|
2011-10-01 18:43:29 +00:00
|
|
|
return [self _nodeFollowingInNaturalDirection: YES];
|
2009-02-09 16:16:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
- (NSXMLNode*) nextSibling
|
|
|
|
{
|
2012-03-12 13:27:32 +00:00
|
|
|
return [NSXMLNode _objectForNode: internal->node->next];
|
2009-02-09 16:16:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
- (id) objectValue
|
|
|
|
{
|
2012-01-04 12:41:45 +00:00
|
|
|
return internal->objectValue;
|
2009-02-09 16:16:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
- (NSXMLNode*) parent
|
|
|
|
{
|
2012-03-12 19:50:51 +00:00
|
|
|
xmlNodePtr parent = NULL;
|
|
|
|
xmlNodePtr node = internal->node;
|
|
|
|
|
|
|
|
if (NULL == node)
|
|
|
|
{
|
|
|
|
return nil;
|
|
|
|
}
|
2012-03-14 10:09:15 +00:00
|
|
|
if (XML_NAMESPACE_DECL == node->type)
|
2012-03-12 19:50:51 +00:00
|
|
|
{
|
2012-03-14 10:09:15 +00:00
|
|
|
return nil;
|
2012-03-12 19:50:51 +00:00
|
|
|
}
|
2012-03-14 10:09:15 +00:00
|
|
|
|
|
|
|
parent = node->parent;
|
2012-03-12 19:50:51 +00:00
|
|
|
return [NSXMLNode _objectForNode: parent];
|
2009-02-09 16:16:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
- (NSString*) prefix
|
|
|
|
{
|
2012-02-22 10:55:12 +00:00
|
|
|
return [[self class] prefixForName: [self name]];
|
2009-02-09 16:16:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
- (NSXMLNode*) previousNode
|
|
|
|
{
|
2011-10-01 18:43:29 +00:00
|
|
|
return [self _nodeFollowingInNaturalDirection: NO];
|
2009-02-09 16:16:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
- (NSXMLNode*) previousSibling
|
|
|
|
{
|
2012-03-12 13:27:32 +00:00
|
|
|
return [NSXMLNode _objectForNode: internal->node->prev];
|
2009-02-09 16:16:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
- (NSXMLDocument*) rootDocument
|
|
|
|
{
|
2012-03-01 06:11:30 +00:00
|
|
|
return
|
2012-03-12 13:27:32 +00:00
|
|
|
(NSXMLDocument *)[NSXMLNode _objectForNode: (xmlNodePtr)(internal->node->doc)];
|
2009-02-09 16:16:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
- (NSString*) stringValue
|
|
|
|
{
|
2012-03-12 13:27:32 +00:00
|
|
|
xmlNodePtr node = internal->node;
|
2012-02-20 03:40:15 +00:00
|
|
|
xmlChar *content = xmlNodeGetContent(node);
|
|
|
|
NSString *result = nil;
|
|
|
|
|
2012-03-05 22:48:18 +00:00
|
|
|
if (NULL != content)
|
|
|
|
{
|
|
|
|
result = StringFromXMLStringPtr(content);
|
|
|
|
xmlFree(content);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
result = @"";
|
|
|
|
}
|
2012-02-20 03:40:15 +00:00
|
|
|
|
|
|
|
return result;
|
2009-02-09 16:16:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
- (void) setObjectValue: (id)value
|
|
|
|
{
|
2012-03-05 22:48:18 +00:00
|
|
|
NSString *stringValue;
|
|
|
|
|
|
|
|
// FIXME: Use correct formatter here
|
|
|
|
stringValue = [value description];
|
|
|
|
[self setStringValue: stringValue];
|
|
|
|
|
2012-01-04 12:41:45 +00:00
|
|
|
ASSIGN(internal->objectValue, value);
|
2009-02-09 16:16:11 +00:00
|
|
|
}
|
|
|
|
|
2012-01-02 07:27:56 +00:00
|
|
|
- (void) setName: (NSString *)name
|
2009-02-09 16:16:11 +00:00
|
|
|
{
|
2012-03-12 19:50:51 +00:00
|
|
|
xmlNodePtr node = internal->node;
|
|
|
|
|
2012-03-01 08:41:20 +00:00
|
|
|
if (NSXMLInvalidKind == internal->kind)
|
2012-01-06 12:22:30 +00:00
|
|
|
{
|
2012-03-01 08:41:20 +00:00
|
|
|
return;
|
2012-01-06 12:22:30 +00:00
|
|
|
}
|
2012-03-01 08:41:20 +00:00
|
|
|
|
2012-03-12 19:50:51 +00:00
|
|
|
if (node->type == XML_NAMESPACE_DECL)
|
|
|
|
{
|
|
|
|
xmlNsPtr ns = (xmlNsPtr)node;
|
|
|
|
|
|
|
|
if (ns->prefix != NULL)
|
|
|
|
{
|
|
|
|
xmlFree((xmlChar *)ns->prefix);
|
|
|
|
}
|
|
|
|
ns->prefix = XMLStringCopy(name);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
xmlNodeSetName(node, XMLSTRING(name));
|
|
|
|
}
|
2009-02-09 16:16:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
- (void) setStringValue: (NSString*)string
|
|
|
|
{
|
|
|
|
[self setStringValue: string resolvingEntities: NO];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void) setStringValue: (NSString*)string resolvingEntities: (BOOL)resolve
|
|
|
|
{
|
2012-03-12 13:27:32 +00:00
|
|
|
xmlNodePtr node = internal->node;
|
2012-02-22 22:52:31 +00:00
|
|
|
|
2012-03-12 19:50:51 +00:00
|
|
|
if (node->type == XML_NAMESPACE_DECL)
|
2012-01-03 17:15:29 +00:00
|
|
|
{
|
2012-03-12 19:50:51 +00:00
|
|
|
xmlNsPtr ns = (xmlNsPtr)node;
|
|
|
|
if (ns->href != NULL)
|
|
|
|
{
|
|
|
|
xmlFree((xmlChar *)ns->href);
|
|
|
|
}
|
|
|
|
ns->href = XMLStringCopy(string);
|
2012-01-03 17:15:29 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2012-03-12 19:50:51 +00:00
|
|
|
if (resolve == NO)
|
|
|
|
{
|
|
|
|
xmlNodeSetContent(node, XMLSTRING(string));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// need to actually resolve entities...
|
|
|
|
// is this the right functionality?? xmlEncodeSpecialChars()
|
|
|
|
xmlChar *newstr = xmlEncodeEntitiesReentrant(node->doc, XMLSTRING(string));
|
|
|
|
xmlNodeSetContent(node, newstr);
|
|
|
|
xmlMemFree(newstr);
|
|
|
|
}
|
2012-01-06 12:22:30 +00:00
|
|
|
}
|
2012-03-05 22:48:18 +00:00
|
|
|
ASSIGN(internal->objectValue, string);
|
2009-02-09 16:16:11 +00:00
|
|
|
}
|
|
|
|
|
2012-02-22 10:55:12 +00:00
|
|
|
- (void) setURI: (NSString*)URI
|
|
|
|
{
|
2012-03-01 08:41:20 +00:00
|
|
|
if (NSXMLInvalidKind == internal->kind)
|
2012-02-22 10:55:12 +00:00
|
|
|
{
|
2012-03-01 08:41:20 +00:00
|
|
|
return;
|
2012-02-22 10:55:12 +00:00
|
|
|
}
|
2012-03-01 08:41:20 +00:00
|
|
|
ASSIGNCOPY(internal->URI, URI);
|
2012-03-12 13:27:32 +00:00
|
|
|
//xmlNodeSetBase(internal->node, XMLSTRING(URI));
|
2012-02-22 10:55:12 +00:00
|
|
|
}
|
|
|
|
|
2012-02-22 22:52:31 +00:00
|
|
|
- (NSString*) URI
|
|
|
|
{
|
2012-02-23 17:57:50 +00:00
|
|
|
if (NSXMLInvalidKind == internal->kind)
|
2012-02-22 22:52:31 +00:00
|
|
|
{
|
|
|
|
return nil;
|
|
|
|
}
|
2012-03-01 08:41:20 +00:00
|
|
|
return internal->URI;
|
2012-03-12 13:27:32 +00:00
|
|
|
//return StringFromXMLStringPtr(xmlNodeGetBase(NULL, internal->node));
|
2012-02-22 22:52:31 +00:00
|
|
|
}
|
|
|
|
|
2012-02-22 10:55:12 +00:00
|
|
|
- (NSString*) XMLString
|
|
|
|
{
|
2012-02-22 22:52:31 +00:00
|
|
|
return [self XMLStringWithOptions: NSXMLNodeOptionsNone];
|
2012-02-22 10:55:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
- (NSString*) XMLStringWithOptions: (NSUInteger)options
|
|
|
|
{
|
|
|
|
NSString *string = nil;
|
|
|
|
xmlChar *buf = NULL;
|
2012-03-14 10:09:15 +00:00
|
|
|
xmlBufferPtr buffer;
|
2012-02-22 10:55:12 +00:00
|
|
|
int error = 0;
|
|
|
|
int len = 0;
|
2012-03-14 21:12:19 +00:00
|
|
|
xmlSaveCtxtPtr ctxt;
|
|
|
|
int xmlOptions = 0;
|
2012-02-22 10:55:12 +00:00
|
|
|
|
2012-03-14 21:12:19 +00:00
|
|
|
buffer = xmlBufferCreate();
|
2012-03-14 10:09:15 +00:00
|
|
|
|
2012-03-14 21:12:19 +00:00
|
|
|
// XML_SAVE_XHTML XML_SAVE_AS_HTML XML_SAVE_NO_DECL XML_SAVE_NO_XHTML
|
2012-03-16 07:56:15 +00:00
|
|
|
#if LIBXML_VERSION >= 20702
|
2012-03-14 21:12:19 +00:00
|
|
|
xmlOptions |= XML_SAVE_AS_XML;
|
2012-03-16 07:56:15 +00:00
|
|
|
#endif
|
|
|
|
#if LIBXML_VERSION >= 20708
|
2012-03-14 21:12:19 +00:00
|
|
|
if (options & NSXMLNodePreserveWhitespace)
|
|
|
|
{
|
|
|
|
xmlOptions |= XML_SAVE_WSNONSIG;
|
2012-03-12 19:50:51 +00:00
|
|
|
}
|
2012-03-16 07:56:15 +00:00
|
|
|
#endif
|
|
|
|
#if LIBXML_VERSION >= 20622
|
2012-03-14 21:12:19 +00:00
|
|
|
if (options & NSXMLNodeCompactEmptyElement)
|
|
|
|
{
|
|
|
|
xmlOptions |= XML_SAVE_NO_EMPTY;
|
|
|
|
}
|
2012-03-16 07:56:15 +00:00
|
|
|
#endif
|
|
|
|
#if LIBXML_VERSION >= 20617
|
2012-03-14 21:12:19 +00:00
|
|
|
if (options & NSXMLNodePrettyPrint)
|
|
|
|
{
|
|
|
|
xmlOptions |= XML_SAVE_FORMAT;
|
|
|
|
}
|
2012-03-16 07:56:15 +00:00
|
|
|
#endif
|
2012-03-14 21:12:19 +00:00
|
|
|
|
|
|
|
ctxt = xmlSaveToBuffer(buffer, "utf-8", xmlOptions);
|
|
|
|
xmlSaveTree(ctxt, internal->node);
|
|
|
|
error = xmlSaveClose(ctxt);
|
2012-02-22 22:52:31 +00:00
|
|
|
if (-1 == error)
|
|
|
|
{
|
|
|
|
xmlBufferFree(buffer);
|
|
|
|
return nil;
|
|
|
|
}
|
2012-02-22 10:55:12 +00:00
|
|
|
buf = buffer->content;
|
|
|
|
len = buffer->use;
|
2012-03-14 10:09:15 +00:00
|
|
|
string = StringFromXMLString(buf, len);
|
2012-02-22 10:55:12 +00:00
|
|
|
xmlBufferFree(buffer);
|
|
|
|
|
|
|
|
return string;
|
|
|
|
}
|
|
|
|
|
2009-02-09 16:16:11 +00:00
|
|
|
- (NSString*) XPath
|
|
|
|
{
|
2012-03-12 13:27:32 +00:00
|
|
|
xmlNodePtr node = internal->node;
|
2012-02-20 03:40:15 +00:00
|
|
|
return StringFromXMLStringPtr(xmlGetNodePath(node));
|
2009-02-09 16:16:11 +00:00
|
|
|
}
|
|
|
|
|
2012-02-20 03:40:15 +00:00
|
|
|
- (NSArray*) nodesForXPath: (NSString*)anxpath error: (NSError**)error
|
2009-02-09 16:16:11 +00:00
|
|
|
{
|
2012-02-23 17:57:50 +00:00
|
|
|
if (error != NULL)
|
2012-02-20 03:40:15 +00:00
|
|
|
{
|
|
|
|
*error = NULL;
|
|
|
|
}
|
|
|
|
return execute_xpath(self, anxpath, NULL);
|
2009-02-09 16:16:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
- (NSArray*) objectsForXQuery: (NSString*)xquery
|
|
|
|
constants: (NSDictionary*)constants
|
|
|
|
error: (NSError**)error
|
|
|
|
{
|
|
|
|
return [self notImplemented: _cmd];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (NSArray*) objectsForXQuery: (NSString*)xquery error: (NSError**)error
|
|
|
|
{
|
2012-03-08 00:15:08 +00:00
|
|
|
return [self objectsForXQuery: xquery
|
|
|
|
constants: nil
|
|
|
|
error: error];
|
2009-02-09 16:16:11 +00:00
|
|
|
}
|
|
|
|
@end
|
|
|
|
|
2012-02-20 03:40:15 +00:00
|
|
|
#endif
|