PageRenderTime 60ms CodeModel.GetById 12ms RepoModel.GetById 0ms app.codeStats 1ms

/core/externals/update-engine/externals/gdata-objectivec-client/Source/BaseClasses/GDataObject.m

http://macfuse.googlecode.com/
Objective C | 2822 lines | 1782 code | 591 blank | 449 comment | 382 complexity | 979f3f606e6704b412a550b6c7c0d4e7 MD5 | raw file
Possible License(s): Apache-2.0, BSD-3-Clause, GPL-2.0
  1. /* Copyright (c) 2007 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. //
  16. // GDataObject.m
  17. //
  18. #define GDATAOBJECT_DEFINE_GLOBALS 1
  19. #import "GDataObject.h"
  20. #import "GDataDateTime.h"
  21. // for automatic-determination of feed and entry class types
  22. #import "GDataFeedBase.h"
  23. #import "GDataEntryBase.h"
  24. #import "GDataCategory.h"
  25. static inline NSMutableDictionary *GDataCreateStaticDictionary(void) {
  26. NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
  27. #if !GDATA_IPHONE
  28. Class cls = NSClassFromString(@"NSGarbageCollector");
  29. if (cls) {
  30. id collector = [cls performSelector:@selector(defaultCollector)];
  31. [collector performSelector:@selector(disableCollectorForPointer:)
  32. withObject:dict];
  33. }
  34. #endif
  35. return dict;
  36. }
  37. // in a cache of attribute declarations, this marker indicates that the class
  38. // also declared that it wants child text parsed as the content value or child
  39. // xml held as xml element objects
  40. //
  41. // these start with a space to avoid colliding with any real attribute name
  42. static NSString* const kContentValueDeclarationMarker = @" __content";
  43. static NSString* const kChildXMLDeclarationMarker = @" __childXML";
  44. // Elements may call -addExtensionDeclarationForParentClass:childClass: and
  45. // addAttributeExtensionDeclarationForParentClass: to declare extensions to be
  46. // parsed; the declaration applies in the element and all children of the element.
  47. @interface GDataExtensionDeclaration : NSObject {
  48. Class parentClass_;
  49. Class childClass_;
  50. BOOL isAttribute_;
  51. }
  52. - (id)initWithParentClass:(Class)parentClass childClass:(Class)childClass isAttribute:(BOOL)attrFlag;
  53. - (Class)parentClass;
  54. - (Class)childClass;
  55. - (BOOL)isAttribute;
  56. @end
  57. @interface GDataObject (PrivateMethods)
  58. // array of local attribute names to be automatically parsed and
  59. // generated
  60. - (void)setAttributeDeclarationsCache:(NSDictionary *)decls;
  61. - (NSMutableDictionary *)attributeDeclarationsCache;
  62. // array of attribute declarations for the current class, from the cache
  63. - (void)setAttributeDeclarations:(NSArray *)array;
  64. - (NSMutableArray *)attributeDeclarations;
  65. - (void)parseAttributesForElement:(NSXMLElement *)element;
  66. - (void)addAttributesToElement:(NSXMLElement *)element;
  67. // routines for comparing attributes
  68. - (BOOL)hasAttributesEqualToAttributesOf:(GDataObject *)other;
  69. - (NSArray *)attributesIgnoredForEquality;
  70. // element string content
  71. - (void)parseContentValueForElement:(NSXMLElement *)element;
  72. - (void)addContentValueToElement:(NSXMLElement *)element;
  73. - (BOOL)hasContentValueEqualToContentValueOf:(GDataObject *)other;
  74. // XML values content (kept unparsed)
  75. - (void)keepChildXMLElementsForElement:(NSXMLElement *)element;
  76. - (void)addChildXMLElementsToElement:(NSXMLElement *)element;
  77. - (BOOL)hasChildXMLElementsEqualToChildXMLElementsOf:(GDataObject *)other;
  78. // dictionary of all extensions actually found in the XML element
  79. - (void)setExtensions:(NSDictionary *)extensions;
  80. - (NSDictionary *)extensions;
  81. // cache of arrays of extensions that may be found in this class and in
  82. // subclasses of this class.
  83. - (void)setExtensionDeclarationsCache:(NSDictionary *)decls;
  84. - (NSMutableDictionary *)extensionDeclarationsCache;
  85. - (NSMutableArray *)extensionDeclarationsForParentClass:(Class)parentClass;
  86. - (void)addExtensionDeclarationForParentClass:(Class)parentClass
  87. childClass:(Class)childClass
  88. isAttribute:(BOOL)isAttribute;
  89. - (void)addUnknownChildNodesForElement:(NSXMLElement *)element;
  90. - (void)parseExtensionsForElement:(NSXMLElement *)element;
  91. - (void)handleParsedElement:(NSXMLNode *)element;
  92. - (void)handleParsedElements:(NSArray *)array;
  93. - (NSString *)qualifiedNameForExtensionClass:(Class)theClass;
  94. + (Class)classForCategoryWithScheme:(NSString *)scheme
  95. term:(NSString *)term
  96. fromMap:(NSDictionary *)map;
  97. @end
  98. @implementation GDataObject
  99. // The qualified name map avoids the need to regenerate qualified
  100. // element names (foo:bar) repeatedly
  101. static NSMutableDictionary *gQualifiedNameMap = nil;
  102. + (void)load {
  103. // Initialize gQualifiedNameMap early so we can @synchronize on accesses
  104. // to it
  105. gQualifiedNameMap = GDataCreateStaticDictionary();
  106. }
  107. + (id)object {
  108. return [[[self alloc] init] autorelease];
  109. }
  110. - (id)init {
  111. self = [super init];
  112. if (self) {
  113. // there is no parent
  114. extensionDeclarationsCache_ = [[NSMutableDictionary alloc] init];
  115. attributeDeclarationsCache_ = [[NSMutableDictionary alloc] init];
  116. [self addParseDeclarations];
  117. }
  118. return self;
  119. }
  120. // intended mainly for testing, initWithServiceVersion allows the service
  121. // version to be set prior to declaring extensions; this is useful
  122. // for overriding the default service version for the class when
  123. // manually allocating a copy of the object
  124. - (id)initWithServiceVersion:(NSString *)serviceVersion {
  125. [self setServiceVersion:serviceVersion];
  126. return [self init];
  127. }
  128. // this init routine is only used when passing in a top-level surrogates
  129. // dictionary
  130. - (id)initWithXMLElement:(NSXMLElement *)element
  131. parent:(GDataObject *)parent
  132. serviceVersion:(NSString *)serviceVersion
  133. surrogates:(NSDictionary *)surrogates
  134. shouldIgnoreUnknowns:(BOOL)shouldIgnoreUnknowns {
  135. [self setServiceVersion:serviceVersion];
  136. [self setSurrogates:surrogates];
  137. [self setShouldIgnoreUnknowns:shouldIgnoreUnknowns];
  138. id obj = [self initWithXMLElement:element
  139. parent:parent];
  140. return obj;
  141. }
  142. // subclasses will typically override initWithXMLElement:parent:
  143. // and do their own parsing after this method returns
  144. - (id)initWithXMLElement:(NSXMLElement *)element
  145. parent:(GDataObject *)parent {
  146. self = [super init];
  147. if (self) {
  148. [self setParent:parent];
  149. if (parent != nil) {
  150. // top-level objects (feeds and entries) have nil parents, and
  151. // have their service version set previously in
  152. // initWithXMLElement:parent:serviceVersion:surrogates:; child
  153. // objects have their service version set here
  154. [self setServiceVersion:[parent serviceVersion]];
  155. // feeds may specify that contained entries and their child elements
  156. // should ignore any unparsed XML
  157. [self setShouldIgnoreUnknowns:[parent shouldIgnoreUnknowns]];
  158. // get the parent's declaration caches, and temporarily hang on to them
  159. // in our ivar to avoid the need to get them recursively from the topmost
  160. // parent
  161. //
  162. // We'll release these below, so that only the topmost parent retains
  163. // them. The topmost parent retains them in case some subclass code still
  164. // wants to do parsing after we return.
  165. extensionDeclarationsCache_ = [[parent extensionDeclarationsCache] retain];
  166. GDATA_DEBUG_ASSERT(extensionDeclarationsCache_ != nil, @"missing extn decl");
  167. attributeDeclarationsCache_ = [[parent attributeDeclarationsCache] retain];
  168. GDATA_DEBUG_ASSERT(extensionDeclarationsCache_ != nil, @"missing attr decl");
  169. } else {
  170. // parent is nil, so this is the topmost parent
  171. extensionDeclarationsCache_ = [[NSMutableDictionary alloc] init];
  172. attributeDeclarationsCache_ = [[NSMutableDictionary alloc] init];
  173. }
  174. [self setNamespaces:[[self class] dictionaryForElementNamespaces:element]];
  175. [self addUnknownChildNodesForElement:element];
  176. // if we've not previously cached declarations for this class,
  177. // add the declarations now
  178. Class currClass = [self class];
  179. NSDictionary *prevExtnDecls = [extensionDeclarationsCache_ objectForKey:currClass];
  180. if (prevExtnDecls == nil) {
  181. [self addExtensionDeclarations];
  182. }
  183. NSMutableArray *prevAttrDecls = [attributeDeclarationsCache_ objectForKey:currClass];
  184. if (prevAttrDecls == nil) {
  185. [self addParseDeclarations];
  186. // if any parse declarations are added, attributeDeclarations_ will be set
  187. // to the cached copy of this object's attribute decls
  188. } else {
  189. GDATA_DEBUG_ASSERT(attributeDeclarations_ == nil, @"attrDecls previously set");
  190. attributeDeclarations_ = [prevAttrDecls retain];
  191. }
  192. [self parseExtensionsForElement:element];
  193. [self parseAttributesForElement:element];
  194. [self parseContentValueForElement:element];
  195. [self keepChildXMLElementsForElement:element];
  196. [self setElementName:[element name]];
  197. if (parent != nil) {
  198. // rather than keep a reference to the cache of declarations in the
  199. // parent, set our pointer to nil; if a subclass continues to parse, the
  200. // getter will obtain them by calling into the parent. This lets callers
  201. // free up the extensionDeclarations_ when parsing is done by just
  202. // freeing them in the topmost parent with clearExtensionDeclarationsCache
  203. [extensionDeclarationsCache_ release];
  204. extensionDeclarationsCache_ = nil;
  205. [attributeDeclarationsCache_ release];
  206. attributeDeclarationsCache_ = nil;
  207. }
  208. #if GDATA_USES_LIBXML
  209. if (!shouldIgnoreUnknowns_) {
  210. // retain the element so that pointers to internal nodes remain valid
  211. [self setProperty:element forKey:kGDataXMLElementPropertyKey];
  212. }
  213. #endif
  214. }
  215. return self;
  216. }
  217. - (BOOL)isEqual:(GDataObject *)other {
  218. if (self == other) return YES;
  219. if (![other isKindOfClass:[self class]]) return NO;
  220. // We used to compare the local names of the objects with
  221. // NSXMLNode's localNameForName: on each object's elementName, but that
  222. // prevents us from comparing the contents of a manually-constructed object
  223. // (which lacks a specific local name) with one found in an actual XML feed.
  224. #if GDATA_USES_LIBXML
  225. // libxml adds namespaces when copying elements, so we can't rely
  226. // on those when comparing nodes
  227. return AreEqualOrBothNil([self extensions], [other extensions])
  228. && [self hasAttributesEqualToAttributesOf:other]
  229. && [self hasContentValueEqualToContentValueOf:other]
  230. && [self hasChildXMLElementsEqualToChildXMLElementsOf:other];
  231. #else
  232. return AreEqualOrBothNil([self extensions], [other extensions])
  233. && [self hasAttributesEqualToAttributesOf:other]
  234. && [self hasContentValueEqualToContentValueOf:other]
  235. && [self hasChildXMLElementsEqualToChildXMLElementsOf:other]
  236. && AreEqualOrBothNil([self namespaces], [other namespaces]);
  237. #endif
  238. // What we're not comparing here:
  239. // parent object pointers
  240. // extension declarations
  241. // unknown attributes & children
  242. // local element names
  243. // service version
  244. // userData
  245. }
  246. // By definition, for two objects to potentially be considered equal,
  247. // they must have the same hash value. The hash is mostly ignored,
  248. // but removeObjectsInArray: in Leopard does seem to check the hash,
  249. // and NSObject's default hash method just returns the instance pointer.
  250. // We'll define hash here for all of our GDataObjects.
  251. - (NSUInteger)hash {
  252. return (NSUInteger) (void *) [GDataObject class];
  253. }
  254. - (id)copyWithZone:(NSZone *)zone {
  255. GDataObject* newObject = [[[self class] allocWithZone:zone] init];
  256. [newObject setElementName:[self elementName]];
  257. [newObject setParent:nil];
  258. [newObject setServiceVersion:[self serviceVersion]];
  259. NSDictionary *namespaces =
  260. [GDataUtilities mutableDictionaryWithCopiesOfObjectsInDictionary:[self namespaces]];
  261. [newObject setNamespaces:namespaces];
  262. NSDictionary *extensions =
  263. [GDataUtilities mutableDictionaryWithCopiesOfArraysInDictionary:[self extensions]];
  264. [newObject setExtensions:extensions];
  265. NSDictionary *attributes =
  266. [GDataUtilities mutableDictionaryWithCopiesOfObjectsInDictionary:[self attributes]];
  267. [newObject setAttributes:attributes];
  268. [newObject setAttributeDeclarations:[self attributeDeclarations]];
  269. // we copy the attribute declarations, which are retained by this object,
  270. // but we do not copy not the caches of extension or attribute declarations,
  271. // as those will be invalid once the top parent is released
  272. // a marker in the attributes cache indicates the content value and
  273. // and child XML declaration settings
  274. if ([self hasDeclaredContentValue]) {
  275. [newObject setContentStringValue:[self contentStringValue]];
  276. }
  277. if ([self hasDeclaredChildXMLElements]) {
  278. NSArray *childElements = [self childXMLElements];
  279. NSArray *arr = [GDataUtilities arrayWithCopiesOfObjectsInArray:childElements];
  280. [newObject setChildXMLElements:arr];
  281. }
  282. BOOL shouldIgnoreUnknowns = [self shouldIgnoreUnknowns];
  283. [newObject setShouldIgnoreUnknowns:shouldIgnoreUnknowns];
  284. if (!shouldIgnoreUnknowns) {
  285. NSArray *unknownChildren =
  286. [GDataUtilities mutableArrayWithCopiesOfObjectsInArray:[self unknownChildren]];
  287. [newObject setUnknownChildren:unknownChildren];
  288. NSArray *unknownAttributes =
  289. [GDataUtilities mutableArrayWithCopiesOfObjectsInArray:[self unknownAttributes]];
  290. [newObject setUnknownAttributes:unknownAttributes];
  291. }
  292. return newObject;
  293. // What we're not copying:
  294. // parent object pointer
  295. // surrogates
  296. // userData
  297. // userProperties
  298. }
  299. - (void)dealloc {
  300. [elementName_ release];
  301. [namespaces_ release];
  302. [extensionDeclarationsCache_ release];
  303. [attributeDeclarationsCache_ release];
  304. [attributeDeclarations_ release];
  305. [extensions_ release];
  306. [attributes_ release];
  307. [contentValue_ release];
  308. [childXMLElements_ release];
  309. [unknownChildren_ release];
  310. [unknownAttributes_ release];
  311. [surrogates_ release];
  312. [serviceVersion_ release];
  313. [coreProtocolVersion_ release];
  314. [userData_ release];
  315. [userProperties_ release];
  316. [super dealloc];
  317. }
  318. // XMLElement must be implemented by subclasses
  319. - (NSXMLElement *)XMLElement {
  320. // subclass should override if they have custom elements or attributes
  321. NSXMLElement *element = [self XMLElementWithExtensionsAndDefaultName:nil];
  322. return element;
  323. }
  324. - (NSXMLDocument *)XMLDocument {
  325. NSXMLElement *element = [self XMLElement];
  326. NSXMLDocument *doc = [[[NSXMLDocument alloc] initWithRootElement:(id)element] autorelease];
  327. [doc setVersion:@"1.0"];
  328. [doc setCharacterEncoding:@"UTF-8"];
  329. return doc;
  330. }
  331. - (BOOL)generateContentInputStream:(NSInputStream **)outInputStream
  332. length:(unsigned long long *)outLength
  333. headers:(NSDictionary **)outHeaders {
  334. // subclasses may return a data stream representing this object
  335. // for uploading
  336. return NO;
  337. }
  338. - (NSString *)uploadMIMEType {
  339. // subclasses may return the type of data to be uploaded
  340. return nil;
  341. }
  342. - (NSData *)uploadData {
  343. // subclasses may return data to be uploaded along with the object
  344. return nil;
  345. }
  346. - (NSFileHandle *)uploadFileHandle {
  347. // subclasses may return a file handle to be uploaded along with the object
  348. return nil;
  349. }
  350. - (NSURL *)uploadLocationURL {
  351. // subclasses may return a resumable upload location URL for restarting
  352. // uploads
  353. return nil;
  354. }
  355. - (BOOL)shouldUploadDataOnly {
  356. return NO;
  357. }
  358. #pragma mark -
  359. - (void)setElementName:(NSString *)name {
  360. [elementName_ release];
  361. elementName_ = [name copy];
  362. }
  363. - (NSString *)elementName {
  364. return elementName_;
  365. }
  366. - (void)setNamespaces:(NSDictionary *)dict {
  367. [namespaces_ release];
  368. namespaces_ = [dict mutableCopy];
  369. }
  370. - (void)addNamespaces:(NSDictionary *)dict {
  371. if (namespaces_ == nil) {
  372. namespaces_ = [[NSMutableDictionary alloc] init];
  373. }
  374. [namespaces_ addEntriesFromDictionary:dict];
  375. }
  376. - (NSDictionary *)namespaces {
  377. return namespaces_;
  378. }
  379. - (NSDictionary *)completeNamespaces {
  380. // return a dictionary containing all namespaces
  381. // in this object and its parent objects
  382. NSDictionary *parentNamespaces = [parent_ completeNamespaces];
  383. NSDictionary *ownNamespaces = namespaces_;
  384. if (ownNamespaces == nil) return parentNamespaces;
  385. if (parentNamespaces == nil) return ownNamespaces;
  386. // combine them, replacing parent-defined prefixes with own ones
  387. NSMutableDictionary *mutableDict;
  388. mutableDict = [NSMutableDictionary dictionaryWithDictionary:parentNamespaces];
  389. [mutableDict addEntriesFromDictionary:ownNamespaces];
  390. return mutableDict;
  391. }
  392. - (void)pruneInheritedNamespaces {
  393. if (parent_ == nil || [namespaces_ count] == 0) return;
  394. // if a prefix is explicitly defined the same for the parent as it is locally,
  395. // remove it, since we can rely on the parent's definition
  396. NSMutableDictionary *prunedNamespaces
  397. = [NSMutableDictionary dictionaryWithDictionary:namespaces_];
  398. NSDictionary *parentNamespaces = [parent_ completeNamespaces];
  399. for (NSString *prefix in namespaces_) {
  400. NSString *ownURI = [namespaces_ objectForKey:prefix];
  401. NSString *parentURI = [parentNamespaces objectForKey:prefix];
  402. if (AreEqualOrBothNil(ownURI, parentURI)) {
  403. [prunedNamespaces removeObjectForKey:prefix];
  404. }
  405. }
  406. [self setNamespaces:prunedNamespaces];
  407. }
  408. - (void)setParent:(GDataObject *)obj {
  409. parent_ = obj; // parent_ is a weak (not retained) reference
  410. }
  411. - (GDataObject *)parent {
  412. return parent_;
  413. }
  414. - (void)setAttributeDeclarationsCache:(NSDictionary *)cache {
  415. [attributeDeclarationsCache_ autorelease];
  416. attributeDeclarationsCache_ = [cache mutableCopy];
  417. }
  418. - (NSMutableDictionary *)attributeDeclarationsCache {
  419. // warning: rely on this only during parsing; it will not be safe if the
  420. // top parent is no longer allocated
  421. if (attributeDeclarationsCache_) {
  422. return attributeDeclarationsCache_;
  423. }
  424. return [[self parent] attributeDeclarationsCache];
  425. }
  426. - (void)setAttributeDeclarations:(NSArray *)array {
  427. [attributeDeclarations_ autorelease];
  428. attributeDeclarations_ = [array mutableCopy];
  429. }
  430. - (NSMutableArray *)attributeDeclarations {
  431. return attributeDeclarations_;
  432. }
  433. - (void)setAttributes:(NSDictionary *)dict {
  434. [attributes_ autorelease];
  435. attributes_ = [dict mutableCopy];
  436. }
  437. - (NSDictionary *)attributes {
  438. return attributes_;
  439. }
  440. - (void)setExtensions:(NSDictionary *)extensions {
  441. [extensions_ autorelease];
  442. extensions_ = [extensions mutableCopy];
  443. }
  444. - (NSDictionary *)extensions {
  445. return extensions_;
  446. }
  447. - (void)setExtensionDeclarationsCache:(NSDictionary *)decls {
  448. [extensionDeclarationsCache_ autorelease];
  449. extensionDeclarationsCache_ = [decls mutableCopy];
  450. }
  451. - (NSMutableDictionary *)extensionDeclarationsCache {
  452. // warning: rely on this only during parsing; it will not be safe if the
  453. // top parent is no longer allocated
  454. if (extensionDeclarationsCache_) {
  455. return extensionDeclarationsCache_;
  456. }
  457. return [[self parent] extensionDeclarationsCache];
  458. }
  459. - (void)clearExtensionDeclarationsCache {
  460. // allows external classes to free up the declarations
  461. [self setExtensionDeclarationsCache:nil];
  462. }
  463. - (void)setUnknownChildren:(NSArray *)arr {
  464. [unknownChildren_ autorelease];
  465. unknownChildren_ = [arr mutableCopy];
  466. }
  467. - (NSArray *)unknownChildren {
  468. return unknownChildren_;
  469. }
  470. - (void)setUnknownAttributes:(NSArray *)arr {
  471. [unknownAttributes_ autorelease];
  472. unknownAttributes_ = [arr mutableCopy];
  473. }
  474. - (NSArray *)unknownAttributes {
  475. return unknownAttributes_;
  476. }
  477. - (void)setShouldIgnoreUnknowns:(BOOL)flag {
  478. shouldIgnoreUnknowns_ = flag;
  479. }
  480. - (BOOL)shouldIgnoreUnknowns {
  481. return shouldIgnoreUnknowns_;
  482. }
  483. - (void)setSurrogates:(NSDictionary *)surrogates {
  484. [surrogates_ autorelease];
  485. surrogates_ = [surrogates retain];
  486. }
  487. - (NSDictionary *)surrogates {
  488. return surrogates_;
  489. }
  490. + (NSString *)defaultServiceVersion {
  491. return nil;
  492. }
  493. - (void)setServiceVersion:(NSString *)str {
  494. if (!AreEqualOrBothNil(str, serviceVersion_)) {
  495. // reset the core protocol version, since it's based on the service version
  496. [self setCoreProtocolVersion:nil];
  497. [serviceVersion_ autorelease];
  498. serviceVersion_ = [str copy];
  499. }
  500. }
  501. - (NSString *)serviceVersion {
  502. if (serviceVersion_ != nil) {
  503. return serviceVersion_;
  504. }
  505. NSString *str = [[self class] defaultServiceVersion];
  506. return str;
  507. }
  508. - (BOOL)isServiceVersionAtLeast:(NSString *)otherVersion {
  509. NSString *serviceVersion = [self serviceVersion];
  510. NSComparisonResult result = [GDataUtilities compareVersion:serviceVersion
  511. toVersion:otherVersion];
  512. return (result != NSOrderedAscending);
  513. }
  514. - (BOOL)isServiceVersionAtMost:(NSString *)otherVersion {
  515. NSString *serviceVersion = [self serviceVersion];
  516. NSComparisonResult result = [GDataUtilities compareVersion:serviceVersion
  517. toVersion:otherVersion];
  518. return (result != NSOrderedDescending);
  519. }
  520. - (void)setCoreProtocolVersion:(NSString *)str {
  521. [coreProtocolVersion_ autorelease];
  522. coreProtocolVersion_ = [str copy];
  523. }
  524. - (NSString *)coreProtocolVersion {
  525. if (coreProtocolVersion_ != nil) {
  526. return coreProtocolVersion_;
  527. }
  528. NSString *serviceVersion = [self serviceVersion];
  529. NSString *coreVersion = [[self class] coreProtocolVersionForServiceVersion:serviceVersion];
  530. [self setCoreProtocolVersion:coreVersion];
  531. return coreVersion;
  532. }
  533. - (BOOL)isCoreProtocolVersion1 {
  534. NSString *coreVersion = [self coreProtocolVersion];
  535. // technically the version number is <integer>.<integer> rather than a float,
  536. // but intValue is a simple way to test just the major portion
  537. int majorVer = [coreVersion intValue];
  538. return (majorVer <= 1);
  539. }
  540. + (NSString *)coreProtocolVersionForServiceVersion:(NSString *)str {
  541. // subclasses may override this when their service versions
  542. // do not match the core protocol version
  543. return str;
  544. }
  545. #pragma mark userData and properties
  546. - (void)setUserData:(id)userData {
  547. [userData_ autorelease];
  548. userData_ = [userData retain];
  549. }
  550. - (id)userData {
  551. // be sure the returned pointer has the life of the autorelease pool,
  552. // in case self is released immediately
  553. return [[userData_ retain] autorelease];
  554. }
  555. - (void)setProperties:(NSDictionary *)dict {
  556. [userProperties_ autorelease];
  557. userProperties_ = [dict mutableCopy];
  558. }
  559. - (NSDictionary *)properties {
  560. // be sure the returned pointer has the life of the autorelease pool,
  561. // in case self is released immediately
  562. return [[userProperties_ retain] autorelease];
  563. }
  564. - (void)setProperty:(id)obj forKey:(NSString *)key {
  565. if (obj == nil) {
  566. // user passed in nil, so delete the property
  567. [userProperties_ removeObjectForKey:key];
  568. } else {
  569. // be sure the property dictionary exists
  570. if (userProperties_ == nil) {
  571. [self setProperties:[NSDictionary dictionary]];
  572. }
  573. [userProperties_ setObject:obj forKey:key];
  574. }
  575. }
  576. - (id)propertyForKey:(NSString *)key {
  577. id obj = [userProperties_ objectForKey:key];
  578. // be sure the returned pointer has the life of the autorelease pool,
  579. // in case self is released immediately
  580. return [[obj retain] autorelease];
  581. }
  582. #pragma mark XML generation helpers
  583. - (void)addNamespacesToElement:(NSXMLElement *)element {
  584. // we keep namespaces in a dictionary with prefixes
  585. // as keys. We'll step through our namespaces and convert them
  586. // to NSXML-stype namespaces.
  587. for (NSString *prefix in namespaces_) {
  588. NSString *uri = [namespaces_ objectForKey:prefix];
  589. // no per-version namespace transforms are currently needed
  590. // uri = [self updatedVersionedNamespaceURIForPrefix:prefix
  591. // URI:uri];
  592. [element addNamespace:[NSXMLElement namespaceWithName:prefix
  593. stringValue:uri]];
  594. }
  595. }
  596. - (void)addExtensionsToElement:(NSXMLElement *)element {
  597. // extensions are in a dictionary of arrays, keyed by the class
  598. // of each kind of element
  599. // note: this adds actual extensions, not declarations
  600. NSDictionary *extensions = [self extensions];
  601. // step through each extension, by class, and add those
  602. // objects to the XML element
  603. for (Class oneClass in extensions) {
  604. id objectOrArray = [extensions_ objectForKey:oneClass];
  605. if ([objectOrArray isKindOfClass:[NSArray class]]) {
  606. [self addToElement:element XMLElementsForArray:objectOrArray];
  607. } else {
  608. [self addToElement:element XMLElementForObject:objectOrArray];
  609. }
  610. }
  611. }
  612. - (void)addUnknownChildNodesToElement:(NSXMLElement *)element {
  613. // we'll add every element and attribute as "unknown", then remove them
  614. // from this list as we parse them to create the GData object. Anything
  615. // left remaining in this list is considered unknown.
  616. if (shouldIgnoreUnknowns_) return;
  617. // we have to copy the children so they don't point at the previous parent
  618. // nodes
  619. for (NSXMLNode *child in unknownChildren_) {
  620. [element addChild:[[child copy] autorelease]];
  621. }
  622. for (NSXMLNode *attr in unknownAttributes_) {
  623. GDATA_DEBUG_ASSERT([element attributeForName:[attr name]] == nil,
  624. @"adding duplicate of attribute %@ (perhaps an object parsed with"
  625. "attributeForName: instead of attributeForName:fromElement:)",
  626. attr);
  627. [element addAttribute:[[attr copy] autorelease]];
  628. }
  629. }
  630. // this method creates a basic XML element from this GData object.
  631. //
  632. // this is called by the XMLElement method of subclasses; they will add their
  633. // own attributes and children to the element returned by this method
  634. //
  635. // extensions may pass nil for defaultName to use the name specified in their
  636. // extensionElementLocalName and extensionElementPrefix
  637. - (NSXMLElement *)XMLElementWithExtensionsAndDefaultName:(NSString *)defaultName {
  638. #if 0
  639. // code sometimes useful for finding unparsed xml; this can be turned on
  640. // during testing
  641. if ([unknownAttributes_ count]) {
  642. NSLog(@"%@ %p: unknown attributes %@\n%@\n", [self class], self, unknownAttributes_, self);
  643. }
  644. if ([unknownChildren_ count]) {
  645. NSLog(@"%@ %p: unknown children %@\n%@\n", [self class], self, unknownChildren_, self);
  646. }
  647. #endif
  648. // use the name from the XML
  649. NSString *elementName = [self elementName];
  650. if (!elementName) {
  651. // if no name from the XML, use the name our class's XML element
  652. // routine supplied as a default
  653. if (defaultName) {
  654. elementName = defaultName;
  655. } else {
  656. // if no default name from the class, and this class is an extension,
  657. // use the extension's default element name
  658. if ([[self class] conformsToProtocol:@protocol(GDataExtension)]) {
  659. elementName = [self qualifiedNameForExtensionClass:[self class]];
  660. } else {
  661. // if not an extension, just use the class name
  662. elementName = NSStringFromClass([self class]);
  663. GDATA_DEBUG_LOG(@"GDataObject generating XML element with unknown name for class %@",
  664. elementName);
  665. }
  666. }
  667. }
  668. NSXMLElement *element = [NSXMLNode elementWithName:elementName];
  669. [self addNamespacesToElement:element];
  670. [self addAttributesToElement:element];
  671. [self addContentValueToElement:element];
  672. [self addChildXMLElementsToElement:element];
  673. [self addExtensionsToElement:element];
  674. [self addUnknownChildNodesToElement:element];
  675. return element;
  676. }
  677. - (NSXMLNode *)addToElement:(NSXMLElement *)element
  678. attributeValueIfNonNil:(NSString *)val
  679. withName:(NSString *)name {
  680. if (val) {
  681. NSString *filtered = [GDataUtilities stringWithControlsFilteredForString:val];
  682. NSXMLNode* attr = [NSXMLNode attributeWithName:name stringValue:filtered];
  683. [element addAttribute:attr];
  684. return attr;
  685. }
  686. return nil;
  687. }
  688. - (NSXMLNode *)addToElement:(NSXMLElement *)element
  689. attributeValueIfNonNil:(NSString *)val
  690. withQualifiedName:(NSString *)qName
  691. URI:(NSString *)attributeURI {
  692. if (attributeURI == nil) {
  693. return [self addToElement:element
  694. attributeValueIfNonNil:val
  695. withName:qName];
  696. }
  697. if (val) {
  698. NSString *filtered = [GDataUtilities stringWithControlsFilteredForString:val];
  699. NSXMLNode *attr = [NSXMLNode attributeWithName:qName
  700. URI:attributeURI
  701. stringValue:filtered];
  702. if (attr != nil) {
  703. [element addAttribute:attr];
  704. return attr;
  705. }
  706. }
  707. return nil;
  708. }
  709. - (NSXMLNode *)addToElement:(NSXMLElement *)element
  710. attributeValueWithInteger:(NSInteger)val
  711. withName:(NSString *)name {
  712. NSString* str = [NSString stringWithFormat:@"%ld", (long)val];
  713. NSXMLNode* attr = [NSXMLNode attributeWithName:name stringValue:str];
  714. [element addAttribute:attr];
  715. return attr;
  716. }
  717. // adding a child to an XML element
  718. - (NSXMLNode *)addToElement:(NSXMLElement *)element
  719. childWithStringValueIfNonEmpty:(NSString *)str
  720. withName:(NSString *)name {
  721. if ([str length] > 0) {
  722. NSXMLNode *child = [NSXMLElement elementWithName:name stringValue:str];
  723. [element addChild:child];
  724. return child;
  725. }
  726. return nil;
  727. }
  728. // call the object's XMLElement method, and add the result as a new XML child
  729. // element
  730. - (void)addToElement:(NSXMLElement *)element
  731. XMLElementForObject:(id)object {
  732. if ([object isKindOfClass:[GDataAttribute class]]) {
  733. // attribute extensions are not GDataObjects and don't implement
  734. // XMLElement; we just get the attribute value from them
  735. NSString *str = [object stringValue];
  736. NSString *qName = [self qualifiedNameForExtensionClass:[object class]];
  737. NSString *theURI = [[object class] extensionElementURI];
  738. [self addToElement:element
  739. attributeValueIfNonNil:str
  740. withQualifiedName:qName
  741. URI:theURI];
  742. } else {
  743. // element extension
  744. NSXMLElement *child = [object XMLElement];
  745. if (child) {
  746. [element addChild:child];
  747. }
  748. }
  749. }
  750. // call the XMLElement method for each object in the array
  751. - (void)addToElement:(NSXMLElement *)element
  752. XMLElementsForArray:(NSArray *)arrayOfGDataObjects {
  753. for(id item in arrayOfGDataObjects) {
  754. [self addToElement:element XMLElementForObject:item];
  755. }
  756. }
  757. #pragma mark description method helpers
  758. #if !GDATA_SIMPLE_DESCRIPTIONS
  759. // if the description label begins with version<= or version>= then do a service
  760. // version check
  761. //
  762. // returns the label with any version prefix removed, or returns nil if the
  763. // description fails the version check and should not be evaluated
  764. - (NSString *)labelAdjustedForVersion:(NSString *)origLabel {
  765. BOOL checkMinVersion = NO;
  766. BOOL checkMaxVersion = NO;
  767. NSString *prefix = nil;
  768. static NSString *const kMinVersionPrefix = @"version>=";
  769. static NSString *const kMaxVersionPrefix = @"version<=";
  770. if ([origLabel hasPrefix:kMinVersionPrefix]) {
  771. checkMinVersion = YES;
  772. prefix = kMinVersionPrefix;
  773. } else if ([origLabel hasPrefix:kMaxVersionPrefix]) {
  774. checkMaxVersion = YES;
  775. prefix = kMaxVersionPrefix;
  776. }
  777. if (!checkMaxVersion && !checkMinVersion) return origLabel;
  778. // there is a version prefix; scan and test the version string,
  779. // and if the test succeeds, return the label without the prefix
  780. NSString *newLabel = origLabel;
  781. NSString *versionStr = nil;
  782. NSScanner *scanner = [NSScanner scannerWithString:origLabel];
  783. if ([scanner scanString:prefix intoString:NULL]
  784. && [scanner scanUpToString:@":" intoString:&versionStr]
  785. && [scanner scanString:@":" intoString:NULL]
  786. && [scanner scanUpToString:@"\n" intoString:&newLabel]) {
  787. if ((checkMinVersion && ![self isServiceVersionAtLeast:versionStr])
  788. || (checkMaxVersion && ![self isServiceVersionAtMost:versionStr])) {
  789. // version test failed
  790. return nil;
  791. }
  792. }
  793. return newLabel;
  794. }
  795. #endif
  796. - (void)addDescriptionRecords:(GDataDescriptionRecord *)descRecordList
  797. toItems:(NSMutableArray *)items {
  798. #if !GDATA_SIMPLE_DESCRIPTIONS
  799. // the final descRecord in the list should be { nil, nil, 0 }
  800. for (NSUInteger idx = 0; descRecordList[idx].label != nil; idx++) {
  801. GDataDescRecTypes reportType = descRecordList[idx].reportType;
  802. NSString *label = descRecordList[idx].label;
  803. NSString *keyPath = descRecordList[idx].keyPath;
  804. label = [self labelAdjustedForVersion:label];
  805. if (label == nil) continue;
  806. id value;
  807. NSString *str;
  808. if (reportType == kGDataDescValueIsKeyPath) {
  809. value = keyPath;
  810. } else {
  811. value = [self valueForKeyPath:keyPath];
  812. }
  813. switch (reportType) {
  814. case kGDataDescValueLabeled:
  815. case kGDataDescValueIsKeyPath:
  816. [self addToArray:items objectDescriptionIfNonNil:value withName:label];
  817. break;
  818. case kGDataDescLabelIfNonNil:
  819. if (value != nil) [items addObject:label];
  820. break;
  821. case kGDataDescArrayCount:
  822. if ([(NSArray *)value count] > 0) {
  823. str = [NSString stringWithFormat:@"%lu", (unsigned long) [(NSArray *)value count]];
  824. [self addToArray:items objectDescriptionIfNonNil:str withName:label];
  825. }
  826. break;
  827. case kGDataDescArrayDescs:
  828. if ([(NSArray *)value count] > 0) {
  829. [self addToArray:items objectDescriptionIfNonNil:value withName:label];
  830. }
  831. break;
  832. case kGDataDescBooleanLabeled:
  833. // display the label with YES or NO
  834. str = ([value boolValue] ? @"YES" : @"NO");
  835. [self addToArray:items objectDescriptionIfNonNil:str withName:label];
  836. break;
  837. case kGDataDescBooleanPresent:
  838. // display the label:YES only if present
  839. if ([value boolValue]) {
  840. [self addToArray:items objectDescriptionIfNonNil:@"YES" withName:label];
  841. }
  842. break;
  843. case kGDataDescNonZeroLength:
  844. // display the length if non-zero
  845. if ([(NSData *)value length] > 0) {
  846. str = [NSString stringWithFormat:@"#%lu",
  847. (unsigned long) [(NSData *)value length]];
  848. [self addToArray:items objectDescriptionIfNonNil:str withName:label];
  849. }
  850. break;
  851. }
  852. }
  853. #endif
  854. }
  855. - (void)addToArray:(NSMutableArray *)stringItems
  856. objectDescriptionIfNonNil:(id)obj
  857. withName:(NSString *)name {
  858. if (obj) {
  859. if (name) {
  860. [stringItems addObject:[NSString stringWithFormat:@"%@:%@", name, obj]];
  861. } else {
  862. [stringItems addObject:[obj description]];
  863. }
  864. }
  865. }
  866. - (void)addAttributeDescriptionsToArray:(NSMutableArray *)stringItems {
  867. // add attribute descriptions in the order the attributes were declared
  868. NSArray *attributeDeclarations = [self attributeDeclarations];
  869. for (NSString *name in attributeDeclarations) {
  870. NSString *value = [attributes_ valueForKey:name];
  871. [self addToArray:stringItems objectDescriptionIfNonNil:value withName:name];
  872. }
  873. }
  874. - (void)addContentDescriptionToArray:(NSMutableArray *)stringItems
  875. withName:(NSString *)name {
  876. if ([self hasDeclaredContentValue]) {
  877. NSString *value = [self contentStringValue];
  878. [self addToArray:stringItems objectDescriptionIfNonNil:value withName:name];
  879. }
  880. }
  881. - (void)addChildXMLElementsDescriptionToArray:(NSMutableArray *)stringItems {
  882. if ([self hasDeclaredChildXMLElements]) {
  883. NSArray *childXMLElements = [self childXMLElements];
  884. if ([childXMLElements count] > 0) {
  885. NSArray *xmlStrings = [childXMLElements valueForKey:@"XMLString"];
  886. NSString *combinedStr = [xmlStrings componentsJoinedByString:@""];
  887. [self addToArray:stringItems objectDescriptionIfNonNil:combinedStr withName:@"XML"];
  888. }
  889. }
  890. }
  891. - (NSMutableArray *)itemsForDescription {
  892. NSMutableArray *items = [NSMutableArray array];
  893. [self addAttributeDescriptionsToArray:items];
  894. [self addContentDescriptionToArray:items withName:@"content"];
  895. #if GDATA_SIMPLE_DESCRIPTIONS
  896. // with GDATA_SIMPLE_DESCRIPTIONS set, subclasses aren't adding their
  897. // own description items for extensions, so we'll just list the extension
  898. // elements that are present, by their qualified xml names
  899. //
  900. // The description string will look like
  901. // {extensions:(gCal:color,link(3),gd:etag,id,updated)}
  902. NSMutableArray *extnsItems = [NSMutableArray array];
  903. for (Class extClass in extensions_) {
  904. // add the qualified XML name for each extension, followed by (n) when
  905. // there is more than one instance
  906. NSString *qname = [self qualifiedNameForExtensionClass:extClass];
  907. // there's one instance of this extension, unless the value is an array
  908. NSUInteger numberOfInstances = 1;
  909. id extnObj = [extensions_ objectForKey:extClass];
  910. if ([extnObj isKindOfClass:[NSArray class]]) {
  911. numberOfInstances = [extnObj count];
  912. }
  913. if (numberOfInstances == 1) {
  914. [extnsItems addObject:qname];
  915. } else {
  916. // append number of occurrences to the xml name
  917. NSString *str = [NSString stringWithFormat:@"%@(%lu)", qname,
  918. (unsigned long) numberOfInstances];
  919. [extnsItems addObject:str];
  920. }
  921. }
  922. if ([extnsItems count] > 0) {
  923. // sort for predictable ordering in unit tests
  924. NSArray *sortedItems = [extnsItems sortedArrayUsingSelector:@selector(caseInsensitiveCompare:)];
  925. NSString *extnsStr = [NSString stringWithFormat:@"extensions:(%@)",
  926. [sortedItems componentsJoinedByString:@","]];
  927. [items addObject:extnsStr];
  928. }
  929. #endif
  930. [self addChildXMLElementsDescriptionToArray:items];
  931. return items;
  932. }
  933. - (NSString *)descriptionWithItems:(NSArray *)items {
  934. NSString *str;
  935. if ([items count] > 0) {
  936. str = [NSString stringWithFormat:@"%@ %p: {%@}",
  937. [self class], self, [items componentsJoinedByString:@" "]];
  938. } else {
  939. str = [NSString stringWithFormat:@"%@ %p", [self class], self];
  940. }
  941. return str;
  942. }
  943. - (NSString *)description {
  944. NSMutableArray *items = [self itemsForDescription];
  945. #if !GDATA_SIMPLE_DESCRIPTIONS
  946. // add names of unknown children and attributes to the descriptions
  947. if ([unknownChildren_ count] > 0) {
  948. // remove repeats and put the element names in < > so they are more
  949. // readable
  950. NSArray *names = [unknownChildren_ valueForKey:@"name"];
  951. NSSet *namesSet = [NSSet setWithArray:names];
  952. NSMutableArray *fmtNames = [NSMutableArray arrayWithCapacity:[namesSet count]];
  953. for (NSString *name in namesSet) {
  954. NSString *fmtName = [NSString stringWithFormat:@"<%@>", name];
  955. [fmtNames addObject:fmtName];
  956. }
  957. // sort the names so the output is deterministic despite the set/array
  958. // conversion
  959. NSArray *sortedNames = [fmtNames sortedArrayUsingSelector:@selector(compare:)];
  960. NSString *desc = [sortedNames componentsJoinedByString:@","];
  961. [self addToArray:items objectDescriptionIfNonNil:desc withName:@"unparsed"];
  962. }
  963. if ([unknownAttributes_ count] > 0) {
  964. NSArray *names = [unknownAttributes_ valueForKey:@"name"];
  965. NSString *desc = [names componentsJoinedByString:@","];
  966. [self addToArray:items objectDescriptionIfNonNil:desc withName:@"unparsedAttr"];
  967. }
  968. #endif
  969. NSString *str = [self descriptionWithItems:items];
  970. return str;
  971. }
  972. #pragma mark XML parsing helpers
  973. + (NSDictionary *)dictionaryForElementNamespaces:(NSXMLElement *)element {
  974. NSMutableDictionary *dict = nil;
  975. // for each namespace node, add a dictionary entry with the namespace
  976. // name (prefix) as key and the URI as value
  977. //
  978. // note: the prefix may be an empty string
  979. NSArray *namespaceNodes = [element namespaces];
  980. NSUInteger numberOfNamespaces = [namespaceNodes count];
  981. if (numberOfNamespaces > 0) {
  982. dict = [NSMutableDictionary dictionary];
  983. for (unsigned int idx = 0; idx < numberOfNamespaces; idx++) {
  984. NSXMLNode *node = [namespaceNodes objectAtIndex:idx];
  985. [dict setObject:[node stringValue]
  986. forKey:[node name]];
  987. }
  988. }
  989. return dict;
  990. }
  991. // classOrSurrogateForClass searches this object instance and all parent
  992. // instances for a user surrogate for the supplied class, and returns
  993. // the surrogate, or else the supplied class if no surrogate is found for it
  994. - (Class)classOrSurrogateForClass:(Class)standardClass {
  995. for (GDataObject *currentObject = self;
  996. currentObject != nil;
  997. currentObject = [currentObject parent]) {
  998. // look for an object with a surrogates dict containing the standardClass
  999. NSDictionary *currentSurrogates = [currentObject surrogates];
  1000. Class surrogate = (Class)[currentSurrogates objectForKey:standardClass];
  1001. if (surrogate) return surrogate;
  1002. }
  1003. return standardClass;
  1004. }
  1005. // The following routines which parse XML elements remove the parsed elements
  1006. // from the list of unknowns.
  1007. // objectForElementWithNameIfAny:objectClass:objectClass: creates
  1008. // a single GDataObject of the specified class for the first XML child element
  1009. // with the specified name. Returns nil if no child element is present
  1010. //
  1011. // If objectClass is nil, the class is looked up from the registrations
  1012. // of entry and feed classes.
  1013. - (id)objectForChildOfElement:(NSXMLElement *)parentElement
  1014. qualifiedName:(NSString *)qualifiedName
  1015. namespaceURI:(NSString *)namespaceURI
  1016. objectClass:(Class)objectClass {
  1017. id object = nil;
  1018. NSXMLElement *element = [self childWithQualifiedName:qualifiedName
  1019. namespaceURI:namespaceURI
  1020. fromElement:parentElement];
  1021. if (element) {
  1022. if (objectClass == nil) {
  1023. // if the object is a feed or an entry, we might be able to determine the
  1024. // type from the XML
  1025. objectClass = [[self class] objectClassForXMLElement:element];
  1026. }
  1027. objectClass = [self classOrSurrogateForClass:objectClass];
  1028. object = [[[objectClass alloc] initWithXMLElement:element
  1029. parent:self] autorelease];
  1030. }
  1031. return object;
  1032. }
  1033. // get child elements from an element matching the given name and namespace
  1034. // (trying the namespace first, falling back on the fully-qualified name)
  1035. - (NSArray *)elementsForName:(NSString *)qualifiedName
  1036. namespaceURI:(NSString *)namespaceURI
  1037. parentElement:(NSXMLElement *)parentElement {
  1038. NSArray *objElements = nil;
  1039. if ([namespaceURI length] > 0) {
  1040. NSString *localName = [NSXMLNode localNameForName:qualifiedName];
  1041. objElements = [parentElement elementsForLocalName:localName
  1042. URI:namespaceURI];
  1043. }
  1044. // if we couldn't find the elements by name, fall back on the fully-qualified
  1045. // name
  1046. if ([objElements count] == 0) {
  1047. objElements = [parentElement elementsForName:qualifiedName];
  1048. }
  1049. return objElements;
  1050. }
  1051. // return all child elements of an element which have the given namespace
  1052. // prefix
  1053. - (NSMutableArray *)childrenOfElement:(NSXMLElement *)parentElement
  1054. withPrefix:(NSString *)prefix {
  1055. NSArray *allChildren = [parentElement children];
  1056. NSMutableArray *matchingChildren = [NSMutableArray array];
  1057. for (NSXMLNode *childNode in allChildren) {
  1058. if ([childNode kind] == NSXMLElementKind
  1059. && [[childNode prefix] isEqual:prefix]) {
  1060. [matchingChildren addObject:childNode];
  1061. }
  1062. }
  1063. return matchingChildren;
  1064. }
  1065. // returns a GDataObject or an array of them of the specified class for each XML
  1066. // child element with the specified name
  1067. //
  1068. // If objectClass is nil, the class is looked up from the registrations
  1069. // of entry and feed classes.
  1070. - (id)objectOrArrayForChildrenOfElement:(NSXMLElement *)parentElement
  1071. qualifiedName:(NSString *)qualifiedName
  1072. namespaceURI:(NSString *)namespaceURI
  1073. objectClass:(Class)objectClass {
  1074. id result = nil;
  1075. BOOL isResultAnArray = NO;
  1076. NSArray *objElements = nil;
  1077. NSString *localName = [NSXMLNode localNameForName:qualifiedName];
  1078. if (![localName isEqual:@"*"]) {
  1079. // searching for an actual element name (not a wildcard)
  1080. objElements = [self elementsForName:qualifiedName
  1081. namespaceURI:namespaceURI
  1082. parentElement:parentElement];
  1083. }
  1084. else {
  1085. // we weren't given a local name, so get all objects for this namespace
  1086. // URI's prefix
  1087. NSString *prefixSought = [NSXMLNode prefixForName:qualifiedName];
  1088. if ([prefixSought length] == 0) {
  1089. prefixSought = [parentElement resolvePrefixForNamespaceURI:namespaceURI];
  1090. }
  1091. if (prefixSought) {
  1092. objElements = [self childrenOfElement:parentElement
  1093. withPrefix:prefixSought];
  1094. }
  1095. }
  1096. // if we're creating entries, we'll use an autorelease pool around each
  1097. // allocation, just to bound overall pool size. We'll check the class
  1098. // of the first created object to determine if we want pools.
  1099. BOOL hasCheckedObjectClass = NO;
  1100. BOOL useLocalAutoreleasePool = NO;
  1101. Class entryBaseClass = [GDataEntryBase class];
  1102. // step through all child elements and create an appropriate GData object
  1103. for (NSXMLElement *objElement in objElements) {
  1104. Class elementClass = objectClass;
  1105. if (elementClass == nil) {
  1106. // if the object is a feed or an entry, we might be able to determine the
  1107. // type for this element from the XML
  1108. elementClass = [[self class] objectClassForXMLElement:objElement];
  1109. // if a base feed class doesn't specify entry class, and the entry object
  1110. // class can't be determined by examining its XML, fall back on
  1111. // instantiating the base entry class
  1112. if (elementClass == nil
  1113. && [qualifiedName isEqual:@"entry"]
  1114. && [namespaceURI isEqual:kGDataNamespaceAtom]) {
  1115. elementClass = entryBaseClass;
  1116. }
  1117. }
  1118. elementClass = [self classOrSurrogateForClass:elementClass];
  1119. NSAutoreleasePool *pool = nil;
  1120. if (!hasCheckedObjectClass) {
  1121. useLocalAutoreleasePool = [elementClass isSubclassOfClass:entryBaseClass];
  1122. hasCheckedObjectClass = YES;
  1123. }
  1124. if (useLocalAutoreleasePool) {
  1125. pool = [[NSAutoreleasePool alloc] init];
  1126. }
  1127. id obj = [[elementClass alloc] initWithXMLElement:objElement
  1128. parent:self];
  1129. // We drain here to keep the clang static analyzer quiet.
  1130. [pool drain];
  1131. [obj autorelease];
  1132. if (obj) {
  1133. if (result == nil) {
  1134. // first result
  1135. result = obj;
  1136. } else if (!isResultAnArray) {
  1137. // second result; create an array with the previous and the new result
  1138. result = [NSMutableArray arrayWithObjects:result, obj, nil];
  1139. isResultAnArray = YES;
  1140. } else {
  1141. // third or later result
  1142. [result addObject:obj];
  1143. }
  1144. }
  1145. }
  1146. // remove these elements from the unknown list
  1147. [self handleParsedElements:objElements];
  1148. return result;
  1149. }
  1150. // childOfElement:withName returns the element with the name, or nil if there
  1151. // are not exactly one of the element. Pass "*" wildcards for name and URI
  1152. // to retrieve the child element if there is exactly one.
  1153. - (NSXMLElement *)childWithQualifiedName:(NSString *)qualifiedName
  1154. namespaceURI:(NSString *)namespaceURI
  1155. fromElement:(NSXMLElement *)parentElement {
  1156. NSArray *elementArray;
  1157. if ([qualifiedName isEqual:@"*"] && [namespaceURI isEqual:@"*"]) {
  1158. // wilcards
  1159. elementArray = [parentElement children];
  1160. } else {
  1161. // find the element by name and namespace URI
  1162. elementArray = [self elementsForName:qualifiedName
  1163. namespaceURI:namespaceURI
  1164. parentElement:parentElement];
  1165. }
  1166. NSUInteger numberOfElements = [elementArray count];
  1167. if (numberOfElements == 1) {
  1168. NSXMLElement *element = [elementArray objectAtIndex:0];
  1169. // remove this element from the unknown list
  1170. [self handleParsedElement:element];
  1171. return element;
  1172. }
  1173. // We might want to get rid of this assert if there turns out to be
  1174. // legitimate reasons to call this where there are >1 elements available
  1175. GDATA_ASSERT(numberOfElements == 0, @"childWithQualifiedName: could not handle "
  1176. "multiple '%@' elements in list, use elementsForName:\n"
  1177. "Found elements: %@\nURI: %@", qualifiedName, elementArray,
  1178. namespaceURI);
  1179. return nil;
  1180. }
  1181. #pragma mark element parsing
  1182. - (void)handleParsedElement:(NSXMLNode *)element {
  1183. if (unknownChildren_ != nil && element != nil) {
  1184. [unknownChildren_ removeObjectIdenticalTo:element];
  1185. if ([unknownChildren_ count] == 0) {
  1186. [unknownChildren_ release];
  1187. unknownChildren_ = nil;
  1188. }
  1189. }
  1190. }
  1191. - (void)handleParsedElements:(NSArray *)array {
  1192. if (unknownChildren_ != nil) {
  1193. // rather than use NSMutableArray's removeObjects:, it's faster to iterate and
  1194. // and use removeObjectIdenticalTo: since it avoids comparing the underlying
  1195. // XML for equality
  1196. for (NSXMLNode* element in array) {
  1197. [unknownChildren_ removeObjectIdenticalTo:element];
  1198. }
  1199. if ([unknownChildren_ count] == 0) {
  1200. [unknownChildren_ release];
  1201. unknownChildren_ = nil;
  1202. }
  1203. }
  1204. }
  1205. - (NSString *)stringValueFromElement:(NSXMLElement *)element {
  1206. // Originally, this was
  1207. // NSString *result = [element stringValue];
  1208. // but that recursively descends children to build the string
  1209. // so we'll just walk the remaining nodes and build the string ourselves
  1210. if (element == nil) {
  1211. return nil;
  1212. }
  1213. NSString *result = nil;
  1214. // consider all text child nodes used to make this string value to now be
  1215. // known
  1216. //
  1217. // in most cases, there is only one text node, so we'll optimize for that
  1218. NSArray *children = [element children];
  1219. for (NSXMLNode *childNode in children) {
  1220. if ([childNode kind] == NSXMLTextKind) {
  1221. NSString *newNodeString = [childNode stringValue];
  1222. if (result == nil) {
  1223. result = newNodeString;
  1224. } else {
  1225. result = [result stringByAppendingString:newNodeString];
  1226. }
  1227. [self handleParsedElement:childNode];
  1228. }
  1229. }
  1230. return (result != nil ? result : @"");
  1231. }
  1232. - (GDataDateTime *)dateTimeFromElement:(NSXMLElement *)element {
  1233. NSString *str = [self stringValueFromElement:element];
  1234. if ([str length] > 0) {
  1235. return [GDataDateTime dateTimeWithRFC3339String:str];
  1236. }
  1237. return nil;
  1238. }
  1239. - (NSNumber *)intNumberValueFromElement:(NSXMLElement *)element {
  1240. NSString *str = [self stringValueFromElement:element];
  1241. if ([str length] > 0) {
  1242. NSNumber *number = [NSNumber numberWithInt:[str intValue]];
  1243. return number;
  1244. }
  1245. return nil;
  1246. }
  1247. - (NSNumber *)doubleNumberValueFromElement:(NSXMLElement *)element {
  1248. NSString *str = [self stringValueFromElement:element];
  1249. return [GDataUtilities doubleNumberOrInfForString:str];
  1250. }
  1251. #pragma mark attribute parsing
  1252. - (void)handleParsedAttribute:(NSXMLNode *)attribute {
  1253. if (unknownAttributes_ != nil && attribute != nil) {
  1254. [unknownAttributes_ removeObjectIdenticalTo:attribute];
  1255. if ([unknownAttributes_ count] == 0) {
  1256. [unknownAttributes_ release];
  1257. unknownAttributes_ = nil;
  1258. }
  1259. }
  1260. }
  1261. - (NSXMLNode *)attributeForName:(NSString *)attributeName
  1262. fromElement:(NSXMLElement *)element {
  1263. NSXMLNode* attribute = [element attributeForName:attributeName];
  1264. [self handleParsedAttribute:attribute];
  1265. return attribute;
  1266. }
  1267. - (NSXMLNode *)attributeForLocalName:(NSString *)localName
  1268. URI:(NSString *)attributeURI
  1269. fromElement:(NSXMLElement *)element {
  1270. NSXMLNode* attribute = [element attributeForLocalName:localName
  1271. URI:attributeURI];
  1272. [self handleParsedAttribute:attribute];
  1273. return attribute;
  1274. }
  1275. - (NSString *)stringForAttributeLocalName:(NSString *)localName
  1276. URI:(NSString *)attributeURI
  1277. fromElement:(NSXMLElement *)element {
  1278. NSXMLNode* attribute = [self attributeForLocalName:localName
  1279. URI:attributeURI
  1280. fromElement:element];
  1281. return [attribute stringValue];
  1282. }
  1283. - (NSString *)stringForAttributeName:(NSString *)attributeName
  1284. fromElement:(NSXMLElement *)element {
  1285. NSXMLNode* attribute = [self attributeForName:attributeName
  1286. fromElement:element];
  1287. return [attribute stringValue];
  1288. }
  1289. - (GDataDateTime *)dateTimeForAttributeName:(NSString *)attributeName
  1290. fromElement:(NSXMLElement *)element {
  1291. NSXMLNode* attribute = [self attributeForName:attributeName
  1292. fromElement:element];
  1293. NSString* str = [attribute stringValue];
  1294. if ([str length] > 0) {
  1295. return [GDataDateTime dateTimeWithRFC3339String:str];
  1296. }
  1297. return nil;
  1298. }
  1299. - (BOOL)boolForAttributeName:(NSString *)attributeName
  1300. fromElement:(NSXMLElement *)element {
  1301. NSXMLNode* attribute = [self attributeForName:attributeName
  1302. fromElement:element];
  1303. NSString* str = [attribute stringValue];
  1304. BOOL isTrue = (str && [str caseInsensitiveCompare:@"true"] == NSOrderedSame);
  1305. return isTrue;
  1306. }
  1307. - (NSNumber *)doubleNumberForAttributeName:(NSString *)attributeName
  1308. fromElement:(NSXMLElement *)element {
  1309. NSXMLNode* attribute = [self attributeForName:attributeName
  1310. fromElement:element];
  1311. NSString* str = [attribute stringValue];
  1312. return [GDataUtilities doubleNumberOrInfForString:str];
  1313. }
  1314. - (NSNumber *)intNumberForAttributeName:(NSString *)attributeName
  1315. fromElement:(NSXMLElement *)element {
  1316. NSXMLNode* attribute = [self attributeForName:attributeName
  1317. fromElement:element];
  1318. NSString* str = [attribute stringValue];
  1319. if (str) {
  1320. NSNumber *number = [NSNumber numberWithInt:[str intValue]];
  1321. return number;
  1322. }
  1323. return nil;
  1324. }
  1325. #pragma mark Extensions
  1326. - (void)addExtensionDeclarations {
  1327. // overridden by subclasses which have extensions to add, like:
  1328. //
  1329. // [self addExtensionDeclarationForParentClass:[GDataLink class]
  1330. // childClass:[GDataWebContent class]];
  1331. // and
  1332. //
  1333. // [self addAttributeExtensionDeclarationForParentClass:[GDataExtendedProperty class]
  1334. // childClass:[GDataExtPropValueAttribute class]];
  1335. }
  1336. - (void)addParseDeclarations {
  1337. // overridden by subclasses which have local attributes, like:
  1338. //
  1339. // [self addLocalAttributeDeclarations:[NSArray arrayWithObject:@"size"]];
  1340. //
  1341. // Subclasses should add the attributes in the order they most usefully will
  1342. // appear in the object's -description output (or alternatively they may
  1343. // override -description).
  1344. //
  1345. // Note: this is only for namespace-less attributes or attributes with the
  1346. // fixed xml: namespace, not for attributes that are qualified with variable
  1347. // prefixes. Those attributes should be parsed explicitly in
  1348. // initWithXMLElement: methods, and generated by XMLElement: methods.
  1349. }
  1350. // subclasses call these to declare possible extensions for themselves and their
  1351. // children.
  1352. - (void)addExtensionDeclarationForParentClass:(Class)parentClass
  1353. childClass:(Class)childClass {
  1354. // add an element extension
  1355. [self addExtensionDeclarationForParentClass:parentClass
  1356. childClass:childClass
  1357. isAttribute:NO];
  1358. }
  1359. - (void)addExtensionDeclarationForParentClass:(Class)parentClass
  1360. childClasses:(Class)firstChildClass, ... {
  1361. // like the method above, but for a list of child classes
  1362. Class nextClass;
  1363. va_list argumentList;
  1364. if (firstChildClass != nil) {
  1365. [self addExtensionDeclarationForParentClass:parentClass
  1366. childClass:firstChildClass
  1367. isAttribute:NO];
  1368. va_start(argumentList, firstChildClass);
  1369. while ((nextClass = (Class)va_arg(argumentList, Class)) != nil) {
  1370. [self addExtensionDeclarationForParentClass:parentClass
  1371. childClass:nextClass
  1372. isAttribute:NO];
  1373. }
  1374. va_end(argumentList);
  1375. }
  1376. }
  1377. - (void)addAttributeExtensionDeclarationForParentClass:(Class)parentClass
  1378. childClass:(Class)childClass {
  1379. // add an attribute extension
  1380. [self addExtensionDeclarationForParentClass:parentClass
  1381. childClass:childClass
  1382. isAttribute:YES];
  1383. }
  1384. - (void)addExtensionDeclarationForParentClass:(Class)parentClass
  1385. childClass:(Class)childClass
  1386. isAttribute:(BOOL)isAttribute {
  1387. // get or make the dictionary which caches the extension declarations for
  1388. // this class
  1389. Class currClass = [self class];
  1390. NSMutableDictionary *extensionDeclarationsCache = [self extensionDeclarationsCache];
  1391. GDATA_DEBUG_ASSERT(extensionDeclarationsCache != nil, @"missing extnDecls");
  1392. NSMutableDictionary *extensionDecls = [extensionDeclarationsCache objectForKey:currClass];
  1393. if (extensionDecls == nil) {
  1394. extensionDecls = [NSMutableDictionary dictionary];
  1395. [extensionDeclarationsCache setObject:extensionDecls forKey:(id<NSCopying>)currClass];
  1396. }
  1397. // get this class's extensions for the specified parent class
  1398. NSMutableArray *array = [extensionDecls objectForKey:parentClass];
  1399. if (array == nil) {
  1400. array = [NSMutableArray array];
  1401. [extensionDecls setObject:array forKey:(id<NSCopying>)parentClass];
  1402. }
  1403. GDATA_DEBUG_ASSERT([childClass conformsToProtocol:@protocol(GDataExtension)],
  1404. @"%@ does not conform to GDataExtension protocol", childClass);
  1405. GDataExtensionDeclaration *decl =
  1406. [[[GDataExtensionDeclaration alloc] initWithParentClass:parentClass
  1407. childClass:childClass
  1408. isAttribute:isAttribute] autorelease];
  1409. [array addObject:decl];
  1410. }
  1411. - (void)removeExtensionDeclarationForParentClass:(Class)parentClass
  1412. childClass:(Class)childClass {
  1413. GDataExtensionDeclaration *decl =
  1414. [[[GDataExtensionDeclaration alloc] initWithParentClass:parentClass
  1415. childClass:childClass
  1416. isAttribute:NO] autorelease];
  1417. NSMutableArray *array = [self extensionDeclarationsForParentClass:parentClass];
  1418. [array removeObject:decl];
  1419. }
  1420. - (void)removeAttributeExtensionDeclarationForParentClass:(Class)parentClass
  1421. childClass:(Class)childClass {
  1422. GDataExtensionDeclaration *decl =
  1423. [[[GDataExtensionDeclaration alloc] initWithParentClass:parentClass
  1424. childClass:childClass
  1425. isAttribute:YES] autorelease];
  1426. NSMutableArray *array = [self extensionDeclarationsForParentClass:parentClass];
  1427. [array removeObject:decl];
  1428. }
  1429. // utility routine for getting declared extensions to the specified class
  1430. - (NSMutableArray *)extensionDeclarationsForParentClass:(Class)parentClass {
  1431. // get the declarations for this class
  1432. Class currClass = [self class];
  1433. NSMutableDictionary *cache = [self extensionDeclarationsCache];
  1434. NSMutableDictionary *classMap = [cache objectForKey:currClass];
  1435. // get the extensions for the specified parent class
  1436. NSMutableArray *array = [classMap objectForKey:parentClass];
  1437. return array;
  1438. }
  1439. // objectsForExtensionClass: returns the array of all
  1440. // extension objects of the specified class, or nil
  1441. //
  1442. // this is typically called by the getter methods of subclasses
  1443. - (NSArray *)objectsForExtensionClass:(Class)theClass {
  1444. id obj = [extensions_ objectForKey:theClass];
  1445. if (obj == nil) return nil;
  1446. if ([obj isKindOfClass:[NSArray class]]) {
  1447. return obj;
  1448. }
  1449. return [NSArray arrayWithObject:obj];
  1450. }
  1451. // objectForExtensionClass: returns the first element of
  1452. // any extension objects of the specified class, or nil
  1453. //
  1454. // this is typically called by the getter methods of subclasses
  1455. - (id)objectForExtensionClass:(Class)theClass {
  1456. id obj = [extensions_ objectForKey:theClass];
  1457. if ([obj isKindOfClass:[NSArray class]]) {
  1458. if ([(NSArray *)obj count] > 0) {
  1459. return [obj objectAtIndex:0];
  1460. }
  1461. // an empty array
  1462. return nil;
  1463. }
  1464. return obj;
  1465. }
  1466. // attributeValueForExtensionClass: returns the value of the first object of
  1467. // the array of attribute extension objects of the specified class, or nil
  1468. - (NSString *)attributeValueForExtensionClass:(Class)theClass {
  1469. GDataAttribute *attr = [self objectForExtensionClass:theClass];
  1470. NSString *str = [attr stringValue];
  1471. return str;
  1472. }
  1473. - (void)setAttributeValue:(NSString *)str forExtensionClass:(Class)theClass {
  1474. GDataAttribute *obj = [theClass attributeWithValue:str];
  1475. [self setObject:obj forExtensionClass:theClass];
  1476. }
  1477. // generate the qualified name for this extension's element
  1478. - (NSString *)qualifiedNameForExtensionClass:(Class)theClass {
  1479. NSString *name;
  1480. @synchronized(gQualifiedNameMap) {
  1481. name = [gQualifiedNameMap objectForKey:theClass];
  1482. if (name == nil) {
  1483. NSString *extensionURI = [theClass extensionElementURI];
  1484. if (extensionURI == nil || [extensionURI isEqual:kGDataNamespaceAtom]) {
  1485. name = [theClass extensionElementLocalName];
  1486. } else {
  1487. name = [NSString stringWithFormat:@"%@:%@",
  1488. [theClass extensionElementPrefix],
  1489. [theClass extensionElementLocalName]];
  1490. }
  1491. [gQualifiedNameMap setObject:name forKey:(id<NSCopying>)theClass];
  1492. }
  1493. }
  1494. return name;
  1495. }
  1496. - (void)ensureObject:(GDataObject *)obj hasXMLNameForExtensionClass:(Class)theClass {
  1497. // utility routine for setObjects:forExtensionClass:
  1498. if ([obj isKindOfClass:[GDataObject class]]
  1499. && [[obj elementName] length] == 0) {
  1500. NSString *name = [self qualifiedNameForExtensionClass:theClass];
  1501. [obj setElementName:name];
  1502. }
  1503. }
  1504. // replace all actual extensions of the specified class with an array
  1505. //
  1506. // this is typically called by the setter methods of subclasses
  1507. - (void)setObjects:(NSArray *)objects forExtensionClass:(Class)theClass {
  1508. GDATA_DEBUG_ASSERT(objects == nil || [objects isKindOfClass:[NSArray class]],
  1509. @"array expected");
  1510. if (extensions_ == nil && objects != nil) {
  1511. extensions_ = [[NSMutableDictionary alloc] init];
  1512. }
  1513. if (objects) {
  1514. // be sure each object has an element name so we can generate XML for it
  1515. for (GDataObject *obj in objects) {
  1516. [self ensureObject:obj hasXMLNameForExtensionClass:theClass];
  1517. }
  1518. [extensions_ setObject:objects forKey:(id<NSCopying>)theClass];
  1519. } else {
  1520. [extensions_ removeObjectForKey:theClass];
  1521. }
  1522. }
  1523. // replace all actual extensions of the specified class with a single object
  1524. //
  1525. // this is typically called by the setter methods of subclasses
  1526. - (void)setObject:(id)object forExtensionClass:(Class)theClass {
  1527. GDATA_DEBUG_ASSERT(![object isKindOfClass:[NSArray class]], @"array unexpected");
  1528. if (extensions_ == nil && object != nil) {
  1529. extensions_ = [[NSMutableDictionary alloc] init];
  1530. }
  1531. if (object) {
  1532. [self ensureObject:object hasXMLNameForExtensionClass:theClass];
  1533. [extensions_ setObject:object forKey:(id<NSCopying>)theClass];
  1534. } else {
  1535. [extensions_ removeObjectForKey:theClass];
  1536. }
  1537. }
  1538. // add an extension of the specified class
  1539. //
  1540. // this is typically called by addObject methods of subclasses
  1541. - (void)addObject:(id)newObj forExtensionClass:(Class)theClass {
  1542. if (newObj == nil) return;
  1543. id previousObjOrArray = [extensions_ objectForKey:theClass];
  1544. if (previousObjOrArray) {
  1545. if ([previousObjOrArray isKindOfClass:[NSArray class]]) {
  1546. // add to the existing array
  1547. [self ensureObject:newObj hasXMLNameForExtensionClass:theClass];
  1548. [previousObjOrArray addObject:newObj];
  1549. } else {
  1550. // create an array with the previous object and the new object
  1551. NSMutableArray *array = [NSMutableArray arrayWithObjects:
  1552. previousObjOrArray, newObj, nil];
  1553. [extensions_ setObject:array forKey:(id<NSCopying>)theClass];
  1554. }
  1555. } else {
  1556. // no previous object
  1557. [self setObject:newObj forExtensionClass:theClass];
  1558. }
  1559. }
  1560. // remove a known extension of the specified class
  1561. //
  1562. // this is typically called by removeObject methods of subclasses
  1563. - (void)removeObject:(id)object forExtensionClass:(Class)theClass {
  1564. id previousObjOrArray = [extensions_ objectForKey:theClass];
  1565. if ([previousObjOrArray isKindOfClass:[NSArray class]]) {
  1566. // remove from the array
  1567. [(NSMutableArray *)previousObjOrArray removeObject:object];
  1568. } else if ([(GDataObject *)object isEqual:previousObjOrArray]) {
  1569. // no array, so remove if it matches the sole object
  1570. [extensions_ removeObjectForKey:theClass];
  1571. }
  1572. }
  1573. // addUnknownChildNodesForElement: is called by initWithXMLElement. It builds
  1574. // the initial list of unknown child elements; this list is whittled down by
  1575. // parseExtensionsForElement and objectForChildOfElement.
  1576. - (void)addUnknownChildNodesForElement:(NSXMLElement *)element {
  1577. GDATA_DEBUG_ASSERT(unknownChildren_ == nil, @"unknChildren added twice");
  1578. GDATA_DEBUG_ASSERT(unknownAttributes_ == nil, @"unknAttr added twice");
  1579. if (!shouldIgnoreUnknowns_) {
  1580. NSArray *children = [element children];
  1581. if ([children count] > 0) {
  1582. unknownChildren_ = [[NSMutableArray alloc] initWithArray:children];
  1583. }
  1584. NSArray *attributes = [element attributes];
  1585. if ([attributes count] > 0) {
  1586. unknownAttributes_ = [[NSMutableArray alloc] initWithArray:attributes];
  1587. }
  1588. }
  1589. }
  1590. // parseExtensionsForElement: is called by initWithXMLElement. It starts
  1591. // from the current object and works up the chain of parents, grabbing
  1592. // the declared extensions by each GDataObject in the ancestry and looking
  1593. // at the current element to see if any of the declared extensions are present.
  1594. - (void)parseExtensionsForElement:(NSXMLElement *)element {
  1595. Class classBeingParsed = [self class];
  1596. // For performance, we'll avoid looking up extension elements whose
  1597. // local names aren't present in the element. We don't bother doing
  1598. // this for attribute extensions since those are so rare (most attributes
  1599. // are parsed just by local declaration in parseAttributesForElement:.)
  1600. NSArray *childLocalNames = [element valueForKeyPath:@"children.localName"];
  1601. // allow wildcard lookups
  1602. childLocalNames = [childLocalNames arrayByAddingObject:@"*"];
  1603. Class arrayClass = [NSArray class];
  1604. for (GDataObject * currentExtensionSupplier = self;
  1605. currentExtensionSupplier != nil;
  1606. currentExtensionSupplier = [currentExtensionSupplier parent]) {
  1607. // find all extensions in this supplier with the current class as the parent
  1608. NSArray *extnDecls = [currentExtensionSupplier extensionDeclarationsForParentClass:classBeingParsed];
  1609. if (extnDecls) {
  1610. for (GDataExtensionDeclaration *decl in extnDecls) {
  1611. // if we've not already found this class when parsing at an earlier supplier
  1612. Class extensionClass = [decl childClass];
  1613. if ([extensions_ objectForKey:extensionClass] == nil) {
  1614. // if this extension's local name really matches some child's local
  1615. // name (or this is an attribute extension)
  1616. NSString *declLocalName = [extensionClass extensionElementLocalName];
  1617. if ([childLocalNames containsObject:declLocalName]
  1618. || [decl isAttribute]) {
  1619. GDATA_DEBUG_ASSERT([extensionClass conformsToProtocol:@protocol(GDataExtension)],
  1620. @"%@ does not conform to GDataExtension protocol",
  1621. extensionClass);
  1622. NSString *namespaceURI = [extensionClass extensionElementURI];
  1623. NSString *qualifiedName = [self qualifiedNameForExtensionClass:extensionClass];
  1624. id objectOrArray = nil;
  1625. if ([decl isAttribute]) {
  1626. // parse for an attribute extension
  1627. NSString *str = [self stringForAttributeName:qualifiedName
  1628. fromElement:element];
  1629. if (str) {
  1630. id attr = [[[extensionClass alloc] init] autorelease];
  1631. [attr setStringValue:str];
  1632. objectOrArray = attr;
  1633. }
  1634. } else {
  1635. // parse for an element extension
  1636. objectOrArray = [self objectOrArrayForChildrenOfElement:element
  1637. qualifiedName:qualifiedName
  1638. namespaceURI:namespaceURI
  1639. objectClass:extensionClass];
  1640. }
  1641. if ([objectOrArray isKindOfClass:arrayClass]) {
  1642. if ([(NSArray *)objectOrArray count] > 0) {
  1643. // save the non-empty array of extensions
  1644. [self setObjects:objectOrArray forExtensionClass:extensionClass];
  1645. }
  1646. } else if (objectOrArray != nil) {
  1647. // save the single extension
  1648. [self setObject:objectOrArray forExtensionClass:extensionClass];
  1649. }
  1650. }
  1651. }
  1652. }
  1653. }
  1654. }
  1655. }
  1656. #pragma mark Local Attributes
  1657. - (void)addLocalAttributeDeclarations:(NSArray *)attributeLocalNames {
  1658. // get or make the array which caches the attribute declarations for
  1659. // this class
  1660. if (attributeDeclarations_ == nil) {
  1661. Class currClass = [self class];
  1662. NSMutableDictionary *cache = [self attributeDeclarationsCache];
  1663. GDATA_DEBUG_ASSERT(cache != nil, @"missing attrDeclsCache");
  1664. // we keep a strong pointer to the array in the cache since the cache
  1665. // belongs to the feed or the topmost parent, and that may go away
  1666. attributeDeclarations_ = [[cache objectForKey:currClass] retain];
  1667. if (attributeDeclarations_ == nil) {
  1668. attributeDeclarations_ = [[NSMutableArray alloc] init];
  1669. [cache setObject:attributeDeclarations_ forKey:(id<NSCopying>)currClass];
  1670. }
  1671. }
  1672. #if DEBUG
  1673. // check that no local attributes being declared have a prefix, except for
  1674. // the hardcoded xml: prefix. Namespaced attributes must be parsed and
  1675. // emitted manually, or be declared as GDataAttribute extensions;
  1676. // they cannot be handled as local attributes, since this class makes no
  1677. // attempt to keep track of namespace URIs for local attributes
  1678. for (NSString *attr in attributeLocalNames) {
  1679. GDATA_ASSERT([attr rangeOfString:@":"].location == NSNotFound
  1680. || [attr hasPrefix:@"xml:"],
  1681. @"invalid namespaced local attribute: %@", attr);
  1682. }
  1683. #endif
  1684. [attributeDeclarations_ addObjectsFromArray:attributeLocalNames];
  1685. }
  1686. - (void)addAttributeDeclarationMarker:(NSString *)marker {
  1687. if (![attributeDeclarations_ containsObject:marker]) {
  1688. // add the marker
  1689. if (attributeDeclarations_ != nil) {
  1690. // no need to create the cache
  1691. [attributeDeclarations_ addObject:marker];
  1692. } else {
  1693. // create the cache by calling addLocalAttributeDeclarations:
  1694. NSArray *array = [NSArray arrayWithObject:marker];
  1695. [self addLocalAttributeDeclarations:array];
  1696. }
  1697. }
  1698. }
  1699. // attribute value getters
  1700. - (NSString *)stringValueForAttribute:(NSString *)name {
  1701. GDATA_DEBUG_ASSERT([[self attributeDeclarations] containsObject:name],
  1702. @"%@ getting undeclared attribute: %@", [self class], name);
  1703. return [attributes_ valueForKey:name];
  1704. }
  1705. - (NSNumber *)intNumberForAttribute:(NSString *)name {
  1706. NSString *str = [self stringValueForAttribute:name];
  1707. if ([str length] > 0) {
  1708. NSNumber *number = [NSNumber numberWithInt:[str intValue]];
  1709. return number;
  1710. }
  1711. return nil;
  1712. }
  1713. - (NSNumber *)doubleNumberForAttribute:(NSString *)name {
  1714. NSString *str = [self stringValueForAttribute:name];
  1715. return [GDataUtilities doubleNumberOrInfForString:str];
  1716. }
  1717. - (NSNumber *)longLongNumberForAttribute:(NSString *)name {
  1718. NSString *str = [self stringValueForAttribute:name];
  1719. if (str) {
  1720. long long val = [str longLongValue];
  1721. NSNumber *number = [NSNumber numberWithLongLong:val];
  1722. return number;
  1723. }
  1724. return nil;
  1725. }
  1726. - (NSDecimalNumber *)decimalNumberForAttribute:(NSString *)name {
  1727. NSString *str = [self stringValueForAttribute:name];
  1728. if ([str length] > 0) {
  1729. // require periods as the separator
  1730. NSLocale *usLocale = [[[NSLocale alloc] initWithLocaleIdentifier:@"en_US"] autorelease];
  1731. NSDecimalNumber *number = [NSDecimalNumber decimalNumberWithString:str
  1732. locale:usLocale];
  1733. return number;
  1734. }
  1735. return nil;
  1736. }
  1737. - (GDataDateTime *)dateTimeForAttribute:(NSString *)name {
  1738. NSString *str = [self stringValueForAttribute:name];
  1739. if ([str length] > 0) {
  1740. GDataDateTime *dateTime = [GDataDateTime dateTimeWithRFC3339String:str];
  1741. return dateTime;
  1742. }
  1743. return nil;
  1744. }
  1745. - (BOOL)boolValueForAttribute:(NSString *)name defaultValue:(BOOL)defaultVal {
  1746. NSString *str = [self stringValueForAttribute:name];
  1747. BOOL isTrue;
  1748. if (defaultVal) {
  1749. // default to true, so true if attribute is missing or is not "false"
  1750. isTrue = (str == nil
  1751. || [str caseInsensitiveCompare:@"false"] != NSOrderedSame);
  1752. } else {
  1753. // default to false, so true only if attribute is present and "true"
  1754. isTrue = (str != nil
  1755. && [str caseInsensitiveCompare:@"true"] == NSOrderedSame);
  1756. }
  1757. return isTrue;
  1758. }
  1759. // attribute value setters
  1760. - (void)setStringValue:(NSString *)str forAttribute:(NSString *)name {
  1761. GDATA_DEBUG_ASSERT([[self attributeDeclarations] containsObject:name],
  1762. @"%@ setting undeclared attribute: %@", [self class], name);
  1763. if (attributes_ == nil) {
  1764. attributes_ = [[NSMutableDictionary alloc] init];
  1765. }
  1766. [attributes_ setValue:str forKey:name];
  1767. }
  1768. - (void)setBoolValue:(BOOL)flag defaultValue:(BOOL)defaultVal forAttribute:(NSString *)name {
  1769. NSString *str;
  1770. if (defaultVal) {
  1771. // default to true, so include attribute only if false
  1772. str = (flag ? nil : @"false");
  1773. } else {
  1774. // default to false, so include attribute only if true
  1775. str = (flag ? @"true" : nil);
  1776. }
  1777. [self setStringValue:str forAttribute:name];
  1778. }
  1779. - (void)setExplicitBoolValue:(BOOL)flag forAttribute:(NSString *)name {
  1780. NSString *value = (flag ? @"true" : @"false");
  1781. [self setStringValue:value forAttribute:name];
  1782. }
  1783. - (void)setDecimalNumberValue:(NSDecimalNumber *)num forAttribute:(NSString *)name {
  1784. // for most NSNumbers, just calling -stringValue is fine, but for decimal
  1785. // numbers we want to specify that a period be the separator
  1786. NSLocale *usLocale = [[[NSLocale alloc] initWithLocaleIdentifier:@"en_US"] autorelease];
  1787. NSString *str = [num descriptionWithLocale:usLocale];
  1788. [self setStringValue:str forAttribute:name];
  1789. }
  1790. - (void)setDateTimeValue:(GDataDateTime *)cdate forAttribute:(NSString *)name {
  1791. NSString *str = [cdate RFC3339String];
  1792. [self setStringValue:str forAttribute:name];
  1793. }
  1794. // parseAttributesForElement: is called by initWithXMLElement.
  1795. // It stores the value of all declared & present attributes in the dictionary
  1796. - (void)parseAttributesForElement:(NSXMLElement *)element {
  1797. // for better performance, look up the values for declared attributes only
  1798. // if they are really present in the node
  1799. NSArray *attributes = [element attributes];
  1800. NSArray *attributeDeclarations = [self attributeDeclarations];
  1801. for (NSXMLNode *attribute in attributes) {
  1802. NSString *attrName = [attribute name];
  1803. if ([attributeDeclarations containsObject:attrName]) {
  1804. NSString *str = [attribute stringValue];
  1805. if (str != nil) {
  1806. [self setStringValue:str forAttribute:attrName];
  1807. }
  1808. [self handleParsedAttribute:attribute];
  1809. }
  1810. }
  1811. }
  1812. // XML generator for local attributes
  1813. - (void)addAttributesToElement:(NSXMLElement *)element {
  1814. for (NSString *name in attributes_) {
  1815. NSString *value = [attributes_ valueForKey:name];
  1816. if (value != nil) {
  1817. [self addToElement:element attributeValueIfNonNil:value withName:name];
  1818. }
  1819. }
  1820. }
  1821. // attribute comparison: subclasses may implement attributesIgnoredForEquality:
  1822. // to specify attributes not to be considered for equality comparison
  1823. - (BOOL)hasAttributesEqualToAttributesOf:(GDataObject *)other {
  1824. NSArray *attributesToIgnore = [self attributesIgnoredForEquality];
  1825. NSDictionary *selfAttrs = [self attributes];
  1826. NSDictionary *otherAttrs = [other attributes];
  1827. if ([attributesToIgnore count] == 0) {
  1828. // none to ignore; just compare attribute dictionaries
  1829. return AreEqualOrBothNil(selfAttrs, otherAttrs);
  1830. }
  1831. // step through attributes, comparing each non-ignored attribute
  1832. // to look for a mismatch
  1833. NSArray *attributeDeclarations = [self attributeDeclarations];
  1834. for (NSString *attrKey in attributeDeclarations) {
  1835. if (![attributesToIgnore containsObject:attrKey]) {
  1836. NSString *val1 = [selfAttrs objectForKey:attrKey];
  1837. NSString *val2 = [otherAttrs objectForKey:attrKey];
  1838. if (!AreEqualOrBothNil(val1, val2)) {
  1839. return NO;
  1840. }
  1841. }
  1842. }
  1843. return YES;
  1844. }
  1845. - (NSArray *)attributesIgnoredForEquality {
  1846. // subclasses may override this to specify attributes that should
  1847. // not be considered when comparing objects for equality
  1848. return nil;
  1849. }
  1850. #pragma mark Content Value
  1851. - (void)addContentValueDeclaration {
  1852. // derived classes should call this if they want the element's content
  1853. // to be automatically parsed as a string
  1854. [self addAttributeDeclarationMarker:kContentValueDeclarationMarker];
  1855. }
  1856. - (BOOL)hasDeclaredContentValue {
  1857. NSMutableArray *attrDecls = [self attributeDeclarations];
  1858. BOOL flag = [attrDecls containsObject:kContentValueDeclarationMarker];
  1859. return flag;
  1860. }
  1861. - (void)setContentStringValue:(NSString *)str {
  1862. GDATA_ASSERT([self hasDeclaredContentValue], @"%@ setting undeclared content value",
  1863. [self class]);
  1864. [contentValue_ autorelease];
  1865. contentValue_ = [str copy];
  1866. }
  1867. - (NSString *)contentStringValue {
  1868. GDATA_ASSERT([self hasDeclaredContentValue], @"%@ getting undeclared content value",
  1869. [self class]);
  1870. return contentValue_;
  1871. }
  1872. // parseContentForElement: is called by initWithXMLElement.
  1873. // This stores the content value parsed from the element.
  1874. - (void)parseContentValueForElement:(NSXMLElement *)element {
  1875. if ([self hasDeclaredContentValue]) {
  1876. [self setContentStringValue:[self stringValueFromElement:element]];
  1877. }
  1878. }
  1879. // XML generator for content
  1880. - (void)addContentValueToElement:(NSXMLElement *)element {
  1881. if ([self hasDeclaredContentValue]) {
  1882. NSString *str = [self contentStringValue];
  1883. if ([str length] > 0) {
  1884. [element addStringValue:str];
  1885. }
  1886. }
  1887. }
  1888. - (BOOL)hasContentValueEqualToContentValueOf:(GDataObject *)other {
  1889. if (![self hasDeclaredContentValue]) {
  1890. // no content being stored
  1891. return YES;
  1892. }
  1893. return AreEqualOrBothNil([self contentStringValue], [other contentStringValue]);
  1894. }
  1895. #pragma mark Child XML Elements
  1896. - (void)addChildXMLElementsDeclaration {
  1897. // derived classes should call this if they want the element's unparsed
  1898. // XML children to be accessible later
  1899. [self addAttributeDeclarationMarker:kChildXMLDeclarationMarker];
  1900. }
  1901. - (BOOL)hasDeclaredChildXMLElements {
  1902. NSMutableArray *attrDecls = [self attributeDeclarations];
  1903. BOOL flag = [attrDecls containsObject:kChildXMLDeclarationMarker];
  1904. return flag;
  1905. }
  1906. - (NSArray *)childXMLElements {
  1907. if ([childXMLElements_ count] == 0) {
  1908. return nil;
  1909. }
  1910. return childXMLElements_;
  1911. }
  1912. - (void)setChildXMLElements:(NSArray *)array {
  1913. GDATA_DEBUG_ASSERT([self hasDeclaredChildXMLElements],
  1914. @"%@ setting undeclared XML values", [self class]);
  1915. [childXMLElements_ release];
  1916. childXMLElements_ = [array mutableCopy];
  1917. }
  1918. - (void)addChildXMLElement:(NSXMLNode *)node {
  1919. GDATA_DEBUG_ASSERT([self hasDeclaredChildXMLElements],
  1920. @"%@ adding undeclared XML values", [self class]);
  1921. if (childXMLElements_ == nil) {
  1922. childXMLElements_ = [[NSMutableArray alloc] init];
  1923. }
  1924. [childXMLElements_ addObject:node];
  1925. }
  1926. // keepChildXMLElementsForElement: is called by initWithXMLElement.
  1927. // This stores a copy of the element's child XMLElements.
  1928. - (void)keepChildXMLElementsForElement:(NSXMLElement *)element {
  1929. if ([self hasDeclaredChildXMLElements]) {
  1930. NSArray *children = [element children];
  1931. if (children != nil) {
  1932. // save only top-level nodes that are elements
  1933. for (NSXMLNode *childNode in children) {
  1934. if ([childNode kind] == NSXMLElementKind) {
  1935. if (childXMLElements_ == nil) {
  1936. childXMLElements_ = [[NSMutableArray alloc] init];
  1937. }
  1938. NSXMLNode *childCopy = [[childNode copy] autorelease];
  1939. [childXMLElements_ addObject:childCopy];
  1940. [self handleParsedElement:childNode];
  1941. }
  1942. }
  1943. }
  1944. }
  1945. }
  1946. // XML generator for kept child XML elements
  1947. - (void)addChildXMLElementsToElement:(NSXMLElement *)element {
  1948. if ([self hasDeclaredChildXMLElements]) {
  1949. NSArray *childXMLElements = [self childXMLElements];
  1950. if (childXMLElements != nil) {
  1951. for (NSXMLNode *child in childXMLElements) {
  1952. [element addChild:child];
  1953. }
  1954. }
  1955. }
  1956. }
  1957. - (BOOL)hasChildXMLElementsEqualToChildXMLElementsOf:(GDataObject *)other {
  1958. if (![self hasDeclaredChildXMLElements]) {
  1959. // no values being stored
  1960. return YES;
  1961. }
  1962. return AreEqualOrBothNil([self childXMLElements], [other childXMLElements]);
  1963. }
  1964. #pragma mark Dynamic GDataObject
  1965. // Dynamic object generation is used when the class being created is nil.
  1966. //
  1967. // These maps are populated by +load routines in feeds and entries.
  1968. // They specify category elements which identify the class of feed or entry
  1969. // to be created for a blob of XML.
  1970. static NSString *const kCategoryTemplate = @"{\"%@\":\"%@\"}";
  1971. // registerClass:inMap:forCategoryWithScheme:term: does the work for
  1972. // registerFeedClass: and registerEntryClass:
  1973. //
  1974. // This adds the class to the {"scheme":"term"} map, ensuring
  1975. // that it won't conflict with a previous class or category
  1976. // entry
  1977. + (void)registerClass:(Class)theClass
  1978. inMap:(NSMutableDictionary **)map
  1979. forCategoryWithScheme:(NSString *)scheme
  1980. term:(NSString *)term {
  1981. // there's no autorelease pool in place at +load time, so we'll create our own
  1982. NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
  1983. if (*map == nil) {
  1984. *map = GDataCreateStaticDictionary();
  1985. }
  1986. // ensure this is a unique registration
  1987. GDATA_DEBUG_ASSERT(nil == [*map objectForKey:theClass],
  1988. @"%@ already registered", theClass);
  1989. #if !NS_BLOCK_ASSERTIONS
  1990. Class prevClass = [self classForCategoryWithScheme:scheme
  1991. term:term
  1992. fromMap:*map];
  1993. GDATA_ASSERT(prevClass == nil, @"%@ registration conflicts with %@",
  1994. theClass, prevClass);
  1995. #endif
  1996. // we have a map from the key "scheme:term" to the class
  1997. //
  1998. // generally, scheme will be nil or kGDataCategoryScheme, so we'll
  1999. // use just the term as the key for those categories, avoiding
  2000. // the need to format a string when looking up
  2001. NSString *key;
  2002. if (scheme == nil || [scheme isEqual:kGDataCategoryScheme]) {
  2003. key = term;
  2004. } else {
  2005. key = [NSString stringWithFormat:kCategoryTemplate,
  2006. scheme, term ? term : @""];
  2007. }
  2008. [*map setValue:theClass forKey:key];
  2009. // We drain here to keep the clang static analyzer quiet.
  2010. [pool drain];
  2011. }
  2012. // classForCategoryWithScheme does the work for feedClassForCategory
  2013. // and entryClassForCategory. This method searches the entry
  2014. // or feed map for a class with a matching category.
  2015. //
  2016. // If the registration of the class specified a value, then the corresponding
  2017. // parameter values |scheme| or |term| must match and not be nil.
  2018. + (Class)classForCategoryWithScheme:(NSString *)scheme
  2019. term:(NSString *)term
  2020. fromMap:(NSDictionary *)map {
  2021. // |scheme| and |term| are from the XML that we're using to look up
  2022. // a registered class. The |term| value should be non-nil,
  2023. // though the values stored in the map may have nil scheme or term.
  2024. //
  2025. // if the registered scheme was nil or kGDataCategoryScheme then the key
  2026. // is just the term value.
  2027. NSString *key = term;
  2028. Class result = (Class)[map objectForKey:key];
  2029. if (result) return result;
  2030. if (scheme) {
  2031. key = [NSString stringWithFormat:kCategoryTemplate, scheme, term];
  2032. result = (Class)[map objectForKey:key];
  2033. if (result) return result;
  2034. key = [NSString stringWithFormat:kCategoryTemplate, scheme, @""];
  2035. result = (Class)[map objectForKey:key];
  2036. if (result) return result;
  2037. }
  2038. return nil;
  2039. }
  2040. // objectClassForXMLElement: returns a found registered feed
  2041. // or entry class for the XML according to its contained category,
  2042. // or an Atom service document class
  2043. //
  2044. // If no registered class is found with a matching category,
  2045. // this returns GDataFeedBase for feed elements, GDataEntryBase
  2046. // for entry elements.
  2047. + (Class)objectClassForXMLElement:(NSXMLElement *)element {
  2048. Class result = nil;
  2049. NSString *elementName = [element localName];
  2050. BOOL isFeed = [elementName isEqual:@"feed"];
  2051. BOOL isEntry = [elementName isEqual:@"entry"];
  2052. if (isFeed || isEntry) {
  2053. // get the kind attribute, and see if it matches a registered feed or entry
  2054. // class
  2055. NSXMLNode *kindAttr = [element attributeForLocalName:@"kind"
  2056. URI:kGDataNamespaceGData];
  2057. NSString *kind = [kindAttr stringValue];
  2058. if (kind) {
  2059. if (isFeed) {
  2060. result = [GDataFeedBase feedClassForKindAttributeValue:kind];
  2061. } else {
  2062. result = [GDataEntryBase entryClassForKindAttributeValue:kind];
  2063. }
  2064. }
  2065. if (result == nil) {
  2066. // step through the feed or entry's category elements, looking for one
  2067. // that matches a registered feed or entry class
  2068. //
  2069. // category elements look like <category scheme="blah" term="blahblah"/>
  2070. // and there may be more than one
  2071. NSArray *categories = [element elementsForLocalName:@"category"
  2072. URI:kGDataNamespaceAtom];
  2073. if ([categories count] == 0) {
  2074. NSString *atomPrefix = [element resolvePrefixForNamespaceURI:kGDataNamespaceAtom];
  2075. if ([atomPrefix length] == 0) {
  2076. categories = [element elementsForName:@"category"];
  2077. }
  2078. }
  2079. for (NSXMLElement *categoryNode in categories) {
  2080. NSString *scheme = [[categoryNode attributeForName:@"scheme"] stringValue];
  2081. NSString *term = [[categoryNode attributeForName:@"term"] stringValue];
  2082. if (scheme || term) {
  2083. // we have a scheme or a term, so look for a registered class
  2084. if (isFeed) {
  2085. result = [GDataFeedBase feedClassForCategoryWithScheme:scheme
  2086. term:term];
  2087. } else {
  2088. result = [GDataEntryBase entryClassForCategoryWithScheme:scheme
  2089. term:term];
  2090. }
  2091. if (result) {
  2092. break;
  2093. }
  2094. }
  2095. }
  2096. }
  2097. }
  2098. if (result == nil) {
  2099. if (isFeed) {
  2100. // default to returning a feed base class
  2101. result = [GDataFeedBase class];
  2102. } else if (isEntry) {
  2103. // default to returning this feed's entry base class
  2104. if ([self isSubclassOfClass:[GDataFeedBase class]]) {
  2105. result = (Class)[self performSelector:@selector(defaultClassForEntries)];
  2106. } else {
  2107. result = [GDataEntryBase class];
  2108. }
  2109. } else if ([elementName isEqual:@"service"]) {
  2110. // introspection - return service document, if the class is available
  2111. NSString *serviceDocClassName = @"GDataAtomServiceDocument";
  2112. #ifdef GDATA_TARGET_NAMESPACE
  2113. // prepend the class name prefix
  2114. serviceDocClassName = [NSString stringWithFormat:@"%s_%@",
  2115. GDATA_TARGET_NAMESPACE_STRING, serviceDocClassName];
  2116. #endif
  2117. result = NSClassFromString(serviceDocClassName);
  2118. GDATA_DEBUG_ASSERT(result != nil, @"service class %@ unavailable",
  2119. serviceDocClassName);
  2120. } else {
  2121. // this element is not a feed, entry, or service class; give up
  2122. }
  2123. }
  2124. return result;
  2125. }
  2126. @end
  2127. @implementation NSXMLElement (GDataObjectExtensions)
  2128. - (void)addStringValue:(NSString *)str {
  2129. // NSXMLNode's setStringValue: wipes out other children, so we'll use this
  2130. // instead
  2131. // filter out non-whitespace control characters
  2132. NSString *filtered = [GDataUtilities stringWithControlsFilteredForString:str];
  2133. NSXMLNode *strNode = [NSXMLNode textWithStringValue:filtered];
  2134. [self addChild:strNode];
  2135. }
  2136. + (id)elementWithName:(NSString *)name attributeName:(NSString *)attrName attributeValue:(NSString *)attrValue {
  2137. NSString *filtered = [GDataUtilities stringWithControlsFilteredForString:attrValue];
  2138. NSXMLNode *attr = [NSXMLNode attributeWithName:attrName stringValue:filtered];
  2139. NSXMLElement *element = [NSXMLNode elementWithName:name];
  2140. [element addAttribute:attr];
  2141. return element;
  2142. }
  2143. @end
  2144. @implementation GDataExtensionDeclaration
  2145. - (id)initWithParentClass:(Class)parentClass
  2146. childClass:(Class)childClass
  2147. isAttribute:(BOOL)isAttribute {
  2148. self = [super init];
  2149. if (self) {
  2150. parentClass_ = parentClass;
  2151. childClass_ = childClass;
  2152. isAttribute_ = isAttribute;
  2153. }
  2154. return self;
  2155. }
  2156. - (NSString *)description {
  2157. return [NSString stringWithFormat:@"%@: {%@ can contain %@}%@",
  2158. [self class], parentClass_, childClass_,
  2159. isAttribute_ ? @" (attribute)" : @""];
  2160. }
  2161. - (Class)parentClass {
  2162. return parentClass_;
  2163. }
  2164. - (Class)childClass {
  2165. return childClass_;
  2166. }
  2167. - (BOOL)isAttribute {
  2168. return isAttribute_;
  2169. }
  2170. - (BOOL)isEqual:(GDataExtensionDeclaration *)other {
  2171. if (self == other) return YES;
  2172. if (![other isKindOfClass:[GDataExtensionDeclaration class]]) return NO;
  2173. return AreEqualOrBothNil((id)[self parentClass], (id)[other parentClass])
  2174. && AreEqualOrBothNil((id)[self childClass], (id)[other childClass])
  2175. && [self isAttribute] == [other isAttribute];
  2176. }
  2177. - (NSUInteger)hash {
  2178. return (NSUInteger) (void *) [GDataExtensionDeclaration class];
  2179. }
  2180. @end
  2181. @implementation GDataAttribute
  2182. // This is the base class for attribute extensions.
  2183. //
  2184. // Functionally, this just stores a string value for the attribute.
  2185. + (GDataAttribute *)attributeWithValue:(NSString *)str {
  2186. return [[[self alloc] initWithValue:str] autorelease];
  2187. }
  2188. - (id)initWithValue:(NSString *)value {
  2189. self = [super init];
  2190. if (self) {
  2191. [self setStringValue:value];
  2192. }
  2193. return self;
  2194. }
  2195. - (void)dealloc {
  2196. [value_ release];
  2197. [super dealloc];
  2198. }
  2199. - (id)copyWithZone:(NSZone *)zone {
  2200. GDataAttribute* newObj = [[[self class] allocWithZone:zone] init];
  2201. [newObj setStringValue:[self stringValue]];
  2202. return newObj;
  2203. }
  2204. - (NSString *)description {
  2205. NSString *name;
  2206. NSString *localName = [[self class] extensionElementLocalName];
  2207. NSString *prefix = [[self class] extensionElementPrefix];
  2208. if (prefix) {
  2209. name = [NSString stringWithFormat:@"%@:%@", prefix, localName];
  2210. } else {
  2211. name = localName;
  2212. }
  2213. return [NSString stringWithFormat:@"%@ %p: {%@=%@}",
  2214. [self class], self, name, [self stringValue]];
  2215. }
  2216. - (BOOL)isEqual:(GDataAttribute *)other {
  2217. if (self == other) return YES;
  2218. if (![other isKindOfClass:[GDataAttribute class]]) return NO;
  2219. return AreEqualOrBothNil([self stringValue], [other stringValue]);
  2220. }
  2221. - (NSUInteger)hash {
  2222. return (NSUInteger) (void *) [GDataAttribute class];
  2223. }
  2224. - (void)setStringValue:(NSString *)str {
  2225. [value_ autorelease];
  2226. value_ = [str copy];
  2227. }
  2228. - (NSString *)stringValue {
  2229. return value_;
  2230. }
  2231. @end