/core/externals/update-engine/externals/gdata-objectivec-client/Source/Geo/GDataGeo.m

http://macfuse.googlecode.com/ · Objective C · 426 lines · 259 code · 97 blank · 70 comment · 33 complexity · 979a85a9307faa82d4b8df1c49898fc0 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. // GDataGeo.m
  17. //
  18. #if !GDATA_REQUIRE_SERVICE_INCLUDES || GDATA_INCLUDE_CALENDAR_SERVICE \
  19. || GDATA_INCLUDE_PHOTOS_SERVICE || GDATA_INCLUDE_YOUTUBE_SERVICE
  20. #define GDATAGEO_DEFINE_GLOBALS 1
  21. #import "GDataGeo.h"
  22. #include <math.h>
  23. // http://www.w3.org/2003/01/geo/
  24. // xmlns:geo="http://www.w3.org/2003/01/geo/wgs84_pos#"
  25. // <geo:Point>
  26. // <geo:lat>55.701</geo:lat>
  27. // <geo:long>12.552</geo:long>
  28. // </geo:Point>
  29. @implementation GDataGeoW3CPoint
  30. + (NSString *)extensionElementURI { return kGDataNamespaceGeoW3C; }
  31. + (NSString *)extensionElementPrefix { return kGDataNamespaceGeoW3CPrefix; }
  32. + (NSString *)extensionElementLocalName { return @"Point"; }
  33. - (id)initWithXMLElement:(NSXMLElement *)element
  34. parent:(GDataObject *)parent {
  35. self = [super initWithXMLElement:element
  36. parent:parent];
  37. if (self) {
  38. NSXMLElement *latElem = [self childWithQualifiedName:@"geo:lat"
  39. namespaceURI:[element URI]
  40. fromElement:element];
  41. NSNumber *latNum = [self doubleNumberValueFromElement:latElem];
  42. NSXMLElement *longElem = [self childWithQualifiedName:@"geo:long"
  43. namespaceURI:[element URI]
  44. fromElement:element];
  45. NSNumber *longNum = [self doubleNumberValueFromElement:longElem];
  46. if (latNum && longNum) {
  47. [self setValues:[NSArray arrayWithObjects:latNum, longNum, nil]];
  48. }
  49. }
  50. return self;
  51. }
  52. - (NSXMLElement *)XMLElement {
  53. NSXMLElement *element = [self XMLElementWithExtensionsAndDefaultName:@"geo:Point"];
  54. NSArray *values = [self values];
  55. if ([self isPoint]) {
  56. NSNumber *latNum = [values objectAtIndex:0];
  57. NSNumber *longNum = [values objectAtIndex:1];
  58. // prefix is probably "geo" but it doesn't hurt to be dynamic
  59. NSString *nameFromXML = [self elementName];
  60. NSString *prefix = kGDataNamespaceGeoW3CPrefix;
  61. if (nameFromXML) {
  62. prefix = [NSXMLNode prefixForName:nameFromXML];
  63. }
  64. NSString *latName = [NSString stringWithFormat:@"%@:lat", prefix];
  65. NSString *longName = [NSString stringWithFormat:@"%@:long", prefix];
  66. [self addToElement:element
  67. childWithStringValueIfNonEmpty:[latNum stringValue]
  68. withName:latName];
  69. [self addToElement:element
  70. childWithStringValueIfNonEmpty:[longNum stringValue]
  71. withName:longName];
  72. }
  73. return element;
  74. }
  75. @end
  76. // GeoRSS simple point http://www.georss.org/
  77. //
  78. // <georss:point>45.256 -71.92</georss:point>
  79. @implementation GDataGeoRSSPoint
  80. + (NSString *)extensionElementURI { return kGDataNamespaceGeoRSS; }
  81. + (NSString *)extensionElementPrefix { return kGDataNamespaceGeoRSSPrefix; }
  82. + (NSString *)extensionElementLocalName { return @"point"; }
  83. - (id)initWithXMLElement:(NSXMLElement *)element
  84. parent:(GDataObject *)parent {
  85. self = [super initWithXMLElement:element
  86. parent:parent];
  87. if (self) {
  88. NSString *valueListStr = [self stringValueFromElement:element];
  89. if ([valueListStr length] > 0) {
  90. NSArray *values = [GDataGeo valuesWithCoordinateString:valueListStr];
  91. if ([values count] > 0) {
  92. [self setValues:values];
  93. }
  94. }
  95. }
  96. return self;
  97. }
  98. - (NSXMLElement *)XMLElement {
  99. // our superclass defaults to making a georss:point, so we'll call that
  100. return [super XMLElement];
  101. }
  102. @end
  103. // GeoRSS gml:Point http://www.georss.org/gml.html
  104. //
  105. // <georss:where>
  106. // <gml:Point>
  107. // <gml:pos>45.256 -110.45</gml:pos>
  108. // </gml:Point>
  109. // </georss:where>
  110. //
  111. // Note: georss:where has various other where constructs we're not currently
  112. // handling, like
  113. // <georss:where> <gml:LineString> <gml:posList>
  114. // 45.256 -110.45 46.46 -109.48 43.84 -109.86 45.8 -109.2
  115. // </gml:posList> </gml:LineString> </georss:where>
  116. @implementation GDataGeoRSSWhere
  117. + (NSString *)extensionElementURI { return kGDataNamespaceGeoRSS; }
  118. + (NSString *)extensionElementPrefix { return kGDataNamespaceGeoRSSPrefix; }
  119. + (NSString *)extensionElementLocalName { return @"where"; }
  120. - (id)initWithXMLElement:(NSXMLElement *)element
  121. parent:(GDataObject *)parent {
  122. self = [super initWithXMLElement:element
  123. parent:parent];
  124. if (self) {
  125. NSXMLElement *gmlPointElem = [self childWithQualifiedName:@"gml:Point"
  126. namespaceURI:kGDataNamespaceGeoGML
  127. fromElement:element];
  128. if (gmlPointElem) {
  129. NSXMLElement *gmlPosElem = [self childWithQualifiedName:@"gml:pos"
  130. namespaceURI:kGDataNamespaceGeoGML
  131. fromElement:gmlPointElem];
  132. if (gmlPointElem) {
  133. NSString *valueListStr = [self stringValueFromElement:gmlPosElem];
  134. if ([valueListStr length] > 0) {
  135. NSArray *values = [GDataGeo valuesWithCoordinateString:valueListStr];
  136. if ([values count] > 0) {
  137. [self setValues:values];
  138. }
  139. }
  140. }
  141. }
  142. }
  143. return self;
  144. }
  145. - (NSXMLElement *)XMLElement {
  146. // generate
  147. // <georss:where> <gml:Point> <gml:pos>
  148. // latitude longitude
  149. // </gml:pos> </gml:Point> </georss:where>
  150. NSXMLElement *element = [self XMLElementWithExtensionsAndDefaultName:@"georss:where"];
  151. if ([self isPoint]) {
  152. NSString *valueListStr = [self coordinateString];
  153. NSXMLElement *gmlPointElem = [NSXMLElement elementWithName:@"gml:Point"];
  154. [self addToElement:gmlPointElem
  155. childWithStringValueIfNonEmpty:valueListStr
  156. withName:@"gml:pos"];
  157. [element addChild:gmlPointElem];
  158. }
  159. return element;
  160. }
  161. @end
  162. @implementation GDataGeo
  163. + (NSDictionary *)geoNamespaces {
  164. NSMutableDictionary *namespaces = [NSMutableDictionary dictionary];
  165. [namespaces setObject:kGDataNamespaceGeoRSS
  166. forKey:kGDataNamespaceGeoRSSPrefix]; // "georss"
  167. [namespaces setObject:kGDataNamespaceGeoW3C
  168. forKey:kGDataNamespaceGeoW3CPrefix]; // "geo"
  169. [namespaces setObject:kGDataNamespaceGeoGML
  170. forKey:kGDataNamespaceGeoGMLPrefix]; // "gml"
  171. return namespaces;
  172. }
  173. + (id)geoWithLatitude:(double)latitude
  174. longitude:(double)longitude {
  175. GDataGeo* obj = [self object];
  176. NSNumber *latNum = [NSNumber numberWithDouble:latitude];
  177. NSNumber *longNum = [NSNumber numberWithDouble:longitude];
  178. [obj setValues:[NSArray arrayWithObjects:latNum, longNum, nil]];
  179. return obj;
  180. }
  181. - (id)initWithXMLElement:(NSXMLElement *)element
  182. parent:(GDataObject *)parent {
  183. self = [super initWithXMLElement:element
  184. parent:parent];
  185. if (self) {
  186. // to initialize from XML, this must be one of the
  187. // subclasses that parses the XML appropriately,
  188. // so we'll use a strict equal-to class test
  189. GDATA_ASSERT(![self isMemberOfClass:[GDataGeo class]],
  190. @"Subclass of %@ should handle initWithXMLElement:", [self class]);
  191. }
  192. return self;
  193. }
  194. - (NSXMLElement *)XMLElement {
  195. // usually the subclass will generate the XML, but if we're here there's no
  196. // subclass
  197. //
  198. // generate a georss:point
  199. NSXMLElement *element = [self XMLElementWithExtensionsAndDefaultName:@"georss:point"];
  200. if ([self isPoint]) {
  201. NSString *str = [self coordinateString];
  202. [element addStringValue:str];
  203. }
  204. return element;
  205. }
  206. - (void)dealloc {
  207. [values_ release];
  208. [super dealloc];
  209. }
  210. - (id)copyWithZone:(NSZone *)zone {
  211. GDataGeo* newObj = [super copyWithZone:zone];
  212. [newObj setValues:[GDataUtilities mutableArrayWithCopiesOfObjectsInArray:[self values]]];
  213. return newObj;
  214. }
  215. - (BOOL)isEqual:(GDataGeo *)other {
  216. if (self == other) return YES;
  217. if (![other isKindOfClass:[GDataGeo class]]) return NO;
  218. return [super isEqual:other]
  219. && AreEqualOrBothNil([self values], [other values]);
  220. }
  221. #if !GDATA_SIMPLE_DESCRIPTIONS
  222. - (NSMutableArray *)itemsForDescription {
  223. NSMutableArray *items = [NSMutableArray array];
  224. [self addToArray:items objectDescriptionIfNonNil:values_ withName:@"values"];
  225. return items;
  226. }
  227. #endif
  228. //
  229. // getters and setters
  230. //
  231. - (NSArray *)values {
  232. return values_;
  233. }
  234. - (void)setValues:(NSArray *)array {
  235. [values_ autorelease];
  236. values_ = [array copy];
  237. }
  238. // convenience accessors
  239. - (BOOL)isPoint {
  240. return ([values_ count] == 2);
  241. }
  242. - (NSString *)coordinateString {
  243. NSArray *values = [self values];
  244. NSString *str = [GDataGeo coordinateStringWithValues:values];
  245. return str;
  246. }
  247. - (double)latitude {
  248. if ([self isPoint]) {
  249. return [[values_ objectAtIndex:0] doubleValue];
  250. }
  251. return NAN;
  252. }
  253. - (double)longitude {
  254. if ([self isPoint]) {
  255. return [[values_ objectAtIndex:1] doubleValue];
  256. }
  257. return NAN;
  258. }
  259. //
  260. // utility
  261. //
  262. // valuesWithCoordinateString returns an array of doubles
  263. // scanned from |str|. The values must be in pairs
  264. + (NSArray *)valuesWithCoordinateString:(NSString *)str {
  265. NSMutableArray *array = [NSMutableArray array];
  266. NSScanner *scanner = [NSScanner scannerWithString:str];
  267. // scan pairs of coordinates
  268. double val1, val2;
  269. while ([scanner scanDouble:&val1] && [scanner scanDouble:&val2]) {
  270. [array addObject:[NSNumber numberWithDouble:val1]];
  271. [array addObject:[NSNumber numberWithDouble:val2]];
  272. }
  273. return array;
  274. }
  275. + (NSString *)coordinateStringWithValues:(NSArray *)values {
  276. return [values componentsJoinedByString:@" "];
  277. }
  278. #pragma mark Helpers for other objects using GDataGeo
  279. // call this in addExtensionDeclarations
  280. + (void)addGeoExtensionDeclarationsToObject:(GDataObject *)object
  281. forParentClass:(Class)parentClass {
  282. // we declare three different extensions which can be geoLocation
  283. [object addExtensionDeclarationForParentClass:parentClass
  284. childClass:[GDataGeoRSSPoint class]];
  285. [object addExtensionDeclarationForParentClass:parentClass
  286. childClass:[GDataGeoRSSWhere class]];
  287. [object addExtensionDeclarationForParentClass:parentClass
  288. childClass:[GDataGeoW3CPoint class]];
  289. }
  290. // utility for the getter for GDataGeo
  291. + (GDataGeo *)geoLocationForObject:(GDataObject *)object {
  292. // the location point may be any of GDataGeo's three subclass types
  293. GDataGeo *geo = [object objectForExtensionClass:[GDataGeoRSSPoint class]];
  294. if (!geo) {
  295. geo = [object objectForExtensionClass:[GDataGeoRSSWhere class]];
  296. }
  297. if (!geo) {
  298. geo = [object objectForExtensionClass:[GDataGeoW3CPoint class]];
  299. }
  300. return geo;
  301. }
  302. // utility for the setter for GDataGeo
  303. + (void)setGeoLocation:(GDataGeo *)geo forObject:(GDataObject *)object {
  304. // remove the previous geo, whatever class it was
  305. GDataGeo *oldGeo = [GDataGeo geoLocationForObject:object];
  306. if (oldGeo) {
  307. [object removeObject:oldGeo forExtensionClass:[oldGeo class]];
  308. }
  309. if (geo) {
  310. // GDataGeo itself lacks support for the GDataExtension protocol;
  311. // only instances of its subclasses can be used as extensions
  312. GDATA_ASSERT(![geo isMemberOfClass:[GDataGeo class]],
  313. @"setGeoLocation requires an instance of a subclass of GDataGeo");
  314. [object setObject:geo forExtensionClass:[geo class]];
  315. }
  316. }
  317. @end
  318. #pragma mark -
  319. @implementation GDataGeoRSSFeatureName
  320. + (NSString *)extensionElementURI { return kGDataNamespaceGeoRSS; }
  321. + (NSString *)extensionElementPrefix { return kGDataNamespaceGeoRSSPrefix; }
  322. + (NSString *)extensionElementLocalName { return @"featurename"; }
  323. @end
  324. @implementation GDataGeoRSSRadius
  325. + (NSString *)extensionElementURI { return kGDataNamespaceGeoRSS; }
  326. + (NSString *)extensionElementPrefix { return kGDataNamespaceGeoRSSPrefix; }
  327. + (NSString *)extensionElementLocalName { return @"radius"; }
  328. @end
  329. #endif // !GDATA_REQUIRE_SERVICE_INCLUDES || GDATA_INCLUDE_*_SERVICE