PageRenderTime 122ms CodeModel.GetById 17ms app.highlight 98ms RepoModel.GetById 1ms app.codeStats 0ms

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