GData源码

来源:互联网 发布:肝癌中医诊疗方案优化 编辑:程序博客网 时间:2024/06/16 19:54

/* Copyright (c) 2008 Google Inc.

 *

 * Licensed under the Apache License, Version 2.0 (the "License");

 * you may not use this file except in compliance with the License.

 * You may obtain a copy of the License at

 *

 *     http://www.apache.org/licenses/LICENSE-2.0

 *

 * Unless required by applicable law or agreed to in writing, software

 * distributed under the License is distributed on an "AS IS" BASIS,

 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

 * See the License for the specific language governing permissions and

 * limitations under the License.

 */


// These node, element, and document classes implement a subset of the methods

// provided by NSXML.  While NSXML behavior is mimicked as much as possible,

// there are important differences.

//

// The biggest difference is that, since this is based on libxml2, there

// is no retain model for the underlying node data.  Rather than copy every

// node obtained from a parse tree (which would have a substantial memory

// impact), we rely on weak references, and it is up to the code that

// created a document to retain it for as long as any

// references rely on nodes inside that document tree.



#import <Foundation/Foundation.h>


// libxml includes require that the target Header Search Paths contain

//

//   /usr/include/libxml2

//

// and Other Linker Flags contain

//

//   -lxml2


#import <libxml/tree.h>

#import <libxml/parser.h>

#import <libxml/xmlstring.h>

#import <libxml/xpath.h>

#import <libxml/xpathInternals.h>



#if (MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_4) || defined(GDATA_TARGET_NAMESPACE)

  // we need NSInteger for the 10.4 SDK, or we're using target namespace macros

  #import "GDataDefines.h"

#endif


#undef _EXTERN

#undef _INITIALIZE_AS

#ifdef GDATAXMLNODE_DEFINE_GLOBALS

#define _EXTERN

#define _INITIALIZE_AS(x) =x

#else

#define _EXTERN extern

#define _INITIALIZE_AS(x)

#endif


// when no namespace dictionary is supplied for XPath, the default namespace

// for the evaluated tree is registered with the prefix _def_ns

_EXTERNconst char* kGDataXMLXPathDefaultNamespacePrefix_INITIALIZE_AS("_def_ns");


// Nomenclature for method names:

//

// Node = GData node

// XMLNode = xmlNodePtr

//

// So, for example:

//  + (id)nodeConsumingXMLNode:(xmlNodePtr)theXMLNode;


@class NSArray,NSDictionary, NSError,NSString, NSURL;

@class GDataXMLElement,GDataXMLDocument;


enum {

  GDataXMLInvalidKind =0,

  GDataXMLDocumentKind,

  GDataXMLElementKind,

  GDataXMLAttributeKind,

  GDataXMLNamespaceKind,

  GDataXMLProcessingInstructionKind,

  GDataXMLCommentKind,

  GDataXMLTextKind,

  GDataXMLDTDKind,

  GDataXMLEntityDeclarationKind,

  GDataXMLAttributeDeclarationKind,

  GDataXMLElementDeclarationKind,

  GDataXMLNotationDeclarationKind

};


typedefNSUInteger GDataXMLNodeKind;


@interface GDataXMLNode :NSObject {

@protected

  // NSXMLNodes can have a namespace URI or prefix even if not part

  // of a tree; xmlNodes cannot.  When we create nodes apart from

  // a tree, we'll store the dangling prefix or URI in the xmlNode's name,

  // like

  //   "prefix:name"

  // or

  //   "{http://uri}:name"

 //

  // We will fix up the node's namespace and name (and those of any children)

  // later when adding the node to a tree with addChild: or addAttribute:.

  // See fixUpNamespacesForNode:.


  xmlNodePtr xmlNode_;// may also be an xmlAttrPtr or xmlNsPtr

  BOOL shouldFreeXMLNode_;// if yes, xmlNode_ will be free'd in dealloc


  // cached values

 NSString *cachedName_;

 NSArray *cachedChildren_;

 NSArray *cachedAttributes_;

}


+ (GDataXMLElement *)elementWithName:(NSString *)name;

+ (GDataXMLElement *)elementWithName:(NSString *)name stringValue:(NSString *)value;

+ (GDataXMLElement *)elementWithName:(NSString *)name URI:(NSString *)value;


+ (id)attributeWithName:(NSString *)name stringValue:(NSString *)value;

+ (id)attributeWithName:(NSString *)name URI:(NSString *)attributeURI stringValue:(NSString *)value;


+ (id)namespaceWithName:(NSString *)name stringValue:(NSString *)value;


+ (id)textWithStringValue:(NSString *)value;


// 直接读标签间的String  <age>24</age>

- (NSString *)stringValue;

- (void)setStringValue:(NSString *)str;


- (NSUInteger)childCount;

- (NSArray *)children;

- (GDataXMLNode *)childAtIndex:(unsigned)index;


- (NSString *)localName;


//标签名<age>  这个age就是name

- (NSString *)name;

- (NSString *)prefix;

- (NSString *)URI;


- (GDataXMLNodeKind)kind;


- (NSString *)XMLString;


+ (NSString *)localNameForName:(NSString *)name;

+ (NSString *)prefixForName:(NSString *)name;


// This is the preferred entry point for nodesForXPath.  This takes an explicit

// namespace dictionary (keys are prefixes, values are URIs).

- (NSArray *)nodesForXPath:(NSString *)xpath namespaces:(NSDictionary *)namespaces error:(NSError **)error;


// This implementation of nodesForXPath registers namespaces only from the

// document's root node.  _def_ns may be used as a prefix for the default

// namespace, though there's no guarantee that the default namespace will

// be consistenly the same namespace in server responses.

- (NSArray *)nodesForXPath:(NSString *)xpath error:(NSError **)error;


// access to the underlying libxml node; be sure to release the cached values

// if you change the underlying tree at all

- (xmlNodePtr)XMLNode;

- (void)releaseCachedValues;


@end



@interface GDataXMLElement :GDataXMLNode


- (id)initWithXMLString:(NSString *)str error:(NSError **)error;


- (NSArray *)namespaces;

- (void)setNamespaces:(NSArray *)namespaces;

- (void)addNamespace:(GDataXMLNode *)aNamespace;


- (void)addChild:(GDataXMLNode *)child;

- (void)removeChild:(GDataXMLNode *)child;


- (NSArray *)elementsForName:(NSString *)name;

- (NSArray *)elementsForLocalName:(NSString *)localName URI:(NSString *)URI;


- (NSArray *)attributes;

// 读标签里面的属性<name value="wusj"/>

- (GDataXMLNode *)attributeForName:(NSString *)name;

- (GDataXMLNode *)attributeForLocalName:(NSString *)name URI:(NSString *)attributeURI;

- (void)addAttribute:(GDataXMLNode *)attribute;


- (NSString *)resolvePrefixForNamespaceURI:(NSString *)namespaceURI;


@end


@interface GDataXMLDocument :NSObject {

@protected

  xmlDoc* xmlDoc_;// strong; always free'd in dealloc

}


- (id)initWithXMLString:(NSString *)str options:(unsignedint)mask error:(NSError **)error;

- (id)initWithData:(NSData *)data options:(unsignedint)mask error:(NSError **)error;

- (id)initWithRootElement:(GDataXMLElement *)element;


- (GDataXMLElement *)rootElement;


- (NSData *)XMLData;


- (void)setVersion:(NSString *)version;

- (void)setCharacterEncoding:(NSString *)encoding;


// This is the preferred entry point for nodesForXPath.  This takes an explicit

// namespace dictionary (keys are prefixes, values are URIs).

- (NSArray *)nodesForXPath:(NSString *)xpath namespaces:(NSDictionary *)namespaces error:(NSError **)error;


// This implementation of nodesForXPath registers namespaces only from the

// document's root node.  _def_ns may be used as a prefix for the default

// namespace, though there's no guarantee that the default namespace will

// be consistenly the same namespace in server responses.

- (NSArray *)nodesForXPath:(NSString *)xpath error:(NSError **)error;


- (NSString *)description;

@end



==================================================================================================================================
==================================================================================================================================
==================================================================================================================================
==================================================================================================================================


/* Copyright (c) 2008 Google Inc.

 *

 * Licensed under the Apache License, Version 2.0 (the "License");

 * you may not use this file except in compliance with the License.

 * You may obtain a copy of the License at

 *

 *     http://www.apache.org/licenses/LICENSE-2.0

 *

 * Unless required by applicable law or agreed to in writing, software

 * distributed under the License is distributed on an "AS IS" BASIS,

 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

 * See the License for the specific language governing permissions and

 * limitations under the License.

 */


#define GDATAXMLNODE_DEFINE_GLOBALS 1

#import "GDataXMLNode.h"


@class NSArray, NSDictionary, NSError, NSString, NSURL;

@class GDataXMLElement, GDataXMLDocument;



staticconst int kGDataXMLParseOptions = (XML_PARSE_NOCDATA | XML_PARSE_NOBLANKS);


// dictionary key callbacks for string cache

staticconst void *StringCacheKeyRetainCallBack(CFAllocatorRef allocator,const void *str);

staticvoid StringCacheKeyReleaseCallBack(CFAllocatorRef allocator,const void *str);

static CFStringRef StringCacheKeyCopyDescriptionCallBack(constvoid *str);

static Boolean StringCacheKeyEqualCallBack(constvoid *str1, constvoid *str2);

static CFHashCode StringCacheKeyHashCallBack(constvoid *str);


// isEqual: has the fatal flaw that it doesn't deal well with the received

// being nil. We'll use this utility instead.


// Static copy of AreEqualOrBothNil from GDataObject.m, so that using

// GDataXMLNode does not require pulling in all of GData.

staticBOOL AreEqualOrBothNilPrivate(id obj1,id obj2) {

 if (obj1 == obj2) {

    return YES;

  }

 if (obj1 && obj2) {

   return [obj1 isEqual:obj2];

  }

  return NO;

}



// convert NSString* to xmlChar*

//

// the "Get" part implies that ownership remains with str


static xmlChar* GDataGetXMLString(NSString *str) {

  xmlChar* result = (xmlChar *)[str UTF8String];

 return result;

}


// Make a fake qualified name we use as local name internally in libxml

// data structures when there's no actual namespace node available to point to

// from an element or attribute node

//

// Returns an autoreleased NSString*


static NSString *GDataFakeQNameForURIAndName(NSString *theURI, NSString *name) {


  NSString *localName = [GDataXMLNode localNameForName:name];

  NSString *fakeQName = [NSString stringWithFormat:@"{%@}:%@",

                         theURI, localName];

 return fakeQName;

}



// libxml2 offers xmlSplitQName2, but that searches forwards. Since we may

// be searching for a whole URI shoved in as a prefix, like

//   {http://foo}:name

// we'll search for the prefix in backwards from the end of the qualified name

//

// returns a copy of qname as the local name if there's no prefix

static xmlChar *SplitQNameReverse(const xmlChar *qname, xmlChar **prefix) {


  // search backwards for a colon

 int qnameLen = xmlStrlen(qname);

 for (int idx = qnameLen -1; idx >= 0; idx--) {


   if (qname[idx] == ':') {


      // found the prefix; copy the prefix, if requested

     if (prefix != NULL) {

       if (idx > 0) {

          *prefix = xmlStrsub(qname,0, idx);

        }else {

          *prefix =NULL;

        }

      }


     if (idx < qnameLen - 1) {

        // return a copy of the local name

        xmlChar *localName = xmlStrsub(qname, idx +1, qnameLen - idx - 1);

       return localName;

      }else {

       return NULL;

      }

    }

  }


  // no colon found, so the qualified name is the local name

  xmlChar *qnameCopy = xmlStrdup(qname);

 return qnameCopy;

}


@interface GDataXMLNode (PrivateMethods)


// consuming a node implies it will later be freed when the instance is

// dealloc'd; borrowing it implies that ownership and disposal remain the

// job of the supplier of the node


+ (id)nodeConsumingXMLNode:(xmlNodePtr)theXMLNode;

- (id)initConsumingXMLNode:(xmlNodePtr)theXMLNode;


+ (id)nodeBorrowingXMLNode:(xmlNodePtr)theXMLNode;

- (id)initBorrowingXMLNode:(xmlNodePtr)theXMLNode;


// getters of the underlying node

- (xmlNodePtr)XMLNode;

- (xmlNodePtr)XMLNodeCopy;


// search for an underlying attribute

- (GDataXMLNode *)attributeForXMLNode:(xmlAttrPtr)theXMLNode;


// return an NSString for an xmlChar*, using our strings cache in the

// document

- (NSString *)stringFromXMLString:(const xmlChar *)chars;


// setter/getter of the dealloc flag for the underlying node

- (BOOL)shouldFreeXMLNode;

- (void)setShouldFreeXMLNode:(BOOL)flag;


@end


@interface GDataXMLElement (PrivateMethods)


+ (void)fixUpNamespacesForNode:(xmlNodePtr)nodeToFix

            graftingToTreeNode:(xmlNodePtr)graftPointNode;

@end


@implementation GDataXMLNode


+ (void)load {

  xmlInitParser();

}


// Note on convenience methods for making stand-alone element and

// attribute nodes:

//

// Since we're making a node from scratch, we don't

// have any namespace info.  So the namespace prefix, if

// any, will just be slammed into the node name.

// We'll rely on the -addChild method below to remove

// the namespace prefix and replace it with a proper ns

// pointer.


+ (GDataXMLElement *)elementWithName:(NSString *)name {


  xmlNodePtr theNewNode = xmlNewNode(NULL,// namespace

                                     GDataGetXMLString(name));

 if (theNewNode) {

    // succeeded

   return [self nodeConsumingXMLNode:theNewNode];

  }

  return nil;

}


+ (GDataXMLElement *)elementWithName:(NSString *)name stringValue:(NSString *)value {


  xmlNodePtr theNewNode = xmlNewNode(NULL,// namespace

                                     GDataGetXMLString(name));

 if (theNewNode) {


    xmlNodePtr textNode = xmlNewText(GDataGetXMLString(value));

   if (textNode) {


      xmlNodePtr temp = xmlAddChild(theNewNode, textNode);

     if (temp) {

        // succeeded

       return [self nodeConsumingXMLNode:theNewNode];

      }

    }


    // failed; free the node and any children

    xmlFreeNode(theNewNode);

  }

  return nil;

}


+ (GDataXMLElement *)elementWithName:(NSString *)name URI:(NSString *)theURI {


  // since we don't know a prefix yet, shove in the whole URI; we'll look for

  // a proper namespace ptr later when addChild calls fixUpNamespacesForNode


  NSString *fakeQName = GDataFakeQNameForURIAndName(theURI, name);


  xmlNodePtr theNewNode = xmlNewNode(NULL,// namespace

                                     GDataGetXMLString(fakeQName));

 if (theNewNode) {

     return [self nodeConsumingXMLNode:theNewNode];

  }

  return nil;

}


+ (id)attributeWithName:(NSString *)name stringValue:(NSString *)value {


  xmlChar *xmlName = GDataGetXMLString(name);

  xmlChar *xmlValue = GDataGetXMLString(value);


  xmlAttrPtr theNewAttr = xmlNewProp(NULL,// parent node for the attr

                                     xmlName, xmlValue);

 if (theNewAttr) {

   return [self nodeConsumingXMLNode:(xmlNodePtr) theNewAttr];

  }


  return nil;

}


+ (id)attributeWithName:(NSString *)name URI:(NSString *)attributeURI stringValue:(NSString *)value {


  // since we don't know a prefix yet, shove in the whole URI; we'll look for

  // a proper namespace ptr later when addChild calls fixUpNamespacesForNode


  NSString *fakeQName = GDataFakeQNameForURIAndName(attributeURI, name);


  xmlChar *xmlName = GDataGetXMLString(fakeQName);

  xmlChar *xmlValue = GDataGetXMLString(value);


  xmlAttrPtr theNewAttr = xmlNewProp(NULL,// parent node for the attr

                                     xmlName, xmlValue);

 if (theNewAttr) {

   return [self nodeConsumingXMLNode:(xmlNodePtr) theNewAttr];

  }


  return nil;

}


+ (id)textWithStringValue:(NSString *)value {


  xmlNodePtr theNewText = xmlNewText(GDataGetXMLString(value));

 if (theNewText) {

   return [self nodeConsumingXMLNode:theNewText];

  }

  return nil;

}


+ (id)namespaceWithName:(NSString *)name stringValue:(NSString *)value {


  xmlChar *href = GDataGetXMLString(value);

  xmlChar *prefix;


 if ([name length] > 0) {

    prefix = GDataGetXMLString(name);

  }else {

    // default namespace is represented by a nil prefix

    prefix =nil;

  }


  xmlNsPtr theNewNs = xmlNewNs(NULL,// parent node

                               href, prefix);

 if (theNewNs) {

   return [self nodeConsumingXMLNode:(xmlNodePtr) theNewNs];

  }

  return nil;

}


+ (id)nodeConsumingXMLNode:(xmlNodePtr)theXMLNode {

  Class theClass;


 if (theXMLNode->type == XML_ELEMENT_NODE) {

    theClass = [GDataXMLElement class];

  }else {

    theClass = [GDataXMLNode class];

  }

 return [[[theClass alloc] initConsumingXMLNode:theXMLNode] autorelease];

}


- (id)initConsumingXMLNode:(xmlNodePtr)theXMLNode {

 self = [super init];

 if (self) {

    xmlNode_ = theXMLNode;

    shouldFreeXMLNode_ =YES;

  }

  return self;

}


+ (id)nodeBorrowingXMLNode:(xmlNodePtr)theXMLNode {

  Class theClass;

 if (theXMLNode->type == XML_ELEMENT_NODE) {

    theClass = [GDataXMLElement class];

  }else {

    theClass = [GDataXMLNode class];

  }


 return [[[theClass alloc] initBorrowingXMLNode:theXMLNode] autorelease];

}


- (id)initBorrowingXMLNode:(xmlNodePtr)theXMLNode {

 self = [super init];

 if (self) {

    xmlNode_ = theXMLNode;

    shouldFreeXMLNode_ =NO;

  }

  return self;

}


- (void)releaseCachedValues {


  [cachedName_ release];

  cachedName_ =nil;


  [cachedChildren_ release];

  cachedChildren_ =nil;


  [cachedAttributes_ release];

  cachedAttributes_ =nil;

}



// convert xmlChar* to NSString*

//

// returns an autoreleased NSString*, from the current node's document strings

// cache if possible

- (NSString *)stringFromXMLString:(const xmlChar *)chars {


#if DEBUG

  NSCAssert(chars != NULL,@"GDataXMLNode sees an unexpected empty string");

#endif

 if (chars == NULL)return nil;


  CFMutableDictionaryRef cacheDict =NULL;


  NSString *result =nil;


 if (xmlNode_ != NULL

    && (xmlNode_->type == XML_ELEMENT_NODE

        || xmlNode_->type == XML_ATTRIBUTE_NODE

        || xmlNode_->type == XML_TEXT_NODE)) {

    // there is no xmlDocPtr in XML_NAMESPACE_DECL nodes,

    // so we can't cache the text of those


    // look for a strings cache in the document

   //

    // the cache is in the document's user-defined _private field


   if (xmlNode_->doc != NULL) {


      cacheDict = xmlNode_->doc->_private;


     if (cacheDict) {


        // this document has a strings cache

        result = (NSString *) CFDictionaryGetValue(cacheDict, chars);

       if (result) {

          // we found the xmlChar string in the cache; return the previously

          // allocated NSString, rather than allocate a new one

         return result;

        }

      }

    }

  }


  // allocate a new NSString for this xmlChar*

  result = [NSString stringWithUTF8String:(constchar *) chars];

 if (cacheDict) {

    // save the string in the document's string cache

    CFDictionarySetValue(cacheDict, chars, result);

  }


 return result;

}


- (void)dealloc {


 if (xmlNode_ && shouldFreeXMLNode_) {

    xmlFreeNode(xmlNode_);

  }


  [self releaseCachedValues];

  [super dealloc];

}


#pragma mark -


- (void)setStringValue:(NSString *)str {

 if (xmlNode_ != NULL && str !=nil) {


   if (xmlNode_->type == XML_NAMESPACE_DECL) {


      // for a namespace node, the value is the namespace URI

      xmlNsPtr nsNode = (xmlNsPtr)xmlNode_;


     if (nsNode->href != NULL) xmlFree((char *)nsNode->href);


      nsNode->href = xmlStrdup(GDataGetXMLString(str));


    }else {


      // attribute or element node


      // do we need to call xmlEncodeSpecialChars?

      xmlNodeSetContent(xmlNode_, GDataGetXMLString(str));

    }

  }

}


- (NSString *)stringValue {


  NSString *str =nil;


 if (xmlNode_ != NULL) {


   if (xmlNode_->type == XML_NAMESPACE_DECL) {


      // for a namespace node, the value is the namespace URI

      xmlNsPtr nsNode = (xmlNsPtr)xmlNode_;


      str = [self stringFromXMLString:(nsNode->href)];


    }else {


      // attribute or element node

      xmlChar* chars = xmlNodeGetContent(xmlNode_);

     if (chars) {


        str = [self stringFromXMLString:chars];


        xmlFree(chars);

      }

    }

  }

 return str;

}


- (NSString *)XMLString {


  NSString *str =nil;


 if (xmlNode_ != NULL) {


    xmlBufferPtr buff = xmlBufferCreate();

   if (buff) {


      xmlDocPtr doc =NULL;

     int level = 0;

     int format = 0;


     int result = xmlNodeDump(buff, doc, xmlNode_, level, format);


     if (result > -1) {

        str = [[[NSString alloc] initWithBytes:(xmlBufferContent(buff))

                                        length:(xmlBufferLength(buff))

                                      encoding:NSUTF8StringEncoding] autorelease];

      }

      xmlBufferFree(buff);

    }

  }


  // remove leading and trailing whitespace

  NSCharacterSet *ws = [NSCharacterSet whitespaceAndNewlineCharacterSet];

  NSString *trimmed = [str stringByTrimmingCharactersInSet:ws];

 return trimmed;

}


- (NSString *)localName {

  NSString *str =nil;


 if (xmlNode_ != NULL) {


    str = [self stringFromXMLString:(xmlNode_->name)];


    // if this is part of a detached subtree, str may have a prefix in it

    str = [[self class] localNameForName:str];

  }

 return str;

}


- (NSString *)prefix {


  NSString *str =nil;


 if (xmlNode_ != NULL) {


    // the default namespace's prefix is an empty string, though libxml

    // represents it as NULL for ns->prefix

    str =@"";


   if (xmlNode_->ns != NULL && xmlNode_->ns->prefix != NULL) {

      str = [self stringFromXMLString:(xmlNode_->ns->prefix)];

    }

  }

 return str;

}


- (NSString *)URI {


  NSString *str =nil;


 if (xmlNode_ != NULL) {


   if (xmlNode_->ns != NULL && xmlNode_->ns->href != NULL) {

      str = [self stringFromXMLString:(xmlNode_->ns->href)];

    }

  }

 return str;

}


- (NSString *)qualifiedName {

  // internal utility


  NSString *str =nil;


 if (xmlNode_ != NULL) {

   if (xmlNode_->type == XML_NAMESPACE_DECL) {


      // name of a namespace node

      xmlNsPtr nsNode = (xmlNsPtr)xmlNode_;


      // null is the default namespace; one is the loneliest number

     if (nsNode->prefix == NULL) {

        str =@"";

      }

     else {

        str = [self stringFromXMLString:(nsNode->prefix)];

      }


    }else if (xmlNode_->ns !=NULL && xmlNode_->ns->prefix != NULL) {


      // name of a non-namespace node


      // has a prefix

     char *qname;

     if (asprintf(&qname, "%s:%s", (const char *)xmlNode_->ns->prefix,

                   xmlNode_->name) != -1) {

        str = [self stringFromXMLString:(const xmlChar *)qname];

        free(qname);

      }

    }else {

      // lacks a prefix

      str = [self stringFromXMLString:(xmlNode_->name)];

    }

  }


 return str;

}


- (NSString *)name {


 if (cachedName_ != nil) {

   return cachedName_;

  }


  NSString *str = [self qualifiedName];


  cachedName_ = [str retain];


 return str;

}


+ (NSString *)localNameForName:(NSString *)name {

 if (name != nil) {


    NSRange range = [name rangeOfString:@":"];

   if (range.location != NSNotFound) {


      // found a colon

     if (range.location + 1 < [name length]) {

        NSString *localName = [name substringFromIndex:(range.location +1)];

       return localName;

      }

    }

  }

 return name;

}


+ (NSString *)prefixForName:(NSString *)name {

 if (name != nil) {


    NSRange range = [name rangeOfString:@":"];

   if (range.location != NSNotFound) {


      NSString *prefix = [name substringToIndex:(range.location)];

     return prefix;

    }

  }

  return nil;

}


- (NSUInteger)childCount {


 if (cachedChildren_ != nil) {

   return [cachedChildren_ count];

  }


 if (xmlNode_ != NULL) {


   unsigned int count =0;


    xmlNodePtr currChild = xmlNode_->children;


   while (currChild != NULL) {

      ++count;

      currChild = currChild->next;

    }

   return count;

  }

  return 0;

}


- (NSArray *)children {


 if (cachedChildren_ != nil) {

   return cachedChildren_;

  }


  NSMutableArray *array =nil;


 if (xmlNode_ != NULL) {


    xmlNodePtr currChild = xmlNode_->children;


   while (currChild != NULL) {

      GDataXMLNode *node = [GDataXMLNode nodeBorrowingXMLNode:currChild];


     if (array == nil) {

        array = [NSMutableArray arrayWithObject:node];

      }else {

        [array addObject:node];

      }


      currChild = currChild->next;

    }


    cachedChildren_ = [array retain];

  }

 return array;

}


- (GDataXMLNode *)childAtIndex:(unsigned)index {


  NSArray *children = [self children];


 if ([children count] > index) {


   return [children objectAtIndex:index];

  }

  return nil;

}


- (GDataXMLNodeKind)kind {

 if (xmlNode_ != NULL) {

    xmlElementType nodeType = xmlNode_->type;

   switch (nodeType) {

     case XML_ELEMENT_NODE:         return GDataXMLElementKind;

     case XML_ATTRIBUTE_NODE:       return GDataXMLAttributeKind;

     case XML_TEXT_NODE:            return GDataXMLTextKind;

     case XML_CDATA_SECTION_NODE:   return GDataXMLTextKind;

     case XML_ENTITY_REF_NODE:      return GDataXMLEntityDeclarationKind;

     case XML_ENTITY_NODE:          return GDataXMLEntityDeclarationKind;

     case XML_PI_NODE:              return GDataXMLProcessingInstructionKind;

     case XML_COMMENT_NODE:         return GDataXMLCommentKind;

     case XML_DOCUMENT_NODE:        return GDataXMLDocumentKind;

     case XML_DOCUMENT_TYPE_NODE:   return GDataXMLDocumentKind;

     case XML_DOCUMENT_FRAG_NODE:   return GDataXMLDocumentKind;

     case XML_NOTATION_NODE:        return GDataXMLNotationDeclarationKind;

     case XML_HTML_DOCUMENT_NODE:   return GDataXMLDocumentKind;

     case XML_DTD_NODE:             return GDataXMLDTDKind;

     case XML_ELEMENT_DECL:         return GDataXMLElementDeclarationKind;

     case XML_ATTRIBUTE_DECL:       return GDataXMLAttributeDeclarationKind;

     case XML_ENTITY_DECL:          return GDataXMLEntityDeclarationKind;

     case XML_NAMESPACE_DECL:       return GDataXMLNamespaceKind;

     case XML_XINCLUDE_START:       return GDataXMLProcessingInstructionKind;

     case XML_XINCLUDE_END:         return GDataXMLProcessingInstructionKind;

     case XML_DOCB_DOCUMENT_NODE:   return GDataXMLDocumentKind;

    }

  }

 return GDataXMLInvalidKind;

}


- (NSArray *)nodesForXPath:(NSString *)xpath error:(NSError **)error {

  // call through with no explicit namespace dictionary; that will register the

  // root node's namespaces

 return [self nodesForXPath:xpath namespaces:nil error:error];

}


- (NSArray *)nodesForXPath:(NSString *)xpath

                namespaces:(NSDictionary *)namespaces

                     error:(NSError **)error {


  NSMutableArray *array =nil;


  // xmlXPathNewContext requires a doc for its context, but if our elements

  // are created from GDataXMLElement's initWithXMLString there may not be

  // a document. (We may later decide that we want to stuff the doc used

  // there into a GDataXMLDocument and retain it, but we don't do that now.)

 //

  // We'll temporarily make a document to use for the xpath context.


  xmlDocPtr tempDoc =NULL;

  xmlNodePtr topParent =NULL;


 if (xmlNode_->doc == NULL) {

    tempDoc = xmlNewDoc(NULL);

   if (tempDoc) {

      // find the topmost node of the current tree to make the root of

      // our temporary document

      topParent = xmlNode_;

     while (topParent->parent != NULL) {

        topParent = topParent->parent;

      }

      xmlDocSetRootElement(tempDoc, topParent);

    }

  }


 if (xmlNode_ != NULL && xmlNode_->doc !=NULL) {


    xmlXPathContextPtr xpathCtx = xmlXPathNewContext(xmlNode_->doc);

   if (xpathCtx) {

      // anchor at our current node

      xpathCtx->node = xmlNode_;


      // if a namespace dictionary was provided, register its contents

     if (namespaces) {

        // the dictionary keys are prefixes; the values are URIs

       for (NSString *prefix in namespaces) {

          NSString *uri = [namespaces objectForKey:prefix];


          xmlChar *prefixChars = (xmlChar *) [prefix UTF8String];

          xmlChar *uriChars = (xmlChar *) [uri UTF8String];

         int result = xmlXPathRegisterNs(xpathCtx, prefixChars, uriChars);

         if (result != 0) {

#if DEBUG

            NSCAssert1(result == 0,@"GDataXMLNode XPath namespace %@ issue",

                      prefix);

#endif

          }

        }

      }else {

        // no namespace dictionary was provided

       //

        // register the namespaces of this node, if it's an element, or of

        // this node's root element, if it's a document

        xmlNodePtr nsNodePtr = xmlNode_;

       if (xmlNode_->type == XML_DOCUMENT_NODE) {

          nsNodePtr = xmlDocGetRootElement((xmlDocPtr) xmlNode_);

        }


        // step through the namespaces, if any, and register each with the

        // xpath context

       if (nsNodePtr != NULL) {

         for (xmlNsPtr nsPtr = nsNodePtr->ns; nsPtr != NULL; nsPtr = nsPtr->next) {


            // default namespace is nil in the tree, but there's no way to

            // register a default namespace, so we'll register a fake one,

           // _def_ns

           const xmlChar* prefix = nsPtr->prefix;

           if (prefix == NULL) {

              prefix = (xmlChar*) kGDataXMLXPathDefaultNamespacePrefix;

            }


           int result = xmlXPathRegisterNs(xpathCtx, prefix, nsPtr->href);

           if (result != 0) {

#if DEBUG

              NSCAssert1(result == 0,@"GDataXMLNode XPath namespace %@ issue",

                        prefix);

#endif

            }

          }

        }

      }


      // now evaluate the path

      xmlXPathObjectPtr xpathObj;

      xpathObj = xmlXPathEval(GDataGetXMLString(xpath), xpathCtx);

     if (xpathObj) {


        // we have some result from the search

        array = [NSMutableArray array];


        xmlNodeSetPtr nodeSet = xpathObj->nodesetval;

       if (nodeSet) {


          // add each node in the result set to our array

         for (int index =0; index < nodeSet->nodeNr; index++) {


            xmlNodePtr currNode = nodeSet->nodeTab[index];


            GDataXMLNode *node = [GDataXMLNode nodeBorrowingXMLNode:currNode];

           if (node) {

              [array addObject:node];

            }

          }

        }

        xmlXPathFreeObject(xpathObj);

      }

      xmlXPathFreeContext(xpathCtx);

    }


   if (array == nil) {


      // provide an error

     //

      // TODO(grobbins) obtain better xpath and libxml errors

     const char *msg = xpathCtx->lastError.str1;

      NSDictionary *userInfo =nil;

     if (msg) {

        userInfo = [NSDictionary dictionaryWithObject:[NSString stringWithUTF8String:msg]

                                               forKey:@"error"];

      }

      *error = [NSError errorWithDomain:@"com.google.GDataXML"

                                   code:xpathCtx->lastError.code

                               userInfo:userInfo];

    }

  }


 if (tempDoc != NULL) {

    xmlUnlinkNode(topParent);

    xmlSetTreeDoc(topParent,NULL);

    xmlFreeDoc(tempDoc);

  }

 return array;

}


- (NSString *)description {

 int nodeType = (xmlNode_ ? (int)xmlNode_->type : -1);


  return [NSString stringWithFormat:@"%@ %p: {type:%d name:%@ xml:\"%@\"}",

          [self class],self, nodeType, [self name], [self XMLString]];

}


- (id)copyWithZone:(NSZone *)zone {


  xmlNodePtr nodeCopy = [self XMLNodeCopy];


 if (nodeCopy != NULL) {

   return [[[self class] alloc] initConsumingXMLNode:nodeCopy];

  }

  return nil;

}


- (BOOL)isEqual:(GDataXMLNode *)other {

 if (self == other)return YES;

 if (![other isKindOfClass:[GDataXMLNode class]])return NO;


 return [self XMLNode] == [other XMLNode]

  || ([self kind] == [other kind]

      && AreEqualOrBothNilPrivate([self name], [other name])

      && [[self children] count] == [[other children] count]);


}


- (NSUInteger)hash {

 return (NSUInteger) (void *) [GDataXMLNode class];

}


- (NSMethodSignature *)methodSignatureForSelector:(SEL)selector {

 return [super methodSignatureForSelector:selector];

}


#pragma mark -


- (xmlNodePtr)XMLNodeCopy {

 if (xmlNode_ != NULL) {


    // Note: libxml will create a new copy of namespace nodes (xmlNs records)

    // and attach them to this copy in order to keep namespaces within this

    // node subtree copy value.


    xmlNodePtr nodeCopy = xmlCopyNode(xmlNode_,1); // 1 = recursive

   return nodeCopy;

  }

  return NULL;

}


- (xmlNodePtr)XMLNode {

 return xmlNode_;

}


- (BOOL)shouldFreeXMLNode {

 return shouldFreeXMLNode_;

}


- (void)setShouldFreeXMLNode:(BOOL)flag {

  shouldFreeXMLNode_ = flag;

}


@end




@implementation GDataXMLElement


- (id)initWithXMLString:(NSString *)str error:(NSError **)error {

 self = [super init];

 if (self) {


   const char *utf8Str = [str UTF8String];

    // NOTE: We are assuming a string length that fits into an int

    xmlDocPtr doc = xmlReadMemory(utf8Str, (int)strlen(utf8Str),NULL, // URL

                                 NULL, // encoding

                                  kGDataXMLParseOptions);

   if (doc == NULL) {

     if (error) {

        // TODO(grobbins) use xmlSetGenericErrorFunc to capture error

      }

    }else {

      // copy the root node from the doc

      xmlNodePtr root = xmlDocGetRootElement(doc);

     if (root) {

        xmlNode_ = xmlCopyNode(root,1); // 1: recursive

      }

      xmlFreeDoc(doc);

    }



   if (xmlNode_ == NULL) {

      // failure

     if (error) {

        *error = [NSError errorWithDomain:@"com.google.GDataXML"

                                     code:-1

                                 userInfo:nil];

      }

      [self release];

      return nil;

    }

  }

  return self;

}


- (NSArray *)namespaces {


  NSMutableArray *array =nil;


 if (xmlNode_ != NULL && xmlNode_->nsDef !=NULL) {


    xmlNsPtr currNS = xmlNode_->nsDef;

   while (currNS != NULL) {


      // add this prefix/URI to the list, unless it's the implicit xml prefix

     if (!xmlStrEqual(currNS->prefix, (const xmlChar *)"xml")) {

        GDataXMLNode *node = [GDataXMLNode nodeBorrowingXMLNode:(xmlNodePtr) currNS];


       if (array == nil) {

          array = [NSMutableArray arrayWithObject:node];

        }else {

          [array addObject:node];

        }

      }


      currNS = currNS->next;

    }

  }

 return array;

}


- (void)setNamespaces:(NSArray *)namespaces {


 if (xmlNode_ != NULL) {


    [self releaseCachedValues];


    // remove previous namespaces

   if (xmlNode_->nsDef) {

      xmlFreeNsList(xmlNode_->nsDef);

      xmlNode_->nsDef =NULL;

    }


    // add a namespace for each object in the array

    NSEnumerator *enumerator = [namespaces objectEnumerator];

    GDataXMLNode *namespace;

   while ((namespace = [enumerator nextObject]) !=nil) {


      xmlNsPtr ns = (xmlNsPtr) [namespace XMLNode];

     if (ns) {

        (void)xmlNewNs(xmlNode_, ns->href, ns->prefix);

      }

    }


    // we may need to fix this node's own name; the graft point is where

    // the namespace search starts, so that points to this node too

    [[self class] fixUpNamespacesForNode:xmlNode_

                      graftingToTreeNode:xmlNode_];

  }

}


- (void)addNamespace:(GDataXMLNode *)aNamespace {


 if (xmlNode_ != NULL) {


    [self releaseCachedValues];


    xmlNsPtr ns = (xmlNsPtr) [aNamespace XMLNode];

   if (ns) {

      (void)xmlNewNs(xmlNode_, ns->href, ns->prefix);


      // we may need to fix this node's own name; the graft point is where

      // the namespace search starts, so that points to this node too

      [[self class] fixUpNamespacesForNode:xmlNode_

                        graftingToTreeNode:xmlNode_];

    }

  }

}


- (void)addChild:(GDataXMLNode *)child {

  if ([childkind] == GDataXMLAttributeKind) {

    [selfaddAttribute:child];

    return;

  }


 if (xmlNode_ !=NULL) {


    [selfreleaseCachedValues];


   xmlNodePtr childNodeCopy = [child XMLNodeCopy];

   if (childNodeCopy) {


     xmlNodePtr resultNode = xmlAddChild(xmlNode_, childNodeCopy);

     if (resultNode == NULL) {


        // failed to add

       xmlFreeNode(childNodeCopy);


      }else {

        // added this child subtree successfully; see if it has

        // previously-unresolved namespace prefixes that can now be fixed up

        [[selfclass] fixUpNamespacesForNode:childNodeCopy

                         graftingToTreeNode:xmlNode_];

      }

    }

  }

}


- (void)removeChild:(GDataXMLNode *)child {

  // this is safe for attributes too

 if (xmlNode_ !=NULL) {


    [selfreleaseCachedValues];


   xmlNodePtr node = [child XMLNode];


    xmlUnlinkNode(node);


    // if the child node was borrowing its xmlNodePtr, then we need to

    // explicitly free it, since there is probably no owning object that will

    // free it on dealloc

   if (![child shouldFreeXMLNode]) {

     xmlFreeNode(node);

    }

  }

}


- (NSArray *)elementsForName:(NSString *)name {


 NSString *desiredName = name;


 if (xmlNode_ !=NULL) {


   NSString *prefix = [[selfclass] prefixForName:desiredName];

   if (prefix) {


     xmlChar* desiredPrefix = GDataGetXMLString(prefix);


     xmlNsPtr foundNS = xmlSearchNs(xmlNode_->doc,xmlNode_, desiredPrefix);

     if (foundNS) {


        // we found a namespace; fall back on elementsForLocalName:URI:

        // to get the elements

       NSString *desiredURI = [selfstringFromXMLString:(foundNS->href)];

       NSString *localName = [[selfclass] localNameForName:desiredName];


       NSArray *nsArray = [selfelementsForLocalName:localName URI:desiredURI];

       return nsArray;

      }

    }


    // no namespace found for the node's prefix; try an exact match

    // for the name argument, including any prefix

   NSMutableArray *array = nil;


    // walk our list of cached child nodes

   NSArray *children = [selfchildren];


   for (GDataXMLNode *childin children) {


     xmlNodePtr currNode = [child XMLNode];


      // find all children which are elements with the desired name

     if (currNode->type ==XML_ELEMENT_NODE) {


       NSString *qName = [child name];

       if ([qName isEqual:name]) {


         if (array == nil) {

            array = [NSMutableArrayarrayWithObject:child];

          }else {

            [arrayaddObject:child];

          }

        }

      }

    }

   return array;

  }

  return nil;

}


- (NSArray *)elementsForLocalName:(NSString *)localName URI:(NSString *)URI {


  NSMutableArray *array =nil;


 if (xmlNode_ !=NULL && xmlNode_->children !=NULL) {


   xmlChar* desiredNSHref = GDataGetXMLString(URI);

   xmlChar* requestedLocalName = GDataGetXMLString(localName);

   xmlChar* expectedLocalName = requestedLocalName;


    // resolve the URI at the parent level, since usually children won't

    // have their own namespace definitions, and we don't want to try to

    // resolve it once for every child

   xmlNsPtr foundParentNS = xmlSearchNsByHref(xmlNode_->doc,xmlNode_, desiredNSHref);

   if (foundParentNS == NULL) {

     NSString *fakeQName = GDataFakeQNameForURIAndName(URI, localName);

      expectedLocalName =GDataGetXMLString(fakeQName);

    }


   NSArray *children = [selfchildren];


   for (GDataXMLNode *childin children) {


     xmlNodePtr currChildPtr = [child XMLNode];


      // find all children which are elements with the desired name and

      // namespace, or with the prefixed name and a null namespace

     if (currChildPtr->type ==XML_ELEMENT_NODE) {


        // normally, we can assume the resolution done for the parent will apply

        // to the child, as most children do not define their own namespaces

       xmlNsPtr childLocalNS = foundParentNS;

       xmlChar* childDesiredLocalName = expectedLocalName;


       if (currChildPtr->nsDef !=NULL) {

          // this child has its own namespace definitons; do a fresh resolve

          // of the namespace starting from the child, and see if it differs

          // from the resolve done starting from the parent.  If the resolve

          // finds a different namespace, then override the desired local

          // name just for this child.

          childLocalNS =xmlSearchNsByHref(xmlNode_->doc, currChildPtr, desiredNSHref);

         if (childLocalNS != foundParentNS) {


            // this child does indeed have a different namespace resolution

            // result than was found for its parent

           if (childLocalNS == NULL) {

              // no namespace found

             NSString *fakeQName = GDataFakeQNameForURIAndName(URI, localName);

              childDesiredLocalName =GDataGetXMLString(fakeQName);

            }else {

              // a namespace was found; use the original local name requested,

              // not a faked one expected from resolving the parent

              childDesiredLocalName = requestedLocalName;

            }

          }

        }


        // check if this child's namespace and local name are what we're

        // seeking

       if (currChildPtr->ns == childLocalNS

            && currChildPtr->name !=NULL

            &&xmlStrEqual(currChildPtr->name, childDesiredLocalName)) {


         if (array == nil) {

            array = [NSMutableArrayarrayWithObject:child];

          }else {

            [arrayaddObject:child];

          }

        }

      }

    }

    // we return nil, not an empty array, according to docs

  }

 return array;

}


- (NSArray *)attributes {


  if (cachedAttributes_ !=nil) {

    returncachedAttributes_;

  }


  NSMutableArray *array =nil;


 if (xmlNode_ !=NULL && xmlNode_->properties !=NULL) {


    xmlAttrPtr prop =xmlNode_->properties;

   while (prop != NULL) {


     GDataXMLNode *node = [GDataXMLNodenodeBorrowingXMLNode:(xmlNodePtr) prop];

     if (array == nil) {

        array = [NSMutableArrayarrayWithObject:node];

      }else {

        [arrayaddObject:node];

      }


      prop = prop->next;

    }


    cachedAttributes_ = [arrayretain];

  }

 return array;

}


- (void)addAttribute:(GDataXMLNode *)attribute {


 if (xmlNode_ !=NULL) {


    [selfreleaseCachedValues];


   xmlAttrPtr attrPtr = (xmlAttrPtr) [attributeXMLNode];

   if (attrPtr) {


      // ignore this if an attribute with the name is already present,

      // similar to NSXMLNode's addAttribute

     xmlAttrPtr oldAttr;


     if (attrPtr->ns ==NULL) {

        oldAttr =xmlHasProp(xmlNode_, attrPtr->name);

      }else {

        oldAttr =xmlHasNsProp(xmlNode_, attrPtr->name, attrPtr->ns->href);

      }


     if (oldAttr == NULL) {


       xmlNsPtr newPropNS = NULL;


        // if this attribute has a namespace, search for a matching namespace

        // on the node we're adding to

       if (attrPtr->ns !=NULL) {


          newPropNS =xmlSearchNsByHref(xmlNode_->doc,xmlNode_, attrPtr->ns->href);

         if (newPropNS == NULL) {

            // make a new namespace on the parent node, and use that for the

            // new attribute

            newPropNS =xmlNewNs(xmlNode_, attrPtr->ns->href, attrPtr->ns->prefix);

          }

        }


        // copy the attribute onto this node

       xmlChar *value = xmlNodeGetContent((xmlNodePtr) attrPtr);

       xmlAttrPtr newProp = xmlNewNsProp(xmlNode_, newPropNS, attrPtr->name, value);

       if (newProp != NULL) {

          // we made the property, so clean up the property's namespace


          [[selfclass] fixUpNamespacesForNode:(xmlNodePtr)newProp

                           graftingToTreeNode:xmlNode_];

        }


       if (value != NULL) {

         xmlFree(value);

        }

      }

    }

  }

}


- (GDataXMLNode *)attributeForXMLNode:(xmlAttrPtr)theXMLNode {

  // search the cached attributes list for the GDataXMLNode with

  // the underlying xmlAttrPtr

 NSArray *attributes = [selfattributes];


 for (GDataXMLNode *attrin attributes) {


   if (theXMLNode == (xmlAttrPtr) [attrXMLNode]) {

     return attr;

    }

  }


  return nil;

}


- (GDataXMLNode *)attributeForName:(NSString *)name {


 if (xmlNode_ !=NULL) {


   xmlAttrPtr attrPtr = xmlHasProp(xmlNode_, GDataGetXMLString(name));

   if (attrPtr == NULL) {


      // can we guarantee that xmlAttrPtrs always have the ns ptr and never

      // a namespace as part of the actual attribute name?


      // split the name and its prefix, if any

     xmlNsPtr ns = NULL;

     NSString *prefix = [[selfclass] prefixForName:name];

     if (prefix) {


        // find the namespace for this prefix, and search on its URI to find

        // the xmlNsPtr

        name = [[selfclass] localNameForName:name];

        ns =xmlSearchNs(xmlNode_->doc,xmlNode_, GDataGetXMLString(prefix));

      }


     const xmlChar* nsURI = ((ns !=NULL) ? ns->href :NULL);

      attrPtr =xmlHasNsProp(xmlNode_,GDataGetXMLString(name), nsURI);

    }


   if (attrPtr) {

     GDataXMLNode *attr = [selfattributeForXMLNode:attrPtr];

     return attr;

    }

  }

  return nil;

}


- (GDataXMLNode *)attributeForLocalName:(NSString *)localName

                                    URI:(NSString *)attributeURI {


 if (xmlNode_ !=NULL) {


   const xmlChar* name =GDataGetXMLString(localName);

   const xmlChar* nsURI =GDataGetXMLString(attributeURI);


   xmlAttrPtr attrPtr = xmlHasNsProp(xmlNode_, name, nsURI);


   if (attrPtr == NULL) {

      // if the attribute is in a tree lacking the proper namespace,

      // the local name may include the full URI as a prefix

     NSString *fakeQName = GDataFakeQNameForURIAndName(attributeURI, localName);

     const xmlChar* xmlFakeQName =GDataGetXMLString(fakeQName);


      attrPtr =xmlHasProp(xmlNode_, xmlFakeQName);

    }


   if (attrPtr) {

     GDataXMLNode *attr = [selfattributeForXMLNode:attrPtr];

     return attr;

    }

  }

  return nil;

}


- (NSString *)resolvePrefixForNamespaceURI:(NSString *)namespaceURI {


 if (xmlNode_ !=NULL) {


   xmlChar* desiredNSHref = GDataGetXMLString(namespaceURI);


   xmlNsPtr foundNS = xmlSearchNsByHref(xmlNode_->doc,xmlNode_, desiredNSHref);

   if (foundNS) {


      // we found the namespace

     if (foundNS->prefix !=NULL) {

       NSString *prefix = [selfstringFromXMLString:(foundNS->prefix)];

       return prefix;

      }else {

        // empty prefix is default namespace

       return @"";

      }

    }

  }

  return nil;

}


#pragma mark Namespace fixup routines


+ (void)deleteNamespacePtr:(xmlNsPtr)namespaceToDelete

               fromXMLNode:(xmlNodePtr)node {


  // utilty routine to remove a namespace pointer from an element's

  // namespace definition list.  This is just removing the nsPtr

  // from the singly-linked list, the node's namespace definitions.

 xmlNsPtr currNS = node->nsDef;

 xmlNsPtr prevNS = NULL;


 while (currNS != NULL) {

   xmlNsPtr nextNS = currNS->next;


   if (namespaceToDelete == currNS) {


      // found it; delete it from the head of the node's ns definition list

      // or from the next field of the previous namespace


     if (prevNS != NULL) prevNS->next = nextNS;

     else node->nsDef = nextNS;


     xmlFreeNs(currNS);

     return;

    }

    prevNS = currNS;

    currNS = nextNS;

  }

}


+ (void)fixQualifiedNamesForNode:(xmlNodePtr)nodeToFix

              graftingToTreeNode:(xmlNodePtr)graftPointNode {


  // Replace prefix-in-name with proper namespace pointers

 //

  // This is an inner routine for fixUpNamespacesForNode:

 //

  // see if this node's name lacks a namespace and is qualified, and if so,

  // see if we can resolve the prefix against the parent

 //

  // The prefix may either be normal, "gd:foo", or a URI

  // "{http://blah.com/}:foo"


 if (nodeToFix->ns ==NULL) {

   xmlNsPtr foundNS = NULL;


   xmlChar* prefix = NULL;

   xmlChar* localName = SplitQNameReverse(nodeToFix->name, &prefix);

   if (localName != NULL) {

     if (prefix != NULL) {


        // if the prefix is wrapped by { and } then it's a URI

       int prefixLen = xmlStrlen(prefix);

       if (prefixLen > 2

            && prefix[0] =='{'

            && prefix[prefixLen -1] == '}') {


          // search for the namespace by URI

         xmlChar* uri = xmlStrsub(prefix,1, prefixLen - 2);


         if (uri != NULL) {

            foundNS =xmlSearchNsByHref(graftPointNode->doc, graftPointNode, uri);


           xmlFree(uri);

          }

        }

      }


     if (foundNS == NULL) {

        // search for the namespace by prefix, even if the prefix is nil

        // (nil prefix means to search for the default namespace)

        foundNS =xmlSearchNs(graftPointNode->doc, graftPointNode, prefix);

      }


     if (foundNS != NULL) {

        // we found a namespace, so fix the ns pointer and the local name

       xmlSetNs(nodeToFix, foundNS);

       xmlNodeSetName(nodeToFix, localName);

      }


     if (prefix != NULL) {

       xmlFree(prefix);

        prefix =NULL;

      }


     xmlFree(localName);

    }

  }

}


+ (void)fixDuplicateNamespacesForNode:(xmlNodePtr)nodeToFix

                   graftingToTreeNode:(xmlNodePtr)graftPointNode

             namespaceSubstitutionMap:(NSMutableDictionary *)nsMap {


  // Duplicate namespace removal

 //

  // This is an inner routine for fixUpNamespacesForNode:

 //

  // If any of this node's namespaces are already defined at the graft point

  // level, add that namespace to the map of namespace substitutions

  // so it will be replaced in the children below the nodeToFix, and

  // delete the namespace record


 if (nodeToFix->type ==XML_ELEMENT_NODE) {


    // step through the namespaces defined on this node

   xmlNsPtr definedNS = nodeToFix->nsDef;

   while (definedNS != NULL) {


      // see if this namespace is already defined higher in the tree,

      // with both the same URI and the same prefix; if so, add a mapping for

     // it

     xmlNsPtr foundNS = xmlSearchNsByHref(graftPointNode->doc, graftPointNode,

                                           definedNS->href);

     if (foundNS != NULL

          && foundNS != definedNS

          &&xmlStrEqual(definedNS->prefix, foundNS->prefix)) {


        // store a mapping from this defined nsPtr to the one found higher

        // in the tree

        [nsMapsetObject:[NSValuevalueWithPointer:foundNS]

                 forKey:[NSValuevalueWithPointer:definedNS]];


        // remove this namespace from the ns definition list of this node;

        // all child elements and attributes referencing this namespace

        // now have a dangling pointer and must be updated (that is done later

        // in this method)

       //

        // before we delete this namespace, move our pointer to the

        // next one

       xmlNsPtr nsToDelete = definedNS;

        definedNS = definedNS->next;


        [selfdeleteNamespacePtr:nsToDelete fromXMLNode:nodeToFix];


      }else {

        // this namespace wasn't a duplicate; move to the next

        definedNS = definedNS->next;

      }

    }

  }


  // if this node's namespace is one we deleted, update it to point

  // to someplace better

 if (nodeToFix->ns !=NULL) {


   NSValue *currNS = [NSValuevalueWithPointer:nodeToFix->ns];

   NSValue *replacementNS = [nsMap objectForKey:currNS];


   if (replacementNS != nil) {

     xmlNsPtr replaceNSPtr = [replacementNS pointerValue];


     xmlSetNs(nodeToFix, replaceNSPtr);

    }

  }

}




+ (void)fixUpNamespacesForNode:(xmlNodePtr)nodeToFix

            graftingToTreeNode:(xmlNodePtr)graftPointNode

      namespaceSubstitutionMap:(NSMutableDictionary *)nsMap {


  // This is the inner routine for fixUpNamespacesForNode:graftingToTreeNode:

 //

  // This routine fixes two issues:

 //

  // Because we can create nodes with qualified names before adding

  // them to the tree that declares the namespace for the prefix,

  // we need to set the node namespaces after adding them to the tree.

 //

  // Because libxml adds namespaces to nodes when it copies them,

  // we want to remove redundant namespaces after adding them to

  // a tree.

 //

  // If only the Mac's libxml had xmlDOMWrapReconcileNamespaces, it could do

  // namespace cleanup for us


  // We only care about fixing names of elements and attributes

 if (nodeToFix->type !=XML_ELEMENT_NODE

      && nodeToFix->type !=XML_ATTRIBUTE_NODE) return;


  // Do the fixes

  [selffixQualifiedNamesForNode:nodeToFix

             graftingToTreeNode:graftPointNode];


  [selffixDuplicateNamespacesForNode:nodeToFix

                  graftingToTreeNode:graftPointNode

             namespaceSubstitutionMap:nsMap];


 if (nodeToFix->type ==XML_ELEMENT_NODE) {


    // when fixing element nodes, recurse for each child element and

    // for each attribute

   xmlNodePtr currChild = nodeToFix->children;

   while (currChild != NULL) {

      [selffixUpNamespacesForNode:currChild

               graftingToTreeNode:graftPointNode

          namespaceSubstitutionMap:nsMap];

      currChild = currChild->next;

    }


   xmlAttrPtr currProp = nodeToFix->properties;

   while (currProp != NULL) {

      [selffixUpNamespacesForNode:(xmlNodePtr)currProp

               graftingToTreeNode:graftPointNode

          namespaceSubstitutionMap:nsMap];

      currProp = currProp->next;

    }

  }

}


+ (void)fixUpNamespacesForNode:(xmlNodePtr)nodeToFix

            graftingToTreeNode:(xmlNodePtr)graftPointNode {


  // allocate the namespace map that will be passed

  // down on recursive calls

  NSMutableDictionary *nsMap = [NSMutableDictionarydictionary];


  [selffixUpNamespacesForNode:nodeToFix

           graftingToTreeNode:graftPointNode

      namespaceSubstitutionMap:nsMap];

}


@end



@interface GDataXMLDocument (PrivateMethods)

- (void)addStringsCacheToDoc;

@end


@implementation GDataXMLDocument


- (id)initWithXMLString:(NSString *)str options:(unsignedint)mask error:(NSError **)error {


  NSData *data = [strdataUsingEncoding:NSUTF8StringEncoding];

 GDataXMLDocument *doc = [selfinitWithData:data options:mask error:error];

 return doc;

}


- (id)initWithData:(NSData *)data options:(unsignedint)mask error:(NSError **)error {


 self = [superinit];

 if (self) {


   const char *baseURL =NULL;

   const char *encoding =NULL;


    // NOTE: We are assuming [data length] fits into an int.

   xmlDoc_ = xmlReadMemory([databytes], (int)[datalength], baseURL, encoding,

                            kGDataXMLParseOptions);// TODO(grobbins) map option values

   if (xmlDoc_ ==NULL) {

     if (error) {

       *error = [NSErrorerrorWithDomain:@"com.google.GDataXML"

                                   code:-1

                               userInfo:nil];

        // TODO(grobbins) use xmlSetGenericErrorFunc to capture error

        [selfrelease];

      }

      return nil;

    }else {

     if (error) *error = NULL;


      [selfaddStringsCacheToDoc];

    }

  }


  return self;

}


- (id)initWithRootElement:(GDataXMLElement *)element {


 self = [superinit];

 if (self) {


   xmlDoc_ = xmlNewDoc(NULL);


    (void)xmlDocSetRootElement(xmlDoc_, [elementXMLNodeCopy]);


    [selfaddStringsCacheToDoc];

  }


  return self;

}


- (void)addStringsCacheToDoc {

  // utility routine for init methods


#if DEBUG

 NSCAssert(xmlDoc_ !=NULL && xmlDoc_->_private ==NULL,

            @"GDataXMLDocument cache creation problem");

#endif


  // add a strings cache as private data for the document

 //

  // we'll use plain C pointers (xmlChar*) as the keys, and NSStrings

  // as the values

 CFIndex capacity = 0; // no limit


  CFDictionaryKeyCallBacks keyCallBacks = {

    0,// version

    StringCacheKeyRetainCallBack,

    StringCacheKeyReleaseCallBack,

    StringCacheKeyCopyDescriptionCallBack,

    StringCacheKeyEqualCallBack,

    StringCacheKeyHashCallBack

  };


  CFMutableDictionaryRef dict =CFDictionaryCreateMutable(

    kCFAllocatorDefault, capacity,

    &keyCallBacks, &kCFTypeDictionaryValueCallBacks);


  // we'll use the user-defined _private field for our cache

 xmlDoc_->_private = dict;

}


- (NSString *)description {

  return [NSStringstringWithFormat:@"%@ %p", [selfclass], self];

}


- (void)dealloc {

 if (xmlDoc_ !=NULL) {

    // release the strings cache

   //

    // since it's a CF object, were anyone to use this in a GC environment,

    // this would need to be released in a finalize method, too

   if (xmlDoc_->_private !=NULL) {

     CFRelease(xmlDoc_->_private);

    }


    xmlFreeDoc(xmlDoc_);

  }

  [superdealloc];

}


#pragma mark -


- (GDataXMLElement *)rootElement {

 GDataXMLElement *element = nil;


 if (xmlDoc_ !=NULL) {

    xmlNodePtr rootNode =xmlDocGetRootElement(xmlDoc_);

   if (rootNode) {

      element = [GDataXMLElementnodeBorrowingXMLNode:rootNode];

    }

  }

 return element;

}


- (NSData *)XMLData {


 if (xmlDoc_ !=NULL) {

   xmlChar *buffer = NULL;

   int bufferSize = 0;


   xmlDocDumpMemory(xmlDoc_, &buffer, &bufferSize);


   if (buffer) {

     NSData *data = [NSDatadataWithBytes:buffer

                                   length:bufferSize];

     xmlFree(buffer);

     return data;

    }

  }

  return nil;

}


- (void)setVersion:(NSString *)version {


 if (xmlDoc_ !=NULL) {

   if (xmlDoc_->version !=NULL) {

      // version is a const char* so we must cast

     xmlFree((char *)xmlDoc_->version);

     xmlDoc_->version =NULL;

    }


   if (version != nil) {

     xmlDoc_->version =xmlStrdup(GDataGetXMLString(version));

    }

  }

}


- (void)setCharacterEncoding:(NSString *)encoding {


 if (xmlDoc_ !=NULL) {

   if (xmlDoc_->encoding !=NULL) {

      // version is a const char* so we must cast

     xmlFree((char *)xmlDoc_->encoding);

     xmlDoc_->encoding =NULL;

    }


   if (encoding != nil) {

     xmlDoc_->encoding =xmlStrdup(GDataGetXMLString(encoding));

    }

  }

}


- (NSArray *)nodesForXPath:(NSString *)xpath error:(NSError **)error {

  return [selfnodesForXPath:xpath namespaces:nilerror:error];

}


- (NSArray *)nodesForXPath:(NSString *)xpath

                namespaces:(NSDictionary *)namespaces

                     error:(NSError **)error {

 if (xmlDoc_ !=NULL) {

    GDataXMLNode *docNode = [GDataXMLElementnodeBorrowingXMLNode:(xmlNodePtr)xmlDoc_];

   NSArray *array = [docNode nodesForXPath:xpath

                                namespaces:namespaces

                                     error:error];

   return array;

  }

  return nil;

}


@end


//

// Dictionary key callbacks for our C-string to NSString cache dictionary

//

staticconst void *StringCacheKeyRetainCallBack(CFAllocatorRef allocator,const void *str) {

  // copy the key

 xmlChar* key = xmlStrdup(str);

 return key;

}


staticvoid StringCacheKeyReleaseCallBack(CFAllocatorRef allocator,const void *str) {

  // free the key

 char *chars = (char *)str;

 xmlFree((char *) chars);

}


staticCFStringRef StringCacheKeyCopyDescriptionCallBack(constvoid *str) {

  // make a CFString from the key

  CFStringRef cfStr =CFStringCreateWithCString(kCFAllocatorDefault,

                                                (constchar *)str,

                                               kCFStringEncodingUTF8);

 return cfStr;

}


staticBoolean StringCacheKeyEqualCallBack(constvoid *str1, constvoid *str2) {

  // compare the key strings

 if (str1 == str2) return true;


 int result = xmlStrcmp(str1, str2);

 return (result == 0);

}


staticCFHashCode StringCacheKeyHashCallBack(constvoid *str) {


  // dhb hash, perhttp://www.cse.yorku.ca/~oz/hash.html

 CFHashCode hash = 5381;

 int c;

 const char *chars = (constchar *)str;


 while ((c = *chars++) != 0) {

    hash = ((hash <<5) + hash) + c;

  }

 return hash;

}



0 0