Ei kuvausta

GDataXMLNode.m 50KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837
  1. /* Copyright (c) 2008 Google Inc.
  2. *
  3. * Licensed under the Apache License, Version 2.0 (the "License");
  4. * you may not use this file except in compliance with the License.
  5. * You may obtain a copy of the License at
  6. *
  7. * http://www.apache.org/licenses/LICENSE-2.0
  8. *
  9. * Unless required by applicable law or agreed to in writing, software
  10. * distributed under the License is distributed on an "AS IS" BASIS,
  11. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. * See the License for the specific language governing permissions and
  13. * limitations under the License.
  14. */
  15. #import <libxml/tree.h>
  16. #import <libxml/parser.h>
  17. #import <libxml/xmlstring.h>
  18. #import <libxml/xpath.h>
  19. #import <libxml/xpathInternals.h>
  20. #define GDATAXMLNODE_DEFINE_GLOBALS 1
  21. #import "GDataXMLNode.h"
  22. @class NSArray, NSDictionary, NSError, NSString, NSURL;
  23. @class GDataXMLElement, GDataXMLDocument;
  24. static const int kGDataXMLParseOptions = (XML_PARSE_NOCDATA | XML_PARSE_NOBLANKS);
  25. // dictionary key callbacks for string cache
  26. static const void *StringCacheKeyRetainCallBack(CFAllocatorRef allocator, const void *str);
  27. static void StringCacheKeyReleaseCallBack(CFAllocatorRef allocator, const void *str);
  28. static CFStringRef StringCacheKeyCopyDescriptionCallBack(const void *str);
  29. static Boolean StringCacheKeyEqualCallBack(const void *str1, const void *str2);
  30. static CFHashCode StringCacheKeyHashCallBack(const void *str);
  31. // isEqual: has the fatal flaw that it doesn't deal well with the received
  32. // being nil. We'll use this utility instead.
  33. // Static copy of AreEqualOrBothNil from GDataObject.m, so that using
  34. // GDataXMLNode does not require pulling in all of GData.
  35. static BOOL AreEqualOrBothNilPrivate(id obj1, id obj2) {
  36. if (obj1 == obj2) {
  37. return YES;
  38. }
  39. if (obj1 && obj2) {
  40. return [obj1 isEqual:obj2];
  41. }
  42. return NO;
  43. }
  44. // convert NSString* to xmlChar*
  45. //
  46. // the "Get" part implies that ownership remains with str
  47. static xmlChar* GDataGetXMLString(NSString *str) {
  48. xmlChar* result = (xmlChar *)[str UTF8String];
  49. return result;
  50. }
  51. // Make a fake qualified name we use as local name internally in libxml
  52. // data structures when there's no actual namespace node available to point to
  53. // from an element or attribute node
  54. //
  55. // Returns an autoreleased NSString*
  56. static NSString *GDataFakeQNameForURIAndName(NSString *theURI, NSString *name) {
  57. NSString *localName = [GDataXMLNode localNameForName:name];
  58. NSString *fakeQName = [NSString stringWithFormat:@"{%@}:%@",
  59. theURI, localName];
  60. return fakeQName;
  61. }
  62. // libxml2 offers xmlSplitQName2, but that searches forwards. Since we may
  63. // be searching for a whole URI shoved in as a prefix, like
  64. // {http://foo}:name
  65. // we'll search for the prefix in backwards from the end of the qualified name
  66. //
  67. // returns a copy of qname as the local name if there's no prefix
  68. static xmlChar *SplitQNameReverse(const xmlChar *qname, xmlChar **prefix) {
  69. // search backwards for a colon
  70. int qnameLen = xmlStrlen(qname);
  71. for (int idx = qnameLen - 1; idx >= 0; idx--) {
  72. if (qname[idx] == ':') {
  73. // found the prefix; copy the prefix, if requested
  74. if (prefix != NULL) {
  75. if (idx > 0) {
  76. *prefix = xmlStrsub(qname, 0, idx);
  77. } else {
  78. *prefix = NULL;
  79. }
  80. }
  81. if (idx < qnameLen - 1) {
  82. // return a copy of the local name
  83. xmlChar *localName = xmlStrsub(qname, idx + 1, qnameLen - idx - 1);
  84. return localName;
  85. } else {
  86. return NULL;
  87. }
  88. }
  89. }
  90. // no colon found, so the qualified name is the local name
  91. xmlChar *qnameCopy = xmlStrdup(qname);
  92. return qnameCopy;
  93. }
  94. @interface GDataXMLNode (PrivateMethods)
  95. // consuming a node implies it will later be freed when the instance is
  96. // dealloc'd; borrowing it implies that ownership and disposal remain the
  97. // job of the supplier of the node
  98. + (id)nodeConsumingXMLNode:(xmlNodePtr)theXMLNode;
  99. - (id)initConsumingXMLNode:(xmlNodePtr)theXMLNode;
  100. + (id)nodeBorrowingXMLNode:(xmlNodePtr)theXMLNode;
  101. - (id)initBorrowingXMLNode:(xmlNodePtr)theXMLNode;
  102. // getters of the underlying node
  103. - (xmlNodePtr)XMLNode;
  104. - (xmlNodePtr)XMLNodeCopy;
  105. // search for an underlying attribute
  106. - (GDataXMLNode *)attributeForXMLNode:(xmlAttrPtr)theXMLNode;
  107. // return an NSString for an xmlChar*, using our strings cache in the
  108. // document
  109. - (NSString *)stringFromXMLString:(const xmlChar *)chars;
  110. // setter/getter of the dealloc flag for the underlying node
  111. - (BOOL)shouldFreeXMLNode;
  112. - (void)setShouldFreeXMLNode:(BOOL)flag;
  113. @end
  114. @interface GDataXMLElement (PrivateMethods)
  115. + (void)fixUpNamespacesForNode:(xmlNodePtr)nodeToFix
  116. graftingToTreeNode:(xmlNodePtr)graftPointNode;
  117. @end
  118. @implementation GDataXMLNode
  119. + (void)load {
  120. xmlInitParser();
  121. }
  122. // Note on convenience methods for making stand-alone element and
  123. // attribute nodes:
  124. //
  125. // Since we're making a node from scratch, we don't
  126. // have any namespace info. So the namespace prefix, if
  127. // any, will just be slammed into the node name.
  128. // We'll rely on the -addChild method below to remove
  129. // the namespace prefix and replace it with a proper ns
  130. // pointer.
  131. + (GDataXMLElement *)elementWithName:(NSString *)name {
  132. xmlNodePtr theNewNode = xmlNewNode(NULL, // namespace
  133. GDataGetXMLString(name));
  134. if (theNewNode) {
  135. // succeeded
  136. return [self nodeConsumingXMLNode:theNewNode];
  137. }
  138. return nil;
  139. }
  140. + (GDataXMLElement *)elementWithName:(NSString *)name stringValue:(NSString *)value {
  141. xmlNodePtr theNewNode = xmlNewNode(NULL, // namespace
  142. GDataGetXMLString(name));
  143. if (theNewNode) {
  144. xmlNodePtr textNode = xmlNewText(GDataGetXMLString(value));
  145. if (textNode) {
  146. xmlNodePtr temp = xmlAddChild(theNewNode, textNode);
  147. if (temp) {
  148. // succeeded
  149. return [self nodeConsumingXMLNode:theNewNode];
  150. }
  151. }
  152. // failed; free the node and any children
  153. xmlFreeNode(theNewNode);
  154. }
  155. return nil;
  156. }
  157. + (GDataXMLElement *)elementWithName:(NSString *)name URI:(NSString *)theURI {
  158. // since we don't know a prefix yet, shove in the whole URI; we'll look for
  159. // a proper namespace ptr later when addChild calls fixUpNamespacesForNode
  160. NSString *fakeQName = GDataFakeQNameForURIAndName(theURI, name);
  161. xmlNodePtr theNewNode = xmlNewNode(NULL, // namespace
  162. GDataGetXMLString(fakeQName));
  163. if (theNewNode) {
  164. return [self nodeConsumingXMLNode:theNewNode];
  165. }
  166. return nil;
  167. }
  168. + (id)attributeWithName:(NSString *)name stringValue:(NSString *)value {
  169. xmlChar *xmlName = GDataGetXMLString(name);
  170. xmlChar *xmlValue = GDataGetXMLString(value);
  171. xmlAttrPtr theNewAttr = xmlNewProp(NULL, // parent node for the attr
  172. xmlName, xmlValue);
  173. if (theNewAttr) {
  174. return [self nodeConsumingXMLNode:(xmlNodePtr) theNewAttr];
  175. }
  176. return nil;
  177. }
  178. + (id)attributeWithName:(NSString *)name URI:(NSString *)attributeURI stringValue:(NSString *)value {
  179. // since we don't know a prefix yet, shove in the whole URI; we'll look for
  180. // a proper namespace ptr later when addChild calls fixUpNamespacesForNode
  181. NSString *fakeQName = GDataFakeQNameForURIAndName(attributeURI, name);
  182. xmlChar *xmlName = GDataGetXMLString(fakeQName);
  183. xmlChar *xmlValue = GDataGetXMLString(value);
  184. xmlAttrPtr theNewAttr = xmlNewProp(NULL, // parent node for the attr
  185. xmlName, xmlValue);
  186. if (theNewAttr) {
  187. return [self nodeConsumingXMLNode:(xmlNodePtr) theNewAttr];
  188. }
  189. return nil;
  190. }
  191. + (id)textWithStringValue:(NSString *)value {
  192. xmlNodePtr theNewText = xmlNewText(GDataGetXMLString(value));
  193. if (theNewText) {
  194. return [self nodeConsumingXMLNode:theNewText];
  195. }
  196. return nil;
  197. }
  198. + (id)namespaceWithName:(NSString *)name stringValue:(NSString *)value {
  199. xmlChar *href = GDataGetXMLString(value);
  200. xmlChar *prefix;
  201. if ([name length] > 0) {
  202. prefix = GDataGetXMLString(name);
  203. } else {
  204. // default namespace is represented by a nil prefix
  205. prefix = nil;
  206. }
  207. xmlNsPtr theNewNs = xmlNewNs(NULL, // parent node
  208. href, prefix);
  209. if (theNewNs) {
  210. return [self nodeConsumingXMLNode:(xmlNodePtr) theNewNs];
  211. }
  212. return nil;
  213. }
  214. + (id)nodeConsumingXMLNode:(xmlNodePtr)theXMLNode {
  215. Class theClass;
  216. if (theXMLNode->type == XML_ELEMENT_NODE) {
  217. theClass = [GDataXMLElement class];
  218. } else {
  219. theClass = [GDataXMLNode class];
  220. }
  221. return [[[theClass alloc] initConsumingXMLNode:theXMLNode] autorelease];
  222. }
  223. - (id)initConsumingXMLNode:(xmlNodePtr)theXMLNode {
  224. self = [super init];
  225. if (self) {
  226. xmlNode_ = theXMLNode;
  227. shouldFreeXMLNode_ = YES;
  228. }
  229. return self;
  230. }
  231. + (id)nodeBorrowingXMLNode:(xmlNodePtr)theXMLNode {
  232. Class theClass;
  233. if (theXMLNode->type == XML_ELEMENT_NODE) {
  234. theClass = [GDataXMLElement class];
  235. } else {
  236. theClass = [GDataXMLNode class];
  237. }
  238. return [[[theClass alloc] initBorrowingXMLNode:theXMLNode] autorelease];
  239. }
  240. - (id)initBorrowingXMLNode:(xmlNodePtr)theXMLNode {
  241. self = [super init];
  242. if (self) {
  243. xmlNode_ = theXMLNode;
  244. shouldFreeXMLNode_ = NO;
  245. }
  246. return self;
  247. }
  248. - (void)releaseCachedValues {
  249. [cachedName_ release];
  250. cachedName_ = nil;
  251. [cachedChildren_ release];
  252. cachedChildren_ = nil;
  253. [cachedAttributes_ release];
  254. cachedAttributes_ = nil;
  255. }
  256. // convert xmlChar* to NSString*
  257. //
  258. // returns an autoreleased NSString*, from the current node's document strings
  259. // cache if possible
  260. - (NSString *)stringFromXMLString:(const xmlChar *)chars {
  261. #if DEBUG
  262. NSCAssert(chars != NULL, @"GDataXMLNode sees an unexpected empty string");
  263. #endif
  264. if (chars == NULL) return nil;
  265. CFMutableDictionaryRef cacheDict = NULL;
  266. NSString *result = nil;
  267. if (xmlNode_ != NULL
  268. && (xmlNode_->type == XML_ELEMENT_NODE
  269. || xmlNode_->type == XML_ATTRIBUTE_NODE
  270. || xmlNode_->type == XML_TEXT_NODE)) {
  271. // there is no xmlDocPtr in XML_NAMESPACE_DECL nodes,
  272. // so we can't cache the text of those
  273. // look for a strings cache in the document
  274. //
  275. // the cache is in the document's user-defined _private field
  276. if (xmlNode_->doc != NULL) {
  277. cacheDict = xmlNode_->doc->_private;
  278. if (cacheDict) {
  279. // this document has a strings cache
  280. result = (NSString *) CFDictionaryGetValue(cacheDict, chars);
  281. if (result) {
  282. // we found the xmlChar string in the cache; return the previously
  283. // allocated NSString, rather than allocate a new one
  284. return result;
  285. }
  286. }
  287. }
  288. }
  289. // allocate a new NSString for this xmlChar*
  290. result = [NSString stringWithUTF8String:(const char *) chars];
  291. if (cacheDict) {
  292. // save the string in the document's string cache
  293. CFDictionarySetValue(cacheDict, chars, result);
  294. }
  295. return result;
  296. }
  297. - (void)dealloc {
  298. if (xmlNode_ && shouldFreeXMLNode_) {
  299. xmlFreeNode(xmlNode_);
  300. xmlNode_ = NULL;
  301. }
  302. [self releaseCachedValues];
  303. [super dealloc];
  304. }
  305. #pragma mark -
  306. - (void)setStringValue:(NSString *)str {
  307. if (xmlNode_ != NULL && str != nil) {
  308. if (xmlNode_->type == XML_NAMESPACE_DECL) {
  309. // for a namespace node, the value is the namespace URI
  310. xmlNsPtr nsNode = (xmlNsPtr)xmlNode_;
  311. if (nsNode->href != NULL) xmlFree((char *)nsNode->href);
  312. nsNode->href = xmlStrdup(GDataGetXMLString(str));
  313. } else {
  314. // attribute or element node
  315. // do we need to call xmlEncodeSpecialChars?
  316. xmlNodeSetContent(xmlNode_, GDataGetXMLString(str));
  317. }
  318. }
  319. }
  320. - (NSString *)stringValue {
  321. NSString *str = nil;
  322. if (xmlNode_ != NULL) {
  323. if (xmlNode_->type == XML_NAMESPACE_DECL) {
  324. // for a namespace node, the value is the namespace URI
  325. xmlNsPtr nsNode = (xmlNsPtr)xmlNode_;
  326. str = [self stringFromXMLString:(nsNode->href)];
  327. } else {
  328. // attribute or element node
  329. xmlChar* chars = xmlNodeGetContent(xmlNode_);
  330. if (chars) {
  331. str = [self stringFromXMLString:chars];
  332. xmlFree(chars);
  333. }
  334. }
  335. }
  336. return str;
  337. }
  338. - (NSString *)XMLString {
  339. NSString *str = nil;
  340. if (xmlNode_ != NULL) {
  341. xmlBufferPtr buff = xmlBufferCreate();
  342. if (buff) {
  343. xmlDocPtr doc = NULL;
  344. int level = 0;
  345. int format = 0;
  346. int result = xmlNodeDump(buff, doc, xmlNode_, level, format);
  347. if (result > -1) {
  348. str = [[[NSString alloc] initWithBytes:(xmlBufferContent(buff))
  349. length:(NSUInteger)(xmlBufferLength(buff))
  350. encoding:NSUTF8StringEncoding] autorelease];
  351. }
  352. xmlBufferFree(buff);
  353. }
  354. }
  355. // remove leading and trailing whitespace
  356. NSCharacterSet *ws = [NSCharacterSet whitespaceAndNewlineCharacterSet];
  357. NSString *trimmed = [str stringByTrimmingCharactersInSet:ws];
  358. return trimmed;
  359. }
  360. - (NSString *)localName {
  361. NSString *str = nil;
  362. if (xmlNode_ != NULL) {
  363. str = [self stringFromXMLString:(xmlNode_->name)];
  364. // if this is part of a detached subtree, str may have a prefix in it
  365. str = [[self class] localNameForName:str];
  366. }
  367. return str;
  368. }
  369. - (NSString *)prefix {
  370. NSString *str = nil;
  371. if (xmlNode_ != NULL) {
  372. // the default namespace's prefix is an empty string, though libxml
  373. // represents it as NULL for ns->prefix
  374. str = @"";
  375. if (xmlNode_->ns != NULL && xmlNode_->ns->prefix != NULL) {
  376. str = [self stringFromXMLString:(xmlNode_->ns->prefix)];
  377. }
  378. }
  379. return str;
  380. }
  381. - (NSString *)URI {
  382. NSString *str = nil;
  383. if (xmlNode_ != NULL) {
  384. if (xmlNode_->ns != NULL && xmlNode_->ns->href != NULL) {
  385. str = [self stringFromXMLString:(xmlNode_->ns->href)];
  386. }
  387. }
  388. return str;
  389. }
  390. - (NSString *)qualifiedName {
  391. // internal utility
  392. NSString *str = nil;
  393. if (xmlNode_ != NULL) {
  394. if (xmlNode_->type == XML_NAMESPACE_DECL) {
  395. // name of a namespace node
  396. xmlNsPtr nsNode = (xmlNsPtr)xmlNode_;
  397. // null is the default namespace; one is the loneliest number
  398. if (nsNode->prefix == NULL) {
  399. str = @"";
  400. }
  401. else {
  402. str = [self stringFromXMLString:(nsNode->prefix)];
  403. }
  404. } else if (xmlNode_->ns != NULL && xmlNode_->ns->prefix != NULL) {
  405. // name of a non-namespace node
  406. // has a prefix
  407. char *qname;
  408. if (asprintf(&qname, "%s:%s", (const char *)xmlNode_->ns->prefix,
  409. xmlNode_->name) != -1) {
  410. str = [self stringFromXMLString:(const xmlChar *)qname];
  411. free(qname);
  412. }
  413. } else {
  414. // lacks a prefix
  415. str = [self stringFromXMLString:(xmlNode_->name)];
  416. }
  417. }
  418. return str;
  419. }
  420. - (NSString *)name {
  421. if (cachedName_ != nil) {
  422. return cachedName_;
  423. }
  424. NSString *str = [self qualifiedName];
  425. cachedName_ = [str retain];
  426. return str;
  427. }
  428. + (NSString *)localNameForName:(NSString *)name {
  429. if (name != nil) {
  430. NSRange range = [name rangeOfString:@":"];
  431. if (range.location != NSNotFound) {
  432. // found a colon
  433. if (range.location + 1 < [name length]) {
  434. NSString *localName = [name substringFromIndex:(range.location + 1)];
  435. return localName;
  436. }
  437. }
  438. }
  439. return name;
  440. }
  441. + (NSString *)prefixForName:(NSString *)name {
  442. if (name != nil) {
  443. NSRange range = [name rangeOfString:@":"];
  444. if (range.location != NSNotFound) {
  445. NSString *prefix = [name substringToIndex:(range.location)];
  446. return prefix;
  447. }
  448. }
  449. return nil;
  450. }
  451. - (NSUInteger)childCount {
  452. if (cachedChildren_ != nil) {
  453. return [cachedChildren_ count];
  454. }
  455. if (xmlNode_ != NULL) {
  456. unsigned int count = 0;
  457. xmlNodePtr currChild = xmlNode_->children;
  458. while (currChild != NULL) {
  459. ++count;
  460. currChild = currChild->next;
  461. }
  462. return count;
  463. }
  464. return 0;
  465. }
  466. - (NSArray *)children {
  467. if (cachedChildren_ != nil) {
  468. return cachedChildren_;
  469. }
  470. NSMutableArray *array = nil;
  471. if (xmlNode_ != NULL) {
  472. xmlNodePtr currChild = xmlNode_->children;
  473. while (currChild != NULL) {
  474. GDataXMLNode *node = [GDataXMLNode nodeBorrowingXMLNode:currChild];
  475. if (array == nil) {
  476. array = [NSMutableArray arrayWithObject:node];
  477. } else {
  478. [array addObject:node];
  479. }
  480. currChild = currChild->next;
  481. }
  482. cachedChildren_ = [array retain];
  483. }
  484. return array;
  485. }
  486. - (GDataXMLNode *)childAtIndex:(unsigned)index {
  487. NSArray *children = [self children];
  488. if ([children count] > index) {
  489. return [children objectAtIndex:index];
  490. }
  491. return nil;
  492. }
  493. - (GDataXMLNodeKind)kind {
  494. if (xmlNode_ != NULL) {
  495. xmlElementType nodeType = xmlNode_->type;
  496. switch (nodeType) {
  497. case XML_ELEMENT_NODE: return GDataXMLElementKind;
  498. case XML_ATTRIBUTE_NODE: return GDataXMLAttributeKind;
  499. case XML_TEXT_NODE: return GDataXMLTextKind;
  500. case XML_CDATA_SECTION_NODE: return GDataXMLTextKind;
  501. case XML_ENTITY_REF_NODE: return GDataXMLEntityDeclarationKind;
  502. case XML_ENTITY_NODE: return GDataXMLEntityDeclarationKind;
  503. case XML_PI_NODE: return GDataXMLProcessingInstructionKind;
  504. case XML_COMMENT_NODE: return GDataXMLCommentKind;
  505. case XML_DOCUMENT_NODE: return GDataXMLDocumentKind;
  506. case XML_DOCUMENT_TYPE_NODE: return GDataXMLDocumentKind;
  507. case XML_DOCUMENT_FRAG_NODE: return GDataXMLDocumentKind;
  508. case XML_NOTATION_NODE: return GDataXMLNotationDeclarationKind;
  509. case XML_HTML_DOCUMENT_NODE: return GDataXMLDocumentKind;
  510. case XML_DTD_NODE: return GDataXMLDTDKind;
  511. case XML_ELEMENT_DECL: return GDataXMLElementDeclarationKind;
  512. case XML_ATTRIBUTE_DECL: return GDataXMLAttributeDeclarationKind;
  513. case XML_ENTITY_DECL: return GDataXMLEntityDeclarationKind;
  514. case XML_NAMESPACE_DECL: return GDataXMLNamespaceKind;
  515. case XML_XINCLUDE_START: return GDataXMLProcessingInstructionKind;
  516. case XML_XINCLUDE_END: return GDataXMLProcessingInstructionKind;
  517. case XML_DOCB_DOCUMENT_NODE: return GDataXMLDocumentKind;
  518. }
  519. }
  520. return GDataXMLInvalidKind;
  521. }
  522. - (NSArray *)nodesForXPath:(NSString *)xpath error:(NSError **)error {
  523. // call through with no explicit namespace dictionary; that will register the
  524. // root node's namespaces
  525. return [self nodesForXPath:xpath namespaces:nil error:error];
  526. }
  527. - (NSArray *)nodesForXPath:(NSString *)xpath
  528. namespaces:(NSDictionary *)namespaces
  529. error:(NSError **)error {
  530. NSMutableArray *array = nil;
  531. NSInteger errorCode = -1;
  532. NSDictionary *errorInfo = nil;
  533. // xmlXPathNewContext requires a doc for its context, but if our elements
  534. // are created from GDataXMLElement's initWithXMLString there may not be
  535. // a document. (We may later decide that we want to stuff the doc used
  536. // there into a GDataXMLDocument and retain it, but we don't do that now.)
  537. //
  538. // We'll temporarily make a document to use for the xpath context.
  539. xmlDocPtr tempDoc = NULL;
  540. xmlNodePtr topParent = NULL;
  541. if (xmlNode_->doc == NULL) {
  542. tempDoc = xmlNewDoc(NULL);
  543. if (tempDoc) {
  544. // find the topmost node of the current tree to make the root of
  545. // our temporary document
  546. topParent = xmlNode_;
  547. while (topParent->parent != NULL) {
  548. topParent = topParent->parent;
  549. }
  550. xmlDocSetRootElement(tempDoc, topParent);
  551. }
  552. }
  553. if (xmlNode_ != NULL && xmlNode_->doc != NULL) {
  554. xmlXPathContextPtr xpathCtx = xmlXPathNewContext(xmlNode_->doc);
  555. if (xpathCtx) {
  556. // anchor at our current node
  557. xpathCtx->node = xmlNode_;
  558. // if a namespace dictionary was provided, register its contents
  559. if (namespaces) {
  560. // the dictionary keys are prefixes; the values are URIs
  561. for (NSString *prefix in namespaces) {
  562. NSString *uri = [namespaces objectForKey:prefix];
  563. xmlChar *prefixChars = (xmlChar *) [prefix UTF8String];
  564. xmlChar *uriChars = (xmlChar *) [uri UTF8String];
  565. int result = xmlXPathRegisterNs(xpathCtx, prefixChars, uriChars);
  566. if (result != 0) {
  567. #if DEBUG
  568. NSCAssert1(result == 0, @"GDataXMLNode XPath namespace %@ issue",
  569. prefix);
  570. #endif
  571. }
  572. }
  573. } else {
  574. // no namespace dictionary was provided
  575. //
  576. // register the namespaces of this node, if it's an element, or of
  577. // this node's root element, if it's a document
  578. xmlNodePtr nsNodePtr = xmlNode_;
  579. if (xmlNode_->type == XML_DOCUMENT_NODE) {
  580. nsNodePtr = xmlDocGetRootElement((xmlDocPtr) xmlNode_);
  581. }
  582. // step through the namespaces, if any, and register each with the
  583. // xpath context
  584. if (nsNodePtr != NULL) {
  585. for (xmlNsPtr nsPtr = nsNodePtr->ns; nsPtr != NULL; nsPtr = nsPtr->next) {
  586. // default namespace is nil in the tree, but there's no way to
  587. // register a default namespace, so we'll register a fake one,
  588. // _def_ns
  589. const xmlChar* prefix = nsPtr->prefix;
  590. if (prefix == NULL) {
  591. prefix = (xmlChar*) kGDataXMLXPathDefaultNamespacePrefix;
  592. }
  593. int result = xmlXPathRegisterNs(xpathCtx, prefix, nsPtr->href);
  594. if (result != 0) {
  595. #if DEBUG
  596. NSCAssert1(result == 0, @"GDataXMLNode XPath namespace %s issue",
  597. prefix);
  598. #endif
  599. }
  600. }
  601. }
  602. }
  603. // now evaluate the path
  604. xmlXPathObjectPtr xpathObj;
  605. xpathObj = xmlXPathEval(GDataGetXMLString(xpath), xpathCtx);
  606. if (xpathObj) {
  607. // we have some result from the search
  608. array = [NSMutableArray array];
  609. xmlNodeSetPtr nodeSet = xpathObj->nodesetval;
  610. if (nodeSet) {
  611. // add each node in the result set to our array
  612. for (int index = 0; index < nodeSet->nodeNr; index++) {
  613. xmlNodePtr currNode = nodeSet->nodeTab[index];
  614. GDataXMLNode *node = [GDataXMLNode nodeBorrowingXMLNode:currNode];
  615. if (node) {
  616. [array addObject:node];
  617. }
  618. }
  619. }
  620. xmlXPathFreeObject(xpathObj);
  621. } else {
  622. // provide an error for failed evaluation
  623. const char *msg = xpathCtx->lastError.str1;
  624. errorCode = xpathCtx->lastError.code;
  625. if (msg) {
  626. NSString *errStr = [NSString stringWithUTF8String:msg];
  627. errorInfo = [NSDictionary dictionaryWithObject:errStr
  628. forKey:@"error"];
  629. }
  630. }
  631. xmlXPathFreeContext(xpathCtx);
  632. }
  633. } else {
  634. // not a valid node for using XPath
  635. errorInfo = [NSDictionary dictionaryWithObject:@"invalid node"
  636. forKey:@"error"];
  637. }
  638. if (array == nil && error != nil) {
  639. *error = [NSError errorWithDomain:@"com.google.GDataXML"
  640. code:errorCode
  641. userInfo:errorInfo];
  642. }
  643. if (tempDoc != NULL) {
  644. xmlUnlinkNode(topParent);
  645. xmlSetTreeDoc(topParent, NULL);
  646. xmlFreeDoc(tempDoc);
  647. }
  648. return array;
  649. }
  650. - (NSString *)description {
  651. int nodeType = (xmlNode_ ? (int)xmlNode_->type : -1);
  652. return [NSString stringWithFormat:@"%@ %p: {type:%d name:%@ xml:\"%@\"}",
  653. [self class], self, nodeType, [self name], [self XMLString]];
  654. }
  655. - (id)copyWithZone:(NSZone *)zone {
  656. xmlNodePtr nodeCopy = [self XMLNodeCopy];
  657. if (nodeCopy != NULL) {
  658. return [[[self class] alloc] initConsumingXMLNode:nodeCopy];
  659. }
  660. return nil;
  661. }
  662. - (BOOL)isEqual:(GDataXMLNode *)other {
  663. if (self == other) return YES;
  664. if (![other isKindOfClass:[GDataXMLNode class]]) return NO;
  665. return [self XMLNode] == [other XMLNode]
  666. || ([self kind] == [other kind]
  667. && AreEqualOrBothNilPrivate([self name], [other name])
  668. && [[self children] count] == [[other children] count]);
  669. }
  670. - (NSUInteger)hash {
  671. return (NSUInteger) (void *) [GDataXMLNode class];
  672. }
  673. - (NSMethodSignature *)methodSignatureForSelector:(SEL)selector {
  674. return [super methodSignatureForSelector:selector];
  675. }
  676. #pragma mark -
  677. - (xmlNodePtr)XMLNodeCopy {
  678. if (xmlNode_ != NULL) {
  679. // Note: libxml will create a new copy of namespace nodes (xmlNs records)
  680. // and attach them to this copy in order to keep namespaces within this
  681. // node subtree copy value.
  682. xmlNodePtr nodeCopy = xmlCopyNode(xmlNode_, 1); // 1 = recursive
  683. return nodeCopy;
  684. }
  685. return NULL;
  686. }
  687. - (xmlNodePtr)XMLNode {
  688. return xmlNode_;
  689. }
  690. - (BOOL)shouldFreeXMLNode {
  691. return shouldFreeXMLNode_;
  692. }
  693. - (void)setShouldFreeXMLNode:(BOOL)flag {
  694. shouldFreeXMLNode_ = flag;
  695. }
  696. @end
  697. @implementation GDataXMLElement
  698. - (id)initWithXMLString:(NSString *)str error:(NSError **)error {
  699. self = [super init];
  700. if (self) {
  701. const char *utf8Str = [str UTF8String];
  702. // NOTE: We are assuming a string length that fits into an int
  703. xmlDocPtr doc = xmlReadMemory(utf8Str, (int)strlen(utf8Str), NULL, // URL
  704. NULL, // encoding
  705. kGDataXMLParseOptions);
  706. if (doc == NULL) {
  707. if (error) {
  708. // TODO(grobbins) use xmlSetGenericErrorFunc to capture error
  709. }
  710. } else {
  711. // copy the root node from the doc
  712. xmlNodePtr root = xmlDocGetRootElement(doc);
  713. if (root) {
  714. xmlNode_ = xmlCopyNode(root, 1); // 1: recursive
  715. shouldFreeXMLNode_ = YES;
  716. }
  717. xmlFreeDoc(doc);
  718. }
  719. if (xmlNode_ == NULL) {
  720. // failure
  721. if (error) {
  722. *error = [NSError errorWithDomain:@"com.google.GDataXML"
  723. code:-1
  724. userInfo:nil];
  725. }
  726. [self release];
  727. return nil;
  728. }
  729. }
  730. return self;
  731. }
  732. - (NSArray *)namespaces {
  733. NSMutableArray *array = nil;
  734. if (xmlNode_ != NULL && xmlNode_->nsDef != NULL) {
  735. xmlNsPtr currNS = xmlNode_->nsDef;
  736. while (currNS != NULL) {
  737. // add this prefix/URI to the list, unless it's the implicit xml prefix
  738. if (!xmlStrEqual(currNS->prefix, (const xmlChar *) "xml")) {
  739. GDataXMLNode *node = [GDataXMLNode nodeBorrowingXMLNode:(xmlNodePtr) currNS];
  740. if (array == nil) {
  741. array = [NSMutableArray arrayWithObject:node];
  742. } else {
  743. [array addObject:node];
  744. }
  745. }
  746. currNS = currNS->next;
  747. }
  748. }
  749. return array;
  750. }
  751. - (void)setNamespaces:(NSArray *)namespaces {
  752. if (xmlNode_ != NULL) {
  753. [self releaseCachedValues];
  754. // remove previous namespaces
  755. if (xmlNode_->nsDef) {
  756. xmlFreeNsList(xmlNode_->nsDef);
  757. xmlNode_->nsDef = NULL;
  758. }
  759. // add a namespace for each object in the array
  760. NSEnumerator *enumerator = [namespaces objectEnumerator];
  761. GDataXMLNode *namespaceNode;
  762. while ((namespaceNode = [enumerator nextObject]) != nil) {
  763. xmlNsPtr ns = (xmlNsPtr) [namespaceNode XMLNode];
  764. if (ns) {
  765. (void)xmlNewNs(xmlNode_, ns->href, ns->prefix);
  766. }
  767. }
  768. // we may need to fix this node's own name; the graft point is where
  769. // the namespace search starts, so that points to this node too
  770. [[self class] fixUpNamespacesForNode:xmlNode_
  771. graftingToTreeNode:xmlNode_];
  772. }
  773. }
  774. - (void)addNamespace:(GDataXMLNode *)aNamespace {
  775. if (xmlNode_ != NULL) {
  776. [self releaseCachedValues];
  777. xmlNsPtr ns = (xmlNsPtr) [aNamespace XMLNode];
  778. if (ns) {
  779. (void)xmlNewNs(xmlNode_, ns->href, ns->prefix);
  780. // we may need to fix this node's own name; the graft point is where
  781. // the namespace search starts, so that points to this node too
  782. [[self class] fixUpNamespacesForNode:xmlNode_
  783. graftingToTreeNode:xmlNode_];
  784. }
  785. }
  786. }
  787. - (void)addChild:(GDataXMLNode *)child {
  788. if ([child kind] == GDataXMLAttributeKind) {
  789. [self addAttribute:child];
  790. return;
  791. }
  792. if (xmlNode_ != NULL) {
  793. [self releaseCachedValues];
  794. xmlNodePtr childNodeCopy = [child XMLNodeCopy];
  795. if (childNodeCopy) {
  796. xmlNodePtr resultNode = xmlAddChild(xmlNode_, childNodeCopy);
  797. if (resultNode == NULL) {
  798. // failed to add
  799. xmlFreeNode(childNodeCopy);
  800. } else {
  801. // added this child subtree successfully; see if it has
  802. // previously-unresolved namespace prefixes that can now be fixed up
  803. [[self class] fixUpNamespacesForNode:childNodeCopy
  804. graftingToTreeNode:xmlNode_];
  805. }
  806. }
  807. }
  808. }
  809. - (void)removeChild:(GDataXMLNode *)child {
  810. // this is safe for attributes too
  811. if (xmlNode_ != NULL) {
  812. [self releaseCachedValues];
  813. xmlNodePtr node = [child XMLNode];
  814. xmlUnlinkNode(node);
  815. // if the child node was borrowing its xmlNodePtr, then we need to
  816. // explicitly free it, since there is probably no owning object that will
  817. // free it on dealloc
  818. if (![child shouldFreeXMLNode]) {
  819. xmlFreeNode(node);
  820. }
  821. }
  822. }
  823. - (NSArray *)elementsForName:(NSString *)name {
  824. NSString *desiredName = name;
  825. if (xmlNode_ != NULL) {
  826. NSString *prefix = [[self class] prefixForName:desiredName];
  827. if (prefix) {
  828. xmlChar* desiredPrefix = GDataGetXMLString(prefix);
  829. xmlNsPtr foundNS = xmlSearchNs(xmlNode_->doc, xmlNode_, desiredPrefix);
  830. if (foundNS) {
  831. // we found a namespace; fall back on elementsForLocalName:URI:
  832. // to get the elements
  833. NSString *desiredURI = [self stringFromXMLString:(foundNS->href)];
  834. NSString *localName = [[self class] localNameForName:desiredName];
  835. NSArray *nsArray = [self elementsForLocalName:localName URI:desiredURI];
  836. return nsArray;
  837. }
  838. }
  839. // no namespace found for the node's prefix; try an exact match
  840. // for the name argument, including any prefix
  841. NSMutableArray *array = nil;
  842. // walk our list of cached child nodes
  843. NSArray *children = [self children];
  844. for (GDataXMLNode *child in children) {
  845. xmlNodePtr currNode = [child XMLNode];
  846. // find all children which are elements with the desired name
  847. if (currNode->type == XML_ELEMENT_NODE) {
  848. NSString *qName = [child name];
  849. if ([qName isEqual:name]) {
  850. if (array == nil) {
  851. array = [NSMutableArray arrayWithObject:child];
  852. } else {
  853. [array addObject:child];
  854. }
  855. }
  856. }
  857. }
  858. return array;
  859. }
  860. return nil;
  861. }
  862. - (NSArray *)elementsForLocalName:(NSString *)localName URI:(NSString *)URI {
  863. NSMutableArray *array = nil;
  864. if (xmlNode_ != NULL && xmlNode_->children != NULL) {
  865. xmlChar* desiredNSHref = GDataGetXMLString(URI);
  866. xmlChar* requestedLocalName = GDataGetXMLString(localName);
  867. xmlChar* expectedLocalName = requestedLocalName;
  868. // resolve the URI at the parent level, since usually children won't
  869. // have their own namespace definitions, and we don't want to try to
  870. // resolve it once for every child
  871. xmlNsPtr foundParentNS = xmlSearchNsByHref(xmlNode_->doc, xmlNode_, desiredNSHref);
  872. if (foundParentNS == NULL) {
  873. NSString *fakeQName = GDataFakeQNameForURIAndName(URI, localName);
  874. expectedLocalName = GDataGetXMLString(fakeQName);
  875. }
  876. NSArray *children = [self children];
  877. for (GDataXMLNode *child in children) {
  878. xmlNodePtr currChildPtr = [child XMLNode];
  879. // find all children which are elements with the desired name and
  880. // namespace, or with the prefixed name and a null namespace
  881. if (currChildPtr->type == XML_ELEMENT_NODE) {
  882. // normally, we can assume the resolution done for the parent will apply
  883. // to the child, as most children do not define their own namespaces
  884. xmlNsPtr childLocalNS = foundParentNS;
  885. xmlChar* childDesiredLocalName = expectedLocalName;
  886. if (currChildPtr->nsDef != NULL) {
  887. // this child has its own namespace definitons; do a fresh resolve
  888. // of the namespace starting from the child, and see if it differs
  889. // from the resolve done starting from the parent. If the resolve
  890. // finds a different namespace, then override the desired local
  891. // name just for this child.
  892. childLocalNS = xmlSearchNsByHref(xmlNode_->doc, currChildPtr, desiredNSHref);
  893. if (childLocalNS != foundParentNS) {
  894. // this child does indeed have a different namespace resolution
  895. // result than was found for its parent
  896. if (childLocalNS == NULL) {
  897. // no namespace found
  898. NSString *fakeQName = GDataFakeQNameForURIAndName(URI, localName);
  899. childDesiredLocalName = GDataGetXMLString(fakeQName);
  900. } else {
  901. // a namespace was found; use the original local name requested,
  902. // not a faked one expected from resolving the parent
  903. childDesiredLocalName = requestedLocalName;
  904. }
  905. }
  906. }
  907. // check if this child's namespace and local name are what we're
  908. // seeking
  909. if (currChildPtr->ns == childLocalNS
  910. && currChildPtr->name != NULL
  911. && xmlStrEqual(currChildPtr->name, childDesiredLocalName)) {
  912. if (array == nil) {
  913. array = [NSMutableArray arrayWithObject:child];
  914. } else {
  915. [array addObject:child];
  916. }
  917. }
  918. }
  919. }
  920. // we return nil, not an empty array, according to docs
  921. }
  922. return array;
  923. }
  924. - (NSArray *)attributes {
  925. if (cachedAttributes_ != nil) {
  926. return cachedAttributes_;
  927. }
  928. NSMutableArray *array = nil;
  929. if (xmlNode_ != NULL && xmlNode_->properties != NULL) {
  930. xmlAttrPtr prop = xmlNode_->properties;
  931. while (prop != NULL) {
  932. GDataXMLNode *node = [GDataXMLNode nodeBorrowingXMLNode:(xmlNodePtr) prop];
  933. if (array == nil) {
  934. array = [NSMutableArray arrayWithObject:node];
  935. } else {
  936. [array addObject:node];
  937. }
  938. prop = prop->next;
  939. }
  940. cachedAttributes_ = [array retain];
  941. }
  942. return array;
  943. }
  944. - (void)addAttribute:(GDataXMLNode *)attribute {
  945. if (xmlNode_ != NULL) {
  946. [self releaseCachedValues];
  947. xmlAttrPtr attrPtr = (xmlAttrPtr) [attribute XMLNode];
  948. if (attrPtr) {
  949. // ignore this if an attribute with the name is already present,
  950. // similar to NSXMLNode's addAttribute
  951. xmlAttrPtr oldAttr;
  952. if (attrPtr->ns == NULL) {
  953. oldAttr = xmlHasProp(xmlNode_, attrPtr->name);
  954. } else {
  955. oldAttr = xmlHasNsProp(xmlNode_, attrPtr->name, attrPtr->ns->href);
  956. }
  957. if (oldAttr == NULL) {
  958. xmlNsPtr newPropNS = NULL;
  959. // if this attribute has a namespace, search for a matching namespace
  960. // on the node we're adding to
  961. if (attrPtr->ns != NULL) {
  962. newPropNS = xmlSearchNsByHref(xmlNode_->doc, xmlNode_, attrPtr->ns->href);
  963. if (newPropNS == NULL) {
  964. // make a new namespace on the parent node, and use that for the
  965. // new attribute
  966. newPropNS = xmlNewNs(xmlNode_, attrPtr->ns->href, attrPtr->ns->prefix);
  967. }
  968. }
  969. // copy the attribute onto this node
  970. xmlChar *value = xmlNodeGetContent((xmlNodePtr) attrPtr);
  971. xmlAttrPtr newProp = xmlNewNsProp(xmlNode_, newPropNS, attrPtr->name, value);
  972. if (newProp != NULL) {
  973. // we made the property, so clean up the property's namespace
  974. [[self class] fixUpNamespacesForNode:(xmlNodePtr)newProp
  975. graftingToTreeNode:xmlNode_];
  976. }
  977. if (value != NULL) {
  978. xmlFree(value);
  979. }
  980. }
  981. }
  982. }
  983. }
  984. - (GDataXMLNode *)attributeForXMLNode:(xmlAttrPtr)theXMLNode {
  985. // search the cached attributes list for the GDataXMLNode with
  986. // the underlying xmlAttrPtr
  987. NSArray *attributes = [self attributes];
  988. for (GDataXMLNode *attr in attributes) {
  989. if (theXMLNode == (xmlAttrPtr) [attr XMLNode]) {
  990. return attr;
  991. }
  992. }
  993. return nil;
  994. }
  995. - (GDataXMLNode *)attributeForName:(NSString *)name {
  996. if (xmlNode_ != NULL) {
  997. xmlAttrPtr attrPtr = xmlHasProp(xmlNode_, GDataGetXMLString(name));
  998. if (attrPtr == NULL) {
  999. // can we guarantee that xmlAttrPtrs always have the ns ptr and never
  1000. // a namespace as part of the actual attribute name?
  1001. // split the name and its prefix, if any
  1002. xmlNsPtr ns = NULL;
  1003. NSString *prefix = [[self class] prefixForName:name];
  1004. if (prefix) {
  1005. // find the namespace for this prefix, and search on its URI to find
  1006. // the xmlNsPtr
  1007. name = [[self class] localNameForName:name];
  1008. ns = xmlSearchNs(xmlNode_->doc, xmlNode_, GDataGetXMLString(prefix));
  1009. }
  1010. const xmlChar* nsURI = ((ns != NULL) ? ns->href : NULL);
  1011. attrPtr = xmlHasNsProp(xmlNode_, GDataGetXMLString(name), nsURI);
  1012. }
  1013. if (attrPtr) {
  1014. GDataXMLNode *attr = [self attributeForXMLNode:attrPtr];
  1015. return attr;
  1016. }
  1017. }
  1018. return nil;
  1019. }
  1020. - (GDataXMLNode *)attributeForLocalName:(NSString *)localName
  1021. URI:(NSString *)attributeURI {
  1022. if (xmlNode_ != NULL) {
  1023. const xmlChar* name = GDataGetXMLString(localName);
  1024. const xmlChar* nsURI = GDataGetXMLString(attributeURI);
  1025. xmlAttrPtr attrPtr = xmlHasNsProp(xmlNode_, name, nsURI);
  1026. if (attrPtr == NULL) {
  1027. // if the attribute is in a tree lacking the proper namespace,
  1028. // the local name may include the full URI as a prefix
  1029. NSString *fakeQName = GDataFakeQNameForURIAndName(attributeURI, localName);
  1030. const xmlChar* xmlFakeQName = GDataGetXMLString(fakeQName);
  1031. attrPtr = xmlHasProp(xmlNode_, xmlFakeQName);
  1032. }
  1033. if (attrPtr) {
  1034. GDataXMLNode *attr = [self attributeForXMLNode:attrPtr];
  1035. return attr;
  1036. }
  1037. }
  1038. return nil;
  1039. }
  1040. - (NSString *)resolvePrefixForNamespaceURI:(NSString *)namespaceURI {
  1041. if (xmlNode_ != NULL) {
  1042. xmlChar* desiredNSHref = GDataGetXMLString(namespaceURI);
  1043. xmlNsPtr foundNS = xmlSearchNsByHref(xmlNode_->doc, xmlNode_, desiredNSHref);
  1044. if (foundNS) {
  1045. // we found the namespace
  1046. if (foundNS->prefix != NULL) {
  1047. NSString *prefix = [self stringFromXMLString:(foundNS->prefix)];
  1048. return prefix;
  1049. } else {
  1050. // empty prefix is default namespace
  1051. return @"";
  1052. }
  1053. }
  1054. }
  1055. return nil;
  1056. }
  1057. #pragma mark Namespace fixup routines
  1058. + (void)deleteNamespacePtr:(xmlNsPtr)namespaceToDelete
  1059. fromXMLNode:(xmlNodePtr)node {
  1060. // utilty routine to remove a namespace pointer from an element's
  1061. // namespace definition list. This is just removing the nsPtr
  1062. // from the singly-linked list, the node's namespace definitions.
  1063. xmlNsPtr currNS = node->nsDef;
  1064. xmlNsPtr prevNS = NULL;
  1065. while (currNS != NULL) {
  1066. xmlNsPtr nextNS = currNS->next;
  1067. if (namespaceToDelete == currNS) {
  1068. // found it; delete it from the head of the node's ns definition list
  1069. // or from the next field of the previous namespace
  1070. if (prevNS != NULL) prevNS->next = nextNS;
  1071. else node->nsDef = nextNS;
  1072. xmlFreeNs(currNS);
  1073. return;
  1074. }
  1075. prevNS = currNS;
  1076. currNS = nextNS;
  1077. }
  1078. }
  1079. + (void)fixQualifiedNamesForNode:(xmlNodePtr)nodeToFix
  1080. graftingToTreeNode:(xmlNodePtr)graftPointNode {
  1081. // Replace prefix-in-name with proper namespace pointers
  1082. //
  1083. // This is an inner routine for fixUpNamespacesForNode:
  1084. //
  1085. // see if this node's name lacks a namespace and is qualified, and if so,
  1086. // see if we can resolve the prefix against the parent
  1087. //
  1088. // The prefix may either be normal, "gd:foo", or a URI
  1089. // "{http://blah.com/}:foo"
  1090. if (nodeToFix->ns == NULL) {
  1091. xmlNsPtr foundNS = NULL;
  1092. xmlChar* prefix = NULL;
  1093. xmlChar* localName = SplitQNameReverse(nodeToFix->name, &prefix);
  1094. if (localName != NULL) {
  1095. if (prefix != NULL) {
  1096. // if the prefix is wrapped by { and } then it's a URI
  1097. int prefixLen = xmlStrlen(prefix);
  1098. if (prefixLen > 2
  1099. && prefix[0] == '{'
  1100. && prefix[prefixLen - 1] == '}') {
  1101. // search for the namespace by URI
  1102. xmlChar* uri = xmlStrsub(prefix, 1, prefixLen - 2);
  1103. if (uri != NULL) {
  1104. foundNS = xmlSearchNsByHref(graftPointNode->doc, graftPointNode, uri);
  1105. xmlFree(uri);
  1106. }
  1107. }
  1108. }
  1109. if (foundNS == NULL) {
  1110. // search for the namespace by prefix, even if the prefix is nil
  1111. // (nil prefix means to search for the default namespace)
  1112. foundNS = xmlSearchNs(graftPointNode->doc, graftPointNode, prefix);
  1113. }
  1114. if (foundNS != NULL) {
  1115. // we found a namespace, so fix the ns pointer and the local name
  1116. xmlSetNs(nodeToFix, foundNS);
  1117. xmlNodeSetName(nodeToFix, localName);
  1118. }
  1119. if (prefix != NULL) {
  1120. xmlFree(prefix);
  1121. prefix = NULL;
  1122. }
  1123. xmlFree(localName);
  1124. }
  1125. }
  1126. }
  1127. + (void)fixDuplicateNamespacesForNode:(xmlNodePtr)nodeToFix
  1128. graftingToTreeNode:(xmlNodePtr)graftPointNode
  1129. namespaceSubstitutionMap:(NSMutableDictionary *)nsMap {
  1130. // Duplicate namespace removal
  1131. //
  1132. // This is an inner routine for fixUpNamespacesForNode:
  1133. //
  1134. // If any of this node's namespaces are already defined at the graft point
  1135. // level, add that namespace to the map of namespace substitutions
  1136. // so it will be replaced in the children below the nodeToFix, and
  1137. // delete the namespace record
  1138. if (nodeToFix->type == XML_ELEMENT_NODE) {
  1139. // step through the namespaces defined on this node
  1140. xmlNsPtr definedNS = nodeToFix->nsDef;
  1141. while (definedNS != NULL) {
  1142. // see if this namespace is already defined higher in the tree,
  1143. // with both the same URI and the same prefix; if so, add a mapping for
  1144. // it
  1145. xmlNsPtr foundNS = xmlSearchNsByHref(graftPointNode->doc, graftPointNode,
  1146. definedNS->href);
  1147. if (foundNS != NULL
  1148. && foundNS != definedNS
  1149. && xmlStrEqual(definedNS->prefix, foundNS->prefix)) {
  1150. // store a mapping from this defined nsPtr to the one found higher
  1151. // in the tree
  1152. [nsMap setObject:[NSValue valueWithPointer:foundNS]
  1153. forKey:[NSValue valueWithPointer:definedNS]];
  1154. // remove this namespace from the ns definition list of this node;
  1155. // all child elements and attributes referencing this namespace
  1156. // now have a dangling pointer and must be updated (that is done later
  1157. // in this method)
  1158. //
  1159. // before we delete this namespace, move our pointer to the
  1160. // next one
  1161. xmlNsPtr nsToDelete = definedNS;
  1162. definedNS = definedNS->next;
  1163. [self deleteNamespacePtr:nsToDelete fromXMLNode:nodeToFix];
  1164. } else {
  1165. // this namespace wasn't a duplicate; move to the next
  1166. definedNS = definedNS->next;
  1167. }
  1168. }
  1169. }
  1170. // if this node's namespace is one we deleted, update it to point
  1171. // to someplace better
  1172. if (nodeToFix->ns != NULL) {
  1173. NSValue *currNS = [NSValue valueWithPointer:nodeToFix->ns];
  1174. NSValue *replacementNS = [nsMap objectForKey:currNS];
  1175. if (replacementNS != nil) {
  1176. xmlNsPtr replaceNSPtr = (xmlNsPtr)[replacementNS pointerValue];
  1177. xmlSetNs(nodeToFix, replaceNSPtr);
  1178. }
  1179. }
  1180. }
  1181. + (void)fixUpNamespacesForNode:(xmlNodePtr)nodeToFix
  1182. graftingToTreeNode:(xmlNodePtr)graftPointNode
  1183. namespaceSubstitutionMap:(NSMutableDictionary *)nsMap {
  1184. // This is the inner routine for fixUpNamespacesForNode:graftingToTreeNode:
  1185. //
  1186. // This routine fixes two issues:
  1187. //
  1188. // Because we can create nodes with qualified names before adding
  1189. // them to the tree that declares the namespace for the prefix,
  1190. // we need to set the node namespaces after adding them to the tree.
  1191. //
  1192. // Because libxml adds namespaces to nodes when it copies them,
  1193. // we want to remove redundant namespaces after adding them to
  1194. // a tree.
  1195. //
  1196. // If only the Mac's libxml had xmlDOMWrapReconcileNamespaces, it could do
  1197. // namespace cleanup for us
  1198. // We only care about fixing names of elements and attributes
  1199. if (nodeToFix->type != XML_ELEMENT_NODE
  1200. && nodeToFix->type != XML_ATTRIBUTE_NODE) return;
  1201. // Do the fixes
  1202. [self fixQualifiedNamesForNode:nodeToFix
  1203. graftingToTreeNode:graftPointNode];
  1204. [self fixDuplicateNamespacesForNode:nodeToFix
  1205. graftingToTreeNode:graftPointNode
  1206. namespaceSubstitutionMap:nsMap];
  1207. if (nodeToFix->type == XML_ELEMENT_NODE) {
  1208. // when fixing element nodes, recurse for each child element and
  1209. // for each attribute
  1210. xmlNodePtr currChild = nodeToFix->children;
  1211. while (currChild != NULL) {
  1212. [self fixUpNamespacesForNode:currChild
  1213. graftingToTreeNode:graftPointNode
  1214. namespaceSubstitutionMap:nsMap];
  1215. currChild = currChild->next;
  1216. }
  1217. xmlAttrPtr currProp = nodeToFix->properties;
  1218. while (currProp != NULL) {
  1219. [self fixUpNamespacesForNode:(xmlNodePtr)currProp
  1220. graftingToTreeNode:graftPointNode
  1221. namespaceSubstitutionMap:nsMap];
  1222. currProp = currProp->next;
  1223. }
  1224. }
  1225. }
  1226. + (void)fixUpNamespacesForNode:(xmlNodePtr)nodeToFix
  1227. graftingToTreeNode:(xmlNodePtr)graftPointNode {
  1228. // allocate the namespace map that will be passed
  1229. // down on recursive calls
  1230. NSMutableDictionary *nsMap = [NSMutableDictionary dictionary];
  1231. [self fixUpNamespacesForNode:nodeToFix
  1232. graftingToTreeNode:graftPointNode
  1233. namespaceSubstitutionMap:nsMap];
  1234. }
  1235. @end
  1236. @interface GDataXMLDocument (PrivateMethods)
  1237. - (void)addStringsCacheToDoc;
  1238. @end
  1239. @implementation GDataXMLDocument
  1240. - (id)initWithXMLString:(NSString *)str options:(unsigned int)mask error:(NSError **)error {
  1241. NSData *data = [str dataUsingEncoding:NSUTF8StringEncoding];
  1242. GDataXMLDocument *doc = [self initWithData:data options:mask error:error];
  1243. return doc;
  1244. }
  1245. - (id)initWithData:(NSData *)data options:(unsigned int)mask error:(NSError **)error {
  1246. self = [super init];
  1247. if (self) {
  1248. const char *baseURL = NULL;
  1249. const char *encoding = NULL;
  1250. // NOTE: We are assuming [data length] fits into an int.
  1251. xmlDoc_ = xmlReadMemory((const char*)[data bytes], (int)[data length], baseURL, encoding,
  1252. kGDataXMLParseOptions); // TODO(grobbins) map option values
  1253. if (xmlDoc_ == NULL) {
  1254. if (error) {
  1255. *error = [NSError errorWithDomain:@"com.google.GDataXML"
  1256. code:-1
  1257. userInfo:nil];
  1258. // TODO(grobbins) use xmlSetGenericErrorFunc to capture error
  1259. }
  1260. [self release];
  1261. return nil;
  1262. } else {
  1263. if (error) *error = NULL;
  1264. [self addStringsCacheToDoc];
  1265. }
  1266. }
  1267. return self;
  1268. }
  1269. - (id)initWithRootElement:(GDataXMLElement *)element {
  1270. self = [super init];
  1271. if (self) {
  1272. xmlDoc_ = xmlNewDoc(NULL);
  1273. (void) xmlDocSetRootElement(xmlDoc_, [element XMLNodeCopy]);
  1274. [self addStringsCacheToDoc];
  1275. }
  1276. return self;
  1277. }
  1278. - (void)addStringsCacheToDoc {
  1279. // utility routine for init methods
  1280. #if DEBUG
  1281. NSCAssert(xmlDoc_ != NULL && xmlDoc_->_private == NULL,
  1282. @"GDataXMLDocument cache creation problem");
  1283. #endif
  1284. // add a strings cache as private data for the document
  1285. //
  1286. // we'll use plain C pointers (xmlChar*) as the keys, and NSStrings
  1287. // as the values
  1288. CFIndex capacity = 0; // no limit
  1289. CFDictionaryKeyCallBacks keyCallBacks = {
  1290. 0, // version
  1291. StringCacheKeyRetainCallBack,
  1292. StringCacheKeyReleaseCallBack,
  1293. StringCacheKeyCopyDescriptionCallBack,
  1294. StringCacheKeyEqualCallBack,
  1295. StringCacheKeyHashCallBack
  1296. };
  1297. CFMutableDictionaryRef dict = CFDictionaryCreateMutable(
  1298. kCFAllocatorDefault, capacity,
  1299. &keyCallBacks, &kCFTypeDictionaryValueCallBacks);
  1300. // we'll use the user-defined _private field for our cache
  1301. xmlDoc_->_private = dict;
  1302. }
  1303. - (NSString *)description {
  1304. return [NSString stringWithFormat:@"%@ %p", [self class], self];
  1305. }
  1306. - (void)dealloc {
  1307. if (xmlDoc_ != NULL) {
  1308. // release the strings cache
  1309. //
  1310. // since it's a CF object, were anyone to use this in a GC environment,
  1311. // this would need to be released in a finalize method, too
  1312. if (xmlDoc_->_private != NULL) {
  1313. CFRelease(xmlDoc_->_private);
  1314. }
  1315. xmlFreeDoc(xmlDoc_);
  1316. }
  1317. [super dealloc];
  1318. }
  1319. #pragma mark -
  1320. - (GDataXMLElement *)rootElement {
  1321. GDataXMLElement *element = nil;
  1322. if (xmlDoc_ != NULL) {
  1323. xmlNodePtr rootNode = xmlDocGetRootElement(xmlDoc_);
  1324. if (rootNode) {
  1325. element = [GDataXMLElement nodeBorrowingXMLNode:rootNode];
  1326. }
  1327. }
  1328. return element;
  1329. }
  1330. - (NSData *)XMLData {
  1331. if (xmlDoc_ != NULL) {
  1332. xmlChar *buffer = NULL;
  1333. int bufferSize = 0;
  1334. xmlDocDumpMemory(xmlDoc_, &buffer, &bufferSize);
  1335. if (buffer) {
  1336. NSData *data = [NSData dataWithBytes:buffer
  1337. length:(NSUInteger)bufferSize];
  1338. xmlFree(buffer);
  1339. return data;
  1340. }
  1341. }
  1342. return nil;
  1343. }
  1344. - (void)setVersion:(NSString *)version {
  1345. if (xmlDoc_ != NULL) {
  1346. if (xmlDoc_->version != NULL) {
  1347. // version is a const char* so we must cast
  1348. xmlFree((char *) xmlDoc_->version);
  1349. xmlDoc_->version = NULL;
  1350. }
  1351. if (version != nil) {
  1352. xmlDoc_->version = xmlStrdup(GDataGetXMLString(version));
  1353. }
  1354. }
  1355. }
  1356. - (void)setCharacterEncoding:(NSString *)encoding {
  1357. if (xmlDoc_ != NULL) {
  1358. if (xmlDoc_->encoding != NULL) {
  1359. // version is a const char* so we must cast
  1360. xmlFree((char *) xmlDoc_->encoding);
  1361. xmlDoc_->encoding = NULL;
  1362. }
  1363. if (encoding != nil) {
  1364. xmlDoc_->encoding = xmlStrdup(GDataGetXMLString(encoding));
  1365. }
  1366. }
  1367. }
  1368. - (NSArray *)nodesForXPath:(NSString *)xpath error:(NSError **)error {
  1369. return [self nodesForXPath:xpath namespaces:nil error:error];
  1370. }
  1371. - (NSArray *)nodesForXPath:(NSString *)xpath
  1372. namespaces:(NSDictionary *)namespaces
  1373. error:(NSError **)error {
  1374. if (xmlDoc_ != NULL) {
  1375. GDataXMLNode *docNode = [GDataXMLElement nodeBorrowingXMLNode:(xmlNodePtr)xmlDoc_];
  1376. NSArray *array = [docNode nodesForXPath:xpath
  1377. namespaces:namespaces
  1378. error:error];
  1379. return array;
  1380. }
  1381. return nil;
  1382. }
  1383. @end
  1384. //
  1385. // Dictionary key callbacks for our C-string to NSString cache dictionary
  1386. //
  1387. static const void *StringCacheKeyRetainCallBack(CFAllocatorRef allocator, const void *str) {
  1388. // copy the key
  1389. xmlChar* key = xmlStrdup(str);
  1390. return key;
  1391. }
  1392. static void StringCacheKeyReleaseCallBack(CFAllocatorRef allocator, const void *str) {
  1393. // free the key
  1394. char *chars = (char *)str;
  1395. xmlFree((char *) chars);
  1396. }
  1397. static CFStringRef StringCacheKeyCopyDescriptionCallBack(const void *str) {
  1398. // make a CFString from the key
  1399. CFStringRef cfStr = CFStringCreateWithCString(kCFAllocatorDefault,
  1400. (const char *)str,
  1401. kCFStringEncodingUTF8);
  1402. return cfStr;
  1403. }
  1404. static Boolean StringCacheKeyEqualCallBack(const void *str1, const void *str2) {
  1405. // compare the key strings
  1406. if (str1 == str2) return true;
  1407. int result = xmlStrcmp(str1, str2);
  1408. return (result == 0);
  1409. }
  1410. static CFHashCode StringCacheKeyHashCallBack(const void *str) {
  1411. // dhb hash, per http://www.cse.yorku.ca/~oz/hash.html
  1412. CFHashCode hash = 5381;
  1413. unsigned int c;
  1414. const unsigned char *chars = (const unsigned char *)str;
  1415. while ((c = *chars++) != 0) {
  1416. hash = ((hash << 5) + hash) + c;
  1417. }
  1418. return hash;
  1419. }