Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Implement JSON writing

  • Loading branch information...
commit 27b0b9e6960d2af15e6458857f6f5a1d97c175de 1 parent 507642b
@p2 authored
View
4 growth-charts-helper.xcodeproj/project.pbxproj
@@ -104,8 +104,6 @@
EEEB2D661680E014004DC719 = {
isa = PBXGroup;
children = (
- EEEB2DE11681047B004DC719 /* Quartz.framework */,
- EEEB2DD51680F1E0004DC719 /* QuartzCore.framework */,
EEEB2D7B1680E014004DC719 /* growth-charts-helper */,
EEEB2D741680E014004DC719 /* Frameworks */,
EEEB2D721680E014004DC719 /* Products */,
@@ -123,6 +121,8 @@
EEEB2D741680E014004DC719 /* Frameworks */ = {
isa = PBXGroup;
children = (
+ EEEB2DE11681047B004DC719 /* Quartz.framework */,
+ EEEB2DD51680F1E0004DC719 /* QuartzCore.framework */,
EEEB2D751680E014004DC719 /* Cocoa.framework */,
EEEB2D771680E014004DC719 /* Other Frameworks */,
);
View
2  growth-charts-helper/CHChartAreaView.h
@@ -77,7 +77,5 @@
+ (Class)registeredClassForType:(NSString *)aType;
-+ (NSCharacterSet *)outlinePathSplitSet;
-
@end
View
21 growth-charts-helper/CHChartAreaView.m
@@ -432,27 +432,6 @@ + (Class)registeredClassForType:(NSString *)aType
-#pragma mark - Class Static Methods
-/**
- * Where to split the points in the "outline" property.
- *
- * This usually is just the semi-colon, but we also add whitespace in case the spec is not 100% accurate.
- * @return A character set at which to split the points in the "outline" property.
- */
-+ (NSCharacterSet *)outlinePathSplitSet
-{
- static NSCharacterSet *outlinePathSplitSet = nil;
- if (!outlinePathSplitSet) {
- NSMutableCharacterSet *set = [[NSCharacterSet whitespaceAndNewlineCharacterSet] mutableCopy];
- [set addCharactersInString:@";"];
- outlinePathSplitSet = [set copy];
- }
-
- return outlinePathSplitSet;
-}
-
-
-
#pragma mark - Utilities
- (NSString *)description
{
View
36 growth-charts-helper/CHDocument.m
@@ -66,17 +66,39 @@ - (NSURL *)pdfWithSameName
#pragma mark - File Reading and Writing
- (NSData *)dataOfType:(NSString *)typeName error:(NSError **)outError
{
- // Insert code here to write your document to data of the specified type. If outError != NULL, ensure that you create and set an appropriate error when returning nil.
- // You can also choose to override -fileWrapperOfType:error:, -writeToURL:ofType:error:, or -writeToURL:ofType:forSaveOperation:originalContentsURL:error: instead.
- NSException *exception = [NSException exceptionWithName:@"UnimplementedMethod" reason:[NSString stringWithFormat:@"%@ is unimplemented", NSStringFromSelector(_cmd)] userInfo:nil];
- @throw exception;
- return nil;
+ if (![@"DocumentType" isEqualToString:typeName]) {
+ if (NULL != outError) {
+ NSString *errorMessage = [NSString stringWithFormat:@"I don't know how to write data of type \"%@\"", typeName];
+ NSDictionary *info = @{NSLocalizedDescriptionKey: errorMessage};
+ *outError = [NSError errorWithDomain:NSCocoaErrorDomain code:0 userInfo:info];
+ }
+ return nil;
+ }
+
+ // grab json object
+ id json = [_chart jsonObject];
+ if (!json) {
+ if (NULL != outError) {
+ NSString *errorMessage = [NSString stringWithFormat:@"The chart did not produce a JSON object, but this: %@", json];
+ NSDictionary *info = @{NSLocalizedDescriptionKey: errorMessage};
+ *outError = [NSError errorWithDomain:NSCocoaErrorDomain code:0 userInfo:info];
+ }
+ return nil;
+ }
+
+ // serialize
+ return [NSJSONSerialization dataWithJSONObject:json options:NSJSONWritingPrettyPrinted error:outError];
}
- (BOOL)readFromData:(NSData *)data ofType:(NSString *)typeName error:(NSError **)outError
{
if (![@"DocumentType" isEqualToString:typeName]) {
+ if (NULL != outError) {
+ NSString *errorMessage = [NSString stringWithFormat:@"I don't know how to read data of type \"%@\"", typeName];
+ NSDictionary *info = @{NSLocalizedDescriptionKey: errorMessage};
+ *outError = [NSError errorWithDomain:NSCocoaErrorDomain code:0 userInfo:info];
+ }
return NO;
}
@@ -85,6 +107,10 @@ - (BOOL)readFromData:(NSData *)data ofType:(NSString *)typeName error:(NSError *
if ([dict isKindOfClass:[NSDictionary class]]) {
self.chart = [CHChart newFromJSONObject:dict];
}
+ else if (NULL != outError) {
+ NSDictionary *info = @{NSLocalizedDescriptionKey: @"JSON parsing did not produce a dictionary, cannot read"};
+ *outError = [NSError errorWithDomain:NSCocoaErrorDomain code:0 userInfo:info];
+ }
return (nil != _chart);
}
View
64 growth-charts-helper/FromCharts/CHChart.m
@@ -93,10 +93,10 @@ - (BOOL)setFromJSONObject:(id)dict
}
self.name = [dict objectForKey:@"name"];
+ self.source = [dict objectForKey:@"source"];
self.sourceName = [dict objectForKey:@"sourceName"];
self.sourceAcronym = [dict objectForKey:@"sourceAcronym"];
self.shortDescription = [dict objectForKey:@"description"];
- self.source = [dict objectForKey:@"source"];
self.gender = [[dict objectForKey:@"gender"] intValue];
if (_gender != CHGenderFemale && _gender != CHGenderMale) {
_gender = CHGenderUnknown;
@@ -105,27 +105,71 @@ - (BOOL)setFromJSONObject:(id)dict
// find areas
NSArray *areas = [dict objectForKey:@"areas"];
- if ([areas isKindOfClass:[NSArray class]] && [areas count] > 0) {
- NSMutableSet *chartSet = [NSMutableSet setWithCapacity:[areas count]];
-
- // instantiate areas
- for (NSDictionary *areaDict in areas) {
- if ([areaDict isKindOfClass:[NSDictionary class]]) {
- CHChartArea *area = [CHChartArea newAreaOnChart:self withDictionary:areaDict];
+ if ([areas isKindOfClass:[NSArray class]]) {
+ if ([areas count] > 0) {
+ NSMutableSet *chartSet = [NSMutableSet setWithCapacity:[areas count]];
+
+ // instantiate areas
+ for (NSDictionary *areaDict in areas) {
+ CHChartArea *area = [CHChartArea newFromJSONObject:areaDict];
if (area) {
+ area.chart = self;
+ area.topmost = YES;
[chartSet addObject:area];
}
}
+ self.chartAreas = chartSet;
}
- self.chartAreas = chartSet;
+ }
+ else if (areas) {
+ DLog(@"\"areas\" must be an array, but I got a %@, discarding", NSStringFromClass([areas class]));
}
return YES;
}
+/**
+ * Will create a dictionary representing this chart.
+ */
- (id)jsonObject
{
- return nil;
+ NSMutableDictionary *dict = [NSMutableDictionary new];
+
+ // fill our properties
+ if ([_name length] > 0) {
+ [dict setObject:_name forKey:@"name"];
+ }
+ if ([_source length] > 0) {
+ [dict setObject:_source forKey:@"source"];
+ }
+ if ([_sourceName length] > 0) {
+ [dict setObject:_sourceName forKey:@"sourceName"];
+ }
+ if ([_sourceAcronym length] > 0) {
+ [dict setObject:_sourceAcronym forKey:@"sourceAcronym"];
+ }
+ if ([_shortDescription length] > 0) {
+ [dict setObject:_shortDescription forKey:@"description"];
+ }
+ [dict setObject:[NSNumber numberWithInt:_gender] forKey:@"gender"];
+ if (_ageRange) {
+ [dict setObject:[_ageRange stringValue] forKey:@"ageRange"];
+ }
+
+ // add our areas
+ if ([_chartAreas count] > 0) {
+ NSMutableArray *areas = [NSMutableArray arrayWithCapacity:[_chartAreas count]];
+ for (CHChartArea *area in _chartAreas) {
+ id obj = [area jsonObject];
+ if (obj) {
+ [areas addObject:obj];
+ }
+ }
+
+ [dict setObject:areas forKey:@"areas"];
+ }
+
+ return dict;
}
View
12 growth-charts-helper/FromCharts/CHChartArea.h
@@ -22,6 +22,7 @@
#import <Foundation/Foundation.h>
#import "CHChart.h"
+#import "CHJSONHandling.h"
@class CHChartAreaView;
@@ -29,10 +30,11 @@
/**
* Describes a rectangular area on a chart.
*/
-@interface CHChartArea : NSObject
+@interface CHChartArea : NSObject <CHJSONHandling>
@property (nonatomic, weak) CHChart *chart; ///< The chart to which we belong
@property (nonatomic, copy) NSString *type; ///< The type of the area
+@property (nonatomic, copy) NSArray *outlinePoints; ///< An array of CGPoints (in NSValues) that define the path for our outline
@property (nonatomic, copy) NSDictionary *dictionary; ///< The dictionary representation defining the receiver, kept around to spawn the view objects
@property (nonatomic, assign) NSUInteger page; ///< 1 by default. The page number of the PDF this area resides on
@@ -55,14 +57,14 @@
@property (nonatomic, copy) NSString *yAxisDataType; ///< Plot areas: Y axis data type
@property (nonatomic, strong) NSDecimalNumber *yAxisFrom; ///< Plot areas: Y axis starting point
@property (nonatomic, strong) NSDecimalNumber *yAxisTo; ///< Plot areas: Y axis ending point
+@property (nonatomic, copy) NSString *statsSource; ///< Plot areas: The source for eventual statistics
-@property (nonatomic, readonly, assign) BOOL topmost; ///< YES if this area lies directly on the PDF, i.e. not nested in another area
+@property (nonatomic, assign) BOOL topmost; ///< YES if this area lies directly on the PDF, i.e. not nested in another area
@property (nonatomic, copy) NSArray *areas; ///< An area can have any number of subareas
-+ (id)newAreaOnChart:(CHChart *)chart withDictionary:(NSDictionary *)dict;
-- (void)setFromDictionary:(NSDictionary *)dict;
-
- (CHChartAreaView *)view;
++ (NSCharacterSet *)outlinePathSplitSet;
+
@end
View
206 growth-charts-helper/FromCharts/CHChartArea.m
@@ -26,33 +26,35 @@
@interface CHChartArea ()
-@property (nonatomic, readwrite, assign) BOOL topmost;
-
@end
@implementation CHChartArea
-+ (id)newAreaOnChart:(CHChart *)chart withDictionary:(NSDictionary *)dict
+#pragma mark - JSON Handling
++ (id)newFromJSONObject:(id)object
{
- CHChartArea *this = [self new];
- this.chart = chart;
- this.topmost = YES;
- [this setFromDictionary:dict];
+ CHChartArea *area = [CHChartArea new];
+ if ([area setFromJSONObject:object]) {
+ return area;
+ }
- return this;
+ return nil;
}
-
/**
- * Sets all properties it recognizes from the dict, leaves other properties at their current value.
- *
- * Call super in subclasses unless you know what you are doing.
+ * Fill from a dictionary passed in from decoding JSON.
*/
-- (void)setFromDictionary:(NSDictionary *)dict
+- (BOOL)setFromJSONObject:(id)object
{
+ if (![object isKindOfClass:[NSDictionary class]]) {
+ DLog(@"I need a dictionary, but got a %@: %@", NSStringFromClass([object class]), object);
+ return NO;
+ }
+
+ NSDictionary *dict = (NSDictionary *)object;
NSMutableDictionary *muteDict = [dict mutableCopy];
// type
@@ -85,7 +87,24 @@ - (void)setFromDictionary:(NSDictionary *)dict
self.frame = CGRectMake(rect.origin.x, rect.origin.y, rect.size.width, rect.size.height);
}
else if (rectString) {
- DLog(@"\"rect\" must be a NSString, but I got a %@, discarding", NSStringFromClass([rectString class]));
+ DLog(@"\"rect\" must be a string, but I got a %@, discarding", NSStringFromClass([rectString class]));
+ }
+
+ // outline
+ NSString *outlineString = [dict objectForKey:@"outline"];
+ if ([outlineString isKindOfClass:[NSString class]]) {
+ NSArray *points = [outlineString componentsSeparatedByCharactersInSet:[[self class] outlinePathSplitSet]];
+ if ([points count] > 0) {
+ NSMutableArray *outPoints = [NSMutableArray arrayWithCapacity:[points count]];
+ for (NSString *pointStr in points) {
+ NSPoint point = NSPointFromString(pointStr);
+ [outPoints addObject:[NSValue valueWithPoint:point]];
+ }
+ self.outlinePoints = outPoints;
+ }
+ }
+ else if (outlineString) {
+ DLog(@"\"outline\" must be a string, but I got a %@, discarding", NSStringFromClass([outlineString class]));
}
// fonts
@@ -114,16 +133,25 @@ - (void)setFromDictionary:(NSDictionary *)dict
// x
NSDictionary *xAxisDict = [axesDict objectForKey:@"x"];
self.xAxisUnitName = [xAxisDict objectForKey:@"unit"];
- self.xAxisDataType = [xAxisDict objectForKey:@"datatype"];
- self.xAxisFrom = [NSDecimalNumber decimalNumberWithString:[xAxisDict objectForKey:@"from"]];
- self.xAxisTo = [NSDecimalNumber decimalNumberWithString:[xAxisDict objectForKey:@"to"]];
+ self.xAxisDataType = [xAxisDict objectForKey:@"dataType"];
+ self.xAxisFrom = [NSDecimalNumber decimalNumberWithString:[[xAxisDict objectForKey:@"from"] description]];
+ self.xAxisTo = [NSDecimalNumber decimalNumberWithString:[[xAxisDict objectForKey:@"to"] description]];
// y
NSDictionary *yAxisDict = [axesDict objectForKey:@"y"];
self.yAxisUnitName = [yAxisDict objectForKey:@"unit"];
- self.yAxisDataType = [yAxisDict objectForKey:@"datatype"];
- self.yAxisFrom = [NSDecimalNumber decimalNumberWithString:[yAxisDict objectForKey:@"from"]];
- self.yAxisTo = [NSDecimalNumber decimalNumberWithString:[yAxisDict objectForKey:@"to"]];
+ self.yAxisDataType = [yAxisDict objectForKey:@"dataType"];
+ self.yAxisFrom = [NSDecimalNumber decimalNumberWithString:[[yAxisDict objectForKey:@"from"] description]];
+ self.yAxisTo = [NSDecimalNumber decimalNumberWithString:[[yAxisDict objectForKey:@"to"] description]];
+
+ // stats source
+ NSString *statsSource = [dict objectForKey:@"statsSource"];
+ if ([statsSource isKindOfClass:[NSString class]]) {
+ self.statsSource = statsSource;
+ }
+ else if (statsSource) {
+ DLog(@"\"statsSource\" should be a string, but got a %@, discarding", NSStringFromClass([statsSource class]));
+ }
}
else if ([@"plot" isEqualToString:_type]) {
DLog(@"This plot area does not have axes! %@", dict);
@@ -131,31 +159,130 @@ - (void)setFromDictionary:(NSDictionary *)dict
// ** sub-areas
NSArray *areas = [dict objectForKey:@"areas"];
- if ([areas isKindOfClass:[NSArray class]] && [areas count] > 0) {
- NSMutableArray *myAreas = [NSMutableArray arrayWithCapacity:[areas count]];
-
- // loop sub-areas
- for (NSDictionary *areaDict in areas) {
- if ([areaDict isKindOfClass:[NSDictionary class]]) {
- CHChartArea *area = [CHChartArea newAreaOnChart:_chart withDictionary:areaDict];
+ if ([areas isKindOfClass:[NSArray class]]) {
+ if ([areas count] > 0) {
+ NSMutableArray *myAreas = [NSMutableArray arrayWithCapacity:[areas count]];
+
+ // loop sub-areas
+ for (NSDictionary *areaDict in areas) {
+ CHChartArea *area = [CHChartArea newFromJSONObject:areaDict];
if (area) {
+ area.chart = _chart;
area.topmost = NO;
area.page = self.page;
[myAreas addObject:area];
}
}
+
+ self.areas = myAreas;
}
-
- self.areas = myAreas;
+ }
+ else if (areas) {
+ DLog(@"\"areas\" must be an array, but I got a %@, discarding", NSStringFromClass([areas class]));
}
[muteDict removeObjectForKey:@"areas"];
// remember all the other properties
self.dictionary = muteDict;
+ return YES;
+}
+
+- (id)jsonObject
+{
+ if ([_type length] < 1) {
+ DLog(@"This area does not have a type, not returning a JSON object");
+ return nil;
+ }
+
+ // basic properties
+ NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithObject:_type forKey:@"type"];
+ if (_topmost && _page > 0) {
+ [dict setObject:[NSNumber numberWithUnsignedInteger:_page] forKey:@"page"];
+ }
+ [dict setObject:NSStringFromCGRect(_frame) forKey:@"rect"];
+
+ // the outline
+ if ([_outlinePoints count] > 2) {
+ NSMutableArray *points = [NSMutableArray arrayWithCapacity:[_outlinePoints count]];
+ for (NSValue *point in _outlinePoints) {
+ [points addObject:NSStringFromCGPoint([point pointValue])];
+ }
+ NSString *pointString = [points componentsJoinedByString:@";"];
+ if ([pointString length] > 0) {
+ [dict setObject:pointString forKey:@"outline"];
+ }
+ }
+ else if ([_outlinePoints count] > 0) {
+ DLog(@"We need at least 3 outline points, %d are worthless", (int)[_outlinePoints count]);
+ }
+
+ // plot areas
+ if ([@"plot" isEqualToString:_type]) {
+ NSDictionary *x = @{
+ @"dataType": _xAxisDataType ? _xAxisDataType : @"",
+ @"unit": _xAxisUnitName ? _xAxisUnitName : @"",
+ @"from": _xAxisFrom ? _xAxisFrom : @0,
+ @"to": _xAxisTo ? _xAxisTo : @0
+ };
+ NSDictionary *y = @{
+ @"dataType": _yAxisDataType ? _yAxisDataType : @"",
+ @"unit": _yAxisUnitName ? _yAxisUnitName : @"",
+ @"from": _yAxisFrom ? _yAxisFrom : @0,
+ @"to": _yAxisTo ? _yAxisTo : @0
+ };
+
+ [dict setObject:@{@"x": x, @"y": y} forKey:@"axes"];
+ if ([_statsSource length] > 0) {
+ [dict setObject:_statsSource forKey:@"statsSource"];
+ }
+ }
+
+ // areas with another type
+ else {
+ if ([_fontName length] > 0) {
+ [dict setObject:_fontName forKey:@"fontName"];
+ }
+ if (_fontSize) {
+ [dict setObject:_fontSize forKey:@"fontSize"];
+ }
+ if ([_dataType length] > 0) {
+ [dict setObject:_dataType forKey:@"dataType"];
+ }
+ }
+
+ // subareas
+ if ([_areas count] > 0) {
+ NSMutableArray *subareas = [NSMutableArray arrayWithCapacity:[_areas count]];
+ for (CHChartArea *area in _areas) {
+ id json = [area jsonObject];
+ if (json) {
+ [subareas addObject:json];
+ }
+ }
+ [dict setObject:subareas forKey:@"areas"];
+ }
+
+ return dict;
+}
+
+
+
+#pragma mark - KVC
+- (void)setChart:(CHChart *)chart
+{
+ if (chart != _chart) {
+ _chart = chart;
+
+ // update sub-areas
+ for (CHChartArea *subarea in _areas) {
+ subarea.chart = _chart;
+ }
+ }
}
+
#pragma mark - Returning the View
/**
* Returns a new CHChartAreaView instance created from the properties of the receiver (including sub-areas)
@@ -184,6 +311,27 @@ - (CHChartAreaView *)view
return view;
}
+
+
+
+#pragma mark - Class Static Methods
+ /**
+ * Where to split the points in the "outline" property.
+ *
+ * This usually is just the semi-colon, but we also add whitespace in case the spec is not 100% accurate.
+ * @return A character set at which to split the points in the "outline" property.
+ */
+ + (NSCharacterSet *)outlinePathSplitSet
+ {
+ static NSCharacterSet *outlinePathSplitSet = nil;
+ if (!outlinePathSplitSet) {
+ NSMutableCharacterSet *set = [[NSCharacterSet whitespaceAndNewlineCharacterSet] mutableCopy];
+ [set addCharactersInString:@";"];
+ outlinePathSplitSet = [set copy];
+ }
+
+ return outlinePathSplitSet;
+ }
Please sign in to comment.
Something went wrong with that request. Please try again.