Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

NSCoding

  • Loading branch information...
commit 679ee1a839564ba065b749372ae7d4ba85f4200f 1 parent dfae3d4
@mikkelee authored
View
23 Gedcom/GCAttribute.m
@@ -110,6 +110,29 @@ - (NSComparisonResult)compare:(id)other
return NSOrderedSame;
}
+#pragma mark NSCoding conformance
+
+- (id)initWithCoder:(NSCoder *)aDecoder
+{
+ self = [super initWithType:[aDecoder decodeObjectForKey:@"type"]];
+
+ if (self) {
+ [self setValue:[aDecoder decodeObjectForKey:@"describedObject"] forKey:@"primitiveDescribedObject"];
+ _value = [aDecoder decodeObjectForKey:@"value"];
+ [self decodeProperties:aDecoder];
+ }
+
+ return self;
+}
+
+- (void)encodeWithCoder:(NSCoder *)aCoder
+{
+ [aCoder encodeObject:[self type] forKey:@"type"];
+ [aCoder encodeObject:[self describedObject] forKey:@"describedObject"];
+ [aCoder encodeObject:_value forKey:@"value"];
+ [self encodeProperties:aCoder];
+}
+
#pragma mark Objective-C properties
@synthesize value = _value;
View
2  Gedcom/GCContext.h
@@ -15,7 +15,7 @@
A context provides lookup functionality related to xrefs.
*/
-@interface GCContext : NSObject
+@interface GCContext : NSObject <NSCoding>
/// @name Obtaining a context
View
55 Gedcom/GCContext.m
@@ -35,7 +35,15 @@ + (id)context
- (void)storeXref:(NSString *)xref forEntity:(GCEntity *)obj
{
- [xrefStore setObject:xref forKey:[NSValue valueWithPointer:(const void *)obj]];
+ NSParameterAssert(xref);
+
+ for (NSString *key in [xrefStore allKeysForObject:obj]) {
+ [xrefStore removeObjectForKey:key];
+ }
+
+ //NSLog(@"Storing %@ for %@", xref, obj);
+
+ [xrefStore setObject:obj forKey:xref];
if ([xrefBlocks objectForKey:xref]) {
for (void (^block) (NSString *) in [xrefBlocks objectForKey:xref]) {
@@ -47,27 +55,44 @@ - (void)storeXref:(NSString *)xref forEntity:(GCEntity *)obj
- (NSString *)xrefForEntity:(GCEntity *)obj
{
- NSString *xref = [xrefStore objectForKey:[NSValue valueWithPointer:(const void *)obj]];
+ if (!obj) {
+ return nil;
+ }
+ NSParameterAssert([[obj gedTag] code]);
+
+ //NSLog(@"looking for %@ in %@", obj, self);
+
+ NSString *xref = nil;
+ for (NSString *key in [xrefStore allKeys]) {
+ //NSLog(@"%@: %@", key, [xrefStore objectForKey:key]);
+ if ([xrefStore objectForKey:key] == obj) {
+ xref = key;
+ }
+ }
if (xref == nil) {
int i = 0;
do {
xref = [NSString stringWithFormat:@"@%@%d@", [[obj gedTag] code], ++i];
- } while ([[xrefStore allKeysForObject:xref] count] > 0);
+ } while ([xrefStore objectForKey:xref]);
[self storeXref:xref forEntity:obj];
}
+ //NSLog(@"xref: %@", xref);
+
return xref;
}
- (GCEntity *)entityForXref:(NSString *)xref
{
- return [[[xrefStore allKeysForObject:xref] lastObject] pointerValue];
+ return [xrefStore objectForKey:xref];
}
- (void)registerXref:(NSString *)xref forBlock:(void (^)(NSString *xref))block
{
+ NSParameterAssert(xref);
+
if ([self entityForXref:xref]) {
block(xref);
} else if ([xrefBlocks objectForKey:xref]) {
@@ -77,6 +102,28 @@ - (void)registerXref:(NSString *)xref forBlock:(void (^)(NSString *xref))block
}
}
+#pragma mark NSCoding conformance
+
+- (id)initWithCoder:(NSCoder *)aDecoder
+{
+ self = [super init];
+
+ if (self) {
+ xrefStore = [aDecoder decodeObjectForKey:@"xrefStore"];
+ xrefBlocks = [aDecoder decodeObjectForKey:@"xrefBlocks"];
+ }
+
+ return self;
+}
+
+- (void)encodeWithCoder:(NSCoder *)aCoder
+{
+ [aCoder encodeObject:xrefStore forKey:@"xrefStore"];
+ [aCoder encodeObject:xrefBlocks forKey:@"xrefBlocks"];
+}
+
+#pragma mark Description
+
- (NSString *)description
{
return [NSString stringWithFormat:@"%@: %@", [super description], xrefStore];
View
25 Gedcom/GCEntity.m
@@ -93,6 +93,31 @@ - (NSComparisonResult)compare:(id)other
return NSOrderedSame;
}
+#pragma mark NSCoding conformance
+
+- (id)initWithCoder:(NSCoder *)aDecoder
+{
+ self = [super initWithType:[aDecoder decodeObjectForKey:@"type"]];
+
+ if (self) {
+ _context = [aDecoder decodeObjectForKey:@"context"];
+
+ [self decodeProperties:aDecoder];
+
+ _lastModified = [aDecoder decodeObjectForKey:@"lastModified"];
+ }
+
+ return self;
+}
+
+- (void)encodeWithCoder:(NSCoder *)aCoder
+{
+ [aCoder encodeObject:[self type] forKey:@"type"];
+ [aCoder encodeObject:_context forKey:@"context"];
+ [aCoder encodeObject:_lastModified forKey:@"lastModified"];
+ [self encodeProperties:aCoder];
+}
+
#pragma mark Objective-C properties
- (NSOrderedSet *)subNodes
View
7 Gedcom/GCFile.h
@@ -18,7 +18,7 @@
A file keeps a header and a collection of entities.
*/
-@interface GCFile : NSObject
+@interface GCFile : NSObject <NSCoding>
#pragma mark Initialization
@@ -72,7 +72,7 @@
#pragma mark Objective-C properties
/// The header of the receiver.
-@property GCHeader *head;
+@property GCHeader *header;
/// An optional submission entity.
@property GCEntity *submission; //optional
@@ -87,6 +87,9 @@
/// The receiver as an ordered collection of Gedcom nodes.
@property (readonly) NSArray *gedcomNodes;
+/// The receiver as a Gedcom string.
+@property (readonly) NSString *gedcomString;
+
@end
@interface GCFile (GCConvenienceMethods)
View
54 Gedcom/GCFile.m
@@ -42,10 +42,10 @@ - (id)initWithContext:(GCContext *)context gedcomNodes:(NSArray *)nodes;
Class objectClass = [tag objectClass];
if ([objectClass isEqual:[GCHeader class]]) {
- if (_head) {
+ if (_header) {
NSLog(@"Multiple headers!?");
}
- _head = [GCHeader headerWithGedcomNode:node inContext:context];
+ _header = [GCHeader headerWithGedcomNode:node inContext:context];
} else if ([objectClass isEqual:[GCEntity class]]) {
[_entities addObject:[GCEntity entityWithGedcomNode:node inContext:context]];
} else if ([objectClass isEqual:[GCTrailer class]]) {
@@ -64,7 +64,7 @@ - (id)initWithHeader:(GCHeader *)header entities:(NSArray *)entities
self = [super init];
if (self) {
- _head = header;
+ _header = header;
_context = [header context];
_entities = [NSMutableOrderedSet orderedSetWithCapacity:[entities count]];
@@ -101,9 +101,42 @@ - (void)removeEntity:(GCEntity *)entity
[_entities removeObject:entity];
}
+#pragma mark Equality
+
+- (BOOL)isEqualTo:(id)object
+{
+ if (![object isKindOfClass:[self class]]) {
+ return NO;
+ }
+
+ return [[self gedcomString] isEqualToString:[object gedcomString]];
+}
+
+#pragma mark NSCoding conformance
+
+- (id)initWithCoder:(NSCoder *)aDecoder
+{
+ self = [super init];
+
+ if (self) {
+ _context = [aDecoder decodeObjectForKey:@"context"];
+ _header = [aDecoder decodeObjectForKey:@"header"];
+ _entities = [aDecoder decodeObjectForKey:@"entities"];
+ }
+
+ return self;
+}
+
+- (void)encodeWithCoder:(NSCoder *)aCoder
+{
+ [aCoder encodeObject:_context forKey:@"context"];
+ [aCoder encodeObject:_header forKey:@"header"];
+ [aCoder encodeObject:_entities forKey:@"entities"];
+}
+
#pragma mark Objective-C properties
-@synthesize head = _head;
+@synthesize header = _header;
@synthesize submission = _submission;
@synthesize entities = _entities;
@@ -111,7 +144,7 @@ - (NSArray *)gedcomNodes
{
NSMutableArray *nodes = [NSMutableArray arrayWithCapacity:[_entities count]+2];
- [nodes addObject:[_head gedcomNode]];
+ [nodes addObject:[_header gedcomNode]];
if (_submission) {
[nodes addObject:[_submission gedcomNode]];
@@ -126,6 +159,17 @@ - (NSArray *)gedcomNodes
return nodes;
}
+- (NSString *)gedcomString
+{
+ NSMutableArray *gedcomStrings = [NSMutableArray array];
+
+ for (GCNode *node in [self gedcomNodes]) {
+ [gedcomStrings addObjectsFromArray:[node gedcomLines]];
+ }
+
+ return [gedcomStrings componentsJoinedByString:@"\n"];
+}
+
@end
@implementation GCFile (GCConvenienceMethods)
View
22 Gedcom/GCHeader.m
@@ -20,6 +20,8 @@ @implementation GCHeader {
GCNode *_gedcomNode;
}
+#pragma mark Convenience constructors
+
+ (id)headerWithGedcomNode:(GCNode *)node inContext:(GCContext *)context
{
NSParameterAssert([[node gedTag] isEqualToString:@"HEAD"]);
@@ -31,6 +33,26 @@ + (id)headerWithGedcomNode:(GCNode *)node inContext:(GCContext *)context
return head;
}
+#pragma mark NSCoding conformance
+
+- (id)initWithCoder:(NSCoder *)aDecoder
+{
+ self = [super init];
+
+ if (self) {
+ _gedcomNode = [aDecoder decodeObjectForKey:@"gedcomNode"];
+ }
+
+ return self;
+}
+
+- (void)encodeWithCoder:(NSCoder *)aCoder
+{
+ [aCoder encodeObject:_gedcomNode forKey:@"gedcomNode"];
+}
+
+#pragma mark Objective-C properties
+
@synthesize gedcomNode = _gedcomNode;
@end
View
10 Gedcom/GCNode.m
@@ -290,11 +290,11 @@ - (NSString *)description
- (void)encodeWithCoder:(NSCoder *)encoder
{
- [encoder setValue:[self gedTag] forKey:@"gedTag"];
- [encoder setValue:[self gedValue] forKey:@"gedValue"];
- [encoder setValue:[self xref] forKey:@"xref"];
- [encoder setValue:[self lineSeparator] forKey:@"lineSeparator"];
- [encoder setValue:[self subNodes] forKey:@"subNodes"];
+ [encoder encodeObject:[self gedTag] forKey:@"gedTag"];
+ [encoder encodeObject:[self gedValue] forKey:@"gedValue"];
+ [encoder encodeObject:[self xref] forKey:@"xref"];
+ [encoder encodeObject:[self lineSeparator] forKey:@"lineSeparator"];
+ [encoder encodeObject:[self subNodes] forKey:@"subNodes"];
}
- (id)initWithCoder:(NSCoder *)decoder
View
9 Gedcom/GCObject.h
@@ -20,7 +20,7 @@
Abstract object. Subclasses are GCHeader, GCEntity and GCProperty.
*/
-@interface GCObject : NSObject
+@interface GCObject : NSObject <NSCoding>
#pragma mark Initialization
@@ -200,3 +200,10 @@
- (void)addPropertyWithGedcomNode:(GCNode *)node;
@end
+
+@interface GCObject (GCCodingHelpers)
+
+- (void)decodeProperties:(NSCoder *)aDecoder;
+- (void)encodeProperties:(NSCoder *)aCoder;
+
+@end
View
28 Gedcom/GCObject.m
@@ -273,7 +273,7 @@ - (BOOL)isEqualTo:(id)other
{
return [[self gedcomString] isEqualToString:[other gedcomString]];
}
-
+/*
- (BOOL)isEqual:(id)other
{
if (other == self) {
@@ -286,6 +286,19 @@ - (BOOL)isEqual:(id)other
- (NSUInteger)hash
{
return [[self gedcomString] hash];
+}*/
+
+#pragma mark NSCoding conformance
+
+- (id)initWithCoder:(NSCoder *)aDecoder
+{
+ [self doesNotRecognizeSelector:_cmd];
+ return nil;
+}
+
+- (void)encodeWithCoder:(NSCoder *)aCoder
+{
+ [self doesNotRecognizeSelector:_cmd];
}
#pragma mark Description
@@ -471,3 +484,16 @@ - (void)addPropertyWithGedcomNode:(GCNode *)node
@end
+@implementation GCObject (GCCodingHelpers)
+
+- (void)decodeProperties:(NSCoder *)aDecoder
+{
+ _properties = [aDecoder decodeObjectForKey:@"properties"];
+}
+
+- (void)encodeProperties:(NSCoder *)aCoder
+{
+ [aCoder encodeObject:_properties forKey:@"properties"];
+}
+
+@end
View
23 Gedcom/GCRelationship.m
@@ -71,6 +71,29 @@ - (NSComparisonResult)compare:(id)other
return NSOrderedSame;
}
+#pragma mark NSCoding conformance
+
+- (id)initWithCoder:(NSCoder *)aDecoder
+{
+ self = [super initWithType:[aDecoder decodeObjectForKey:@"type"]];
+
+ if (self) {
+ [self setDescribedObject:[aDecoder decodeObjectForKey:@"describedObject"]];
+ _target = [aDecoder decodeObjectForKey:@"target"];
+ [self decodeProperties:aDecoder];
+ }
+
+ return self;
+}
+
+- (void)encodeWithCoder:(NSCoder *)aCoder
+{
+ [aCoder encodeObject:[self type] forKey:@"type"];
+ [aCoder encodeObject:[self describedObject] forKey:@"describedObject"];
+ [aCoder encodeObject:_target forKey:@"target"];
+ [self encodeProperties:aCoder];
+}
+
#pragma mark Objective-C properties
- (GCEntity *)target
View
2  Gedcom/GCValue.h
@@ -42,7 +42,7 @@ typedef enum {
GCValue objects are immutable. To change the value of an attribute, assign a new value object to it.
*/
-@interface GCValue : NSObject
+@interface GCValue : NSObject <NSCoding>
/// @name Creating and initializing values
View
16 Gedcom/GCValue.m
@@ -344,6 +344,22 @@ - (NSUInteger)hash
return hash;
}
+#pragma mark NSCoding conformance
+
+- (id)initWithCoder:(NSCoder *)aDecoder
+{
+ self = [self initWithType:[aDecoder decodeIntegerForKey:@"type"]
+ value:[aDecoder decodeObjectForKey:@"value"]];
+
+ return self;
+}
+
+- (void)encodeWithCoder:(NSCoder *)aCoder
+{
+ [aCoder encodeInteger:_type forKey:@"type"];
+ [aCoder encodeObject:_value forKey:@"value"];
+}
+
@end
@implementation GCValue (GCConvenienceMethods)
View
21 GedcomTests/GCObjectTests.m
@@ -204,7 +204,7 @@ - (void)testSetGedcomString
GCAttribute *birt2 = [[indi valueForKey:@"Birth"] lastObject];
GCAttribute *deat2 = [[indi valueForKey:@"Death"] lastObject];
- STAssertEqualObjects(birt1, birt2, nil);
+ STAssertTrue([birt1 isEqualTo:birt2], nil);
STAssertFalse([deat1 isEqual:deat2], nil);
//NSLog(@"[indi properties]: %@", [indi properties]);
@@ -212,6 +212,25 @@ - (void)testSetGedcomString
STAssertEquals([[indi properties] count], (NSUInteger)4, nil);
}
+- (void)testCoding
+{
+ NSString *path = [[NSBundle bundleForClass:[self class]] pathForResource:@"simple" ofType:@"ged"];
+ NSString *fileContents = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:nil];
+
+ NSArray *nodes = [GCNode arrayOfNodesFromString:fileContents];
+
+ GCFile *file = [GCFile fileWithGedcomNodes:nodes];
+
+ NSData *fileData = [NSKeyedArchiver archivedDataWithRootObject:file];
+
+ GCFile *decodedFile = [NSKeyedUnarchiver unarchiveObjectWithData:fileData];
+
+ //NSLog(@"file: %@", [file gedcomString]);
+ //NSLog(@"decodedFile: %@", [decodedFile gedcomString]);
+
+ STAssertTrue([file isEqualTo:decodedFile], nil);
+}
+
- (void)testFile
{
NSString *path = [[NSBundle bundleForClass:[self class]] pathForResource:@"simple" ofType:@"ged"];
View
2  README.md
@@ -29,7 +29,6 @@ Full AppleDoc documentation in the headers, can be built with the Documentation
# TODO #
* **tags.json**: Currently only partially done
-* **GCObject**: NSCoding
* **GCObject**: something like -propertiesFulfillingBlock:
* **GCDate**: helpers should accept more values (like containsDate:(GCDate *)date)
* **GCChangedDateFormatter**: Make a true formatter
@@ -37,6 +36,7 @@ Full AppleDoc documentation in the headers, can be built with the Documentation
* **GCContext**: +contextNamed: - refactor methodnames
* **Unit tests**: Better coverage
* **GCTag**: +tagsByName
+* **GCObject**: Validation (check if required properties are present etc).
# Examples #
Please sign in to comment.
Something went wrong with that request. Please try again.