/* Copyright (c) 2011 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include //#import //#import //#import //#import #import "GTLRQuery.h" #import "GTLRRuntimeCommon.h" #import "GTLRService.h" #import "GTLRUtilities.h" #import "GTLRDefines.h" @interface GTLRQuery () @end @implementation GTLRQuery { NSMutableDictionary *_childCache; GTLRServiceExecutionParameters *_executionParameters; } @synthesize additionalURLQueryParameters = _additionalURLQueryParameters, additionalHTTPHeaders = _additionalHTTPHeaders, bodyObject = _bodyObject, completionBlock = _completionBlock, downloadAsDataObjectType = _downloadAsDataObjectType, expectedObjectClass = _expectedObjectClass, httpMethod = _httpMethod, JSON = _json, loggingName = _loggingName, pathParameterNames = _pathParameterNames, pathURITemplate = _pathURITemplate, queryInvalid = _queryInvalid, requestID = _requestID, resumableUploadPathURITemplateOverride = _resumableUploadPathURITemplateOverride, shouldSkipAuthorization = _shouldSkipAuthorization, simpleUploadPathURITemplateOverride = _simpleUploadPathURITemplateOverride, uploadParameters = _uploadParameters, useMediaDownloadService = _useMediaDownloadService; #if DEBUG - (instancetype)init { [self doesNotRecognizeSelector:_cmd]; self = nil; return self; } #endif - (instancetype)initWithPathURITemplate:(NSString *)pathURITemplate HTTPMethod:(nullable NSString *)httpMethod pathParameterNames:(nullable NSArray *)pathParameterNames { self = [super init]; if (self) { _requestID = [[self class] nextRequestID]; _pathURITemplate = [pathURITemplate copy]; _httpMethod = [httpMethod copy]; _pathParameterNames = [pathParameterNames copy]; if (_pathURITemplate.length == 0) { self = nil; } } return self; } - (id)copyWithZone:(NSZone *)zone { GTLR_DEBUG_ASSERT(!self.queryInvalid, @"Cannot copy an executed query: %@", self); GTLRQuery *query = [[[self class] allocWithZone:zone] initWithPathURITemplate:self.pathURITemplate HTTPMethod:self.httpMethod pathParameterNames:self.pathParameterNames]; if (_json.count > 0) { // Deep copy the parameters CFPropertyListRef ref = CFPropertyListCreateDeepCopy(kCFAllocatorDefault, (__bridge CFPropertyListRef)(_json), kCFPropertyListMutableContainers); query.JSON = CFBridgingRelease(ref); } // Using the executionParameters ivar avoids creating the object. query.executionParameters = self.executionParameters; // Copied in the same order as synthesized above. query.additionalHTTPHeaders = self.additionalHTTPHeaders; query.additionalURLQueryParameters = self.additionalURLQueryParameters; query.bodyObject = self.bodyObject; query.completionBlock = self.completionBlock; query.downloadAsDataObjectType = self.downloadAsDataObjectType; query.expectedObjectClass = self.expectedObjectClass; // http method passed to init above. // JSON copied above. query.loggingName = self.loggingName; // pathParameterNames passed to init above. // pathURITemplate passed to init above. query.queryInvalid = self.queryInvalid; query.requestID = self.requestID; query.resumableUploadPathURITemplateOverride = self.resumableUploadPathURITemplateOverride; query.shouldSkipAuthorization = self.shouldSkipAuthorization; query.simpleUploadPathURITemplateOverride = self.simpleUploadPathURITemplateOverride; query.uploadParameters = self.uploadParameters; query.useMediaDownloadService = self.useMediaDownloadService; return query; } #if DEBUG - (NSString *)description { NSArray *keys = self.JSON.allKeys; NSArray *params = [keys sortedArrayUsingSelector:@selector(compare:)]; NSString *paramsSummary = @""; if (params.count > 0) { paramsSummary = [NSString stringWithFormat:@" params:(%@)", [params componentsJoinedByString:@","]]; } NSString *invalidStr = @""; if (self.queryInvalid) { invalidStr = @" [callbacks released]"; } keys = self.additionalURLQueryParameters.allKeys; NSArray *urlQParams = [keys sortedArrayUsingSelector:@selector(compare:)]; NSString *urlQParamsSummary = @""; if (urlQParams.count > 0) { urlQParamsSummary = [NSString stringWithFormat:@" urlQParams:(%@)", [urlQParams componentsJoinedByString:@","]]; } GTLRObject *bodyObj = self.bodyObject; NSString *bodyObjSummary = @""; if (bodyObj != nil) { bodyObjSummary = [NSString stringWithFormat:@" bodyObject:%@", [bodyObj class]]; } NSString *uploadStr = @""; GTLRUploadParameters *uploadParams = self.uploadParameters; if (uploadParams) { uploadStr = [NSString stringWithFormat:@" %@", uploadParams]; } NSString *httpMethod = self.httpMethod; if (httpMethod == nil) { httpMethod = @"GET"; } NSString *dataObjectType = self.downloadAsDataObjectType; NSString *downloadStr = @""; if (dataObjectType.length > 0) { downloadStr = [NSString stringWithFormat:@" downloadDataAs:%@", dataObjectType]; } return [NSString stringWithFormat:@"%@ %p:%@%@ {%@ pathTemplate:%@%@%@%@%@}", [self class], self, invalidStr, downloadStr, httpMethod, self.pathURITemplate, paramsSummary, urlQParamsSummary, bodyObjSummary, uploadStr]; } #endif // DEBUG - (BOOL)isBatchQuery { return NO; } - (void)invalidateQuery { self.queryInvalid = YES; self.completionBlock = nil; self.executionParameters = nil; } - (GTLRServiceExecutionParameters *)executionParameters { @synchronized(self) { if (!_executionParameters) { _executionParameters = [[GTLRServiceExecutionParameters alloc] init]; } return _executionParameters; } } - (void)setExecutionParameters:(nullable GTLRServiceExecutionParameters *)executionParameters { @synchronized(self) { _executionParameters = executionParameters; } } - (BOOL)hasExecutionParameters { return self.executionParameters.hasParameters; } + (NSString *)nextRequestID { static NSUInteger lastRequestID = 0; NSString *result; @synchronized([GTLRQuery class]) { ++lastRequestID; result = [NSString stringWithFormat:@"gtlr_%lu", (unsigned long)lastRequestID]; } return result; } #pragma mark GTLRRuntimeCommon Support - (void)setJSONValue:(id)obj forKey:(NSString *)key { NSMutableDictionary *dict = self.JSON; if (dict == nil && obj != nil) { dict = [NSMutableDictionary dictionaryWithCapacity:1]; self.JSON = dict; } [dict setValue:obj forKey:key]; } - (id)JSONValueForKey:(NSString *)key { id obj = [self.JSON objectForKey:key]; return obj; } // There is no property for _childCache as there shouldn't be KVC/KVO // support for it, since it's an implementation detail. - (void)setCacheChild:(id)obj forKey:(NSString *)key { if (_childCache == nil && obj != nil) { _childCache = [[NSMutableDictionary alloc] initWithObjectsAndKeys:obj, key, nil]; } else { [_childCache setValue:obj forKey:key]; } } - (id)cacheChildForKey:(NSString *)key { id obj = [_childCache objectForKey:key]; return obj; } #pragma mark Methods for Subclasses to Override + (NSDictionary *)parameterNameMap { return nil; } + (NSDictionary *)arrayPropertyToClassMap { return nil; } #pragma mark Runtime Utilities static NSMutableDictionary *gQueryParameterNameMapCache = nil; static NSMutableDictionary *gQueryArrayPropertyToClassMapCache = nil; + (void)initialize { // Note that +initialize is guaranteed by the runtime to be called in a thread-safe manner. if (gQueryParameterNameMapCache == nil) { gQueryParameterNameMapCache = [[NSMutableDictionary alloc] init]; } if (gQueryArrayPropertyToClassMapCache == nil) { gQueryArrayPropertyToClassMapCache = [[NSMutableDictionary alloc] init]; } } + (NSDictionary *)propertyToJSONKeyMapForClass:(Class)aClass { NSDictionary *resultMap = [GTLRRuntimeCommon mergedClassDictionaryForSelector:@selector(parameterNameMap) startClass:aClass ancestorClass:[GTLRQuery class] cache:gQueryParameterNameMapCache]; return resultMap; } + (NSDictionary *)arrayPropertyToClassMapForClass:(Class)aClass { NSDictionary *resultMap = [GTLRRuntimeCommon mergedClassDictionaryForSelector:@selector(arrayPropertyToClassMap) startClass:aClass ancestorClass:[GTLRQuery class] cache:gQueryArrayPropertyToClassMapCache]; return resultMap; } #pragma mark Runtime Support - (id)objectClassResolver { // Stub method just needed for RuntimeCommon. return nil; } + (Class)ancestorClass { return [GTLRQuery class]; } + (BOOL)resolveInstanceMethod:(SEL)sel { BOOL resolved = [GTLRRuntimeCommon resolveInstanceMethod:sel onClass:self]; if (resolved) return YES; return [super resolveInstanceMethod:sel]; } @end @implementation GTLRQueryCollectionImpl @dynamic pageToken; @end