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

http://macfuse.googlecode.com/ · Objective C · 699 lines · 484 code · 144 blank · 71 comment · 57 complexity · e9898e880e6303ea3fccac707f029372 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. // GDataQuery.m
  17. //
  18. #define GDATAQUERY_DEFINE_GLOBALS 1
  19. #import "GDataQuery.h"
  20. static NSString *const kAltParamName = @"alt";
  21. static NSString *const kAuthorParamName = @"author";
  22. static NSString *const kErrorParamName = @"err";
  23. static NSString *const kFieldsParamName = @"fields";
  24. static NSString *const kFullTextQueryStringParamName = @"q";
  25. static NSString *const kLanguageParamName = @"hl";
  26. static NSString *const kMaxResultsParamName = @"max-results";
  27. static NSString *const kOrderByParamName = @"orderby";
  28. static NSString *const kPrettyPrintParamName = @"prettyprint";
  29. static NSString *const kProtocolVersionParamName = @"v";
  30. static NSString *const kPublishedMaxParamName = @"published-max";
  31. static NSString *const kPublishedMinParamName = @"published-min";
  32. static NSString *const kShowDeletedParamName = @"showdeleted";
  33. static NSString *const kRequireAllDeletedParamName = @"requirealldeleted";
  34. static NSString *const kOnlyDeletedParamName = @"onlydeleted";
  35. static NSString *const kSortOrderParamName = @"sortorder";
  36. static NSString *const kStartIndexParamName = @"start-index";
  37. static NSString *const kStrictParamName = @"strict";
  38. static NSString *const kUpdatedMaxParamName = @"updated-max";
  39. static NSString *const kUpdatedMinParamName = @"updated-min";
  40. @implementation GDataCategoryFilter
  41. + (GDataCategoryFilter *)categoryFilter {
  42. return [[[self alloc] init] autorelease];
  43. }
  44. - (void)dealloc {
  45. [categories_ release];
  46. [excludeCategories_ release];
  47. [super dealloc];
  48. }
  49. - (id)copyWithZone:(NSZone *)zone {
  50. GDataCategoryFilter *newFilter = [[[self class] allocWithZone:zone] init];
  51. NSArray *catsCopy = [GDataUtilities mutableArrayWithCopiesOfObjectsInArray:categories_];
  52. [newFilter setCategories:catsCopy];
  53. NSArray *excludeCatsCopy = [GDataUtilities mutableArrayWithCopiesOfObjectsInArray:excludeCategories_];
  54. [newFilter setExcludeCategories:excludeCatsCopy];
  55. return newFilter;
  56. }
  57. - (NSString *)description {
  58. return [NSString stringWithFormat:@"%@ %p: {%@}",
  59. [self class], self, [self stringValue]];
  60. }
  61. - (NSArray *)categories {
  62. return categories_;
  63. }
  64. - (void)setCategories:(NSArray *)array {
  65. [categories_ autorelease];
  66. categories_ = [array mutableCopy];
  67. }
  68. - (void)addCategory:(GDataCategory *)category {
  69. if (!categories_) {
  70. categories_ = [[NSMutableArray alloc] init];
  71. }
  72. [categories_ addObject:category];
  73. }
  74. - (void)addCategoryWithScheme:(NSString *)scheme term:(NSString *)term {
  75. GDataCategory *cat = [GDataCategory categoryWithScheme:scheme term:term];
  76. [self addCategory:cat];
  77. }
  78. - (NSArray *)excludeCategories {
  79. return excludeCategories_;
  80. }
  81. - (void)setExcludeCategories:(NSArray *)array {
  82. [excludeCategories_ autorelease];
  83. excludeCategories_ = [array mutableCopy];
  84. }
  85. - (void)addExcludeCategory:(GDataCategory *)excludeCategory {
  86. if (!excludeCategories_) {
  87. excludeCategories_ = [[NSMutableArray alloc] init];
  88. }
  89. [excludeCategories_ addObject:excludeCategory];
  90. }
  91. - (void)addExcludeCategoryWithScheme:(NSString *)scheme term:(NSString *)term {
  92. GDataCategory *cat = [GDataCategory categoryWithScheme:scheme term:term];
  93. [self addExcludeCategory:cat];
  94. }
  95. - (NSString *)queryStringForCategory:(GDataCategory *)category {
  96. // precede the category term with {scheme} if there's a scheme
  97. NSString *prefix = @"";
  98. NSString *scheme = [category scheme];
  99. if (scheme) {
  100. prefix = [NSString stringWithFormat:@"{%@}", scheme];
  101. }
  102. NSString *term = [category term];
  103. NSString *result = [NSString stringWithFormat:@"%@%@", prefix, term];
  104. return result;
  105. }
  106. - (NSString *)stringValue {
  107. NSMutableString *result = [NSMutableString string];
  108. // append include categories
  109. for (GDataCategory *cat in categories_) {
  110. if ([result length] > 0) {
  111. [result appendString:@"|"];
  112. }
  113. [result appendString:[self queryStringForCategory:cat]];
  114. }
  115. // append exclude categories, preceded by "-"
  116. for (GDataCategory *cat in excludeCategories_) {
  117. if ([result length] > 0) {
  118. [result appendString:@"|"];
  119. }
  120. [result appendFormat:@"-%@", [self queryStringForCategory:cat]];
  121. }
  122. return result;
  123. }
  124. @end
  125. //
  126. // GDataQuery
  127. //
  128. @implementation GDataQuery
  129. + (id)queryWithFeedURL:(NSURL *)feedURL {
  130. return [[[self alloc] initWithFeedURL:feedURL] autorelease];
  131. }
  132. - (id)initWithFeedURL:(NSURL *)feedURL {
  133. self = [super init];
  134. if (self) {
  135. [self setFeedURL:feedURL];
  136. }
  137. return self;
  138. }
  139. - (void)dealloc {
  140. [feedURL_ release];
  141. [categoryFilters_ release];
  142. [customParameters_ release];
  143. [super dealloc];
  144. }
  145. - (id)copyWithZone:(NSZone *)zone {
  146. GDataQuery *query = [[[self class] allocWithZone:zone] init];
  147. [query setFeedURL:feedURL_];
  148. [query setCategoryFilters:categoryFilters_];
  149. [query setCustomParameters:customParameters_];
  150. return query;
  151. }
  152. - (NSString *)description {
  153. return [NSString stringWithFormat:@"%@ %p: {%@}",
  154. [self class], self, [[self URL] absoluteString]];
  155. }
  156. #pragma mark -
  157. - (NSURL *)feedURL {
  158. return feedURL_;
  159. }
  160. - (void)setFeedURL:(NSURL *)feedURL {
  161. [feedURL_ release];
  162. feedURL_ = [feedURL retain];
  163. }
  164. - (NSInteger)startIndex {
  165. return [self intValueForParameterWithName:kStartIndexParamName
  166. missingParameterValue:-1];
  167. }
  168. - (void)setStartIndex:(NSInteger)startIndex {
  169. [self addCustomParameterWithName:kStartIndexParamName
  170. intValue:startIndex
  171. removeForValue:-1];
  172. }
  173. - (NSInteger)maxResults {
  174. return [self intValueForParameterWithName:kMaxResultsParamName
  175. missingParameterValue:-1];
  176. }
  177. - (void)setMaxResults:(NSInteger)maxResults {
  178. [self addCustomParameterWithName:kMaxResultsParamName
  179. intValue:maxResults
  180. removeForValue:-1];
  181. }
  182. - (NSString *)fieldSelection {
  183. NSString *str = [self valueForParameterWithName:kFieldsParamName];
  184. return str;
  185. }
  186. - (void)setFieldSelection:(NSString *)str {
  187. // the library needs gd:kind attributes in feeds and entries during parsing of
  188. // the fetched XML, so field selection queries should always include those
  189. //
  190. // There may be other valid ways to specify a field selection that includes
  191. // the gd:kind attribute (in which case this conditional should be improved)
  192. // but hopefully this will minimize the time developers spend wondering
  193. // why the returned XML isn't instantiating the expected classes.
  194. #if DEBUG
  195. if (str != nil
  196. && [str rangeOfString:@"@gd:kind"].location == NSNotFound
  197. && [str rangeOfString:@"@gd:*"].location == NSNotFound) {
  198. GDATA_DEBUG_LOG(@"query field selection \"%@\" needs @gd:* or @gd:kind",
  199. str);
  200. }
  201. #endif
  202. [self addCustomParameterWithName:kFieldsParamName
  203. value:str];
  204. }
  205. - (NSString *)fullTextQueryString {
  206. NSString *str;
  207. str = [self valueForParameterWithName:kFullTextQueryStringParamName];
  208. return str;
  209. }
  210. - (void)setFullTextQueryString:(NSString *)str {
  211. [self addCustomParameterWithName:kFullTextQueryStringParamName
  212. value:str];
  213. }
  214. - (NSString *)author {
  215. NSString *str = [self valueForParameterWithName:kAuthorParamName];
  216. return str;
  217. }
  218. - (void)setAuthor:(NSString *)str {
  219. [self addCustomParameterWithName:kAuthorParamName
  220. value:str];
  221. }
  222. - (NSString *)orderBy {
  223. NSString *str = [self valueForParameterWithName:kOrderByParamName];
  224. return str;
  225. }
  226. - (void)setOrderBy:(NSString *)str {
  227. [self addCustomParameterWithName:kOrderByParamName
  228. value:str];
  229. }
  230. - (BOOL)isAscendingOrder {
  231. NSString *str = [self valueForParameterWithName:kSortOrderParamName];
  232. BOOL isAscending = (str != nil)
  233. && ([str caseInsensitiveCompare:@"ascending"] == NSOrderedSame);
  234. return isAscending;
  235. }
  236. - (void)setIsAscendingOrder:(BOOL)flag {
  237. NSString *str = (flag ? @"ascending" : @"descending");
  238. [self addCustomParameterWithName:kSortOrderParamName
  239. value:str];
  240. }
  241. - (BOOL)shouldShowDeleted {
  242. return [self boolValueForParameterWithName:kShowDeletedParamName
  243. defaultValue:NO];
  244. }
  245. - (void)setShouldShowDeleted:(BOOL)flag {
  246. [self addCustomParameterWithName:kShowDeletedParamName
  247. boolValue:flag
  248. defaultValue:NO];
  249. }
  250. - (BOOL)shouldRequireAllDeleted {
  251. return [self boolValueForParameterWithName:kRequireAllDeletedParamName
  252. defaultValue:NO];
  253. }
  254. - (void)setShouldRequireAllDeleted:(BOOL)flag {
  255. [self addCustomParameterWithName:kRequireAllDeletedParamName
  256. boolValue:flag
  257. defaultValue:NO];
  258. }
  259. - (BOOL)shouldShowOnlyDeleted {
  260. return [self boolValueForParameterWithName:kOnlyDeletedParamName
  261. defaultValue:NO];
  262. }
  263. - (void)setShouldShowOnlyDeleted:(BOOL)flag {
  264. [self addCustomParameterWithName:kOnlyDeletedParamName
  265. boolValue:flag
  266. defaultValue:NO];
  267. }
  268. - (BOOL)isStrict {
  269. return [self boolValueForParameterWithName:kStrictParamName
  270. defaultValue:NO];
  271. }
  272. - (void)setIsStrict:(BOOL)flag {
  273. [self addCustomParameterWithName:kStrictParamName
  274. boolValue:flag
  275. defaultValue:NO];
  276. }
  277. - (BOOL)shouldPrettyPrint {
  278. return [self boolValueForParameterWithName:kPrettyPrintParamName
  279. defaultValue:NO];
  280. }
  281. - (void)setShouldPrettyPrint:(BOOL)flag {
  282. [self addCustomParameterWithName:kPrettyPrintParamName
  283. boolValue:flag
  284. defaultValue:NO];
  285. }
  286. - (NSString *)protocolVersion {
  287. return [self valueForParameterWithName:kProtocolVersionParamName];
  288. }
  289. - (void)setProtocolVersion:(NSString *)str {
  290. [self addCustomParameterWithName:kProtocolVersionParamName
  291. value:str];
  292. }
  293. - (NSString *)resultFormat {
  294. NSString *str = [self valueForParameterWithName:kAltParamName];
  295. return str;
  296. }
  297. - (void)setResultFormat:(NSString *)str {
  298. [self addCustomParameterWithName:kAltParamName value:str];
  299. }
  300. - (NSString *)language {
  301. return [self valueForParameterWithName:kLanguageParamName];
  302. }
  303. - (void)setLanguage:(NSString *)str {
  304. [self addCustomParameterWithName:kLanguageParamName
  305. value:str];
  306. }
  307. - (GDataDateTime *)publishedMinDateTime {
  308. return [self dateTimeForParameterWithName:kPublishedMinParamName];
  309. }
  310. - (void)setPublishedMinDateTime:(GDataDateTime *)dateTime {
  311. [self addCustomParameterWithName:kPublishedMinParamName
  312. dateTime:dateTime];
  313. }
  314. - (GDataDateTime *)publishedMaxDateTime {
  315. return [self dateTimeForParameterWithName:kPublishedMaxParamName];
  316. }
  317. - (void)setPublishedMaxDateTime:(GDataDateTime *)dateTime {
  318. [self addCustomParameterWithName:kPublishedMaxParamName
  319. dateTime:dateTime];
  320. }
  321. - (GDataDateTime *)updatedMinDateTime {
  322. return [self dateTimeForParameterWithName:kUpdatedMinParamName];
  323. }
  324. - (void)setUpdatedMinDateTime:(GDataDateTime *)dateTime {
  325. [self addCustomParameterWithName:kUpdatedMinParamName
  326. dateTime:dateTime];
  327. }
  328. - (GDataDateTime *)updatedMaxDateTime {
  329. return [self dateTimeForParameterWithName:kUpdatedMaxParamName];
  330. }
  331. - (void)setUpdatedMaxDateTime:(GDataDateTime *)dateTime {
  332. [self addCustomParameterWithName:kUpdatedMaxParamName
  333. dateTime:dateTime];
  334. }
  335. - (BOOL)shouldFormatErrorsAsXML {
  336. NSString *value = [self valueForParameterWithName:kErrorParamName];
  337. BOOL flag = (value != nil)
  338. && ([value caseInsensitiveCompare:@"xml"] == NSOrderedSame);
  339. return flag;
  340. }
  341. - (void)setShouldFormatErrorsAsXML:(BOOL)flag {
  342. [self addCustomParameterWithName:kErrorParamName
  343. value:(flag ? @"xml" : nil)];
  344. }
  345. - (NSArray *)categoryFilters {
  346. return categoryFilters_;
  347. }
  348. - (void)setCategoryFilters:(NSArray *)filters {
  349. [categoryFilters_ autorelease];
  350. categoryFilters_ = [filters mutableCopy];
  351. }
  352. - (void)addCategoryFilter:(GDataCategoryFilter *)filter {
  353. if (!categoryFilters_) {
  354. categoryFilters_ = [[NSMutableArray alloc] init];
  355. }
  356. [categoryFilters_ addObject:filter];
  357. }
  358. - (void)addCategoryFilterWithCategory:(GDataCategory *)category {
  359. GDataCategoryFilter *filter = [GDataCategoryFilter categoryFilter];
  360. [filter addCategory:category];
  361. [self addCategoryFilter:filter];
  362. }
  363. - (void)addCategoryFilterWithScheme:(NSString *)scheme term:(NSString *)term {
  364. GDataCategoryFilter *filter = [GDataCategoryFilter categoryFilter];
  365. [filter addCategoryWithScheme:scheme term:term];
  366. [self addCategoryFilter:filter];
  367. }
  368. #pragma mark -
  369. - (NSDictionary *)customParameters {
  370. return customParameters_;
  371. }
  372. - (void)setCustomParameters:(NSDictionary *)dict {
  373. [customParameters_ autorelease];
  374. customParameters_ = [dict mutableCopy];
  375. }
  376. - (void)addCustomParameterWithName:(NSString *)name
  377. value:(NSString *)value {
  378. if (value == nil) {
  379. [self removeCustomParameterWithName:name];
  380. return;
  381. }
  382. if (!customParameters_) {
  383. customParameters_ = [[NSMutableDictionary alloc] init];
  384. }
  385. [customParameters_ setValue:value forKey:name];
  386. }
  387. - (void)removeCustomParameterWithName:(NSString *)name {
  388. [customParameters_ removeObjectForKey:name];
  389. }
  390. - (NSString *)valueForParameterWithName:(NSString *)name {
  391. NSString *str = [[self customParameters] objectForKey:name];
  392. return str;
  393. }
  394. // convenience methods for dateTime parameters
  395. - (void)addCustomParameterWithName:(NSString *)name
  396. dateTime:(GDataDateTime *)dateTime {
  397. [self addCustomParameterWithName:name
  398. value:[dateTime RFC3339String]];
  399. }
  400. - (GDataDateTime *)dateTimeForParameterWithName:(NSString *)name {
  401. NSString *str = [customParameters_ objectForKey:name];
  402. if (str) {
  403. return [GDataDateTime dateTimeWithRFC3339String:str];
  404. }
  405. return nil;
  406. }
  407. // convenience methods for int parameters
  408. //
  409. // if val==invalidVal, the parameter is removed
  410. - (void)addCustomParameterWithName:(NSString *)name
  411. intValue:(NSInteger)val
  412. removeForValue:(NSInteger)invalidVal {
  413. NSString *str = nil;
  414. if (val != invalidVal) {
  415. str = [[NSNumber numberWithInt:(int)val] stringValue];
  416. }
  417. [self addCustomParameterWithName:name
  418. value:str];
  419. }
  420. // if the named parameter is not found, missingVal is returned
  421. - (NSInteger)intValueForParameterWithName:(NSString *)name
  422. missingParameterValue:(NSInteger)missingVal {
  423. NSString *str = [customParameters_ objectForKey:name];
  424. if (str != nil) return (NSInteger) [str intValue];
  425. return missingVal;
  426. }
  427. // convenience method for boolean parameters
  428. - (void)addCustomParameterWithName:(NSString *)name
  429. boolValue:(BOOL)flag
  430. defaultValue:(BOOL)defaultValue {
  431. NSString *str = nil;
  432. if (defaultValue) {
  433. // default is true
  434. if (!flag) str = @"false";
  435. } else {
  436. // default is false
  437. if (flag) str = @"true";
  438. }
  439. // nil value will remove the parameter
  440. [self addCustomParameterWithName:name
  441. value:str];
  442. }
  443. - (BOOL)boolValueForParameterWithName:(NSString *)name
  444. defaultValue:(BOOL)defaultValue {
  445. NSString *str = [self valueForParameterWithName:name];
  446. if (defaultValue) {
  447. // default is true, so return true if param is missing or
  448. // is "true"
  449. return (str == nil)
  450. || ([str caseInsensitiveCompare:@"true"] == NSOrderedSame);
  451. } else {
  452. // default is false, so return true only if the param is present
  453. // and "true"
  454. return (str != nil)
  455. && ([str caseInsensitiveCompare:@"true"] == NSOrderedSame);
  456. }
  457. }
  458. #pragma mark -
  459. // categoryFilterString generates the category portion of the URL path
  460. - (NSString *)categoryFilterString {
  461. // make a path string containing the category filters
  462. NSMutableString *pathStr = [NSMutableString string];
  463. if ([categoryFilters_ count] > 0) {
  464. [pathStr appendString:@"-"];
  465. for (id filter in categoryFilters_) {
  466. NSString *filterValue = [filter stringValue];
  467. NSString *filterStr = [GDataUtilities stringByURLEncodingForURI:filterValue];
  468. if ([filterStr length] > 0) {
  469. [pathStr appendFormat:@"/%@", filterStr];
  470. }
  471. }
  472. }
  473. return pathStr;
  474. }
  475. - (NSString *)queryParamString {
  476. // make a query string containing all the query params. We'll put them
  477. // all into an array, then use NSArray's componentsJoinedByString:
  478. NSMutableArray *queryItems = [NSMutableArray array];
  479. // sort the custom parameter keys so that we have deterministic parameter
  480. // order for unit tests
  481. NSDictionary *customParameters = [self customParameters];
  482. NSArray *customKeys = [customParameters allKeys];
  483. NSArray *sortedCustomKeys = [customKeys sortedArrayUsingSelector:@selector(caseInsensitiveCompare:)];
  484. for (id paramKey in sortedCustomKeys) {
  485. NSString *paramValue = [customParameters valueForKey:paramKey];
  486. NSString *paramItem = [NSString stringWithFormat:@"%@=%@",
  487. [GDataUtilities stringByURLEncodingStringParameter:paramKey],
  488. [GDataUtilities stringByURLEncodingStringParameter:paramValue]];
  489. [queryItems addObject:paramItem];
  490. }
  491. NSString *paramStr = [queryItems componentsJoinedByString:@"&"];
  492. return paramStr;
  493. }
  494. - (NSURL *)URL {
  495. // add the category filter string and the query params to the feed base URL
  496. // NSURL's URLWithString:relativeToURL: would be appropriate, but it deletes the
  497. // last path component of the feed when we're appending something.
  498. // Note that a similar deletion occurs in Java's resolve call.
  499. //
  500. // CFURLCreateCopyAppendingPathComponent seems to implicitly percent-escape
  501. // the path component, including any ? character, so we can't use it here,
  502. // either.
  503. //
  504. // We'll just do simple string appending instead.
  505. NSString *categoryFilterStr = [self categoryFilterString];
  506. NSString *queryParamString = [self queryParamString];
  507. // split the original URL into path and query components
  508. NSString *feedURLString = [[self feedURL] absoluteString];
  509. NSString *origPath, *origQuery, *newURLStr, *newQuery;
  510. // find the first question mark
  511. NSRange quoteMark = [feedURLString rangeOfString:@"?"];
  512. if (quoteMark.location == NSNotFound) {
  513. // no query part
  514. origPath = feedURLString;
  515. origQuery = @"";
  516. } else {
  517. // has a query part
  518. origPath = [feedURLString substringToIndex:quoteMark.location];
  519. if (quoteMark.location < [feedURLString length]) {
  520. // skip the leading ? mark
  521. origQuery = [feedURLString substringFromIndex:quoteMark.location + 1];
  522. } else {
  523. // nothing follows the ? mark
  524. origQuery = @"";
  525. }
  526. }
  527. newURLStr = origPath;
  528. // add the generated category filter string, if any, to the URL string,
  529. // ensuring it's preceded by a slash
  530. if ([categoryFilterStr length] > 0) {
  531. if (![newURLStr hasSuffix:@"/"]) {
  532. newURLStr = [newURLStr stringByAppendingString:@"/"];
  533. }
  534. newURLStr = [newURLStr stringByAppendingString:categoryFilterStr];
  535. }
  536. // append the generated param query string, if any, to the original query
  537. if ([origQuery length] > 0) {
  538. // there was an original query
  539. if ([queryParamString length] > 0) {
  540. newQuery = [origQuery stringByAppendingFormat:@"&%@", queryParamString];
  541. } else {
  542. newQuery = origQuery;
  543. }
  544. } else {
  545. // there was no original query
  546. newQuery = queryParamString;
  547. }
  548. // append the query to the URL
  549. if ([newQuery length] > 0) {
  550. newURLStr = [newURLStr stringByAppendingFormat:@"?%@", newQuery];
  551. }
  552. NSURL *fullURL = [NSURL URLWithString:newURLStr];
  553. return fullURL;
  554. }
  555. @end