GDataXMLNode.m 51 KB

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