您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

ZXResultParser.m 10KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317
  1. /*
  2. * Copyright 2012 ZXing authors
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. #import "ZXAddressBookAUResultParser.h"
  17. #import "ZXAddressBookDoCoMoResultParser.h"
  18. #import "ZXAddressBookParsedResult.h"
  19. #import "ZXBizcardResultParser.h"
  20. #import "ZXBookmarkDoCoMoResultParser.h"
  21. #import "ZXCalendarParsedResult.h"
  22. #import "ZXEmailAddressParsedResult.h"
  23. #import "ZXEmailAddressResultParser.h"
  24. #import "ZXEmailDoCoMoResultParser.h"
  25. #import "ZXExpandedProductParsedResult.h"
  26. #import "ZXExpandedProductResultParser.h"
  27. #import "ZXGeoParsedResult.h"
  28. #import "ZXGeoResultParser.h"
  29. #import "ZXISBNParsedResult.h"
  30. #import "ZXISBNResultParser.h"
  31. #import "ZXParsedResult.h"
  32. #import "ZXProductParsedResult.h"
  33. #import "ZXProductResultParser.h"
  34. #import "ZXResult.h"
  35. #import "ZXResultParser.h"
  36. #import "ZXSMSMMSResultParser.h"
  37. #import "ZXSMSParsedResult.h"
  38. #import "ZXSMSTOMMSTOResultParser.h"
  39. #import "ZXSMTPResultParser.h"
  40. #import "ZXTelParsedResult.h"
  41. #import "ZXTelResultParser.h"
  42. #import "ZXTextParsedResult.h"
  43. #import "ZXURIParsedResult.h"
  44. #import "ZXURIResultParser.h"
  45. #import "ZXURLTOResultParser.h"
  46. #import "ZXVCardResultParser.h"
  47. #import "ZXVEventResultParser.h"
  48. #import "ZXVINResultParser.h"
  49. #import "ZXWifiParsedResult.h"
  50. #import "ZXWifiResultParser.h"
  51. static NSArray *ZX_PARSERS = nil;
  52. static NSRegularExpression *ZX_DIGITS = nil;
  53. static NSString *ZX_AMPERSAND = @"&";
  54. static NSString *ZX_EQUALS = @"=";
  55. static unichar ZX_BYTE_ORDER_MARK = L'\ufeff';
  56. @implementation ZXResultParser
  57. + (void)initialize {
  58. if ([self class] != [ZXResultParser class]) return;
  59. ZX_PARSERS = @[[[ZXBookmarkDoCoMoResultParser alloc] init],
  60. [[ZXAddressBookDoCoMoResultParser alloc] init],
  61. [[ZXEmailDoCoMoResultParser alloc] init],
  62. [[ZXAddressBookAUResultParser alloc] init],
  63. [[ZXVCardResultParser alloc] init],
  64. [[ZXBizcardResultParser alloc] init],
  65. [[ZXVEventResultParser alloc] init],
  66. [[ZXEmailAddressResultParser alloc] init],
  67. [[ZXSMTPResultParser alloc] init],
  68. [[ZXTelResultParser alloc] init],
  69. [[ZXSMSMMSResultParser alloc] init],
  70. [[ZXSMSTOMMSTOResultParser alloc] init],
  71. [[ZXGeoResultParser alloc] init],
  72. [[ZXWifiResultParser alloc] init],
  73. [[ZXURLTOResultParser alloc] init],
  74. [[ZXURIResultParser alloc] init],
  75. [[ZXISBNResultParser alloc] init],
  76. [[ZXProductResultParser alloc] init],
  77. [[ZXExpandedProductResultParser alloc] init],
  78. [[ZXVINResultParser alloc] init]];
  79. ZX_DIGITS = [[NSRegularExpression alloc] initWithPattern:@"^\\d+$" options:0 error:nil];
  80. }
  81. - (ZXParsedResult *)parse:(ZXResult *)result {
  82. @throw [NSException exceptionWithName:NSInternalInconsistencyException
  83. reason:[NSString stringWithFormat:@"You must override %@ in a subclass", NSStringFromSelector(_cmd)]
  84. userInfo:nil];
  85. }
  86. + (NSString *)massagedText:(ZXResult *)result {
  87. NSString *text = result.text;
  88. if (text.length > 0 && [text characterAtIndex:0] == ZX_BYTE_ORDER_MARK) {
  89. text = [text substringFromIndex:1];
  90. }
  91. return text;
  92. }
  93. + (ZXParsedResult *)parseResult:(ZXResult *)theResult {
  94. for (ZXResultParser *parser in ZX_PARSERS) {
  95. ZXParsedResult *result = [parser parse:theResult];
  96. if (result != nil) {
  97. return result;
  98. }
  99. }
  100. return [ZXTextParsedResult textParsedResultWithText:[theResult text] language:nil];
  101. }
  102. - (void)maybeAppend:(NSString *)value result:(NSMutableString *)result {
  103. if (value != nil) {
  104. [result appendFormat:@"\n%@", value];
  105. }
  106. }
  107. - (void)maybeAppendArray:(NSArray *)value result:(NSMutableString *)result {
  108. if (value != nil) {
  109. for (NSString *s in value) {
  110. [result appendFormat:@"\n%@", s];
  111. }
  112. }
  113. }
  114. - (NSArray *)maybeWrap:(NSString *)value {
  115. return value == nil ? nil : @[value];
  116. }
  117. + (NSString *)unescapeBackslash:(NSString *)escaped {
  118. NSUInteger backslash = [escaped rangeOfString:@"\\"].location;
  119. if (backslash == NSNotFound) {
  120. return escaped;
  121. }
  122. NSUInteger max = [escaped length];
  123. NSMutableString *unescaped = [NSMutableString stringWithCapacity:max - 1];
  124. [unescaped appendString:[escaped substringToIndex:backslash]];
  125. BOOL nextIsEscaped = NO;
  126. for (int i = (int)backslash; i < max; i++) {
  127. unichar c = [escaped characterAtIndex:i];
  128. if (nextIsEscaped || c != '\\') {
  129. [unescaped appendFormat:@"%C", c];
  130. nextIsEscaped = NO;
  131. } else {
  132. nextIsEscaped = YES;
  133. }
  134. }
  135. return unescaped;
  136. }
  137. + (int)parseHexDigit:(unichar)c {
  138. if (c >= '0' && c <= '9') {
  139. return c - '0';
  140. }
  141. if (c >= 'a' && c <= 'f') {
  142. return 10 + (c - 'a');
  143. }
  144. if (c >= 'A' && c <= 'F') {
  145. return 10 + (c - 'A');
  146. }
  147. return -1;
  148. }
  149. + (BOOL)isStringOfDigits:(NSString *)value length:(unsigned int)length {
  150. return value != nil && length > 0 && length == value.length && [ZX_DIGITS numberOfMatchesInString:value options:0 range:NSMakeRange(0, value.length)] > 0;
  151. }
  152. - (NSString *)urlDecode:(NSString *)escaped {
  153. if (escaped == nil) {
  154. return nil;
  155. }
  156. int first = [self findFirstEscape:escaped];
  157. if (first == -1) {
  158. return escaped;
  159. }
  160. NSUInteger max = [escaped length];
  161. NSMutableString *unescaped = [NSMutableString stringWithCapacity:max - 2];
  162. [unescaped appendString:[escaped substringToIndex:first]];
  163. for (int i = first; i < max; i++) {
  164. unichar c = [escaped characterAtIndex:i];
  165. switch (c) {
  166. case '+':
  167. [unescaped appendString:@" "];
  168. break;
  169. case '%':
  170. if (i >= max - 2) {
  171. [unescaped appendString:@"%"];
  172. } else {
  173. int firstDigitValue = [[self class] parseHexDigit:[escaped characterAtIndex:++i]];
  174. int secondDigitValue = [[self class] parseHexDigit:[escaped characterAtIndex:++i]];
  175. if (firstDigitValue < 0 || secondDigitValue < 0) {
  176. [unescaped appendFormat:@"%%%C%C", [escaped characterAtIndex:i - 1], [escaped characterAtIndex:i]];
  177. }
  178. [unescaped appendFormat:@"%C", (unichar)((firstDigitValue << 4) + secondDigitValue)];
  179. }
  180. break;
  181. default:
  182. [unescaped appendFormat:@"%C", c];
  183. break;
  184. }
  185. }
  186. return unescaped;
  187. }
  188. - (int)findFirstEscape:(NSString *)escaped {
  189. NSUInteger max = [escaped length];
  190. for (int i = 0; i < max; i++) {
  191. unichar c = [escaped characterAtIndex:i];
  192. if (c == '+' || c == '%') {
  193. return i;
  194. }
  195. }
  196. return -1;
  197. }
  198. + (BOOL)isSubstringOfDigits:(NSString *)value offset:(int)offset length:(int)length {
  199. if (value == nil || length <= 0) {
  200. return NO;
  201. }
  202. int max = offset + length;
  203. return value.length >= max && [ZX_DIGITS numberOfMatchesInString:value options:0 range:NSMakeRange(offset, max - offset)] > 0;
  204. }
  205. - (NSMutableDictionary *)parseNameValuePairs:(NSString *)uri {
  206. NSUInteger paramStart = [uri rangeOfString:@"?"].location;
  207. if (paramStart == NSNotFound) {
  208. return nil;
  209. }
  210. NSMutableDictionary *result = [NSMutableDictionary dictionaryWithCapacity:3];
  211. for (NSString *keyValue in [[uri substringFromIndex:paramStart + 1] componentsSeparatedByString:ZX_AMPERSAND]) {
  212. [self appendKeyValue:keyValue result:result];
  213. }
  214. return result;
  215. }
  216. - (void)appendKeyValue:(NSString *)keyValue result:(NSMutableDictionary *)result {
  217. NSRange equalsRange = [keyValue rangeOfString:ZX_EQUALS];
  218. if (equalsRange.location != NSNotFound) {
  219. NSString *key = [keyValue substringToIndex:equalsRange.location];
  220. NSString *value = [keyValue substringFromIndex:equalsRange.location + 1];
  221. value = [self urlDecode:value];
  222. result[key] = value;
  223. }
  224. }
  225. + (NSString *)urlDecode:(NSString *)encoded {
  226. NSString *result = [encoded stringByReplacingOccurrencesOfString:@"+" withString:@" "];
  227. result = [result stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
  228. return result;
  229. }
  230. + (NSArray *)matchPrefixedField:(NSString *)prefix rawText:(NSString *)rawText endChar:(unichar)endChar trim:(BOOL)trim {
  231. NSMutableArray *matches = nil;
  232. NSUInteger i = 0;
  233. NSUInteger max = [rawText length];
  234. while (i < max) {
  235. i = [rawText rangeOfString:prefix options:NSLiteralSearch range:NSMakeRange(i, [rawText length] - i - 1)].location;
  236. if (i == NSNotFound) {
  237. break;
  238. }
  239. i += [prefix length]; // Skip past this prefix we found to start
  240. NSUInteger start = i; // Found the start of a match here
  241. BOOL more = YES;
  242. while (more) {
  243. i = [rawText rangeOfString:[NSString stringWithFormat:@"%C", endChar] options:NSLiteralSearch range:NSMakeRange(i, [rawText length] - i)].location;
  244. if (i == NSNotFound) {
  245. // No terminating end character? uh, done. Set i such that loop terminates and break
  246. i = [rawText length];
  247. more = NO;
  248. } else if ([self countPrecedingBackslashes:rawText pos:i] % 2 != 0) {
  249. // semicolon was escaped (odd count of preceding backslashes) so continue
  250. i++;
  251. } else {
  252. // found a match
  253. if (matches == nil) {
  254. matches = [NSMutableArray arrayWithCapacity:3]; // lazy init
  255. }
  256. NSString *element = [self unescapeBackslash:[rawText substringWithRange:NSMakeRange(start, i - start)]];
  257. if (trim) {
  258. element = [element stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
  259. }
  260. if (element.length > 0) {
  261. [matches addObject:element];
  262. }
  263. i++;
  264. more = NO;
  265. }
  266. }
  267. }
  268. if (matches == nil || [matches count] == 0) {
  269. return nil;
  270. }
  271. return matches;
  272. }
  273. + (int)countPrecedingBackslashes:(NSString *)s pos:(NSInteger)pos {
  274. int count = 0;
  275. for (NSInteger i = pos - 1; i >= 0; i--) {
  276. if ([s characterAtIndex:i] == '\\') {
  277. count++;
  278. } else {
  279. break;
  280. }
  281. }
  282. return count;
  283. }
  284. + (NSString *)matchSinglePrefixedField:(NSString *)prefix rawText:(NSString *)rawText endChar:(unichar)endChar trim:(BOOL)trim {
  285. NSArray *matches = [self matchPrefixedField:prefix rawText:rawText endChar:endChar trim:trim];
  286. return matches == nil ? nil : matches[0];
  287. }
  288. @end