diff --git a/KissXML/DDXML.h b/KissXML/DDXML.h index 908fe5a7..e67b34d5 100644 --- a/KissXML/DDXML.h +++ b/KissXML/DDXML.h @@ -81,7 +81,7 @@ // Heap corruption is one of the worst problems to track down. // So to help out, the library provides a debugging macro to track down these problems. // That is, if you invalidate the write-access thread-unsafe rule, -// this macro will tell you when you're trying to access a no-dangling pointer. +// this macro will tell you when you're trying to access a now-dangling pointer. // // How does it work? // Well everytime a DDXML wrapper object is created atop a libxml structure, @@ -90,6 +90,6 @@ // So everytime a DDXML wrapper objects is about to dereference it's pointer, // it first ensures the linkage still exists in the table. // -// The debugging macro adds a significant amount of overhead, and shouldn't be enabled on production builds. +// The debugging macro adds a significant amount of overhead, and should NOT be enabled on production builds. #define DDXML_DEBUG_MEMORY_ISSUES 0 diff --git a/KissXML/DDXMLElement.m b/KissXML/DDXMLElement.m index ae665287..80ff556e 100644 --- a/KissXML/DDXMLElement.m +++ b/KissXML/DDXMLElement.m @@ -105,6 +105,71 @@ - (id)initWithXMLString:(NSString *)string error:(NSError **)error #pragma mark Elements by name //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Helper method elementsForName and elementsForLocalName:URI: so work isn't duplicated. + * The name parameter is required, all others are optional. +**/ +- (NSArray *)_elementsForName:(NSString *)name + localName:(NSString *)localName + prefix:(NSString *)prefix + uri:(NSString *)uri +{ + // This is a private/internal method + + // Rule : !uri => match: name + // Rule : uri && hasPrefix => match: name || (localName && uri) + // Rule : uri && !hasPefix => match: name && uri + + xmlNodePtr node = (xmlNodePtr)genericPtr; + + NSMutableArray *result = [NSMutableArray array]; + + BOOL hasPrefix = [prefix length] > 0; + + const xmlChar *xmlName = [name xmlChar]; + const xmlChar *xmlLocalName = [localName xmlChar]; + const xmlChar *xmlUri = [uri xmlChar]; + + xmlNodePtr child = node->children; + while (child) + { + if (IsXmlNodePtr(child)) + { + BOOL match = NO; + + if (uri == nil) + { + match = xmlStrEqual(child->name, xmlName); + } + else + { + BOOL nameMatch = xmlStrEqual(child->name, xmlName); + BOOL localNameMatch = xmlStrEqual(child->name, xmlLocalName); + + BOOL uriMatch = NO; + if (child->ns) + { + uriMatch = xmlStrEqual(child->ns->href, xmlUri); + } + + if (hasPrefix) + match = nameMatch || (localNameMatch && uriMatch); + else + match = nameMatch && uriMatch; + } + + if (match) + { + [result addObject:[DDXMLElement nodeWithElementPrimitive:child owner:self]]; + } + } + + child = child->next; + } + + return result; +} + /** * Returns the child element nodes (as DDXMLElement objects) of the receiver that have a specified name. * @@ -125,25 +190,30 @@ - (NSArray *)elementsForName:(NSString *)name // and then search for any elements that have the same name (including prefix) OR have the same URI. // Otherwise we loop through the children as usual and do a string compare on the name - NSString *prefix = [[self class] prefixForName:name]; + NSString *prefix; + NSString *localName; + + [DDXMLNode getPrefix:&prefix localName:&localName forName:name]; + if ([prefix length] > 0) { xmlNodePtr node = (xmlNodePtr)genericPtr; + + // Note: We use xmlSearchNs instead of resolveNamespaceForName: because + // we want to avoid creating wrapper objects when possible. + xmlNsPtr ns = xmlSearchNs(node->doc, node, [prefix xmlChar]); - if (ns != NULL) + if (ns) { NSString *uri = [NSString stringWithUTF8String:((const char *)ns->href)]; - return [self _elementsForName:name uri:uri]; + return [self _elementsForName:name localName:localName prefix:prefix uri:uri]; } - - // Note: We used xmlSearchNs instead of resolveNamespaceForName: because - // we want to avoid creating wrapper objects when possible. } - return [self _elementsForName:name uri:nil]; + return [self _elementsForName:name localName:localName prefix:prefix uri:nil]; } -- (NSArray *)elementsForLocalName:(NSString *)localName URI:(NSString *)URI +- (NSArray *)elementsForLocalName:(NSString *)localName URI:(NSString *)uri { #if DDXML_DEBUG_MEMORY_ISSUES DDXMLNotZombieAssert(); @@ -154,75 +224,22 @@ - (NSArray *)elementsForLocalName:(NSString *)localName URI:(NSString *)URI // We need to figure out what the prefix is for this URI. // Then we search for elements that are named prefix:localName OR (named localName AND have the given URI). - NSString *prefix = [self _recursiveResolvePrefixForURI:URI atNode:(xmlNodePtr)genericPtr]; - if (prefix != nil) + NSString *prefix = [self _recursiveResolvePrefixForURI:uri atNode:(xmlNodePtr)genericPtr]; + if (prefix) { NSString *name = [NSString stringWithFormat:@"%@:%@", prefix, localName]; - return [self _elementsForName:name uri:URI]; + return [self _elementsForName:name localName:localName prefix:prefix uri:uri]; } else { - return [self _elementsForName:localName uri:URI]; - } -} - -/** - * Helper method elementsForName and elementsForLocalName:URI: so work isn't duplicated. - * The name parameter is required, URI is optional. -**/ -- (NSArray *)_elementsForName:(NSString *)name uri:(NSString *)uri -{ - // This is a private/internal method - - // Supplied: name, !uri : match: name - // Supplied: p:name, uri : match: p:name || (name && uri) - // Supplied: name, uri : match: name && uri - - NSMutableArray *result = [NSMutableArray array]; - - xmlNodePtr node = (xmlNodePtr)genericPtr; - - BOOL hasPrefix = [[[self class] prefixForName:name] length] > 0; - NSString *localName = [[self class] localNameForName:name]; - - xmlNodePtr child = node->children; - while (child != NULL) - { - if (child->type == XML_ELEMENT_NODE) - { - BOOL match = NO; - if (uri == nil) - { - match = xmlStrEqual(child->name, [name xmlChar]); - } - else - { - BOOL nameMatch = xmlStrEqual(child->name, [name xmlChar]); - BOOL localNameMatch = xmlStrEqual(child->name, [localName xmlChar]); - - BOOL uriMatch = NO; - if (child->ns != NULL) - { - uriMatch = xmlStrEqual(child->ns->href, [uri xmlChar]); - } - - if (hasPrefix) - match = nameMatch || (localNameMatch && uriMatch); - else - match = nameMatch && uriMatch; - } - - if (match) - { - [result addObject:[DDXMLElement nodeWithElementPrimitive:child owner:self]]; - } - } + NSString *prefix; + NSString *realLocalName; - child = child->next; + [DDXMLNode getPrefix:&prefix localName:&realLocalName forName:localName]; + + return [self _elementsForName:localName localName:realLocalName prefix:prefix uri:uri]; } - - return result; } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -234,10 +251,9 @@ - (BOOL)_hasAttributeWithName:(NSString *)name // This is a private/internal method xmlAttrPtr attr = ((xmlNodePtr)genericPtr)->properties; - if (attr != NULL) + if (attr) { const xmlChar *xmlName = [name xmlChar]; - do { if (xmlStrEqual(attr->name, xmlName)) @@ -246,7 +262,7 @@ - (BOOL)_hasAttributeWithName:(NSString *)name } attr = attr->next; - } while (attr != NULL); + } while (attr); } return NO; @@ -269,10 +285,9 @@ - (void)_removeAllAttributes - (void)_removeAttributeForName:(NSString *)name { xmlAttrPtr attr = ((xmlNodePtr)genericPtr)->properties; - if (attr != NULL) + if (attr) { const xmlChar *xmlName = [name xmlChar]; - do { if (xmlStrEqual(attr->name, xmlName)) @@ -282,7 +297,7 @@ - (void)_removeAttributeForName:(NSString *)name } attr = attr->next; - } while(attr != NULL); + } while(attr); } } @@ -344,33 +359,36 @@ - (DDXMLNode *)attributeForName:(NSString *)name DDXMLNotZombieAssert(); #endif - const xmlChar *attrName = [name xmlChar]; - xmlAttrPtr attr = ((xmlNodePtr)genericPtr)->properties; - while (attr != NULL) + if (attr) { - if (attr->ns && attr->ns->prefix) + const xmlChar *xmlName = [name xmlChar]; + do { - // If the attribute name was originally something like "xml:quack", - // then attr->name is "quack" and attr->ns->prefix is "xml". - // - // So if the user is searching for "xml:quack" we need to take the prefix into account. - // Note that "xml:quack" is what would be printed if we output the attribute. - - if (xmlStrQEqual(attr->ns->prefix, attr->name, attrName)) + if (attr->ns && attr->ns->prefix) { - return [DDXMLAttributeNode nodeWithAttrPrimitive:attr owner:self]; + // If the attribute name was originally something like "xml:quack", + // then attr->name is "quack" and attr->ns->prefix is "xml". + // + // So if the user is searching for "xml:quack" we need to take the prefix into account. + // Note that "xml:quack" is what would be printed if we output the attribute. + + if (xmlStrQEqual(attr->ns->prefix, attr->name, xmlName)) + { + return [DDXMLAttributeNode nodeWithAttrPrimitive:attr owner:self]; + } } - } - else - { - if (xmlStrEqual(attr->name, attrName)) + else { - return [DDXMLAttributeNode nodeWithAttrPrimitive:attr owner:self]; + if (xmlStrEqual(attr->name, xmlName)) + { + return [DDXMLAttributeNode nodeWithAttrPrimitive:attr owner:self]; + } } - } - - attr = attr->next; + + attr = attr->next; + + } while (attr); } return nil; } @@ -542,13 +560,18 @@ - (DDXMLNode *)namespaceForPrefix:(NSString *)prefix else { xmlNsPtr ns = ((xmlNodePtr)genericPtr)->nsDef; - while (ns != NULL) + if (ns) { - if (xmlStrEqual(ns->prefix, [prefix xmlChar])) + const xmlChar *xmlPrefix = [prefix xmlChar]; + do { - return [DDXMLNamespaceNode nodeWithNsPrimitive:ns nsParent:(xmlNodePtr)genericPtr owner:self]; - } - ns = ns->next; + if (xmlStrEqual(ns->prefix, xmlPrefix)) + { + return [DDXMLNamespaceNode nodeWithNsPrimitive:ns nsParent:(xmlNodePtr)genericPtr owner:self]; + } + ns = ns->next; + + } while (ns); } } @@ -583,13 +606,18 @@ - (DDXMLNode *)_recursiveResolveNamespaceForPrefix:(NSString *)prefix atNode:(xm if (nodePtr == NULL) return nil; xmlNsPtr ns = nodePtr->nsDef; - while (ns != NULL) + if (ns) { - if (xmlStrEqual(ns->prefix, [prefix xmlChar])) + const xmlChar *xmlPrefix = [prefix xmlChar]; + do { - return [DDXMLNamespaceNode nodeWithNsPrimitive:ns nsParent:nodePtr owner:self]; - } - ns = ns->next; + if (xmlStrEqual(ns->prefix, xmlPrefix)) + { + return [DDXMLNamespaceNode nodeWithNsPrimitive:ns nsParent:nodePtr owner:self]; + } + ns = ns->next; + + } while(ns); } return [self _recursiveResolveNamespaceForPrefix:prefix atNode:nodePtr->parent]; @@ -635,16 +663,21 @@ - (NSString *)_recursiveResolvePrefixForURI:(NSString *)uri atNode:(xmlNodePtr)n if (nodePtr == NULL) return nil; xmlNsPtr ns = nodePtr->nsDef; - while (ns != NULL) + if (ns) { - if (xmlStrEqual(ns->href, [uri xmlChar])) + const xmlChar *xmlUri = [uri xmlChar]; + do { - if (ns->prefix != NULL) + if (xmlStrEqual(ns->href, xmlUri)) { - return [NSString stringWithUTF8String:((const char *)ns->prefix)]; + if (ns->prefix != NULL) + { + return [NSString stringWithUTF8String:((const char *)ns->prefix)]; + } } - } - ns = ns->next; + ns = ns->next; + + } while (ns); } return [self _recursiveResolvePrefixForURI:uri atNode:nodePtr->parent]; diff --git a/KissXML/DDXMLNode.m b/KissXML/DDXMLNode.m index a4e86b94..316ce2f0 100644 --- a/KissXML/DDXMLNode.m +++ b/KissXML/DDXMLNode.m @@ -250,7 +250,7 @@ - (void)dealloc } else { - NSAssert1(NO, @"Cannot free unknown node type: %i", ((xmlKindPtr)genericPtr)->type); + NSAssert1(NO, @"Cannot free unknown node type: %i", genericPtr->type); } } @@ -972,6 +972,58 @@ - (NSString *)URI 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. * @@ -985,16 +1037,10 @@ + (NSString *)localNameForName:(NSString *)name { // This is a public/API method - if (name) - { - NSRange range = [name rangeOfString:@":"]; - - if (range.length != 0) - return [name substringFromIndex:(range.location + range.length)]; - else - return name; - } - return nil; + NSString *localName; + [self getPrefix:NULL localName:&localName forName:name]; + + return localName; } /** @@ -1011,16 +1057,10 @@ + (NSString *)prefixForName:(NSString *)name { // This is a public/API method - if (name) - { - NSRange range = [name rangeOfString:@":"]; - - if (range.length != 0) - { - return [name substringToIndex:range.location]; - } - } - return @""; + NSString *prefix; + [self getPrefix:&prefix localName:NULL forName:name]; + + return prefix; } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/KissXML/Private/DDXMLPrivate.h b/KissXML/Private/DDXMLPrivate.h index db799cdb..72bf832b 100644 --- a/KissXML/Private/DDXMLPrivate.h +++ b/KissXML/Private/DDXMLPrivate.h @@ -153,6 +153,8 @@ NS_INLINE BOOL IsXmlNsPtr(void *kindPtr) - (BOOL)_hasParent; + (void)recursiveStripDocPointersFromNode:(xmlNodePtr)node; ++ (void)getHasPrefix:(BOOL *)hasPrefixPtr localName:(NSString **)localNamePtr forName:(NSString *)name; ++ (void)getPrefix:(NSString **)prefixPtr localName:(NSString **)localNamePtr forName:(NSString *)name; + (void)detachAttribute:(xmlAttrPtr)attr fromNode:(xmlNodePtr)node; + (void)removeAttribute:(xmlAttrPtr)attr fromNode:(xmlNodePtr)node; @@ -181,8 +183,6 @@ BOOL DDXMLIsZombie(void *xmlPtr, DDXMLNode *wrapper); + (id)nodeWithElementPrimitive:(xmlNodePtr)node owner:(DDXMLNode *)owner; - (id)initWithElementPrimitive:(xmlNodePtr)node owner:(DDXMLNode *)owner; -- (NSArray *)_elementsForName:(NSString *)name uri:(NSString *)URI; - - (DDXMLNode *)_recursiveResolveNamespaceForPrefix:(NSString *)prefix atNode:(xmlNodePtr)nodePtr; - (NSString *)_recursiveResolvePrefixForURI:(NSString *)uri atNode:(xmlNodePtr)nodePtr; diff --git a/UnitTesting/DDXMLTesting.m b/UnitTesting/DDXMLTesting.m index cef8c6f6..50538a0f 100644 --- a/UnitTesting/DDXMLTesting.m +++ b/UnitTesting/DDXMLTesting.m @@ -41,7 +41,7 @@ + (void)testNodesForXPath; + (void)testNSXMLBugs; + (void)testInsertChild; + (void)testElementSerialization; -+ (void)testAttributeWithColonInName; ++ (void)testAttrWithColonInName; + (void)testMemoryIssueDebugging; @end @@ -84,7 +84,7 @@ + (void)performTests [self testNSXMLBugs]; [self testInsertChild]; [self testElementSerialization]; - [self testAttributeWithColonInName]; + [self testAttrWithColonInName]; [self testMemoryIssueDebugging]; [self tearDown]; @@ -1762,7 +1762,7 @@ + (void)testElementSerialization [pool drain]; } -+ (void)testAttributeWithColonInName ++ (void)testAttrWithColonInName { NSLog(@"Starting %@...", NSStringFromSelector(_cmd)); NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; diff --git a/UnitTesting/KissXML.xcodeproj/project.pbxproj b/UnitTesting/KissXML.xcodeproj/project.pbxproj index ae270ac2..8846ed97 100644 --- a/UnitTesting/KissXML.xcodeproj/project.pbxproj +++ b/UnitTesting/KissXML.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 45; + objectVersion = 46; objects = { /* Begin PBXBuildFile section */ @@ -209,8 +209,11 @@ /* Begin PBXProject section */ 29B97313FDCFA39411CA2CEA /* Project object */ = { isa = PBXProject; + attributes = { + LastUpgradeCheck = 0420; + }; buildConfigurationList = C01FCF4E08A954540054247B /* Build configuration list for PBXProject "KissXML" */; - compatibilityVersion = "Xcode 3.1"; + compatibilityVersion = "Xcode 3.2"; developmentRegion = English; hasScannedForEncodings = 1; knownRegions = ( @@ -281,7 +284,6 @@ ALWAYS_SEARCH_USER_PATHS = NO; COPY_PHASE_STRIP = NO; GCC_DYNAMIC_NO_PIC = NO; - GCC_ENABLE_FIX_AND_CONTINUE = YES; GCC_MODEL_TUNING = G5; GCC_OPTIMIZATION_LEVEL = 0; GCC_PRECOMPILE_PREFIX_HEADER = YES; @@ -318,7 +320,6 @@ MACOSX_DEPLOYMENT_TARGET = 10.6; ONLY_ACTIVE_ARCH = YES; OTHER_LDFLAGS = "-lxml2"; - PREBINDING = NO; }; name = Debug; }; @@ -332,7 +333,6 @@ HEADER_SEARCH_PATHS = /usr/include/libxml2; MACOSX_DEPLOYMENT_TARGET = 10.6; OTHER_LDFLAGS = "-lxml2"; - PREBINDING = NO; }; name = Release; };