GTLRObject.m 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770
  1. /* Copyright (c) 2011 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. #include <objc/runtime.h>
  16. //#import <GoogleAPIClientForREST/GTLRObject.h>
  17. //#import <GoogleAPIClientForREST/GTLRRuntimeCommon.h>
  18. //#import <GoogleAPIClientForREST/GTLRUtilities.h>
  19. #import "GTLRObject.h"
  20. #import "GTLRRuntimeCommon.h"
  21. #import "GTLRUtilities.h"
  22. #import "GTLRDefines.h"
  23. static NSString *const kUserDataPropertyKey = @"_userData";
  24. static NSString *const kGTLRObjectJSONCoderKey = @"json";
  25. static NSMutableDictionary *DeepMutableCopyOfJSONDictionary(NSDictionary *initialJSON);
  26. @interface GTLRObject () <GTLRRuntimeCommon>
  27. @property(nonatomic, strong) id<GTLRObjectClassResolver>objectClassResolver;
  28. @end
  29. @implementation GTLRObject {
  30. // Any complex object hung off this object goes into the cache so the
  31. // next fetch will get the same object back instead of having to recreate
  32. // it.
  33. NSMutableDictionary *_childCache;
  34. }
  35. @synthesize JSON = _json,
  36. objectClassResolver = _objectClassResolver,
  37. userProperties = _userProperties;
  38. + (instancetype)object {
  39. return [[self alloc] init];
  40. }
  41. + (instancetype)objectWithJSON:(NSDictionary *)dict {
  42. GTLRObject *obj = [self object];
  43. obj->_json = DeepMutableCopyOfJSONDictionary(dict);
  44. return obj;
  45. }
  46. + (instancetype)objectWithJSON:(nullable NSDictionary *)dict
  47. objectClassResolver:(id<GTLRObjectClassResolver>)objectClassResolver {
  48. GTLRObject *obj = [self objectWithJSON:dict];
  49. obj->_objectClassResolver = objectClassResolver;
  50. return obj;
  51. }
  52. + (NSDictionary<NSString *, NSString *> *)propertyToJSONKeyMap {
  53. return nil;
  54. }
  55. + (NSDictionary<NSString *, Class> *)arrayPropertyToClassMap {
  56. return nil;
  57. }
  58. + (Class)classForAdditionalProperties {
  59. return Nil;
  60. }
  61. + (BOOL)isKindValidForClassRegistry {
  62. return YES;
  63. }
  64. - (BOOL)isEqual:(GTLRObject *)other {
  65. if (self == other) return YES;
  66. if (other == nil) return NO;
  67. // The objects should be the same class, or one should be a subclass of the
  68. // other's class
  69. if (![other isKindOfClass:[self class]]
  70. && ![self isKindOfClass:[other class]]) return NO;
  71. // What we're not comparing here:
  72. // properties
  73. return GTLR_AreEqualOrBothNil(_json, [other JSON]);
  74. }
  75. // By definition, for two objects to potentially be considered equal,
  76. // they must have the same hash value. The hash is mostly ignored,
  77. // but removeObjectsInArray: in Leopard does seem to check the hash,
  78. // and NSObject's default hash method just returns the instance pointer.
  79. // We'll define hash here for all of our GTLRObjects.
  80. - (NSUInteger)hash {
  81. return (NSUInteger) (__bridge void *) [GTLRObject class];
  82. }
  83. - (id)copyWithZone:(NSZone *)zone {
  84. GTLRObject *newObject = [[[self class] allocWithZone:zone] init];
  85. newObject.JSON = DeepMutableCopyOfJSONDictionary(self.JSON);
  86. newObject.objectClassResolver = self.objectClassResolver;
  87. // What we're not copying:
  88. // userProperties
  89. return newObject;
  90. }
  91. - (NSString *)descriptionWithLocale:(id)locale {
  92. return self.description;
  93. }
  94. + (BOOL)supportsSecureCoding {
  95. return YES;
  96. }
  97. - (instancetype)initWithCoder:(NSCoder *)decoder {
  98. self = [super init];
  99. if (self) {
  100. // NSDictionary/NSArray seem to allow strings and numbers with secure coding
  101. // just fine, but to allow sub arrays or dictionaries (or an null) the
  102. // classes have to be explicitly listed to decode correctly.
  103. NSSet *expectedClasses =
  104. [NSSet setWithObjects:
  105. [NSMutableDictionary class], [NSMutableArray class], [NSNull class], nil];
  106. _json = [decoder decodeObjectOfClasses:expectedClasses
  107. forKey:kGTLRObjectJSONCoderKey];
  108. }
  109. return self;
  110. }
  111. - (void)encodeWithCoder:(NSCoder *)encoder {
  112. [encoder encodeObject:_json forKey:kGTLRObjectJSONCoderKey];
  113. }
  114. #pragma mark JSON values
  115. - (void)setJSONValue:(id)obj forKey:(NSString *)key {
  116. NSMutableDictionary *dict = self.JSON;
  117. if (dict == nil && obj != nil) {
  118. dict = [NSMutableDictionary dictionaryWithCapacity:1];
  119. self.JSON = dict;
  120. }
  121. [dict setValue:obj forKey:key];
  122. }
  123. - (id)JSONValueForKey:(NSString *)key {
  124. id obj = [self.JSON objectForKey:key];
  125. return obj;
  126. }
  127. - (NSString *)JSONString {
  128. NSError *error;
  129. NSDictionary *json = self.JSON;
  130. if (json) {
  131. NSData *data = [NSJSONSerialization dataWithJSONObject:json
  132. options:NSJSONWritingPrettyPrinted
  133. error:&error];
  134. GTLR_DEBUG_ASSERT(data != nil, @"JSONString generate failed: %@\n JSON: %@", error, json);
  135. if (data) {
  136. NSString *jsonStr = [[NSString alloc] initWithData:data
  137. encoding:NSUTF8StringEncoding];
  138. if (jsonStr) return jsonStr;
  139. }
  140. }
  141. return @"";
  142. }
  143. - (NSArray<NSString *> *)additionalJSONKeys {
  144. NSArray *knownKeys = [[self class] allKnownKeys];
  145. NSMutableArray *result;
  146. NSArray *allKeys = _json.allKeys;
  147. if (allKeys) {
  148. result = [NSMutableArray arrayWithArray:allKeys];
  149. [result removeObjectsInArray:knownKeys];
  150. // Return nil instead of an empty array.
  151. if (result.count == 0) {
  152. result = nil;
  153. }
  154. }
  155. return result;
  156. }
  157. #pragma mark Partial - Fields
  158. - (NSString *)fieldsDescription {
  159. NSString *str = [GTLRObject fieldsDescriptionForJSON:self.JSON];
  160. return str;
  161. }
  162. + (NSString *)fieldsDescriptionForJSON:(NSDictionary *)targetJSON {
  163. // Internal routine: recursively generate a string field description
  164. // by joining elements
  165. NSArray *array = [self fieldsElementsForJSON:targetJSON];
  166. NSString *str = [array componentsJoinedByString:@","];
  167. return str;
  168. }
  169. + (NSArray *)fieldsElementsForJSON:(NSDictionary *)targetJSON {
  170. // Internal routine: recursively generate an array of field description
  171. // element strings
  172. NSMutableArray *resultFields = [NSMutableArray array];
  173. // Sorting the dictionary keys gives us deterministic results when iterating
  174. NSArray *sortedKeys = [targetJSON.allKeys sortedArrayUsingSelector:@selector(caseInsensitiveCompare:)];
  175. for (NSString *key in sortedKeys) {
  176. // We'll build a comma-separated list of fields
  177. id value = [targetJSON objectForKey:key];
  178. if ([value isKindOfClass:[NSString class]]
  179. || [value isKindOfClass:[NSNumber class]]) {
  180. // Basic type (string, number), so the key is what we want
  181. [resultFields addObject:key];
  182. } else if ([value isKindOfClass:[NSDictionary class]]) {
  183. // Object (dictionary): "parent/child1,parent/child2,parent/child3"
  184. NSArray *subElements = [self fieldsElementsForJSON:value];
  185. for (NSString *subElem in subElements) {
  186. NSString *prepended = [NSString stringWithFormat:@"%@/%@",
  187. key, subElem];
  188. [resultFields addObject:prepended];
  189. }
  190. } else if ([value isKindOfClass:[NSArray class]]) {
  191. // Array; we'll generate from the first array entry:
  192. // "parent(child1,child2,child3)"
  193. //
  194. // Open question: should this instead create the union of elements for
  195. // all items in the array, rather than just get fields from the first
  196. // array object?
  197. if (((NSArray *)value).count > 0) {
  198. id firstObj = [value objectAtIndex:0];
  199. if ([firstObj isKindOfClass:[NSDictionary class]]) {
  200. // An array of objects
  201. NSString *contentsStr = [self fieldsDescriptionForJSON:firstObj];
  202. NSString *encapsulated = [NSString stringWithFormat:@"%@(%@)",
  203. key, contentsStr];
  204. [resultFields addObject:encapsulated];
  205. } else {
  206. // An array of some basic type, or of arrays
  207. [resultFields addObject:key];
  208. }
  209. }
  210. } else {
  211. NSAssert(0, @"GTLRObject unknown field element for %@ (%@)",
  212. key, NSStringFromClass([value class]));
  213. }
  214. }
  215. return resultFields;
  216. }
  217. #pragma mark Partial - Patch
  218. - (id)patchObjectFromOriginal:(GTLRObject *)original {
  219. GTLRObject *resultObj;
  220. NSMutableDictionary *resultJSON = [GTLRObject patchDictionaryForJSON:self.JSON
  221. fromOriginalJSON:original.JSON];
  222. if (resultJSON.count > 0) {
  223. // Avoid an extra copy by assigning the JSON directly rather than using +objectWithJSON:
  224. resultObj = [[self class] object];
  225. resultObj.JSON = resultJSON;
  226. } else {
  227. // Client apps should not attempt to patch with an object containing
  228. // empty JSON
  229. resultObj = nil;
  230. }
  231. return resultObj;
  232. }
  233. + (NSMutableDictionary *)patchDictionaryForJSON:(NSDictionary *)newJSON
  234. fromOriginalJSON:(NSDictionary *)originalJSON {
  235. // Internal recursive routine to create an object suitable for
  236. // our patch semantics
  237. NSMutableDictionary *resultJSON = [NSMutableDictionary dictionary];
  238. // Iterate through keys present in the old object
  239. NSArray *originalKeys = originalJSON.allKeys;
  240. for (NSString *key in originalKeys) {
  241. id originalValue = [originalJSON objectForKey:key];
  242. id newValue = [newJSON valueForKey:key];
  243. if (newValue == nil) {
  244. // There is no new value for this key, so set the value to NSNull
  245. [resultJSON setValue:[NSNull null] forKey:key];
  246. } else if (!GTLR_AreEqualOrBothNil(originalValue, newValue)) {
  247. // The values for this key differ
  248. if ([originalValue isKindOfClass:[NSDictionary class]]
  249. && [newValue isKindOfClass:[NSDictionary class]]) {
  250. // Both are objects; recurse
  251. NSMutableDictionary *subDict = [self patchDictionaryForJSON:newValue
  252. fromOriginalJSON:originalValue];
  253. [resultJSON setValue:subDict forKey:key];
  254. } else {
  255. // They are non-object values; the new replaces the old. Per the
  256. // documentation for patch, this replaces entire arrays.
  257. [resultJSON setValue:newValue forKey:key];
  258. }
  259. } else {
  260. // The values are the same; omit this key-value pair
  261. }
  262. }
  263. // Iterate through keys present only in the new object, and add them to the
  264. // result
  265. NSMutableArray *newKeys = [NSMutableArray arrayWithArray:newJSON.allKeys];
  266. [newKeys removeObjectsInArray:originalKeys];
  267. for (NSString *key in newKeys) {
  268. id value = [newJSON objectForKey:key];
  269. [resultJSON setValue:value forKey:key];
  270. }
  271. return resultJSON;
  272. }
  273. + (id)nullValue {
  274. return [NSNull null];
  275. }
  276. #pragma mark Additional Properties
  277. - (id)additionalPropertyForName:(NSString *)name {
  278. // Return the cached object, if any, before creating one.
  279. id result = [self cacheChildForKey:name];
  280. if (result != nil) {
  281. return result;
  282. }
  283. Class defaultClass = [[self class] classForAdditionalProperties];
  284. id jsonObj = [self JSONValueForKey:name];
  285. BOOL shouldCache = NO;
  286. if (jsonObj != nil) {
  287. id<GTLRObjectClassResolver>objectClassResolver = self.objectClassResolver;
  288. result = [GTLRRuntimeCommon objectFromJSON:jsonObj
  289. defaultClass:defaultClass
  290. objectClassResolver:objectClassResolver
  291. isCacheable:&shouldCache];
  292. }
  293. [self setCacheChild:(shouldCache ? result : nil)
  294. forKey:name];
  295. return result;
  296. }
  297. - (void)setAdditionalProperty:(id)obj forName:(NSString *)name {
  298. BOOL shouldCache = NO;
  299. Class defaultClass = [[self class] classForAdditionalProperties];
  300. id json = [GTLRRuntimeCommon jsonFromAPIObject:obj
  301. expectedClass:defaultClass
  302. isCacheable:&shouldCache];
  303. [self setJSONValue:json forKey:name];
  304. [self setCacheChild:(shouldCache ? obj : nil)
  305. forKey:name];
  306. }
  307. - (NSDictionary<NSString *, id> *)additionalProperties {
  308. NSMutableDictionary *result = [NSMutableDictionary dictionary];
  309. NSArray *propertyNames = [self additionalJSONKeys];
  310. for (NSString *name in propertyNames) {
  311. id obj = [self additionalPropertyForName:name];
  312. [result setObject:obj forKey:name];
  313. }
  314. return result;
  315. }
  316. #pragma mark Child Cache methods
  317. // There is no property for _childCache as there shouldn't be KVC/KVO
  318. // support for it, it's an implementation detail.
  319. - (void)setCacheChild:(id)obj forKey:(NSString *)key {
  320. if (_childCache == nil && obj != nil) {
  321. _childCache = [[NSMutableDictionary alloc] initWithObjectsAndKeys:
  322. obj, key, nil];
  323. } else {
  324. [_childCache setValue:obj forKey:key];
  325. }
  326. }
  327. - (id)cacheChildForKey:(NSString *)key {
  328. id obj = [_childCache objectForKey:key];
  329. return obj;
  330. }
  331. #pragma mark Support methods
  332. + (NSMutableArray *)allDeclaredProperties {
  333. NSMutableArray *array = [NSMutableArray array];
  334. // walk from this class up the hierarchy to GTLRObject
  335. Class topClass = class_getSuperclass([GTLRObject class]);
  336. for (Class currClass = self;
  337. currClass != topClass;
  338. currClass = class_getSuperclass(currClass)) {
  339. // step through this class's properties, and add the property names to the
  340. // array
  341. objc_property_t *properties = class_copyPropertyList(currClass, NULL);
  342. if (properties) {
  343. for (objc_property_t *prop = properties;
  344. *prop != NULL;
  345. ++prop) {
  346. const char *propName = property_getName(*prop);
  347. // We only want dynamic properties; their attributes contain ",D".
  348. const char *attr = property_getAttributes(*prop);
  349. const char *dynamicMarker = strstr(attr, ",D");
  350. if (dynamicMarker &&
  351. (dynamicMarker[2] == 0 || dynamicMarker[2] == ',' )) {
  352. [array addObject:(id _Nonnull)@(propName)];
  353. }
  354. }
  355. free(properties);
  356. }
  357. }
  358. return array;
  359. }
  360. + (NSArray *)allKnownKeys {
  361. NSArray *allProps = [self allDeclaredProperties];
  362. NSMutableArray *knownKeys = [NSMutableArray arrayWithArray:allProps];
  363. NSDictionary *propMap = [GTLRObject propertyToJSONKeyMapForClass:[self class]];
  364. NSUInteger idx = 0;
  365. for (NSString *propName in allProps) {
  366. NSString *jsonKey = [propMap objectForKey:propName];
  367. if (jsonKey) {
  368. [knownKeys replaceObjectAtIndex:idx
  369. withObject:jsonKey];
  370. }
  371. ++idx;
  372. }
  373. return knownKeys;
  374. }
  375. - (NSString *)description {
  376. NSString *jsonDesc = [self JSONDescription];
  377. NSString *str = [NSString stringWithFormat:@"%@ %p: %@",
  378. [self class], self, jsonDesc];
  379. return str;
  380. }
  381. // Internal utility for creating an appropriate description summary for the object's JSON.
  382. - (NSString *)JSONDescription {
  383. // Find the list of declared and otherwise known JSON keys for this class.
  384. NSArray *knownKeys = [[self class] allKnownKeys];
  385. NSMutableString *descStr = [NSMutableString stringWithString:@"{"];
  386. NSString *spacer = @"";
  387. for (NSString *key in [[_json allKeys] sortedArrayUsingSelector:@selector(caseInsensitiveCompare:)]) {
  388. NSString *value = nil;
  389. // show question mark for JSON keys not supported by a declared property:
  390. // foo?:"Hi mom."
  391. NSString *qmark = [knownKeys containsObject:key] ? @"" : @"?";
  392. // determine property value to dislay
  393. id rawValue = [_json valueForKey:key];
  394. if ([rawValue isKindOfClass:[NSDictionary class]]) {
  395. // for dictionaries, show the list of keys:
  396. // {key1,key2,key3}
  397. NSArray *subKeys = [((NSDictionary *)rawValue).allKeys sortedArrayUsingSelector:@selector(caseInsensitiveCompare:)];
  398. NSString *subkeyList = [subKeys componentsJoinedByString:@","];
  399. value = [NSString stringWithFormat:@"{%@}", subkeyList];
  400. } else if ([rawValue isKindOfClass:[NSArray class]]) {
  401. // for arrays, show the number of items in the array:
  402. // [3]
  403. value = [NSString stringWithFormat:@"[%lu]",
  404. (unsigned long)((NSArray *)rawValue).count];
  405. } else if ([rawValue isKindOfClass:[NSString class]]) {
  406. // for strings, show the string in quotes:
  407. // "Hi mom."
  408. value = [NSString stringWithFormat:@"\"%@\"", rawValue];
  409. } else {
  410. // for numbers, show just the number
  411. value = [rawValue description];
  412. }
  413. [descStr appendFormat:@"%@%@%@:%@", spacer, key, qmark, value];
  414. spacer = @" ";
  415. }
  416. [descStr appendString:@"}"];
  417. return descStr;
  418. }
  419. #pragma mark Object Instantiation
  420. + (GTLRObject *)objectForJSON:(NSMutableDictionary *)json
  421. defaultClass:(Class)defaultClass
  422. objectClassResolver:(id<GTLRObjectClassResolver>)objectClassResolver {
  423. if (((id)json == [NSNull null]) || json.count == 0) {
  424. if (json != nil && defaultClass != Nil) {
  425. // The JSON included an empty dictionary, just create the object.
  426. Class classToCreate =
  427. GTLRObjectResolveClass(objectClassResolver,
  428. [NSDictionary dictionary],
  429. defaultClass);
  430. return [classToCreate object];
  431. }
  432. // No actual result, such as the response from a delete.
  433. return nil;
  434. }
  435. if (defaultClass == Nil) {
  436. defaultClass = self;
  437. }
  438. Class classToCreate =
  439. GTLRObjectResolveClass(objectClassResolver, json, defaultClass);
  440. // now instantiate the GTLRObject
  441. GTLRObject *parsedObject = [classToCreate object];
  442. parsedObject.objectClassResolver = objectClassResolver;
  443. parsedObject.JSON = json;
  444. return parsedObject;
  445. }
  446. #pragma mark Runtime Utilities
  447. static NSMutableDictionary *gJSONKeyMapCache = nil;
  448. static NSMutableDictionary *gArrayPropertyToClassMapCache = nil;
  449. + (void)initialize {
  450. // Note that initialize is guaranteed by the runtime to be called in a
  451. // thread-safe manner
  452. if (gJSONKeyMapCache == nil) {
  453. gJSONKeyMapCache = [[NSMutableDictionary alloc] init];
  454. }
  455. if (gArrayPropertyToClassMapCache == nil) {
  456. gArrayPropertyToClassMapCache = [[NSMutableDictionary alloc] init];
  457. }
  458. }
  459. + (NSDictionary *)propertyToJSONKeyMapForClass:(Class<GTLRRuntimeCommon>)aClass {
  460. NSDictionary *resultMap =
  461. [GTLRRuntimeCommon mergedClassDictionaryForSelector:@selector(propertyToJSONKeyMap)
  462. startClass:aClass
  463. ancestorClass:[GTLRObject class]
  464. cache:gJSONKeyMapCache];
  465. return resultMap;
  466. }
  467. + (NSDictionary *)arrayPropertyToClassMapForClass:(Class<GTLRRuntimeCommon>)aClass {
  468. NSDictionary *resultMap =
  469. [GTLRRuntimeCommon mergedClassDictionaryForSelector:@selector(arrayPropertyToClassMap)
  470. startClass:aClass
  471. ancestorClass:[GTLRObject class]
  472. cache:gArrayPropertyToClassMapCache];
  473. return resultMap;
  474. }
  475. #pragma mark Runtime Support
  476. + (Class<GTLRRuntimeCommon>)ancestorClass {
  477. return [GTLRObject class];
  478. }
  479. + (BOOL)resolveInstanceMethod:(SEL)sel {
  480. BOOL resolved = [GTLRRuntimeCommon resolveInstanceMethod:sel onClass:self];
  481. if (resolved)
  482. return YES;
  483. return [super resolveInstanceMethod:sel];
  484. }
  485. @end
  486. @implementation GTLRCollectionObject
  487. + (NSString *)collectionItemsKey {
  488. // GTLRCollectionObject fast enumeration, indexed access, and automatic pagination
  489. // (when shouldFetchNextPages is enabled) applies to the object array property "items".
  490. // The array property's key may be different if subclasses override this method.
  491. return @"items";
  492. }
  493. - (id)objectAtIndexedSubscript:(NSUInteger)idx {
  494. NSString *key = [[self class] collectionItemsKey];
  495. NSArray *items = [self valueForKey:key];
  496. if (items == nil) {
  497. [NSException raise:NSRangeException
  498. format:@"index %lu beyond bounds (%@ property \"%@\" is nil)",
  499. (unsigned long)idx, [self class], key];
  500. }
  501. id result = [items objectAtIndexedSubscript:idx];
  502. return result;
  503. }
  504. // NSFastEnumeration protocol
  505. - (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state
  506. objects:(__unsafe_unretained id _Nonnull *)stackbuf
  507. count:(NSUInteger)len {
  508. NSString *key = [[self class] collectionItemsKey];
  509. NSArray *items = [self valueForKey:key];
  510. NSUInteger result = [items countByEnumeratingWithState:state
  511. objects:stackbuf
  512. count:len];
  513. return result;
  514. }
  515. @end
  516. @implementation GTLRDataObject
  517. @synthesize data = _data,
  518. contentType = _contentType;
  519. - (NSString *)description {
  520. NSString *jsonDesc = @"";
  521. if (self.JSON.count > 0) {
  522. jsonDesc = [self JSONDescription];
  523. }
  524. return [NSString stringWithFormat:@"%@ %p: %lu bytes, contentType:%@ %@",
  525. [self class], self, (unsigned long)self.data.length, self.contentType,
  526. jsonDesc];
  527. }
  528. - (id)copyWithZone:(NSZone *)zone {
  529. GTLRDataObject *newObj = [super copyWithZone:zone];
  530. newObj.data = [self.data copy];
  531. newObj.contentType = self.contentType;
  532. return newObj;
  533. }
  534. @end
  535. @implementation GTLRResultArray
  536. - (NSArray *)itemsWithItemClass:(Class)itemClass {
  537. // Return the cached array before creating on demand.
  538. NSString *cacheKey = @"result_array_items";
  539. NSMutableArray *cachedArray = [self cacheChildForKey:cacheKey];
  540. if (cachedArray != nil) {
  541. return cachedArray;
  542. }
  543. NSArray *result = nil;
  544. NSArray *array = (NSArray *)self.JSON;
  545. if (array != nil) {
  546. if ([array isKindOfClass:[NSArray class]]) {
  547. id<GTLRObjectClassResolver>objectClassResolver = self.objectClassResolver;
  548. result = [GTLRRuntimeCommon objectFromJSON:array
  549. defaultClass:itemClass
  550. objectClassResolver:objectClassResolver
  551. isCacheable:NULL];
  552. } else {
  553. #if DEBUG
  554. if (![array isKindOfClass:[NSNull class]]) {
  555. GTLR_DEBUG_LOG(@"GTLRObject: unexpected JSON: %@ should be an array, actually is a %@:\n%@",
  556. NSStringFromClass([self class]),
  557. NSStringFromClass([array class]),
  558. array);
  559. }
  560. #endif
  561. result = array;
  562. }
  563. }
  564. [self setCacheChild:result forKey:cacheKey];
  565. return result;
  566. }
  567. - (NSString *)JSONDescription {
  568. // Just like GTLRObject's handing of arrays, just return the count.
  569. return [NSString stringWithFormat:@"[%lu]", (unsigned long)self.JSON.count];
  570. }
  571. @end
  572. Class GTLRObjectResolveClass(
  573. id<GTLRObjectClassResolver>objectClassResolver,
  574. NSDictionary *json,
  575. Class defaultClass) {
  576. Class result = [objectClassResolver classForJSON:json
  577. defaultClass:defaultClass];
  578. if (result == Nil) {
  579. result = defaultClass;
  580. }
  581. return result;
  582. }
  583. @implementation GTLRObjectClassResolver {
  584. NSDictionary<NSString *, Class> *_kindToClassMap;
  585. NSDictionary<Class, Class> *_surrogates;
  586. }
  587. + (instancetype)resolverWithKindMap:(NSDictionary<NSString *, Class> *)kindStringToClassMap {
  588. GTLRObjectClassResolver *result = [[self alloc] initWithKindMap:kindStringToClassMap
  589. surrogates:nil];
  590. return result;
  591. }
  592. + (instancetype)resolverWithKindMap:(NSDictionary<NSString *, Class> *)kindStringToClassMap
  593. surrogates:(NSDictionary<Class, Class> *)surrogates {
  594. GTLRObjectClassResolver *result = [[self alloc] initWithKindMap:kindStringToClassMap
  595. surrogates:surrogates];
  596. return result;
  597. }
  598. - (instancetype)initWithKindMap:(NSDictionary<NSString *, Class> *)kindStringToClassMap
  599. surrogates:(NSDictionary<Class, Class> *)surrogates {
  600. self = [super init];
  601. if (self) {
  602. _kindToClassMap = [kindStringToClassMap copy];
  603. _surrogates = [surrogates copy];
  604. }
  605. return self;
  606. }
  607. - (Class)classForJSON:(NSDictionary *)json
  608. defaultClass:(Class)defaultClass {
  609. Class result = defaultClass;
  610. // Apply kind map.
  611. BOOL shouldUseKind = (result == Nil) || [result isKindValidForClassRegistry];
  612. if (shouldUseKind && [json isKindOfClass:[NSDictionary class]]) {
  613. NSString *kind = [json valueForKey:@"kind"];
  614. if ([kind isKindOfClass:[NSString class]] && kind.length > 0) {
  615. Class dynamicClass = [_kindToClassMap objectForKey:kind];
  616. if (dynamicClass) {
  617. result = dynamicClass;
  618. }
  619. }
  620. }
  621. // Apply surrogate map.
  622. Class surrogate = [_surrogates objectForKey:result];
  623. if (surrogate) {
  624. result = surrogate;
  625. }
  626. return result;
  627. }
  628. @end
  629. static NSMutableDictionary *DeepMutableCopyOfJSONDictionary(NSDictionary *initialJSON) {
  630. if (!initialJSON) return nil;
  631. NSMutableDictionary *result;
  632. CFPropertyListRef ref = CFPropertyListCreateDeepCopy(kCFAllocatorDefault,
  633. (__bridge CFPropertyListRef)(initialJSON),
  634. kCFPropertyListMutableContainers);
  635. if (ref) {
  636. result = CFBridgingRelease(ref);
  637. } else {
  638. // Failed to copy, probably due to a non-plist type such as NSNull.
  639. //
  640. // As a fallback, round-trip through NSJSONSerialization.
  641. NSError *serializationError;
  642. NSData *data = [NSJSONSerialization dataWithJSONObject:initialJSON
  643. options:0
  644. error:&serializationError];
  645. if (!data) {
  646. GTLR_DEBUG_ASSERT(0, @"Copy failed due to serialization: %@\nJSON: %@",
  647. serializationError, initialJSON);
  648. } else {
  649. result = [NSJSONSerialization JSONObjectWithData:data
  650. options:NSJSONReadingMutableContainers
  651. error:&serializationError];
  652. GTLR_DEBUG_ASSERT(result != nil, @"Copy failed due to deserialization: %@\nJSON: %@",
  653. serializationError,
  654. [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);
  655. }
  656. }
  657. return result;
  658. }