PageRenderTime 90ms CodeModel.GetById 30ms app.highlight 55ms RepoModel.GetById 1ms app.codeStats 0ms

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