#import "DDXMLPrivate.h"
#import "NSString+DDXML.h"

#import <libxml/xpath.h>
#import <libxml/xpathInternals.h>

#if ! __has_feature(objc_arc)
#warning This file must be compiled with ARC. Use -fobjc-arc flag (or convert project to ARC).
#endif

/**
 * Welcome to KissXML.
 * 
 * The project page has documentation if you have questions.
 * https://github.com/robbiehanson/KissXML
 * 
 * If you're new to the project you may wish to read the "Getting Started" wiki.
 * https://github.com/robbiehanson/KissXML/wiki/GettingStarted
 * 
 * KissXML provides a drop-in replacement for Apple's NSXML class cluster.
 * The goal is to get the exact same behavior as the NSXML classes.
 * 
 * For API Reference, see Apple's excellent documentation,
 * either via Xcode's Mac OS X documentation, or via the web:
 * 
 * https://github.com/robbiehanson/KissXML/wiki/Reference
**/

@implementation DDXMLNode

static void MyErrorHandler(void * userData, xmlErrorPtr error);

#if DDXML_DEBUG_MEMORY_ISSUES

static CFMutableDictionaryRef zombieTracker;
static dispatch_queue_t zombieQueue;

static void RecursiveMarkZombiesFromNode(xmlNodePtr node);
static void RecursiveMarkZombiesFromDoc(xmlDocPtr doc);

static void MarkZombies(void *xmlPtr);
static void MarkBirth(void *xmlPtr, DDXMLNode *wrapper);
static void MarkDeath(void *xmlPtr, DDXMLNode *wrapper);

#endif

/**
 * From Apple's Documentation:
 * 
 * The runtime sends initialize to each class in a program exactly one time just before the class,
 * or any class that inherits from it, is sent its first message from within the program. (Thus the method may
 * never be invoked if the class is not used.) The runtime sends the initialize message to classes
 * in a thread-safe manner. Superclasses receive this message before their subclasses.
 * 
 * The method may also be called directly (assumably by accident), hence the safety mechanism.
**/
+ (void)initialize
{
	static dispatch_once_t onceToken;
	dispatch_once(&onceToken, ^{
		
		// Redirect error output to our own function (don't clog up the console)
		initGenericErrorDefaultFunc(NULL);
		xmlSetStructuredErrorFunc(NULL, MyErrorHandler);
		
		// Tell libxml not to keep ignorable whitespace (such as node indentation, formatting, etc).
		// NSXML ignores such whitespace.
		// This also has the added benefit of taking up less RAM when parsing formatted XML documents.
		xmlKeepBlanksDefault(0);
		
		#if DDXML_DEBUG_MEMORY_ISSUES
		{
			zombieTracker = CFDictionaryCreateMutable(NULL, 0, NULL, &kCFTypeDictionaryValueCallBacks);
			zombieQueue = dispatch_queue_create("DDXMLZombieQueue", NULL);
		}
		#endif
		
	});
}

+ (id)elementWithName:(NSString *)name
{
	return [[DDXMLElement alloc] initWithName:name];
}

+ (id)elementWithName:(NSString *)name stringValue:(NSString *)string
{
	return [[DDXMLElement alloc] initWithName:name stringValue:string];
}

+ (id)elementWithName:(NSString *)name children:(NSArray *)children attributes:(NSArray *)attributes
{
	DDXMLElement *result = [[DDXMLElement alloc] initWithName:name];
	[result setChildren:children];
	[result setAttributes:attributes];
	
	return result;
}

+ (id)elementWithName:(NSString *)name URI:(NSString *)URI
{
	return [[DDXMLElement alloc] initWithName:name URI:URI];
}

+ (id)attributeWithName:(NSString *)name stringValue:(NSString *)stringValue
{
	xmlAttrPtr attr = xmlNewProp(NULL, [name xmlChar], [stringValue xmlChar]);
	
	if (attr == NULL) return nil;
	
	return [[DDXMLAttributeNode alloc] initWithAttrPrimitive:attr owner:nil];
}

+ (id)attributeWithName:(NSString *)name URI:(NSString *)URI stringValue:(NSString *)stringValue
{
	xmlAttrPtr attr = xmlNewProp(NULL, [name xmlChar], [stringValue xmlChar]);
	
	if (attr == NULL) return nil;
	
	DDXMLAttributeNode *result = [[DDXMLAttributeNode alloc] initWithAttrPrimitive:attr owner:nil];
	[result setURI:URI];
	
	return result;
}

+ (id)namespaceWithName:(NSString *)name stringValue:(NSString *)stringValue
{
	// If the user passes a nil or empty string name, they are trying to create a default namespace
	const xmlChar *xmlName = [name length] > 0 ? [name xmlChar] : NULL;
	
	xmlNsPtr ns = xmlNewNs(NULL, [stringValue xmlChar], xmlName);
	
	if (ns == NULL) return nil;
	
	return [[DDXMLNamespaceNode alloc] initWithNsPrimitive:ns nsParent:NULL owner:nil];
}

+ (id)processingInstructionWithName:(NSString *)name stringValue:(NSString *)stringValue
{
	xmlNodePtr procInst = xmlNewPI([name xmlChar], [stringValue xmlChar]);
	
	if (procInst == NULL) return nil;
	
	return [[DDXMLNode alloc] initWithPrimitive:(xmlKindPtr)procInst owner:nil];
}

+ (id)commentWithStringValue:(NSString *)stringValue
{
	xmlNodePtr comment = xmlNewComment([stringValue xmlChar]);
	
	if (comment == NULL) return nil;
	
	return [[DDXMLNode alloc] initWithPrimitive:(xmlKindPtr)comment owner:nil];
}

+ (id)textWithStringValue:(NSString *)stringValue
{
	xmlNodePtr text = xmlNewText([stringValue xmlChar]);
	
	if (text == NULL) return nil;
	
	return [[DDXMLNode alloc] initWithPrimitive:(xmlKindPtr)text owner:nil];
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#pragma mark Init, Dealloc
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

+ (id)nodeWithUnknownPrimitive:(xmlKindPtr)kindPtr owner:(DDXMLNode *)owner
{
	if (kindPtr->type == XML_DOCUMENT_NODE)
	{
		return [DDXMLDocument nodeWithDocPrimitive:(xmlDocPtr)kindPtr owner:owner];
	}
	else if (kindPtr->type == XML_ELEMENT_NODE)
	{
		return [DDXMLElement nodeWithElementPrimitive:(xmlNodePtr)kindPtr owner:owner];
	}
	else if (kindPtr->type == XML_NAMESPACE_DECL)
	{
		// Todo: This may be a problem...
		
		return [DDXMLNamespaceNode nodeWithNsPrimitive:(xmlNsPtr)kindPtr nsParent:NULL owner:owner];
	}
	else if (kindPtr->type == XML_ATTRIBUTE_NODE)
	{
		return [DDXMLAttributeNode nodeWithAttrPrimitive:(xmlAttrPtr)kindPtr owner:owner];
	}
	else
	{
		return [DDXMLNode nodeWithPrimitive:kindPtr owner:owner];
	}
}

/**
 * Returns a DDXML wrapper object for the given primitive node.
 * The given node MUST be non-NULL and of the proper type.
**/
+ (id)nodeWithPrimitive:(xmlKindPtr)kindPtr owner:(DDXMLNode *)owner
{
	return [[DDXMLNode alloc] initWithPrimitive:kindPtr owner:owner];
}

/**
 * Returns a DDXML wrapper object for the given primitive node.
 * The given node MUST be non-NULL and of the proper type.
**/
- (id)initWithPrimitive:(xmlKindPtr)kindPtr owner:(DDXMLNode *)inOwner
{
	if ((self = [super init]))
	{
		genericPtr = kindPtr;
		owner = inOwner;
		
	#if DDXML_DEBUG_MEMORY_ISSUES
		MarkBirth(genericPtr, self);
	#endif
	}
	return self;
}

/**
 * This method shouldn't be used.
 * To maintain compatibility with Apple, we return an invalid node.
**/
- (id)init
{
	self = [super init];
	
	if ([self isKindOfClass:[DDXMLInvalidNode class]])
	{
		return self;
	}
	else
	{
		return [[DDXMLInvalidNode alloc] init];
	}
}

- (void)dealloc
{
#if DDXML_DEBUG_MEMORY_ISSUES
	MarkDeath(genericPtr, self);
#endif
	
	// We also check if genericPtr is NULL.
	// This may be the case if, e.g., DDXMLElement calls [self release] from it's init method.
	
	if ((owner == nil) && (genericPtr != NULL))
	{
		if (IsXmlNsPtr(genericPtr))
		{
		#if DDXML_DEBUG_MEMORY_ISSUES
			MarkZombies(genericPtr);
		#endif
			xmlFreeNs((xmlNsPtr)genericPtr);
		}
		else if (IsXmlAttrPtr(genericPtr))
		{
		#if DDXML_DEBUG_MEMORY_ISSUES
			MarkZombies(genericPtr);
		#endif
			xmlFreeProp((xmlAttrPtr)genericPtr);
		}
		else if (IsXmlDtdPtr(genericPtr))
		{
		#if DDXML_DEBUG_MEMORY_ISSUES
			MarkZombies(genericPtr);
		#endif
			xmlFreeDtd((xmlDtdPtr)genericPtr);
		}
		else if (IsXmlDocPtr(genericPtr))
		{
			xmlDocPtr doc = (xmlDocPtr)genericPtr;
			
		#if DDXML_DEBUG_MEMORY_ISSUES
			RecursiveMarkZombiesFromDoc(doc);
		#endif
			xmlFreeDoc(doc);
		}
		else if (IsXmlNodePtr(genericPtr))
		{
			xmlNodePtr node = (xmlNodePtr)genericPtr;
			
		#if DDXML_DEBUG_MEMORY_ISSUES
			RecursiveMarkZombiesFromNode(node);
		#endif
			xmlFreeNode(node);
		}
		else
		{
			NSAssert1(NO, @"Cannot free unknown node type: %i", genericPtr->type);
		}
	}
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#pragma mark Copying
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

- (id)copyWithZone:(NSZone *)zone
{
#if DDXML_DEBUG_MEMORY_ISSUES
	DDXMLNotZombieAssert();
#endif
	
	if (IsXmlDocPtr(genericPtr))
	{
		xmlDocPtr copyDocPtr = xmlCopyDoc((xmlDocPtr)genericPtr, 1);
		
		if (copyDocPtr == NULL) return nil;
		
		return [[DDXMLDocument alloc] initWithDocPrimitive:copyDocPtr owner:nil];
	}
	
	if (IsXmlNodePtr(genericPtr))
	{
		xmlNodePtr copyNodePtr = xmlCopyNode((xmlNodePtr)genericPtr, 1);
		
		if (copyNodePtr == NULL) return nil;
		
		if ([self isKindOfClass:[DDXMLElement class]])
			return [[DDXMLElement alloc] initWithElementPrimitive:copyNodePtr owner:nil];
		else
			return [[DDXMLNode alloc] initWithPrimitive:(xmlKindPtr)copyNodePtr owner:nil];
	}
	
	if (IsXmlAttrPtr(genericPtr))
	{
		xmlAttrPtr copyAttrPtr = xmlCopyProp(NULL, (xmlAttrPtr)genericPtr);
		
		if (copyAttrPtr == NULL) return nil;
		
		return [[DDXMLAttributeNode alloc] initWithAttrPrimitive:copyAttrPtr owner:nil];
	}
	
	if (IsXmlNsPtr(genericPtr))
	{
		xmlNsPtr copyNsPtr = xmlCopyNamespace((xmlNsPtr)genericPtr);
		
		if (copyNsPtr == NULL) return nil;
		
		return [[DDXMLNamespaceNode alloc] initWithNsPrimitive:copyNsPtr nsParent:NULL owner:nil];
	}
	
	if (IsXmlDtdPtr(genericPtr))
	{
		xmlDtdPtr copyDtdPtr = xmlCopyDtd((xmlDtdPtr)genericPtr);
		
		if (copyDtdPtr == NULL) return nil;
		
		return [[DDXMLNode alloc] initWithPrimitive:(xmlKindPtr)copyDtdPtr owner:nil];
	}
	
	return nil;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#pragma mark Equality
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

- (BOOL)isEqual:(id)anObject
{
	// DDXMLNode, DDXMLElement, and DDXMLDocument are simply light-weight wrappers atop a libxml structure.
	// 
	// To provide maximum speed and thread-safety,
	// multiple DDXML wrapper objects may be created that wrap the same underlying libxml node.
	// 
	// Thus equality is simply a matter of what underlying libxml node DDXML is wrapping.
	
	if ([anObject class] == [self class])
	{
		DDXMLNode *aNode = (DDXMLNode *)anObject;
		
		return (genericPtr == aNode->genericPtr);
	}
	
	return NO;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#pragma mark Properties
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

- (DDXMLNodeKind)kind
{
#if DDXML_DEBUG_MEMORY_ISSUES
	DDXMLNotZombieAssert();
#endif
	
	if (genericPtr != NULL)
		return genericPtr->type;
	else
		return DDXMLInvalidKind;
}

- (void)setName:(NSString *)name
{
	// Note: DDXMLNamespaceNode overrides this method
	
#if DDXML_DEBUG_MEMORY_ISSUES
	DDXMLNotZombieAssert();
#endif
	
	// The xmlNodeSetName function works for both nodes and attributes
	xmlNodeSetName((xmlNodePtr)genericPtr, [name xmlChar]);
}

- (NSString *)name
{
	// Note: DDXMLNamespaceNode overrides this method
	// Note: DDXMLAttributeNode overrides this method
	
#if DDXML_DEBUG_MEMORY_ISSUES
	DDXMLNotZombieAssert();
#endif
	
	const xmlChar *xmlName = ((xmlStdPtr)genericPtr)->name;
	if (xmlName == NULL)
	{
		return nil;
	}
	
	NSString *name = [NSString stringWithUTF8String:(const char *)xmlName];
	
	if (IsXmlNodePtr(genericPtr))
	{
		xmlNodePtr node = (xmlNodePtr)genericPtr;
		
		NSRange range = [name rangeOfString:@":"];
		if (range.length == 0)
		{
			if (node->ns && node->ns->prefix)
			{
				return [NSString stringWithFormat:@"%s:%@", node->ns->prefix, name];
			}
		}
	}
	
	return name;
}

- (void)setStringValue:(NSString *)string
{
	// Note: DDXMLNamespaceNode overrides this method
	// Note: DDXMLAttributeNode overrides this method
	
#if DDXML_DEBUG_MEMORY_ISSUES
	DDXMLNotZombieAssert();
#endif
	
	if (IsXmlNodePtr(genericPtr))
	{
		xmlStdPtr node = (xmlStdPtr)genericPtr;
		
		// Setting the content of a node erases any existing child nodes.
		// Therefore, we need to remove them properly first.
		[[self class] removeAllChildrenFromNode:(xmlNodePtr)node];
		
		xmlChar *escapedString = xmlEncodeSpecialChars(node->doc, [string xmlChar]);
		xmlNodeSetContent((xmlNodePtr)node, escapedString);
		xmlFree(escapedString);
	}
}

/**
 * Returns the content of the receiver as a string value.
 * 
 * If the receiver is a node object of element kind, the content is that of any text-node children.
 * This method recursively visits elements nodes and concatenates their text nodes in document order with
 * no intervening spaces.
**/
- (NSString *)stringValue
{
	// Note: DDXMLNamespaceNode overrides this method
	// Note: DDXMLAttributeNode overrides this method
	
#if DDXML_DEBUG_MEMORY_ISSUES
	DDXMLNotZombieAssert();
#endif
	
	if (IsXmlNodePtr(genericPtr))
	{
		xmlChar *content = xmlNodeGetContent((xmlNodePtr)genericPtr);
		
		NSString *result = [NSString stringWithUTF8String:(const char *)content];
		
		xmlFree(content);
		return result;
	}
	
	return nil;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#pragma mark Tree Navigation
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

/**
 * Returns the index of the receiver identifying its position relative to its sibling nodes.
 * The first child node of a parent has an index of zero.
**/
- (NSUInteger)index
{
	// Note: DDXMLNamespaceNode overrides this method
	
#if DDXML_DEBUG_MEMORY_ISSUES
	DDXMLNotZombieAssert();
#endif
	
	NSUInteger result = 0;
	
	xmlStdPtr node = ((xmlStdPtr)genericPtr)->prev;
	while (node != NULL)
	{
		result++;
		node = node->prev;
	}
	
	return result;
}

/**
 * Returns the nesting level of the receiver within the tree hierarchy.
 * The root element of a document has a nesting level of one.
**/
- (NSUInteger)level
{
	// Note: DDXMLNamespaceNode overrides this method
	
#if DDXML_DEBUG_MEMORY_ISSUES
	DDXMLNotZombieAssert();
#endif
	
	NSUInteger result = 0;
	
	xmlNodePtr currentNode = ((xmlStdPtr)genericPtr)->parent;
	while (currentNode != NULL)
	{
		result++;
		currentNode = currentNode->parent;
	}
	
	return result;
}

/**
 * Returns the DDXMLDocument object containing the root element and representing the XML document as a whole.
 * If the receiver is a standalone node (that is, a node at the head of a detached branch of the tree), this
 * method returns nil.
**/
- (DDXMLDocument *)rootDocument
{
	// Note: DDXMLNamespaceNode overrides this method
	
#if DDXML_DEBUG_MEMORY_ISSUES
	DDXMLNotZombieAssert();
#endif
	
	xmlStdPtr node = (xmlStdPtr)genericPtr;
	
	if (node == NULL || node->doc == NULL)
		return nil;
	else
		return [DDXMLDocument nodeWithDocPrimitive:node->doc owner:self];
}

/**
 * Returns the parent node of the receiver.
 * 
 * Document nodes and standalone nodes (that is, the root of a detached branch of a tree) have no parent, and
 * sending this message to them returns nil. A one-to-one relationship does not always exists between a parent and
 * its children; although a namespace or attribute node cannot be a child, it still has a parent element.
**/
- (DDXMLNode *)parent
{
	// Note: DDXMLNamespaceNode overrides this method
	
#if DDXML_DEBUG_MEMORY_ISSUES
	DDXMLNotZombieAssert();
#endif
	
	xmlStdPtr node = (xmlStdPtr)genericPtr;
	
	if (node->parent == NULL)
		return nil;
	else
		return [DDXMLNode nodeWithUnknownPrimitive:(xmlKindPtr)node->parent owner:self];
}

/**
 * Returns the number of child nodes the receiver has.
 * For performance reasons, use this method instead of getting the count from the array returned by children.
**/
- (NSUInteger)childCount
{
	// Note: DDXMLNamespaceNode overrides this method
	
#if DDXML_DEBUG_MEMORY_ISSUES
	DDXMLNotZombieAssert();
#endif
	
	if (!IsXmlDocPtr(genericPtr) && !IsXmlNodePtr(genericPtr) && !IsXmlDtdPtr(genericPtr))
	{
		return 0;
	}
	
	NSUInteger result = 0;
	
	xmlNodePtr child = ((xmlStdPtr)genericPtr)->children;
	while (child != NULL)
	{
		result++;
		child = child->next;
	}
	
	return result;
}

/**
 * Returns an immutable array containing the child nodes of the receiver (as DDXMLNode objects).
**/
- (NSArray *)children
{
	// Note: DDXMLNamespaceNode overrides this method
	
#if DDXML_DEBUG_MEMORY_ISSUES
	DDXMLNotZombieAssert();
#endif
	
	if (!IsXmlDocPtr(genericPtr) && !IsXmlNodePtr(genericPtr) && !IsXmlDtdPtr(genericPtr))
	{
		return nil;
	}
	
	NSMutableArray *result = [NSMutableArray array];
	
	xmlNodePtr child = ((xmlStdPtr)genericPtr)->children;
	while (child != NULL)
	{
		[result addObject:[DDXMLNode nodeWithUnknownPrimitive:(xmlKindPtr)child owner:self]];
		
		child = child->next;
	}
	
	return [result copy];
}

/**
 * Returns the child node of the receiver at the specified location.
 * Returns a DDXMLNode object or nil if the receiver has no children.
 * 
 * If the receive has children and index is out of bounds, an exception is raised.
 * 
 * The receiver should be a DDXMLNode object representing a document, element, or document type declaration.
 * The returned node object can represent an element, comment, text, or processing instruction.
**/
- (DDXMLNode *)childAtIndex:(NSUInteger)index
{
	// Note: DDXMLNamespaceNode overrides this method
	
#if DDXML_DEBUG_MEMORY_ISSUES
	DDXMLNotZombieAssert();
#endif
	
	if (!IsXmlDocPtr(genericPtr) && !IsXmlNodePtr(genericPtr) && !IsXmlDtdPtr(genericPtr))
	{
		return nil;
	}
	
	NSUInteger i = 0;
	
	xmlNodePtr child = ((xmlStdPtr)genericPtr)->children;
	
	if (child == NULL)
	{
		// NSXML doesn't raise an exception if there are no children
		return nil;
	}
	
	while (child != NULL)
	{
		if (i == index)
		{
			return [DDXMLNode nodeWithUnknownPrimitive:(xmlKindPtr)child owner:self];
		}
		
		i++;
		child = child->next;
	}
	
	// NSXML version uses this same assertion
	DDXMLAssert(NO, @"index (%u) beyond bounds (%u)", (unsigned)index, (unsigned)i);
	
	return nil;
}

/**
 * Returns the previous DDXMLNode object that is a sibling node to the receiver.
 * 
 * This object will have an index value that is one less than the receiverÕs.
 * If there are no more previous siblings (that is, other child nodes of the receiverÕs parent) the method returns nil.
**/
- (DDXMLNode *)previousSibling
{
	// Note: DDXMLNamespaceNode overrides this method
	
#if DDXML_DEBUG_MEMORY_ISSUES
	DDXMLNotZombieAssert();
#endif
	
	xmlStdPtr node = (xmlStdPtr)genericPtr;
	
	if (node->prev == NULL)
		return nil;
	else
		return [DDXMLNode nodeWithUnknownPrimitive:(xmlKindPtr)node->prev owner:self];
}

/**
 * Returns the next DDXMLNode object that is a sibling node to the receiver.
 * 
 * This object will have an index value that is one more than the receiverÕs.
 * If there are no more subsequent siblings (that is, other child nodes of the receiverÕs parent) the
 * method returns nil.
**/
- (DDXMLNode *)nextSibling
{
	// Note: DDXMLNamespaceNode overrides this method
	
#if DDXML_DEBUG_MEMORY_ISSUES
	DDXMLNotZombieAssert();
#endif
	
	xmlStdPtr node = (xmlStdPtr)genericPtr;
	
	if (node->next == NULL)
		return nil;
	else
		return [DDXMLNode nodeWithUnknownPrimitive:(xmlKindPtr)node->next owner:self];
}

/**
 * Returns the previous DDXMLNode object in document order.
 * 
 * You use this method to ÒwalkÓ backward through the tree structure representing an XML document or document section.
 * (Use nextNode to traverse the tree in the opposite direction.) Document order is the natural order that XML
 * constructs appear in markup text. If you send this message to the first node in the tree (that is, the root element),
 * nil is returned. DDXMLNode bypasses namespace and attribute nodes when it traverses a tree in document order.
**/
- (DDXMLNode *)previousNode
{
	// Note: DDXMLNamespaceNode overrides this method
	// Note: DDXMLAttributeNode overrides this method
	
#if DDXML_DEBUG_MEMORY_ISSUES
	DDXMLNotZombieAssert();
#endif
	
	// If the node has a previous sibling,
	// then we need the last child of the last child of the last child etc
	
	// Note: Try to accomplish this task without creating dozens of intermediate wrapper objects
	
	xmlStdPtr node = (xmlStdPtr)genericPtr;
	xmlStdPtr previousSibling = node->prev;
	
	if (previousSibling != NULL)
	{
		if (previousSibling->last != NULL)
		{
			xmlNodePtr lastChild = previousSibling->last;
			while (lastChild->last != NULL)
			{
				lastChild = lastChild->last;
			}
			
			return [DDXMLNode nodeWithUnknownPrimitive:(xmlKindPtr)lastChild owner:self];
		}
		else
		{
			// The previous sibling has no children, so the previous node is simply the previous sibling
			return [DDXMLNode nodeWithUnknownPrimitive:(xmlKindPtr)previousSibling owner:self];
		}
	}
	
	// If there are no previous siblings, then the previous node is simply the parent
	
	// Note: rootNode.parent == docNode
	
	if (node->parent == NULL || node->parent->type == XML_DOCUMENT_NODE)
		return nil;
	else
		return [DDXMLNode nodeWithUnknownPrimitive:(xmlKindPtr)node->parent owner:self];
}

/**
 * Returns the next DDXMLNode object in document order.
 * 
 * You use this method to ÒwalkÓ forward through the tree structure representing an XML document or document section.
 * (Use previousNode to traverse the tree in the opposite direction.) Document order is the natural order that XML
 * constructs appear in markup text. If you send this message to the last node in the tree, nil is returned.
 * DDXMLNode bypasses namespace and attribute nodes when it traverses a tree in document order.
**/
- (DDXMLNode *)nextNode
{
	// Note: DDXMLNamespaceNode overrides this method
	// Note: DDXMLAttributeNode overrides this method
	
#if DDXML_DEBUG_MEMORY_ISSUES
	DDXMLNotZombieAssert();
#endif
	
	// If the node has children, then next node is the first child
	DDXMLNode *firstChild = [self childAtIndex:0];
	if (firstChild)
		return firstChild;
	
	// If the node has a next sibling, then next node is the same as next sibling
	
	DDXMLNode *nextSibling = [self nextSibling];
	if (nextSibling)
		return nextSibling;
	
	// There are no children, and no more siblings, so we need to get the next sibling of the parent.
	// If that is nil, we need to get the next sibling of the grandparent, etc.
	
	// Note: Try to accomplish this task without creating dozens of intermediate wrapper objects
	
	xmlNodePtr parent = ((xmlStdPtr)genericPtr)->parent;
	while (parent != NULL)
	{
		xmlNodePtr parentNextSibling = parent->next;
		if (parentNextSibling != NULL)
			return [DDXMLNode nodeWithUnknownPrimitive:(xmlKindPtr)parentNextSibling owner:self];
		else
			parent = parent->parent;
	}
	
	return nil;
}

/**
 * Detaches the receiver from its parent node.
 *
 * This method is applicable to DDXMLNode objects representing elements, text, comments, processing instructions,
 * attributes, and namespaces. Once the node object is detached, you can add it as a child node of another parent.
**/
- (void)detach
{
	// Note: DDXMLNamespaceNode overrides this method
	// Note: DDXMLAttributeNode overrides this method
	
#if DDXML_DEBUG_MEMORY_ISSUES
	DDXMLNotZombieAssert();
#endif
	
	xmlStdPtr node = (xmlStdPtr)genericPtr;
	
	if (node->parent != NULL)
	{
		if (IsXmlNodePtr(genericPtr))
		{
			[[self class] detachChild:(xmlNodePtr)node];
			
			owner = nil;
		}
	}
}

- (xmlStdPtr)_XPathPreProcess:(NSMutableString *)result
{
	// This is a private/internal method
	
	// Note: DDXMLNamespaceNode overrides this method
	// Note: DDXMLAttributeNode overrides this method
	
	return (xmlStdPtr)genericPtr;
}

- (NSString *)XPath
{
#if DDXML_DEBUG_MEMORY_ISSUES
	DDXMLNotZombieAssert();
#endif
	
	NSMutableString *result = [NSMutableString stringWithCapacity:25];
	
	// Examples:
	// /rootElement[1]/subElement[4]/thisNode[2]
	// topElement/thisNode[2]
	
	xmlStdPtr node = [self _XPathPreProcess:result];
	
	// Note: rootNode.parent == docNode
		
	while ((node != NULL) && (node->type != XML_DOCUMENT_NODE))
	{
		if ((node->parent == NULL) && (node->doc == NULL))
		{
			// We're at the top of the heirarchy, and there is no xml document.
			// Thus we don't use a leading '/', and we don't need an index.
			
			[result insertString:[NSString stringWithFormat:@"%s", node->name] atIndex:0];
		}
		else
		{
			// Find out what index this node is.
			// If it's the first node with this name, the index is 1.
			// If there are previous siblings with the same name, the index is greater than 1.
			
			int index = 1;
			xmlStdPtr prevNode = node->prev;
			
			while (prevNode != NULL)
			{
				if (xmlStrEqual(node->name, prevNode->name))
				{
					index++;
				}
				prevNode = prevNode->prev;
			}
			
			[result insertString:[NSString stringWithFormat:@"/%s[%i]", node->name, index] atIndex:0];
		}
		
		node = (xmlStdPtr)node->parent;
	}
	
	return [result copy];
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#pragma mark QNames
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

/**
 * Returns the local name of the receiver.
 * 
 * The local name is the part of a node name that follows a namespace-qualifying colon or the full name if
 * there is no colon. For example, ÒchapterÓ is the local name in the qualified name Òacme:chapterÓ.
**/
- (NSString *)localName
{
	// Note: DDXMLNamespaceNode overrides this method
	
	// Zombie test occurs in [self name]
	
	return [[self class] localNameForName:[self name]];
}

/**
 * Returns the prefix of the receiverÕs name.
 * 
 * The prefix is the part of a namespace-qualified name that precedes the colon.
 * For example, ÒacmeÓ is the local name in the qualified name Òacme:chapterÓ.
 * This method returns an empty string if the receiverÕs name is not qualified by a namespace.
**/
- (NSString *)prefix
{
	// Note: DDXMLNamespaceNode overrides this method
	
	// Zombie test occurs in [self name]
	
	return [[self class] prefixForName:[self name]];
}

/**
 * Sets the URI identifying the source of this document.
 * Pass nil to remove the current URI.
**/
- (void)setURI:(NSString *)URI
{
	// Note: DDXMLNamespaceNode overrides this method
	// Note: DDXMLAttributeNode overrides this method
	
#if DDXML_DEBUG_MEMORY_ISSUES
	DDXMLNotZombieAssert();
#endif
	
	if (IsXmlNodePtr(genericPtr))
	{
		xmlNodePtr node = (xmlNodePtr)genericPtr;
		if (node->ns != NULL)
		{
			[[self class] removeNamespace:node->ns fromNode:node];
		}
		
		if (URI)
		{
			// Create a new xmlNsPtr, add it to the nsDef list, and make ns point to it
			xmlNsPtr ns = xmlNewNs(NULL, [URI xmlChar], NULL);
			ns->next = node->nsDef;
			node->nsDef = ns;
			node->ns = ns;
		}
	}
}

/**
 * Returns the URI associated with the receiver.
 * 
 * A nodeÕs URI is derived from its namespace or a documentÕs URI; for documents, the URI comes either from the
 * parsed XML or is explicitly set. You cannot change the URI for a particular node other for than a namespace
 * or document node.
**/
- (NSString *)URI
{
	// Note: DDXMLNamespaceNode overrides this method
	// Note: DDXMLAttributeNode overrides this method
	
#if DDXML_DEBUG_MEMORY_ISSUES
	DDXMLNotZombieAssert();
#endif
	
	if (IsXmlNodePtr(genericPtr))
	{
		xmlNodePtr node = (xmlNodePtr)genericPtr;
		if (node->ns != NULL)
		{
			return [NSString stringWithUTF8String:((const char *)node->ns->href)];
		}
	}
	
	return nil;
}

+ (void)getHasPrefix:(BOOL *)hasPrefixPtr localName:(NSString **)localNamePtr forName:(NSString *)name
{
	// This is a private/internal method
	
	if (name)
	{
		NSRange range = [name rangeOfString:@":"];
		
		if (range.length != 0)
		{
			if (hasPrefixPtr) *hasPrefixPtr = range.location > 0;
			if (localNamePtr) *localNamePtr = [name substringFromIndex:(range.location + range.length)];
		}
		else
		{
			if (hasPrefixPtr) *hasPrefixPtr = NO;
			if (localNamePtr) *localNamePtr = name;
		}
	}
	else
	{
		if (hasPrefixPtr) *hasPrefixPtr = NO;
		if (localNamePtr) *localNamePtr = nil;
	}
}

+ (void)getPrefix:(NSString **)prefixPtr localName:(NSString **)localNamePtr forName:(NSString *)name
{
	// This is a private/internal method
	
	if (name)
	{
		NSRange range = [name rangeOfString:@":"];
		
		if (range.length != 0)
		{
			if (prefixPtr)    *prefixPtr    = [name substringToIndex:range.location];
			if (localNamePtr) *localNamePtr = [name substringFromIndex:(range.location + range.length)];
		}
		else
		{
			if (prefixPtr)    *prefixPtr    = @"";
			if (localNamePtr) *localNamePtr = name;
		}
	}
	else
	{
		if (prefixPtr)    *prefixPtr    = @"";
		if (localNamePtr) *localNamePtr = nil;
	}
}

/**
 * Returns the local name from the specified qualified name.
 * 
 * Examples:
 * "a:node" -> "node"
 * "a:a:node" -> "a:node"
 * "node" -> "node"
 * nil - > nil
**/
+ (NSString *)localNameForName:(NSString *)name
{
	// This is a public/API method
	
	NSString *localName;
	[self getPrefix:NULL localName:&localName forName:name];
	
	return localName;
}

/**
 * Extracts the prefix from the given name.
 * If name is nil, or has no prefix, an empty string is returned.
 * 
 * Examples:
 * "a:deusty.com" -> "a"
 * "a:a:deusty.com" -> "a"
 * "node" -> ""
 * nil -> ""
**/
+ (NSString *)prefixForName:(NSString *)name
{
	// This is a public/API method
	
	NSString *prefix;
	[self getPrefix:&prefix localName:NULL forName:name];
	
	return prefix;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#pragma mark Output
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

- (NSString *)description
{
	// Zombie test occurs in XMLStringWithOptions:
	
	return [self XMLStringWithOptions:0];
}

- (NSString *)XMLString
{
	// Zombie test occurs in XMLStringWithOptions:
	
	return [self XMLStringWithOptions:0];
}

- (NSString *)XMLStringWithOptions:(NSUInteger)options
{
#if DDXML_DEBUG_MEMORY_ISSUES
	DDXMLNotZombieAssert();
#endif
	
	// xmlSaveNoEmptyTags:
	// Global setting, asking the serializer to not output empty tags
	// as <empty/> but <empty></empty>. those two forms are undistinguishable
	// once parsed.
	// Disabled by default
	
	if (options & DDXMLNodeCompactEmptyElement)
		xmlSaveNoEmptyTags = 0;
	else
		xmlSaveNoEmptyTags = 1;
	
	int format = 0;
	if (options & DDXMLNodePrettyPrint)
	{
		format = 1;
		xmlIndentTreeOutput = 1;
	}
	
	int dumpCnt;
	
	xmlBufferPtr bufferPtr = xmlBufferCreate();
	if (IsXmlNsPtr(genericPtr))
		dumpCnt = xmlNodeDump(bufferPtr, NULL, (xmlNodePtr)genericPtr, 0, format);
	else
		dumpCnt = xmlNodeDump(bufferPtr, ((xmlStdPtr)genericPtr)->doc, (xmlNodePtr)genericPtr, 0, format);
	
	if (dumpCnt < 0)
	{
		return nil;
	}
	
	if ([self kind] == DDXMLTextKind)
	{
		NSString *result = [NSString stringWithUTF8String:(const char *)bufferPtr->content];
		
		xmlBufferFree(bufferPtr);
		
		return result;
	}
	else
	{
		NSMutableString *resTmp = [NSMutableString stringWithUTF8String:(const char *)bufferPtr->content];
		CFStringTrimWhitespace((__bridge CFMutableStringRef)resTmp);
		
		xmlBufferFree(bufferPtr);
		
		return [resTmp copy];
	}
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#pragma mark XPath/XQuery
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

- (NSArray *)nodesForXPath:(NSString *)xpath error:(NSError **)error
{
#if DDXML_DEBUG_MEMORY_ISSUES
	DDXMLNotZombieAssert();
#endif
	
	xmlXPathContextPtr xpathCtx;
	xmlXPathObjectPtr xpathObj;
	
	BOOL isTempDoc = NO;
	xmlDocPtr doc;
	
	if (IsXmlDocPtr(genericPtr))
	{
		doc = (xmlDocPtr)genericPtr;
	}
	else if (IsXmlNodePtr(genericPtr))
	{
		doc = ((xmlNodePtr)genericPtr)->doc;
		
		if(doc == NULL)
		{
			isTempDoc = YES;
			
			doc = xmlNewDoc(NULL);
			xmlDocSetRootElement(doc, (xmlNodePtr)genericPtr);
		}
	}
	else
	{
		return nil;
	}
	
	xpathCtx = xmlXPathNewContext(doc);
	xpathCtx->node = (xmlNodePtr)genericPtr;
		
	xmlNodePtr rootNode = (doc)->children;
	if(rootNode != NULL)
	{
		xmlNsPtr ns = rootNode->nsDef;
		while(ns != NULL)
		{
			xmlXPathRegisterNs(xpathCtx, ns->prefix, ns->href);
			
			ns = ns->next;
		}
	}
	
	xpathObj = xmlXPathEvalExpression([xpath xmlChar], xpathCtx);
	
	NSArray *result;
	
	if(xpathObj == NULL)
	{
		if(error) *error = [[self class] lastError];
		result = nil;
	}
	else
	{
		if(error) *error = nil;
		
		int count = xmlXPathNodeSetGetLength(xpathObj->nodesetval);
		
		if(count == 0)
		{
			result = [NSArray array];
		}
		else
		{
			NSMutableArray *mResult = [NSMutableArray arrayWithCapacity:count];
			
			int i;
			for (i = 0; i < count; i++)
			{
				xmlNodePtr node = xpathObj->nodesetval->nodeTab[i];
				
				[mResult addObject:[DDXMLNode nodeWithUnknownPrimitive:(xmlKindPtr)node owner:self]];
			}
			
			result = mResult;
		}
	}
	
	if(xpathObj) xmlXPathFreeObject(xpathObj);
	if(xpathCtx) xmlXPathFreeContext(xpathCtx);
	
	if (isTempDoc)
	{
		xmlUnlinkNode((xmlNodePtr)genericPtr);
		xmlFreeDoc(doc);
		
		// xmlUnlinkNode doesn't remove the doc ptr
		[[self class] recursiveStripDocPointersFromNode:(xmlNodePtr)genericPtr];
	}
	
	return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#pragma mark Private API
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

// ---------- MEMORY MANAGEMENT ARCHITECTURE ----------
// 
// KissXML is designed to be read-access thread-safe.
// It is not write-access thread-safe as this would require significant overhead.
// 
// What exactly does read-access thread-safe mean?
// It means that multiple threads can safely read from the same xml structure,
// so long as none of them attempt to alter the xml structure (add/remove nodes, change attributes, etc).
// 
// This read-access thread-safety includes parsed xml structures as well as xml structures created by you.
// Let's walk through a few examples to get a deeper understanding.
// 
// 
// 
// Example #1 - Parallel processing of children
// 
// DDXMLElement *root = [[DDXMLElement alloc] initWithXMLString:str error:nil];
// NSArray *children = [root children];
// 
// dispatch_queue_t q = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
// dispatch_apply([children count], q, ^(size_t i) {
//     DDXMLElement *child = [children objectAtIndex:i];
//     <process child>
// });
// 
// 
// 
// Example #2 - Asynchronous child processing
// 
// DDXMLElement *root = [[DDXMLElement alloc] initWithXMLString:str error:nil];
// DDXMLElement *child = [root elementForName:@"starbucks"];
// 
// dispatch_async(queue, ^{
//     <process child>
// });
// 
// [root release];
// 
// You may have noticed that we possibly released the root node before the child was processed.
// Is this safe?
// 
// The answer is YES.
// The child node retains a reference to the root node,
// so the xml tree heirarchy won't be freed until you're done using all associated nodes.
// 
// 
// 


/**
 * Returns whether or not the node has a parent.
 * Use this method instead of parent when you only need to ensure parent is nil.
 * This prevents the unnecessary creation of a parent node wrapper.
**/
- (BOOL)_hasParent
{
	// This is a private/internal method
	
	// Note: DDXMLNamespaceNode overrides this method
	
	xmlStdPtr node = (xmlStdPtr)genericPtr;
	
	return (node->parent != NULL);
}

+ (void)stripDocPointersFromAttr:(xmlAttrPtr)attr
{
	xmlNodePtr child = attr->children;
	while (child != NULL)
	{
		child->doc = NULL;
		child = child->next;
	}
	
	attr->doc = NULL;
}

+ (void)recursiveStripDocPointersFromNode:(xmlNodePtr)node
{
	xmlAttrPtr attr = node->properties;
	while (attr != NULL)
	{
		[self stripDocPointersFromAttr:attr];
		attr = attr->next;
	}
	
	xmlNodePtr child = node->children;
	while (child != NULL)
	{
		[self recursiveStripDocPointersFromNode:child];
		child = child->next;
	}
	
	node->doc = NULL;
}

/**
 * If node->ns is pointing to the given ns, the pointer is nullified.
 * The same goes for any attributes and childrend of node.
**/
+ (void)recursiveStripNamespace:(xmlNsPtr)ns fromNode:(xmlNodePtr)node
{
	if (node->ns == ns)
	{
		node->ns = NULL;
	}
	
	xmlAttrPtr attr = node->properties;
	while (attr)
	{
		if (attr->ns == ns)
		{
			attr->ns = NULL;
		}
		attr = attr->next;
	}
	
	xmlNodePtr child = node->children;
	while (child)
	{
		[self recursiveStripNamespace:ns fromNode:child];
		child = child->next;
	}
}

/**
 * If node or any of its attributes or children are referencing the the given old namespace,
 * they are migrated to reference the new namespace instead.
 * 
 * If newNs is NULL, and a reference to oldNs is found, then oldNs is copied,
 * and the copy is used for the remainder of the recursion.
 * 
 * This method makes copies of oldNs as needed so that oldNs can be cleanly detached from the tree.
**/
+ (void)recursiveMigrateNamespace:(xmlNsPtr)oldNs to:(xmlNsPtr)newNs node:(xmlNodePtr)node
{
	// Do we need to copy old namespace?
	
	if (newNs == NULL)
	{
		// A copy needs to be made if:
		//  * node->ns == oldNs
		//  * attr->ns == oldNs
		// 
		// Remember: The namespaces in node->nsDef are owned by node.
		//           That's not what we're migrating.
		
		BOOL needsCopy = (node->ns == oldNs);
		
		if (!needsCopy)
		{
			xmlAttrPtr attr = node->properties;
			while (attr)
			{
				if (attr->ns == oldNs)
				{
					needsCopy = YES;
					break;
				}
				attr = attr->next;
			}
		}
		
		if (needsCopy)
		{
			// Copy oldNs, and place at the end of the node's namspace list
			newNs = xmlNewNs(NULL, oldNs->href, oldNs->prefix);
			
			if (node->nsDef == NULL)
			{
				node->nsDef = newNs;
			}
			else
			{
				xmlNsPtr lastNs = node->nsDef;
				while (lastNs->next)
				{
					lastNs = lastNs->next;
				}
				
				lastNs->next = newNs;
			}
		}
	}
	
	// Migrate node & attributes
	
	if (newNs)
	{
		if (node->ns == oldNs)
		{
			node->ns = newNs;
		}
		
		xmlAttrPtr attr = node->properties;
		while (attr)
		{
			if (attr->ns == oldNs)
			{
				attr->ns = newNs;
			}
			attr = attr->next;
		}
	}
	
	// Migrate children
	
	xmlNodePtr child = node->children;
	while (child)
	{
		[self recursiveMigrateNamespace:oldNs to:newNs node:child];
		child = child->next;
	}
}

/**
 * If node has a default namespace allocated outside the given root,
 * this method copies the namespace so that the given root can be cleanly detached from its tree.
**/
+ (void)recursiveFixDefaultNamespacesInNode:(xmlNodePtr)node withNewRoot:(xmlNodePtr)rootNode
{
	NSAssert(rootNode != NULL, @"Must specify rootNode.");
	
	// Step 1 of 3
	// 
	// Copy our namespace.
	// It's important to do this first (before the other steps).
	// This way attributes and children can reference our copy. (prevents multiple copies throughout tree)
	
	xmlNsPtr nodeNs = node->ns;
	if (nodeNs)
	{
		// Does the namespace reside within the new root somewhere?
		// We can find out by searching for it in nsDef lists up to the given root.
		
		BOOL nsResidesWithinNewRoot = NO;
		
		xmlNodePtr treeNode = node;
		while (treeNode)
		{
			xmlNsPtr treeNs = treeNode->nsDef;
			while (treeNs)
			{
				if (treeNs == nodeNs)
				{
					nsResidesWithinNewRoot = YES;
					break;
				}
				
				treeNs = treeNs->next;
			}
			
			if (nsResidesWithinNewRoot || treeNode == rootNode)
				treeNode = NULL;
			else
				treeNode = treeNode->parent;
		}
		
		if (!nsResidesWithinNewRoot)
		{
			// Create a copy of the namespace, add to nsDef list, and then set as ns
			xmlNsPtr nodeNsCopy = xmlNewNs(NULL, nodeNs->href, nodeNs->prefix);
			
			nodeNsCopy->next = node->nsDef;
			node->nsDef = nodeNsCopy;
			
			node->ns = nodeNsCopy;
		}
	}
	
	// Step 2 of 3
	// 
	// If any attributes are referencing namespaces outside the new root,
	// copy the namespaces into node, and have the attributes reference the copy.
	
	xmlAttrPtr attr = node->properties;
	while (attr)
	{
		xmlNsPtr attrNs = attr->ns;
		while (attrNs)
		{
			BOOL nsResidesWithinNewRoot = NO;
			
			xmlNodePtr treeNode = node;
			while (treeNode)
			{
				xmlNsPtr treeNs = treeNode->nsDef;
				while (treeNs)
				{
					if (treeNs == attrNs)
					{
						nsResidesWithinNewRoot = YES;
						break;
					}
					
					treeNs = treeNs->next;
				}
				
				if (nsResidesWithinNewRoot || treeNode == rootNode)
					treeNode = NULL;
				else
					treeNode = treeNode->parent;
			}
			
			if (!nsResidesWithinNewRoot)
			{
				// Create a copy of the namespace, add to node's nsDef list, and then set as attribute's ns
				xmlNsPtr attrNsCopy = xmlNewNs(NULL, attrNs->href, attrNs->prefix);
				
				attrNsCopy->next = node->nsDef;
				node->nsDef = attrNsCopy;
				
				attr->ns = attrNsCopy;
			}
			
			attrNs = attrNs->next;
		}
		
		attr = attr->next;
	}
	
	// Step 3 of 3
	// 
	// Copy namespaces into children
	
	xmlNodePtr childNode = node->children;
	while (childNode)
	{
		[self recursiveFixDefaultNamespacesInNode:childNode withNewRoot:rootNode];
		
		childNode = childNode->next;
	}
}

/**
 * Detaches the given namespace from the given node.
 * The namespace's surrounding next pointers are properly updated to remove the namespace from the node's nsDef list.
 * Then the namespace's parent and next pointers are destroyed.
**/
+ (void)detachNamespace:(xmlNsPtr)ns fromNode:(xmlNodePtr)node
{
	// If node, or any of node's attributes are referring to this namespace,
	// then we need to nullify those references.
	// 
	// However, if any children are referring to this namespace,
	// then we instruct those children to make copies.
	
	if (node->ns == ns)
	{
		node->ns = NULL;
	}
	
	xmlAttrPtr attr = node->properties;
	while (attr)
	{
		if (attr->ns == ns)
		{
			attr->ns = NULL;
		}
		attr = attr->next;
	}
	
	xmlNodePtr child = node->children;
	while (child)
	{
		[self recursiveMigrateNamespace:ns to:NULL node:child];
		child = child->next;
	}
	
	// Now detach namespace from the namespace list.
	// 
	// Namespace nodes have no previous pointer, so we have to search for the node
	
	xmlNsPtr previousNs = NULL;
	xmlNsPtr currentNs = node->nsDef;
	
	while (currentNs != NULL)
	{
		if (currentNs == ns)
		{
			if (previousNs == NULL)
				node->nsDef = currentNs->next;
			else
				previousNs->next = currentNs->next;
			
			break;
		}
		
		previousNs = currentNs;
		currentNs = currentNs->next;
	}
	
	// Nullify pointers
	ns->next = NULL;
}

/**
 * Removes the given namespace from the given node.
 * The namespace's surrounding next pointers are properly updated to remove the namespace from the nsDef list.
 * Then the namespace is freed if it's no longer being referenced.
 * Otherwise, it's nsParent and next pointers are destroyed.
**/
+ (void)removeNamespace:(xmlNsPtr)ns fromNode:(xmlNodePtr)node
{
#if DDXML_DEBUG_MEMORY_ISSUES
	MarkZombies(ns);
#endif
	
	[self detachNamespace:ns fromNode:node];
	
	xmlFreeNs(ns);
}

/**
 * Removes all namespaces from the given node.
 * All namespaces are either freed, or their nsParent and next pointers are properly destroyed.
 * Upon return, the given node's nsDef pointer is NULL.
**/
+ (void)removeAllNamespacesFromNode:(xmlNodePtr)node
{
	xmlNsPtr ns = node->nsDef;
	while (ns != NULL)
	{
		xmlNsPtr nextNs = ns->next;
		
	#if DDXML_DEBUG_MEMORY_ISSUES
		MarkZombies(ns);
	#endif
		
		xmlFreeNs(ns);
		ns = nextNs;
	}
	
	node->nsDef = NULL;
	node->ns = NULL;
}

/**
 * Detaches the given attribute from its parent node.
 * The attribute's surrounding prev/next pointers are properly updated to remove the attribute from the attr list.
 * Then, if the clean flag is YES, the attribute's parent, prev, next and doc pointers are set to null.
**/
+ (void)detachAttribute:(xmlAttrPtr)attr andClean:(BOOL)clean
{
	xmlNodePtr parent = attr->parent;
	
	// Update the surrounding prev/next pointers
	if (attr->prev == NULL)
	{
		if (attr->next == NULL)
		{
			parent->properties = NULL;
		}
		else
		{
			parent->properties = attr->next;
			attr->next->prev = NULL;
		}
	}
	else
	{
		if (attr->next == NULL)
		{
			attr->prev->next = NULL;
		}
		else
		{
			attr->prev->next = attr->next;
			attr->next->prev = attr->prev;
		}
	}
	
	if (clean)
	{
		// Nullify pointers
		attr->parent = NULL;
		attr->prev   = NULL;
		attr->next   = NULL;
		attr->ns     = NULL;
		if (attr->doc != NULL) [self stripDocPointersFromAttr:attr];
	}
}

/**
 * Detaches the given attribute from its parent node.
 * The attribute's surrounding prev/next pointers are properly updated to remove the attribute from the attr list.
 * Then the attribute's parent, prev, next and doc pointers are destroyed.
**/
+ (void)detachAttribute:(xmlAttrPtr)attr
{
	[self detachAttribute:attr andClean:YES];
}

/**
 * Removes and free's the given attribute from its parent node.
 * The attribute's surrounding prev/next pointers are properly updated to remove the attribute from the attr list.
**/
+ (void)removeAttribute:(xmlAttrPtr)attr
{
#if DDXML_DEBUG_MEMORY_ISSUES
	MarkZombies(attr);
#endif
	
	// We perform a bit of optimization here.
	// No need to bother nullifying pointers since we're about to free the node anyway.
	[self detachAttribute:attr andClean:NO];
	
	xmlFreeProp(attr);
}

/**
 * Removes and frees all attributes from the given node.
 * Upon return, the given node's properties pointer is NULL.
**/
+ (void)removeAllAttributesFromNode:(xmlNodePtr)node
{
	xmlAttrPtr attr = node->properties;
	while (attr != NULL)
	{
		xmlAttrPtr nextAttr = attr->next;
		
	#if DDXML_DEBUG_MEMORY_ISSUES
		MarkZombies(attr);
	#endif
		
		xmlFreeProp(attr);
		attr = nextAttr;
	}
	
	node->properties = NULL;
}

/**
 * Detaches the given child from its parent.
 * The child's surrounding prev/next pointers are properly updated to remove the child from the node's children list.
 * Then, if the clean flag is YES, the child's parent, prev, next and doc pointers are set to null.
**/
+ (void)detachChild:(xmlNodePtr)child andClean:(BOOL)clean andFixNamespaces:(BOOL)fixNamespaces
{
	xmlNodePtr parent = child->parent;
	
	// Update the surrounding prev/next pointers
	if (child->prev == NULL)
	{
		if (child->next == NULL)
		{
			parent->children = NULL;
			parent->last = NULL;
		}
		else
		{
			parent->children = child->next;
			child->next->prev = NULL;
		}
	}
	else
	{
		if (child->next == NULL)
		{
			parent->last = child->prev;
			child->prev->next = NULL;
		}
		else
		{
			child->prev->next = child->next;
			child->next->prev = child->prev;
		}
	}
	
	if (fixNamespaces)
	{
		// Fix namesapces (namespace references that now point outside tree)
		// Note: This must be done before we nullify pointers so we can search up the tree.
		[self recursiveFixDefaultNamespacesInNode:child withNewRoot:child];
	}
	if (clean)
	{
		// Nullify pointers
		child->parent = NULL;
		child->prev   = NULL;
		child->next   = NULL;
		if (child->doc != NULL) [self recursiveStripDocPointersFromNode:child];
	}
}

/**
 * Detaches the given child from its parent.
 * The child's surrounding prev/next pointers are properly updated to remove the child from the node's children list.
 * Then the child's parent, prev, next and doc pointers are set to null.
**/
+ (void)detachChild:(xmlNodePtr)child
{
	[self detachChild:child andClean:YES andFixNamespaces:YES];
}

/**
 * Removes the given child from its parent node.
 * The child's surrounding prev/next pointers are properly updated to remove the child from the node's children list.
 * Then the child is recursively freed if it's no longer being referenced.
 * Otherwise, it's parent, prev, next and doc pointers are destroyed.
 * 
 * During the recursive free, subnodes still being referenced are properly handled.
**/
+ (void)removeChild:(xmlNodePtr)child
{
#if DDXML_DEBUG_MEMORY_ISSUES
	RecursiveMarkZombiesFromNode(child);
#endif
	
	// We perform a bit of optimization here.
	// No need to bother nullifying pointers since we're about to free the node anyway.
	[self detachChild:child andClean:NO andFixNamespaces:NO];
	
	xmlFreeNode(child);
}

/**
 * Removes all children from the given node.
 * All children are either recursively freed, or their parent, prev, next and doc pointers are properly destroyed.
 * Upon return, the given node's children pointer is NULL.
 * 
 * During the recursive free, subnodes still being referenced are properly handled.
**/
+ (void)removeAllChildrenFromNode:(xmlNodePtr)node
{
	xmlNodePtr child = node->children;
	while (child != NULL)
	{
		xmlNodePtr nextChild = child->next;
		
	#if DDXML_DEBUG_MEMORY_ISSUES
		RecursiveMarkZombiesFromNode(child);
	#endif
		
		xmlFreeNode(child);
		child = nextChild;
	}
	
	node->children = NULL;
	node->last = NULL;
}

/**
 * Returns the last error encountered by libxml.
 * Errors are caught in the MyErrorHandler method within DDXMLDocument.
**/
+ (NSError *)lastError
{
	NSValue *lastErrorValue = [[[NSThread currentThread] threadDictionary] objectForKey:DDLastErrorKey];
	if(lastErrorValue)
	{
		xmlError lastError;
		[lastErrorValue getValue:&lastError];
		
		int errCode = lastError.code;
		NSString *errMsg = [[NSString stringWithFormat:@"%s", lastError.message] stringByTrimming];
		
		NSDictionary *info = [NSDictionary dictionaryWithObject:errMsg forKey:NSLocalizedDescriptionKey];
			
		return [NSError errorWithDomain:@"DDXMLErrorDomain" code:errCode userInfo:info];
	}
	else
	{
		return nil;
	}
}

static void MyErrorHandler(void * userData, xmlErrorPtr error)
{
	// This method is called by libxml when an error occurs.
	// We register for this error in the initialize method below.
	
	// Extract error message and store in the current thread's dictionary.
	// This ensure's thread safey, and easy access for all other DDXML classes.
	
	if (error == NULL)
	{
		[[[NSThread currentThread] threadDictionary] removeObjectForKey:DDLastErrorKey];
	}
	else
	{
		NSValue *errorValue = [NSValue valueWithBytes:error objCType:@encode(xmlError)];
		
		[[[NSThread currentThread] threadDictionary] setObject:errorValue forKey:DDLastErrorKey];
	}
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#pragma mark Zombie Tracking
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#if DDXML_DEBUG_MEMORY_ISSUES

// What is zombie tracking and how does it work?
// 
// It is all explained in full detail here:
// https://github.com/robbiehanson/KissXML/wiki/MemoryManagementThreadSafety
// 
// But here's a quick overview in case you're on a plane right now
// (and the plane doesn't have internet access, or charges some ridiculous amount and you don't want to pay for it.)
// 
// <starbucks>
//   <latte/>
//   <cappuchino/>
// </starbucks>
// 
// You have a reference to the latte node, and you release/dealloc the starbucks node. Uh oh!
// The latte node is now a zombie, since the xmlNode it was pointing to (wrapping) is now gone.
// If you attempt to read info from the latte node, you might get a crash.
// Or you might get junk results.
// And if you attempt to write info to the latte node, you might just wind up with some ugly heap corruption.
// And if this happens, well.. it's a huge P.I.T.A to track down.
// 
// But I've been there before. And I feel your pain. That's where this debug option comes in.
// 
// The debugging option keeps a dictionary where the keys are the xml pointers (xmlNodePtr, xmlAttrPtr, etc),
// and the values are mutable arrays. Any wrapper objects (DDXMLElement, DDXMLNode, etc) get added to the
// mutable array for which the wrapper is pointing.
// 
// If the xml (xmlNodePtr, xmlAttrPtr, etc) is to be freed, it first removes its key from the dictionary,
// and in doing so destroys any associated mutable array.
// 
// So a zombie check ensures that the xml structure the wrapper is referring to hasn't been freed.
// If it has an exception is thrown to help track down the problem.
// 
// In other words, if you try to read info from the latte node, or attempt to alter the latte node
// (after you release/dealloc starbucks), you'll immediately get a helpful exception.
// (Goodbye junk values and heap corruption.)
// 
// This is helpful in debugging, as it is sometimes easy to forget about the memory rules of the xml heirarchy.
// Or simply due to combinations of passing subelements around and using asynchronous operations.


static void RecursiveMarkZombiesFromNode(xmlNodePtr node)
{
	// This method only exists if DDXML_DEBUG_MEMORY_ISSUES is enabled.
	
	// Mark attributes
	xmlAttrPtr attr = node->properties;
	while (attr != NULL)
	{
		MarkZombies(attr);
		attr = attr->next;
	}
	
	// Mark namespaces
	xmlNsPtr ns = node->nsDef;
	while (ns != NULL)
	{
		MarkZombies(ns);
		ns = ns->next;
	}
	if (node->ns)
	{
		MarkZombies(node->ns);
	}
	
	// Recursively mark children
	xmlNodePtr child = node->children;
	while (child != NULL)
	{
		RecursiveMarkZombiesFromNode(child);
		child = child->next;
	}
	
	MarkZombies(node);
}

static void RecursiveMarkZombiesFromDoc(xmlDocPtr doc)
{
	// This method only exists if DDXML_DEBUG_MEMORY_ISSUES is enabled.
	
	xmlNodePtr child = doc->children;
	while (child != NULL)
	{
		RecursiveMarkZombiesFromNode(child);
		
		child = child->next;
	}
	
	MarkZombies(doc);
}

static void MarkZombies(void *xmlPtr)
{
	// This method only exists if DDXML_DEBUG_MEMORY_ISSUES is enabled.
	
	dispatch_async(zombieQueue, ^{
		
	//	NSLog(@"MarkZombies: %p", xmlPtr);
		
		CFDictionaryRemoveValue(zombieTracker, xmlPtr);
	});
}

static void MarkBirth(void *xmlPtr, DDXMLNode *wrapper)
{
	// This method only exists if DDXML_DEBUG_MEMORY_ISSUES is enabled.
	
	const void *value = (void *)wrapper;
	
	dispatch_async(zombieQueue, ^{
		
	//	NSLog(@"MarkBirth: %p, %p", xmlPtr, value);
		
		CFMutableArrayRef values = (CFMutableArrayRef)CFDictionaryGetValue(zombieTracker, xmlPtr);
		if (values == NULL)
		{
			values = CFArrayCreateMutable(NULL, /*MaxCapacity:*/0, /*ValueCallbacks:*/NULL);
			CFArrayAppendValue(values, value);
			
			CFDictionarySetValue(zombieTracker, xmlPtr, values);
			CFRelease(values);
		}
		else
		{
			CFArrayAppendValue(values, value);
		}
	});
}

static void MarkDeath(void *xmlPtr, DDXMLNode *wrapper)
{
	// This method only exists if DDXML_DEBUG_MEMORY_ISSUES is enabled.
	
	const void *value = (void *)wrapper;
	
	dispatch_async(zombieQueue, ^{
		
	//	NSLog(@"MarkDeath: %p, %p", xmlPtr, value);
		
		CFMutableArrayRef values = (CFMutableArrayRef)CFDictionaryGetValue(zombieTracker, xmlPtr);
		if (values)
		{
			CFRange range = CFRangeMake(0, CFArrayGetCount(values));
			CFIndex index = CFArrayGetFirstIndexOfValue(values, range, value);
			if (index >= 0)
			{
				CFArrayRemoveValueAtIndex(values, index);
			}
		}
	});
}

BOOL DDXMLIsZombie(void *xmlPtr, DDXMLNode *wrapper)
{
	// This method only exists if DDXML_DEBUG_MEMORY_ISSUES is enabled.
	
	__block BOOL result;
	
	const void *value = (void *)wrapper;
	
	dispatch_sync(zombieQueue, ^{
		
		CFMutableArrayRef values = (CFMutableArrayRef)CFDictionaryGetValue(zombieTracker, xmlPtr);
		if (values)
		{
			CFRange range = CFRangeMake(0, CFArrayGetCount(values));
			CFIndex index = CFArrayGetFirstIndexOfValue(values, range, value);
			
			result = (index < 0);
		}
		else
		{
			result = YES;
		}
	});
	
	return result;
}

#endif

@end

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#pragma mark -
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

@implementation DDXMLNamespaceNode

/**
 * Returns a DDXML wrapper object for the given primitive node.
 * The given node MUST be non-NULL and of the proper type.
**/
+ (id)nodeWithNsPrimitive:(xmlNsPtr)ns nsParent:(xmlNodePtr)parent owner:(DDXMLNode *)owner
{
	return [[DDXMLNamespaceNode alloc] initWithNsPrimitive:ns nsParent:parent owner:owner];
}

/**
 * Returns a DDXML wrapper object for the given primitive node.
 * The given node MUST be non-NULL and of the proper type.
**/
- (id)initWithNsPrimitive:(xmlNsPtr)ns nsParent:(xmlNodePtr)parent owner:(DDXMLNode *)inOwner
{
	if ((self = [super initWithPrimitive:(xmlKindPtr)ns owner:inOwner]))
	{
		nsParentPtr = parent;
	}
	return self;
}

+ (id)nodeWithPrimitive:(xmlKindPtr)kindPtr owner:(DDXMLNode *)owner
{
	// Promote initializers which use proper parameter types to enable compiler to catch more mistakes.
	NSAssert(NO, @"Use nodeWithNsPrimitive:nsParent:owner:");
	
	return nil;
}

- (id)initWithPrimitive:(xmlKindPtr)kindPtr owner:(DDXMLNode *)inOwner
{
	// Promote initializers which use proper parameter types to enable compiler to catch more mistakes.
	NSAssert(NO, @"Use initWithNsPrimitive:nsParent:owner:");
	
	return nil;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#pragma mark Properties
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

- (void)setName:(NSString *)name
{
#if DDXML_DEBUG_MEMORY_ISSUES
	DDXMLNotZombieAssert();
#endif
	
	xmlNsPtr ns = (xmlNsPtr)genericPtr;
	
	xmlFree((xmlChar *)ns->prefix);
	ns->prefix = xmlStrdup([name xmlChar]);
}

- (NSString *)name
{
#if DDXML_DEBUG_MEMORY_ISSUES
	DDXMLNotZombieAssert();
#endif
	
	xmlNsPtr ns = (xmlNsPtr)genericPtr;
	if (ns->prefix != NULL)
		return [NSString stringWithUTF8String:((const char*)ns->prefix)];
	else
		return @"";
}

- (void)setStringValue:(NSString *)string
{
#if DDXML_DEBUG_MEMORY_ISSUES
	DDXMLNotZombieAssert();
#endif
	
	xmlNsPtr ns = (xmlNsPtr)genericPtr;
	
	xmlFree((xmlChar *)ns->href);
	ns->href = xmlEncodeSpecialChars(NULL, [string xmlChar]);
}

- (NSString *)stringValue
{
#if DDXML_DEBUG_MEMORY_ISSUES
	DDXMLNotZombieAssert();
#endif
	
	return [NSString stringWithUTF8String:((const char *)((xmlNsPtr)genericPtr)->href)];
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#pragma mark Tree Navigation
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

- (NSUInteger)index
{
#if DDXML_DEBUG_MEMORY_ISSUES
	DDXMLNotZombieAssert();
#endif
	
	xmlNsPtr ns = (xmlNsPtr)genericPtr;
	
	// The xmlNsPtr has no prev pointer, so we have to search from the parent
	
	if (nsParentPtr == NULL)
	{
		return 0;
	}
	
	NSUInteger result = 0;
	
	xmlNsPtr currentNs = nsParentPtr->nsDef;
	while (currentNs != NULL)
	{
		if (currentNs == ns)
		{
			return result;
		}
		result++;
		currentNs = currentNs->next;
	}
	
	return 0; // Yes 0, not result, because ns wasn't found in list
}

- (NSUInteger)level
{
#if DDXML_DEBUG_MEMORY_ISSUES
	DDXMLNotZombieAssert();
#endif
	
	NSUInteger result = 0;
	
	xmlNodePtr currentNode = nsParentPtr;
	while (currentNode != NULL)
	{
		result++;
		currentNode = currentNode->parent;
	}
	
	return result;
}

- (DDXMLDocument *)rootDocument
{
#if DDXML_DEBUG_MEMORY_ISSUES
	DDXMLNotZombieAssert();
#endif
	
	xmlStdPtr node = (xmlStdPtr)nsParentPtr;
	
	if (node == NULL || node->doc == NULL)
		return nil;
	else
		return [DDXMLDocument nodeWithDocPrimitive:node->doc owner:self];
}

- (DDXMLNode *)parent
{
#if DDXML_DEBUG_MEMORY_ISSUES
	DDXMLNotZombieAssert();
#endif
	
	if (nsParentPtr == NULL)
		return nil;
	else
		return [DDXMLNode nodeWithUnknownPrimitive:(xmlKindPtr)nsParentPtr owner:self];
}

- (NSUInteger)childCount
{
#if DDXML_DEBUG_MEMORY_ISSUES
	DDXMLNotZombieAssert();
#endif
	
	return 0;
}

- (NSArray *)children
{
#if DDXML_DEBUG_MEMORY_ISSUES
	DDXMLNotZombieAssert();
#endif
	
	return nil;
}

- (DDXMLNode *)childAtIndex:(NSUInteger)index
{
#if DDXML_DEBUG_MEMORY_ISSUES
	DDXMLNotZombieAssert();
#endif
	
	return nil;
}

- (DDXMLNode *)previousSibling
{
#if DDXML_DEBUG_MEMORY_ISSUES
	DDXMLNotZombieAssert();
#endif
	
	return nil;
}

- (DDXMLNode *)nextSibling
{
#if DDXML_DEBUG_MEMORY_ISSUES
	DDXMLNotZombieAssert();
#endif
	
	return nil;
}

- (DDXMLNode *)previousNode
{
#if DDXML_DEBUG_MEMORY_ISSUES
	DDXMLNotZombieAssert();
#endif
	
	return nil;
}

- (DDXMLNode *)nextNode
{
#if DDXML_DEBUG_MEMORY_ISSUES
	DDXMLNotZombieAssert();
#endif
	
	return nil;
}

- (void)detach
{
#if DDXML_DEBUG_MEMORY_ISSUES
	DDXMLNotZombieAssert();
#endif
	
	if (nsParentPtr != NULL)
	{
		[DDXMLNode detachNamespace:(xmlNsPtr)genericPtr fromNode:nsParentPtr];
		
		owner = nil;
		nsParentPtr = NULL;
	}
}

- (xmlStdPtr)_XPathPreProcess:(NSMutableString *)result
{
	// This is a private/internal method
	
	xmlStdPtr parent = (xmlStdPtr)nsParentPtr;
		
	if (parent == NULL)
		[result appendFormat:@"namespace::%@", [self name]];
	else
		[result appendFormat:@"/namespace::%@", [self name]];
	
	return parent;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#pragma mark QNames
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

- (NSString *)localName
{
#if DDXML_DEBUG_MEMORY_ISSUES
	DDXMLNotZombieAssert();
#endif
	
	// Strangely enough, the localName of a namespace is the prefix, and the prefix is an empty string
	xmlNsPtr ns = (xmlNsPtr)genericPtr;
	if (ns->prefix != NULL)
		return [NSString stringWithUTF8String:((const char *)ns->prefix)];
	else
		return @"";
}

- (NSString *)prefix
{
#if DDXML_DEBUG_MEMORY_ISSUES
	DDXMLNotZombieAssert();
#endif
	
	// Strangely enough, the localName of a namespace is the prefix, and the prefix is an empty string
	return @"";
}

- (void)setURI:(NSString *)URI
{
#if DDXML_DEBUG_MEMORY_ISSUES
	DDXMLNotZombieAssert();
#endif
	
	// Do nothing
}

- (NSString *)URI
{
#if DDXML_DEBUG_MEMORY_ISSUES
	DDXMLNotZombieAssert();
#endif
	
	return nil;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#pragma mark Private API
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

- (BOOL)_hasParent
{
	// This is a private/internal method
	
	return (nsParentPtr != NULL);
}

- (xmlNodePtr)_nsParentPtr
{
	// This is a private/internal method
	
	return nsParentPtr;
}

- (void)_setNsParentPtr:(xmlNodePtr)parentPtr
{
	// This is a private/internal method
	
	nsParentPtr = parentPtr;
}

@end

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#pragma mark -
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

@implementation DDXMLAttributeNode

+ (id)nodeWithAttrPrimitive:(xmlAttrPtr)attr owner:(DDXMLNode *)owner
{
	return [[DDXMLAttributeNode alloc] initWithAttrPrimitive:attr owner:owner];
}

- (id)initWithAttrPrimitive:(xmlAttrPtr)attr owner:(DDXMLNode *)inOwner
{
	self = [super initWithPrimitive:(xmlKindPtr)attr owner:inOwner];
	return self;
}

+ (id)nodeWithPrimitive:(xmlKindPtr)kindPtr owner:(DDXMLNode *)owner
{
	// Promote initializers which use proper parameter types to enable compiler to catch more mistakes.
	NSAssert(NO, @"Use nodeWithAttrPrimitive:nsParent:owner:");
	
	return nil;
}

- (id)initWithPrimitive:(xmlKindPtr)kindPtr owner:(DDXMLNode *)inOwner
{
	// Promote initializers which use proper parameter types to enable compiler to catch more mistakes.
	NSAssert(NO, @"Use initWithAttrPrimitive:nsParent:owner:");
	
	return nil;
}

- (void)dealloc
{
	if (attrNsPtr) xmlFreeNs(attrNsPtr);
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#pragma mark Properties
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

- (NSString *)name
{
#if DDXML_DEBUG_MEMORY_ISSUES
	DDXMLNotZombieAssert();
#endif
	
	xmlAttrPtr attr = (xmlAttrPtr)genericPtr;
	
	const xmlChar *xmlName = attr->name;
	if (xmlName == NULL)
	{
		return nil;
	}
	
	NSString *name = [NSString stringWithUTF8String:(const char *)xmlName];
	
	NSRange range = [name rangeOfString:@":"];
	if (range.length == 0)
	{
		if (attr->ns && attr->ns->prefix)
		{
			return [NSString stringWithFormat:@"%s:%@", attr->ns->prefix, name];
		}
	}
	
	return name;
}

- (void)setStringValue:(NSString *)string
{
#if DDXML_DEBUG_MEMORY_ISSUES
	DDXMLNotZombieAssert();
#endif
	
	xmlAttrPtr attr = (xmlAttrPtr)genericPtr;
	
	if (attr->children != NULL)
	{
		xmlChar *escapedString = xmlEncodeSpecialChars(attr->doc, [string xmlChar]);
		xmlNodeSetContent((xmlNodePtr)attr, escapedString);
		xmlFree(escapedString);
	}
	else
	{
		xmlNodePtr text = xmlNewText([string xmlChar]);
		attr->children = text;
	}
}

- (NSString *)stringValue
{
#if DDXML_DEBUG_MEMORY_ISSUES
	DDXMLNotZombieAssert();
#endif
	
	xmlAttrPtr attr = (xmlAttrPtr)genericPtr;
	
	if (attr->children != NULL)
	{
		return [NSString stringWithUTF8String:(const char *)attr->children->content];
	}
	
	return nil;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#pragma mark Tree Navigation
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

- (DDXMLNode *)previousNode
{
#if DDXML_DEBUG_MEMORY_ISSUES
	DDXMLNotZombieAssert();
#endif
	
	return nil;
}

- (DDXMLNode *)nextNode
{
#if DDXML_DEBUG_MEMORY_ISSUES
	DDXMLNotZombieAssert();
#endif
	
	return nil;
}

- (void)detach
{
#if DDXML_DEBUG_MEMORY_ISSUES
	DDXMLNotZombieAssert();
#endif
	
	xmlAttrPtr attr = (xmlAttrPtr)genericPtr;
	
	if (attr->parent != NULL)
	{
		// If this attribute is associated with a namespace,
		// then we need to copy the namespace in order to maintain the association.
		// 
		// Remember: attr->ns cannot be an owner of an allocated namespaces,
		//           so we need to use DDXMLAttributeNode's attrNsPtr.
		
		if (attr->ns && (attr->ns != attrNsPtr))
		{
			attrNsPtr = xmlNewNs(NULL, attr->ns->href, attr->ns->prefix);
		}
		
		[[self class] detachAttribute:attr];
		
		if (attrNsPtr)
		{
			attr->ns = attrNsPtr;
		}
		
		owner = nil;
	}
}

- (xmlStdPtr)_XPathPreProcess:(NSMutableString *)result
{
	// This is a private/internal method
	
	// Note: DDXMLNamespaceNode overrides this method
	// Note: DDXMLAttributeNode overrides this method
	
	xmlAttrPtr attr = (xmlAttrPtr)genericPtr;
	xmlStdPtr parent = (xmlStdPtr)attr->parent;
	
	if (parent == NULL)
		[result appendFormat:@"@%@", [self name]];
	else
		[result appendFormat:@"/@%@", [self name]];
	
	return parent;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#pragma mark QNames
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

- (void)setURI:(NSString *)URI
{
#if DDXML_DEBUG_MEMORY_ISSUES
	DDXMLNotZombieAssert();
#endif
	
	// An attribute can only have a single namespace attached to it.
	// In addition, this namespace can only be accessed via the URI method.
	// There is no way, within the API, to get a DDXMLNode wrapper for the attribute's namespace.
	
	// Remember: attr->ns is simply a pointer to a namespace owned by somebody else.
	//           Unless that points to our attrNsPtr (defined in DDXMLAttributeNode) we cannot free it.
	
	if (attrNsPtr != NULL)
	{
		xmlFreeNs(attrNsPtr);
		attrNsPtr = NULL;
	}
	
	xmlAttrPtr attr = (xmlAttrPtr)genericPtr;
	attr->ns = NULL;
	
	if (URI)
	{
		// If there's a namespace defined further up the tree with this URI,
		// then we want attr->ns to point to it.
		
		const xmlChar *uri = [URI xmlChar];
		
		xmlNodePtr parent = attr->parent;
		while (parent)
		{
			xmlNsPtr ns = parent->nsDef;
			while (ns)
			{
				if (xmlStrEqual(ns->href, uri))
				{
					attr->ns = ns;
					return;
				}
				
				ns = ns->next;
			}
			
			parent = parent->parent;
		}
		
		// There is no namespace further up the tree with this URI.
		// We'll have to create it ourself...
		// 
		// Remember: The attr->ns pointer is not allowed to have direct ownership.
		
		attrNsPtr = xmlNewNs(NULL, uri, NULL);
		attr->ns = attrNsPtr;
	}
}

- (NSString *)URI
{
#if DDXML_DEBUG_MEMORY_ISSUES
	DDXMLNotZombieAssert();
#endif
	
	xmlAttrPtr attr = (xmlAttrPtr)genericPtr;
	if (attr->ns != NULL)
	{
		if (attr->ns->href != NULL)
		{
			return [NSString stringWithUTF8String:((const char *)attr->ns->href)];
		}
	}
	
	// The attribute doesn't explicitly have a namespace.
	// But if the attribute is something like animal:duck='quack', then we should look for the URI for 'animal'.
	// 
	// Note: [self prefix] returns an empty string if there is no prefix. (Not nil)
	
	NSString *prefix = [self prefix];
	if ([prefix length] > 0)
	{
		xmlNsPtr ns = xmlSearchNs(attr->doc, attr->parent, [prefix xmlChar]);
		if (ns && ns->href)
		{
			return [NSString stringWithUTF8String:((const char *)ns->href)];
		}
	}
	
	return nil;
}

@end

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#pragma mark -
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

@implementation DDXMLInvalidNode

// #pragma mark Properties

- (DDXMLNodeKind)kind {
	return DDXMLInvalidKind;
}

- (void)setName:(NSString *)name { }
- (NSString *)name {
	return nil;
}

- (void)setObjectValue:(id)value { }
- (id)objectValue {
	return nil;
}

- (void)setStringValue:(NSString *)string { }
- (void)setStringValue:(NSString *)string resolvingEntities:(BOOL)resolve { }
- (NSString *)stringValue {
	return nil;
}

// #pragma mark Tree Navigation

- (NSUInteger)index {
	return 0;
}

- (NSUInteger)level {
	return 0;
}

- (DDXMLDocument *)rootDocument {
	return nil;
}

- (DDXMLNode *)parent {
	return nil;
}
- (NSUInteger)childCount {
	return 0;
}
- (NSArray *)children {
	return [NSArray array];
}
- (DDXMLNode *)childAtIndex:(NSUInteger)index {
	return nil;
}

- (DDXMLNode *)previousSibling {
	return nil;
}
- (DDXMLNode *)nextSibling {
	return nil;
}

- (DDXMLNode *)previousNode {
	return nil;
}
- (DDXMLNode *)nextNode {
	return nil;
}

- (void)detach { }

- (NSString *)XPath {
	return @"";
}

// #pragma mark QNames

- (NSString *)localName {
	return nil;
}
- (NSString *)prefix {
	return @"";
}

- (void)setURI:(NSString *)URI { }
- (NSString *)URI {
	return nil;
}

// #pragma mark Output

- (NSString *)description {
	return @"";
}
- (NSString *)XMLString {
	return @"";
}
- (NSString *)XMLStringWithOptions:(NSUInteger)options {
	return @"";
}
- (NSString *)canonicalXMLStringPreservingComments:(BOOL)comments {
	return nil;
}

// #pragma mark XPath/XQuery

- (NSArray *)nodesForXPath:(NSString *)xpath error:(NSError **)error {
	return [NSArray array];
}

- (NSArray *)objectsForXQuery:(NSString *)xquery constants:(NSDictionary *)constants error:(NSError **)error {
	return [NSArray array];
}
- (NSArray *)objectsForXQuery:(NSString *)xquery error:(NSError **)error {
	return [NSArray array];
}

@end

