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

http://macfuse.googlecode.com/ · Objective C · 792 lines · 529 code · 181 blank · 82 comment · 34 complexity · 58dcf35f90df14d6ae6f277fdcd4a7b2 MD5 · raw file

  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. // GDataFeedBase.m
  17. //
  18. #define GDATAFEEDBASE_DEFINE_GLOBALS 1
  19. #import "GDataFeedBase.h"
  20. #import "GDataBaseElements.h"
  21. @interface GDataFeedBase (PrivateMethods)
  22. - (void)setupFromXMLElement:(NSXMLElement *)root;
  23. @end
  24. @implementation GDataFeedBase
  25. + (NSString *)standardFeedKind {
  26. // overridden by feed subclasses
  27. //
  28. // Feeds and entries typically have a "kind" atom:category element
  29. // indicating their contents; see
  30. // http://code.google.com/apis/gdata/elements.html#Introduction
  31. //
  32. // Subclasses may override this method with the "term" attribute string
  33. // for their kind category. This is used in the plain -init method,
  34. // and from +registerFeedClass
  35. //
  36. return nil;
  37. }
  38. + (NSString *)standardKindAttributeValue {
  39. // overridden by feed subclasses
  40. //
  41. // Feeds and entries in core v2.1 and later have a kind attribute rather than
  42. // kind categories
  43. //
  44. // Subclasses may override this method with the proper kind attribute string
  45. // used to identify this class
  46. //
  47. return nil;
  48. }
  49. - (void)addExtensionDeclarations {
  50. [super addExtensionDeclarations];
  51. Class feedClass = [self class];
  52. [self addExtensionDeclarationForParentClass:feedClass
  53. childClasses:
  54. // GData extensions
  55. [GDataResourceID class],
  56. // Atom extensions
  57. [GDataAtomID class],
  58. [GDataAtomTitle class],
  59. [GDataAtomSubtitle class],
  60. [GDataAtomRights class],
  61. [GDataAtomIcon class],
  62. [GDataAtomLogo class],
  63. [GDataLink class],
  64. [GDataAtomAuthor class],
  65. [GDataAtomContributor class],
  66. [GDataCategory class],
  67. [GDataAtomUpdatedDate class],
  68. // atom publishing control support
  69. [GDataAtomPubControl class],
  70. // batch support
  71. [GDataBatchOperation class],
  72. nil];
  73. [self addExtensionDeclarationForParentClass:feedClass
  74. childClasses:
  75. [GDataOpenSearchTotalResults class],
  76. [GDataOpenSearchStartIndex class],
  77. [GDataOpenSearchItemsPerPage class],
  78. nil];
  79. // Attributes
  80. [self addAttributeExtensionDeclarationForParentClass:feedClass
  81. childClass:[GDataETagAttribute class]];
  82. [self addAttributeExtensionDeclarationForParentClass:feedClass
  83. childClass:[GDataFieldsAttribute class]];
  84. [self addAttributeExtensionDeclarationForParentClass:feedClass
  85. childClass:[GDataKindAttribute class]];
  86. }
  87. + (id)feedWithXMLData:(NSData *)data {
  88. return [[[self alloc] initWithData:data] autorelease];
  89. }
  90. - (id)init {
  91. self = [super init];
  92. if (self) {
  93. // if the subclass declares a kind, then add a category element for the
  94. // kind
  95. NSString *categoryKind = [[self class] standardFeedKind];
  96. if (categoryKind) {
  97. GDataCategory *category;
  98. category = [GDataCategory categoryWithScheme:kGDataCategoryScheme
  99. term:categoryKind];
  100. [self addCategory:category];
  101. }
  102. NSString *attributeKind = [[self class] standardKindAttributeValue];
  103. if (attributeKind) {
  104. [self setKind:attributeKind];
  105. }
  106. }
  107. return self;
  108. }
  109. - (id)initWithXMLElement:(NSXMLElement *)element
  110. parent:(GDataObject *)parent {
  111. // entry point for creation of feeds inside elements
  112. self = [super initWithXMLElement:element
  113. parent:nil];
  114. if (self) {
  115. [self setupFromXMLElement:element];
  116. }
  117. return self;
  118. }
  119. - (id)initWithData:(NSData *)data {
  120. return [self initWithData:data
  121. serviceVersion:nil
  122. shouldIgnoreUnknowns:NO];
  123. }
  124. - (id)initWithData:(NSData *)data
  125. serviceVersion:(NSString *)serviceVersion
  126. shouldIgnoreUnknowns:(BOOL)shouldIgnoreUnknowns {
  127. // entry point for creation of feeds from file or network data
  128. NSError *error = nil;
  129. NSXMLDocument *xmlDocument = [[[NSXMLDocument alloc] initWithData:data
  130. options:0
  131. error:&error] autorelease];
  132. if (xmlDocument) {
  133. NSXMLElement* root = [xmlDocument rootElement];
  134. self = [super initWithXMLElement:root
  135. parent:nil
  136. serviceVersion:serviceVersion
  137. surrogates:nil
  138. shouldIgnoreUnknowns:NO];
  139. if (self) {
  140. // we're done parsing; the extension declarations won't be needed again
  141. [self clearExtensionDeclarationsCache];
  142. #if GDATA_USES_LIBXML
  143. // retain the document so that pointers to internal nodes remain valid
  144. [self setProperty:xmlDocument forKey:kGDataXMLDocumentPropertyKey];
  145. #endif
  146. }
  147. return self;
  148. } else {
  149. // could not parse XML into a document
  150. [self release];
  151. return nil;
  152. }
  153. }
  154. - (void)setupFromXMLElement:(NSXMLElement *)root {
  155. // we'll parse the generator manually rather than declare it to be an
  156. // extension so that it won't be compared in isEquals: for the feed
  157. [self setGenerator:[self objectForChildOfElement:root
  158. qualifiedName:@"generator"
  159. namespaceURI:kGDataNamespaceAtom
  160. objectClass:[GDataGenerator class]]];
  161. // call subclasses to set up their feed ivars
  162. [self initFeedWithXMLElement:root];
  163. // allocate individual entries
  164. Class entryClass = [self classForEntries];
  165. GDATA_DEBUG_ASSERT([[root localName] isEqual:@"feed"],
  166. @"initing a feed from a non-feed element (%@)", [root name]);
  167. // create entries of the proper class from each "entry" element
  168. id entryObj = [self objectOrArrayForChildrenOfElement:root
  169. qualifiedName:@"entry"
  170. namespaceURI:kGDataNamespaceAtom
  171. objectClass:entryClass];
  172. if ([entryObj isKindOfClass:[NSArray class]]) {
  173. // save the array
  174. [self setEntries:entryObj];
  175. } else if (entryObj != nil) {
  176. // save the object into an array
  177. [self addEntry:entryObj];
  178. }
  179. }
  180. - (void)dealloc {
  181. [generator_ release];
  182. [entries_ release];
  183. [super dealloc];
  184. }
  185. - (id)copyWithZone:(NSZone *)zone {
  186. GDataFeedBase* newFeed = [super copyWithZone:zone];
  187. [newFeed setGenerator:[self generator]];
  188. [newFeed setEntriesWithEntries:[self entries]];
  189. return newFeed;
  190. }
  191. - (BOOL)isEqual:(GDataFeedBase *)other {
  192. if (self == other) return YES;
  193. if (![other isKindOfClass:[GDataFeedBase class]]) return NO;
  194. return [super isEqual:other]
  195. && AreEqualOrBothNil([self entries], [other entries]);
  196. // excluding generator
  197. }
  198. #if !GDATA_SIMPLE_DESCRIPTIONS
  199. - (NSMutableArray *)itemsForDescription { // subclasses may implement this
  200. NSArray *linkNames = [GDataLink linkNamesFromLinks:[self links]];
  201. NSString *linksStr = [linkNames componentsJoinedByString:@","];
  202. struct GDataDescriptionRecord descRecs[] = {
  203. { @"v", @"serviceVersion", kGDataDescValueLabeled },
  204. { @"entries", @"entries", kGDataDescArrayCount },
  205. { @"etag", @"ETag", kGDataDescValueLabeled },
  206. { @"kind", @"kind", kGDataDescValueLabeled },
  207. { @"fields", @"fieldSelection", kGDataDescValueLabeled },
  208. { @"resourceID", @"resourceID", kGDataDescValueLabeled },
  209. { @"title", @"title.stringValue", kGDataDescValueLabeled },
  210. { @"subtitle", @"subtitle.stringValue", kGDataDescValueLabeled },
  211. { @"rights", @"rights.stringValue", kGDataDescValueLabeled },
  212. { @"updated", @"updatedDate.stringValue", kGDataDescValueLabeled },
  213. { @"authors", @"authors", kGDataDescArrayCount },
  214. { @"contributors", @"contributors", kGDataDescArrayCount },
  215. { @"categories", @"categories", kGDataDescArrayCount },
  216. { @"links", linksStr, kGDataDescValueIsKeyPath },
  217. { @"id", @"identifier", kGDataDescValueLabeled },
  218. { nil, nil, (GDataDescRecTypes)0 }
  219. };
  220. // these are present but not very useful most of the time...
  221. // @"totalResults"
  222. // @"startIndex"
  223. // @"itemsPerPage"
  224. NSMutableArray *items = [super itemsForDescription];
  225. [self addDescriptionRecords:descRecs toItems:items];
  226. return items;
  227. }
  228. #endif
  229. - (NSXMLElement *)XMLElement {
  230. NSXMLElement *element = [self XMLElementWithExtensionsAndDefaultName:@"feed"];
  231. if ([self generator]) {
  232. [element addChild:[[self generator] XMLElement]];
  233. }
  234. [self addToElement:element XMLElementsForArray:[self entries]];
  235. return element;
  236. }
  237. #pragma mark -
  238. - (void)initFeedWithXMLElement:(NSXMLElement *)element {
  239. // subclasses override this to set up their feed ivars from the XML
  240. }
  241. // subclasses may override this, and may return a specific entry class or
  242. // kUseRegisteredEntryClass
  243. - (Class)classForEntries {
  244. return kUseRegisteredEntryClass;
  245. }
  246. // subclasses may override this to specify a "generic" class for
  247. // the feed's entries, if not GDataEntryBase, mainly for when there
  248. // is no registered entry class found
  249. + (Class)defaultClassForEntries {
  250. return [GDataEntryBase class];
  251. }
  252. - (BOOL)canPost {
  253. return ([self postLink] != nil);
  254. }
  255. #pragma mark Dynamic object generation - Entry registration
  256. //
  257. // feed registration & lookup for dynamic object generation
  258. //
  259. static NSMutableDictionary *gFeedClassKindMap = nil;
  260. + (void)registerFeedClass {
  261. NSString *categoryKind = [self standardFeedKind];
  262. if (categoryKind) {
  263. [self registerClass:self
  264. inMap:&gFeedClassKindMap
  265. forCategoryWithScheme:kGDataCategoryScheme
  266. term:categoryKind];
  267. }
  268. NSString *attributeKind = [self standardKindAttributeValue];
  269. if (attributeKind) {
  270. [self registerClass:self
  271. inMap:&gFeedClassKindMap
  272. forCategoryWithScheme:nil
  273. term:attributeKind];
  274. }
  275. GDATA_DEBUG_ASSERT(attributeKind != nil || categoryKind != nil,
  276. @"cannot register feed without a kind");
  277. }
  278. + (void)registerFeedClass:(Class)theClass
  279. forCategoryWithScheme:(NSString *)scheme
  280. term:(NSString *)term {
  281. // temporary bridge method - will be removed when subclasses all call
  282. // -registerFeedClass
  283. [self registerClass:theClass
  284. inMap:&gFeedClassKindMap
  285. forCategoryWithScheme:scheme
  286. term:term];
  287. }
  288. + (Class)feedClassForCategoryWithScheme:(NSString *)scheme
  289. term:(NSString *)term {
  290. return [self classForCategoryWithScheme:scheme
  291. term:term
  292. fromMap:gFeedClassKindMap];
  293. }
  294. + (Class)feedClassForKindAttributeValue:(NSString *)kind {
  295. return [self classForCategoryWithScheme:nil
  296. term:kind
  297. fromMap:gFeedClassKindMap];
  298. }
  299. #pragma mark Getters and Setters
  300. - (NSString *)identifier {
  301. GDataAtomID *obj = [self objectForExtensionClass:[GDataAtomID class]];
  302. return [obj stringValue];
  303. }
  304. - (void)setIdentifier:(NSString *)str {
  305. GDataAtomID *obj = [GDataAtomID valueWithString:str];
  306. [self setObject:obj forExtensionClass:[GDataAtomID class]];
  307. }
  308. - (GDataGenerator *)generator {
  309. return generator_;
  310. }
  311. - (void)setGenerator:(GDataGenerator *)gen {
  312. [generator_ autorelease];
  313. generator_ = [gen copy];
  314. }
  315. - (GDataTextConstruct *)title {
  316. GDataAtomTitle *obj = [self objectForExtensionClass:[GDataAtomTitle class]];
  317. return obj;
  318. }
  319. - (void)setTitle:(GDataTextConstruct *)obj {
  320. [self setObject:obj forExtensionClass:[GDataAtomTitle class]];
  321. }
  322. - (void)setTitleWithString:(NSString *)str {
  323. GDataAtomTitle *obj = [GDataAtomTitle textConstructWithString:str];
  324. [self setObject:obj forExtensionClass:[GDataAtomTitle class]];
  325. }
  326. - (GDataTextConstruct *)subtitle {
  327. GDataAtomSubtitle *obj = [self objectForExtensionClass:[GDataAtomSubtitle class]];
  328. return obj;
  329. }
  330. - (void)setSubtitle:(GDataTextConstruct *)obj {
  331. [self setObject:obj forExtensionClass:[GDataAtomSubtitle class]];
  332. }
  333. - (void)setSubtitleWithString:(NSString *)str {
  334. GDataAtomSubtitle *obj = [GDataAtomSubtitle textConstructWithString:str];
  335. [self setObject:obj forExtensionClass:[GDataAtomSubtitle class]];
  336. }
  337. - (GDataTextConstruct *)rights {
  338. GDataTextConstruct *obj;
  339. obj = [self objectForExtensionClass:[GDataAtomRights class]];
  340. return obj;
  341. }
  342. - (void)setRights:(GDataTextConstruct *)obj {
  343. [self setObject:obj forExtensionClass:[GDataAtomRights class]];
  344. }
  345. - (void)setRightsWithString:(NSString *)str {
  346. GDataAtomRights *obj;
  347. obj = [GDataAtomRights textConstructWithString:str];
  348. [self setObject:obj forExtensionClass:[GDataAtomRights class]];
  349. }
  350. - (NSString *)icon {
  351. GDataAtomIcon *obj = [self objectForExtensionClass:[GDataAtomIcon class]];
  352. return [obj stringValue];
  353. }
  354. - (void)setIcon:(NSString *)str {
  355. GDataAtomIcon *obj = [GDataAtomID valueWithString:str];
  356. [self setObject:obj forExtensionClass:[GDataAtomIcon class]];
  357. }
  358. - (NSString *)logo {
  359. GDataAtomLogo *obj = [self objectForExtensionClass:[GDataAtomLogo class]];
  360. return [obj stringValue];
  361. }
  362. - (void)setLogo:(NSString *)str {
  363. GDataAtomLogo *obj = [GDataAtomLogo valueWithString:str];
  364. [self setObject:obj forExtensionClass:[GDataAtomLogo class]];
  365. }
  366. - (NSArray *)links {
  367. NSArray *array = [self objectsForExtensionClass:[GDataLink class]];
  368. return array;
  369. }
  370. - (void)setLinks:(NSArray *)array {
  371. [self setObjects:array forExtensionClass:[GDataLink class]];
  372. }
  373. - (void)addLink:(GDataLink *)obj {
  374. [self addObject:obj forExtensionClass:[GDataLink class]];
  375. }
  376. - (void)removeLink:(GDataLink *)obj {
  377. [self removeObject:obj forExtensionClass:[GDataLink class]];
  378. }
  379. - (NSArray *)authors {
  380. NSArray *array = [self objectsForExtensionClass:[GDataAtomAuthor class]];
  381. return array;
  382. }
  383. - (void)setAuthors:(NSArray *)array {
  384. [self setObjects:array forExtensionClass:[GDataAtomAuthor class]];
  385. }
  386. - (void)addAuthor:(GDataPerson *)obj {
  387. [self addObject:obj forExtensionClass:[GDataAtomAuthor class]];
  388. }
  389. - (NSArray *)contributors {
  390. NSArray *array = [self objectsForExtensionClass:[GDataAtomContributor class]];
  391. return array;
  392. }
  393. - (void)setContributors:(NSArray *)array {
  394. [self setObjects:array forExtensionClass:[GDataAtomContributor class]];
  395. }
  396. - (void)addContributor:(GDataPerson *)obj {
  397. [self addObject:obj forExtensionClass:[GDataAtomContributor class]];
  398. }
  399. - (NSArray *)categories {
  400. NSArray *array = [self objectsForExtensionClass:[GDataCategory class]];
  401. return array;
  402. }
  403. - (void)setCategories:(NSArray *)array {
  404. [self setObjects:array forExtensionClass:[GDataCategory class]];
  405. }
  406. - (void)addCategory:(GDataCategory *)obj {
  407. [self addObject:obj forExtensionClass:[GDataCategory class]];
  408. }
  409. - (void)removeCategory:(GDataCategory *)obj {
  410. [self removeObject:obj forExtensionClass:[GDataCategory class]];
  411. }
  412. - (GDataDateTime *)updatedDate {
  413. GDataAtomUpdatedDate *obj;
  414. obj = [self objectForExtensionClass:[GDataAtomUpdatedDate class]];
  415. return [obj dateTimeValue];
  416. }
  417. - (void)setUpdatedDate:(GDataDateTime *)dateTime {
  418. GDataAtomUpdatedDate *obj;
  419. obj = [GDataAtomUpdatedDate valueWithDateTime:dateTime];
  420. [self setObject:obj forExtensionClass:[GDataAtomUpdatedDate class]];
  421. }
  422. - (NSNumber *)totalResults {
  423. GDataValueElementConstruct *obj;
  424. obj = [self objectForExtensionClass:[GDataOpenSearchTotalResults class]];
  425. return [obj intNumberValue];
  426. }
  427. - (void)setTotalResults:(NSNumber *)num {
  428. GDataValueElementConstruct *obj;
  429. obj = [GDataOpenSearchTotalResults valueWithNumber:num];
  430. [self setObject:obj forExtensionClass:[GDataOpenSearchTotalResults class]];
  431. }
  432. - (NSNumber *)startIndex {
  433. GDataValueElementConstruct *obj;
  434. obj = [self objectForExtensionClass:[GDataOpenSearchStartIndex class]];
  435. return [obj intNumberValue];
  436. }
  437. - (void)setStartIndex:(NSNumber *)num {
  438. GDataValueElementConstruct *obj;
  439. obj = [GDataOpenSearchStartIndex valueWithNumber:num];
  440. [self setObject:obj forExtensionClass:[GDataOpenSearchStartIndex class]];
  441. }
  442. - (NSNumber *)itemsPerPage {
  443. GDataValueElementConstruct *obj;
  444. obj = [self objectForExtensionClass:[GDataOpenSearchItemsPerPage class]];
  445. return [obj intNumberValue];
  446. }
  447. - (void)setItemsPerPage:(NSNumber *)num {
  448. GDataValueElementConstruct *obj;
  449. obj = [GDataOpenSearchItemsPerPage valueWithNumber:num];
  450. [self setObject:obj forExtensionClass:[GDataOpenSearchItemsPerPage class]];
  451. }
  452. - (NSString *)ETag {
  453. NSString *str = [self attributeValueForExtensionClass:[GDataETagAttribute class]];
  454. return str;
  455. }
  456. - (void)setETag:(NSString *)str {
  457. [self setAttributeValue:str forExtensionClass:[GDataETagAttribute class]];
  458. }
  459. - (NSString *)fieldSelection {
  460. NSString *str = [self attributeValueForExtensionClass:[GDataFieldsAttribute class]];
  461. return str;
  462. }
  463. - (void)setFieldSelection:(NSString *)str {
  464. [self setAttributeValue:str forExtensionClass:[GDataFieldsAttribute class]];
  465. }
  466. - (NSString *)kind {
  467. NSString *str = [self attributeValueForExtensionClass:[GDataKindAttribute class]];
  468. return str;
  469. }
  470. - (void)setKind:(NSString *)str {
  471. [self setAttributeValue:str forExtensionClass:[GDataKindAttribute class]];
  472. }
  473. - (NSString *)resourceID {
  474. GDataResourceID *obj = [self objectForExtensionClass:[GDataResourceID class]];
  475. return [obj stringValue];
  476. }
  477. - (void)setResourceID:(NSString *)str {
  478. GDataResourceID *obj = [GDataResourceID valueWithString:str];
  479. [self setObject:obj forExtensionClass:[GDataResourceID class]];
  480. }
  481. - (NSArray *)entries {
  482. return entries_;
  483. }
  484. // setEntries: and addEntry: expect the entries to have parents that are
  485. // nil or this feed instance; setEntriesWithEntries: and addEntryWithEntry:
  486. // make copies of the supplied entries
  487. - (void)setEntries:(NSArray *)entries {
  488. [entries_ autorelease];
  489. entries_ = [entries mutableCopy];
  490. // step through the entries, ensure that none have other parents,
  491. // make each have this feed as parent
  492. for (GDataObject* entry in entries_) {
  493. #if !NS_BLOCK_ASSERTIONS
  494. GDataObject *oldParent = [entry parent];
  495. GDATA_ASSERT(oldParent == self || oldParent == nil,
  496. @"Trying to replace existing feed parent; use setEntriesWithEntries: instead");
  497. #endif
  498. [entry setParent:self];
  499. }
  500. }
  501. - (void)addEntry:(GDataEntryBase *)obj {
  502. if (!entries_) {
  503. entries_ = [[NSMutableArray alloc] init];
  504. }
  505. // ensure the entry doesn't have another parent
  506. #if !NS_BLOCK_ASSERTIONS
  507. GDataObject *oldParent = [obj parent];
  508. GDATA_ASSERT(oldParent == self || oldParent == nil,
  509. @"Trying to replace existing feed parent; use addEntryWithEntry: instead");
  510. #endif
  511. [obj setParent:self];
  512. [entries_ addObject:obj];
  513. }
  514. - (void)setEntriesWithEntries:(NSArray *)entries {
  515. // make an array containing copies of the entries with this feed
  516. // as the parent of each entry copy
  517. [entries_ autorelease];
  518. entries_ = nil;
  519. if (entries != nil) {
  520. entries_ = [[NSMutableArray alloc] initWithCapacity:[entries count]];
  521. for (GDataObject *entry in entries) {
  522. GDataEntryBase *entryCopy = [[entry copy] autorelease]; // clears parent in copy
  523. [entryCopy setParent:self];
  524. [entries_ addObject:entryCopy];
  525. }
  526. }
  527. }
  528. - (void)addEntryWithEntry:(GDataEntryBase *)obj {
  529. GDataEntryBase *entryCopy = [[obj copy] autorelease]; // clears parent in copy
  530. [self addEntry:entryCopy];
  531. }
  532. // extensions for Atom publishing control
  533. - (GDataAtomPubControl *)atomPubControl {
  534. return [self objectForExtensionClass:[GDataAtomPubControl class]];
  535. }
  536. - (void)setAtomPubControl:(GDataAtomPubControl *)obj {
  537. [self setObject:obj forExtensionClass:[GDataAtomPubControl class]];
  538. }
  539. // extensions for batch support
  540. - (GDataBatchOperation *)batchOperation {
  541. return [self objectForExtensionClass:[GDataBatchOperation class]];
  542. }
  543. - (void)setBatchOperation:(GDataBatchOperation *)obj {
  544. [self setObject:obj forExtensionClass:[GDataBatchOperation class]];
  545. }
  546. // convenience routines
  547. - (GDataLink *)linkWithRelAttributeValue:(NSString *)rel {
  548. return [GDataLink linkWithRel:rel
  549. type:nil
  550. fromLinks:[self links]];
  551. }
  552. - (GDataLink *)feedLink {
  553. return [self linkWithRelAttributeValue:kGDataLinkRelFeed];
  554. }
  555. - (GDataLink *)alternateLink {
  556. return [self linkWithRelAttributeValue:@"alternate"];
  557. }
  558. - (GDataLink *)relatedLink {
  559. return [self linkWithRelAttributeValue:@"related"];
  560. }
  561. - (GDataLink *)postLink {
  562. return [self linkWithRelAttributeValue:kGDataLinkRelPost];
  563. }
  564. - (GDataLink *)uploadLink {
  565. return [self linkWithRelAttributeValue:kGDataLinkRelResumableCreate];
  566. }
  567. - (GDataLink *)batchLink {
  568. return [self linkWithRelAttributeValue:kGDataLinkRelBatch];
  569. }
  570. - (GDataLink *)selfLink {
  571. return [self linkWithRelAttributeValue:@"self"];
  572. }
  573. - (GDataLink *)nextLink {
  574. return [self linkWithRelAttributeValue:@"next"];
  575. }
  576. - (GDataLink *)previousLink {
  577. return [self linkWithRelAttributeValue:@"previous"];
  578. }
  579. - (id)entryForIdentifier:(NSString *)str {
  580. GDataEntryBase *desiredEntry;
  581. desiredEntry = [GDataUtilities firstObjectFromArray:[self entries]
  582. withValue:str
  583. forKeyPath:@"identifier"];
  584. return desiredEntry;
  585. }
  586. - (id)firstEntry {
  587. return [self entryAtIndex:0];
  588. }
  589. - (id)entryAtIndex:(NSUInteger)idx {
  590. NSArray *entries = [self entries];
  591. if ([entries count] > idx) {
  592. return [entries objectAtIndex:idx];
  593. }
  594. return nil;
  595. }
  596. - (NSArray *)entriesWithCategoryKind:(NSString *)term {
  597. NSArray *kindEntries = [GDataUtilities objectsFromArray:[self entries]
  598. withValue:term
  599. forKeyPath:@"kindCategory.term"];
  600. return kindEntries;
  601. }
  602. // NSFastEnumeration protocol
  603. - (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state
  604. objects:(id *)stackbuf
  605. count:(NSUInteger)len {
  606. NSUInteger result = [entries_ countByEnumeratingWithState:state
  607. objects:stackbuf
  608. count:len];
  609. return result;
  610. }
  611. @end