diff --git a/ChangeLog b/ChangeLog index 94ca2bffa..7c094ebec 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,12 @@ +2012-03-17 Fred Kiefer + + * Source/NSXMLPrivate.h: Remvoe unused ivars. + * Source/NSXMLDocument.m: Get DTD from node and and validation in + init methods. + * Source/NSXMLDTD.m: Implement. + * Source/NSXMLNode.m: No children for attribute nodes. + * Tests/base/NSXMLNode/basic.m: Test empty attribute children + 2012-03-16 Fred Kiefer * Source/NSXMLNode.m (-XMLStringWithOptions:): Protect the diff --git a/Source/NSXMLDTD.m b/Source/NSXMLDTD.m index 9aed3c472..ab84d9489 100644 --- a/Source/NSXMLDTD.m +++ b/Source/NSXMLDTD.m @@ -35,7 +35,53 @@ GS_PRIVATE_INTERNAL(NSXMLDTD) + (NSXMLDTDNode*) predefinedEntityDeclarationForName: (NSString*)name { - [self notImplemented: _cmd]; + // FIXME: We should cache these instances + if ([name isEqualToString: @"lt"]) + { + NSXMLDTDNode *node; + + node = [[NSXMLDTDNode alloc] initWithKind: NSXMLEntityDeclarationKind]; + [node setName: @"lt"]; + [node setStringValue: @"<"]; + return AUTORELEASE(node); + } + if ([name isEqualToString: @"gt"]) + { + NSXMLDTDNode *node; + + node = [[NSXMLDTDNode alloc] initWithKind: NSXMLEntityDeclarationKind]; + [node setName: @"gt"]; + [node setStringValue: @">"]; + return AUTORELEASE(node); + } + if ([name isEqualToString: @"amp"]) + { + NSXMLDTDNode *node; + + node = [[NSXMLDTDNode alloc] initWithKind: NSXMLEntityDeclarationKind]; + [node setName: @"amp"]; + [node setStringValue: @"&"]; + return AUTORELEASE(node); + } + if ([name isEqualToString: @"quot"]) + { + NSXMLDTDNode *node; + + node = [[NSXMLDTDNode alloc] initWithKind: NSXMLEntityDeclarationKind]; + [node setName: @"qout"]; + [node setStringValue: @"\""]; + return AUTORELEASE(node); + } + if ([name isEqualToString: @"apos"]) + { + NSXMLDTDNode *node; + + node = [[NSXMLDTDNode alloc] initWithKind: NSXMLEntityDeclarationKind]; + [node setName: @"apos"]; + [node setStringValue: @"'"]; + return AUTORELEASE(node); + } + return nil; } @@ -43,35 +89,100 @@ GS_PRIVATE_INTERNAL(NSXMLDTD) { if (GS_EXISTS_INTERNAL) { - [internal->entities release]; - [internal->elements release]; - [internal->notations release]; - [internal->original release]; } [super dealloc]; } - (void) addChild: (NSXMLNode*)child { - [self notImplemented: _cmd]; + [self insertChild: child atIndex: [self childCount]]; } - (NSXMLDTDNode*) attributeDeclarationForName: (NSString*)name - elementName: (NSString*)elementName + elementName: (NSString*)elementName { - [self notImplemented: _cmd]; + xmlDtdPtr node = internal->node; + xmlNodePtr children = NULL; + const xmlChar *xmlName = XMLSTRING(name); + const xmlChar *xmlElementName = XMLSTRING(elementName); + + if ((node == NULL) || + (node->children == NULL)) + { + return nil; + } + + for (children = node->children; children; children = children->next) + { + if (children->type == XML_ATTRIBUTE_DECL) + { + xmlAttributePtr attr = (xmlAttributePtr)children; + + if ((xmlStrcmp(attr->name, xmlName) == 0) && + (xmlStrcmp(attr->elem, xmlElementName) == 0)) + { + return (NSXMLDTDNode*)[NSXMLNode _objectForNode: children]; + } + } + } + return nil; } - (NSXMLDTDNode*) elementDeclarationForName: (NSString*)name { - [self notImplemented: _cmd]; + xmlDtdPtr node = internal->node; + xmlNodePtr children = NULL; + const xmlChar *xmlName = XMLSTRING(name); + + if ((node == NULL) || + (node->children == NULL)) + { + return nil; + } + + for (children = node->children; children; children = children->next) + { + if (children->type == XML_ELEMENT_DECL) + { + xmlElementPtr elem = (xmlElementPtr)children; + + if (xmlStrcmp(elem->name, xmlName) == 0) + { + return (NSXMLDTDNode*)[NSXMLNode _objectForNode: children]; + } + } + } + return nil; } - (NSXMLDTDNode*) entityDeclarationForName: (NSString*)name { - [self notImplemented: _cmd]; + //xmlGetEntityFromDtd + xmlDtdPtr node = internal->node; + xmlNodePtr children = NULL; + const xmlChar *xmlName = XMLSTRING(name); + + if ((node == NULL) || + (node->children == NULL)) + { + return nil; + } + + for (children = node->children; children; children = children->next) + { + if (children->type == XML_ENTITY_DECL) + { + xmlEntityPtr entity = (xmlEntityPtr)children; + + if (xmlStrcmp(entity->name, xmlName) == 0) + { + return (NSXMLDTDNode*)[NSXMLNode _objectForNode: children]; + } + } + } + return nil; } @@ -102,8 +213,27 @@ GS_PRIVATE_INTERNAL(NSXMLDTD) options: (NSUInteger)mask error: (NSError**)error { - [self notImplemented: _cmd]; - return nil; + NSXMLDocument *tempDoc = + [[NSXMLDocument alloc] initWithData: data + options: mask + error: error]; + if (tempDoc != nil) + { + NSArray *children = [tempDoc children]; + NSEnumerator *enumerator = [children objectEnumerator]; + NSXMLNode *child; + + self = [self initWithKind: NSXMLDTDKind options: mask]; + + while ((child = [enumerator nextObject]) != nil) + { + [child detach]; // detach from document. + [self addChild: child]; + } + [tempDoc release]; + } + + return self; } - (id) initWithKind: (NSXMLNodeKind)kind options: (NSUInteger)theOptions @@ -122,7 +252,22 @@ GS_PRIVATE_INTERNAL(NSXMLDTD) - (void) insertChild: (NSXMLNode*)child atIndex: (NSUInteger)index { - [self notImplemented: _cmd]; + NSXMLNodeKind kind = [child kind]; + NSUInteger childCount = [self childCount]; + + // Check to make sure this is a valid addition... + NSAssert(nil != child, NSInvalidArgumentException); + NSAssert(index <= childCount, NSInvalidArgumentException); + NSAssert(nil == [child parent], NSInvalidArgumentException); + NSAssert(NSXMLAttributeKind != kind, NSInvalidArgumentException); + NSAssert(NSXMLDTDKind != kind, NSInvalidArgumentException); + NSAssert(NSXMLDocumentKind != kind, NSInvalidArgumentException); + NSAssert(NSXMLElementKind != kind, NSInvalidArgumentException); + NSAssert(NSXMLInvalidKind != kind, NSInvalidArgumentException); + NSAssert(NSXMLNamespaceKind != kind, NSInvalidArgumentException); + NSAssert(NSXMLTextKind != kind, NSInvalidArgumentException); + + [self _insertChild: child atIndex: index]; } - (void) insertChildren: (NSArray*)children atIndex: (NSUInteger)index @@ -138,13 +283,28 @@ GS_PRIVATE_INTERNAL(NSXMLDTD) - (NSXMLDTDNode*) notationDeclarationForName: (NSString*)name { - NSXMLDTDNode *notation = [internal->notations objectForKey: name]; + xmlDtdPtr node = internal->node; + xmlNodePtr children = NULL; + const xmlChar *xmlName = XMLSTRING(name); - if (notation == nil) + if ((node == NULL) || + (node->children == NULL)) { - [self notImplemented: _cmd]; + return nil; } - return notation; + + for (children = node->children; children; children = children->next) + { + if (children->type == XML_NOTATION_NODE) + { + if (xmlStrcmp(children->name, xmlName) == 0) + { + return (NSXMLDTDNode*)[NSXMLNode _objectForNode: children]; + } + } + } + + return nil; } - (NSString*) publicID @@ -156,17 +316,34 @@ GS_PRIVATE_INTERNAL(NSXMLDTD) - (void) removeChildAtIndex: (NSUInteger)index { - [self notImplemented: _cmd]; + NSXMLNode *child; + + if (index >= [self childCount]) + { + [NSException raise: NSRangeException + format: @"index too large"]; + } + + child = [self childAtIndex: index]; + [child detach]; } - (void) replaceChildAtIndex: (NSUInteger)index withNode: (NSXMLNode*)node { - [self notImplemented: _cmd]; + [self insertChild: node atIndex: index]; + [self removeChildAtIndex: index + 1]; } - (void) setChildren: (NSArray*)children { - [self notImplemented: _cmd]; + NSUInteger count = [self childCount]; + + while (count-- > 0) + { + [self removeChildAtIndex: count]; + } + + [self insertChildren: children atIndex: 0]; } - (void) setPublicID: (NSString*)publicID diff --git a/Source/NSXMLDocument.m b/Source/NSXMLDocument.m index a48094ba0..f18b684bf 100644 --- a/Source/NSXMLDocument.m +++ b/Source/NSXMLDocument.m @@ -49,7 +49,6 @@ GS_PRIVATE_INTERNAL(NSXMLDocument) { if (GS_EXISTS_INTERNAL) { - [internal->docType release]; [internal->MIMEType release]; } [super dealloc]; @@ -70,7 +69,8 @@ GS_PRIVATE_INTERNAL(NSXMLDocument) - (NSXMLDTD*) DTD { - return internal->docType; + xmlDtdPtr dtd = xmlGetIntSubset(internal->node); + return (NSXMLDTD *)[NSXMLNode _objectForNode: (xmlNodePtr)dtd]; } - (void) _createInternal @@ -139,10 +139,19 @@ GS_PRIVATE_INTERNAL(NSXMLDocument) code: 0 userInfo: nil]; } + return nil; } - // FIXME: Free old node + + // Free old node + xmlFreeDoc((xmlDocPtr)internal->node); [self _setNode: doc]; + + if (mask & NSXMLDocumentValidate) + { + [self validateAndReturnError: error]; + } } + return self; } @@ -221,10 +230,16 @@ GS_PRIVATE_INTERNAL(NSXMLDocument) - (void) setDTD: (NSXMLDTD*)documentTypeDeclaration { + NSXMLDTD *old; + NSAssert(documentTypeDeclaration != nil, NSInvalidArgumentException); - // FIXME: do node house keeping, remove ivar, use intSubset - ASSIGNCOPY(internal->docType, documentTypeDeclaration); - internal->node->extSubset = [documentTypeDeclaration _node]; + + // detach the old DTD, this also removes the corresponding child + old = [self DTD]; + [old detach]; + + internal->node->intSubset = (xmlDtdPtr)[documentTypeDeclaration _node]; + [self addChild: documentTypeDeclaration]; } - (void) setMIMEType: (NSString*)MIMEType @@ -300,7 +315,7 @@ GS_PRIVATE_INTERNAL(NSXMLDocument) NSAssert(NSXMLNamespaceKind != kind, NSInvalidArgumentException); NSAssert(NSXMLNotationDeclarationKind != kind, NSInvalidArgumentException); - [self _insertChild:child atIndex:index]; + [self _insertChild: child atIndex: index]; } - (void) insertChildren: (NSArray*)children atIndex: (NSUInteger)index @@ -387,14 +402,16 @@ GS_PRIVATE_INTERNAL(NSXMLDocument) while ((key = [en nextObject]) != nil) { params[index] = (xmlChar *)XMLSTRING(key); - params[index+1] = (xmlChar *)XMLSTRING([arguments objectForKey: key]); + params[index + 1] = (xmlChar *)XMLSTRING([arguments objectForKey: key]); index += 2; } + params[index] = NULL; + params[index + 1] = NULL; } // Apply the stylesheet and get the result... - resultDoc - = xsltApplyStylesheet(stylesheet, internal->node, (const char **)params); + resultDoc = xsltApplyStylesheet(stylesheet, internal->node, + (const char **)params); // Cleanup... xsltFreeStylesheet(stylesheet); @@ -446,8 +463,8 @@ GS_PRIVATE_INTERNAL(NSXMLDocument) NSXMLDocument *c = (NSXMLDocument*)[super copyWithZone: zone]; [c setMIMEType: [self MIMEType]]; - // the extSubset isnt copied by libxml2 - [c setDTD: [self DTD]]; + // the intSubset is copied by libxml2 + //[c setDTD: [self DTD]]; [c setDocumentContentKind: [self documentContentKind]]; return c; } diff --git a/Source/NSXMLNode.m b/Source/NSXMLNode.m index 157e495a3..08aa1e1d4 100644 --- a/Source/NSXMLNode.m +++ b/Source/NSXMLNode.m @@ -538,10 +538,10 @@ clearPrivatePointers(xmlNodePtr aNode) if (aNode->type == XML_NAMESPACE_DECL) { - xmlNsPtr ns = (xmlNsPtr)aNode; + xmlNsPtr ns = (xmlNsPtr)aNode; ns->_private = NULL; - clearPrivatePointers((xmlNodePtr)ns->next); + clearPrivatePointers((xmlNodePtr)(ns->next)); return; } @@ -551,6 +551,17 @@ clearPrivatePointers(xmlNodePtr aNode) if (aNode->type == XML_ELEMENT_NODE) { clearPrivatePointers((xmlNodePtr)(aNode->properties)); + clearPrivatePointers((xmlNodePtr)(aNode->nsDef)); + } + if (aNode->type == XML_ELEMENT_DECL) + { + xmlElementPtr elem = (xmlElementPtr)aNode; + clearPrivatePointers((xmlNodePtr)(elem->attributes)); + } + if (aNode->type == XML_DOCUMENT_NODE) + { + xmlDocPtr doc = (xmlDocPtr)aNode; + clearPrivatePointers((xmlNodePtr)(doc->intSubset)); } // FIXME: Handle more node types } @@ -924,7 +935,8 @@ execute_xpath(NSXMLNode *xmlNode, NSString *xpath_exp, NSString *nmspaces) return 0; } - if (node->type == XML_NAMESPACE_DECL) + if ((node->type == XML_NAMESPACE_DECL) || + (node->type == XML_ATTRIBUTE_NODE)) { return 0; } @@ -952,6 +964,7 @@ execute_xpath(NSXMLNode *xmlNode, NSString *xpath_exp, NSString *nmspaces) if ((node == NULL) || (node->type == XML_NAMESPACE_DECL) || + (node->type == XML_ATTRIBUTE_NODE) || (node->children == NULL)) { return nil; diff --git a/Source/NSXMLPrivate.h b/Source/NSXMLPrivate.h index dc1d8de62..c93e3fd51 100644 --- a/Source/NSXMLPrivate.h +++ b/Source/NSXMLPrivate.h @@ -136,25 +136,15 @@ StringFromXMLString(const unsigned char *bytes, unsigned length) * is imported and before GSInternal.h is imported. */ #define GS_NSXMLDocument_IVARS SUPERIVARS(GS_NSXMLNode_IVARS) \ - NSXMLDTD *docType; \ NSString *MIMEType; \ NSInteger contentKind; \ - /* Instance variables for NSXMLDTD with/without the instance * variable 'inherited' from NSXMLNode. * This macro needs to be defined before the NSXMLDTD.h header * is imported and before GSInternal.h is imported. */ -#define GS_NSXMLDTD_IVARS SUPERIVARS(GS_NSXMLNode_IVARS) \ - NSString *publicID; \ - NSString *systemID; \ - NSMutableDictionary *entities; \ - NSMutableDictionary *elements; \ - NSMutableDictionary *notations; \ - NSMutableDictionary *attributes; \ - NSString *original; \ - +#define GS_NSXMLDTD_IVARS SUPERIVARS(GS_NSXMLNode_IVARS) /* Instance variables for NSXMLDTDNode with/without the instance * variable 'inherited' from NSXMLNode. @@ -229,4 +219,3 @@ StringFromXMLString(const unsigned char *bytes, unsigned length) #endif /* HAVE_LIBXML */ #endif - diff --git a/Tests/base/NSXMLNode/basic.m b/Tests/base/NSXMLNode/basic.m index 935f3ae3a..72b20f604 100644 --- a/Tests/base/NSXMLNode/basic.m +++ b/Tests/base/NSXMLNode/basic.m @@ -101,9 +101,9 @@ int main() PASS_EQUAL([attr URI], nil, "attr node URI is nil"); PASS_EQUAL([attr objectValue], @"value", "attr node object value works"); PASS_EQUAL([attr stringValue], @"value", "string value on attr node works"); - // In libxml2 the value is on a child node - //PASS_EQUAL([attr children], nil, "attr node children is nil"); - //PASS([attr childCount] == 0, "No child on attr node"); + // In libxml2 the value is on a child node, but we don't report that + PASS_EQUAL([attr children], nil, "attr node children is nil"); + PASS([attr childCount] == 0, "No child on attr node"); [attr setName: @"name"]; PASS_EQUAL([attr name], @"name",