Initial Cocoa (Objective-C) API.
authorJens Alfke <jens@mooseyard.com>
Thu Sep 24 21:47:06 2009 -0700 (2009-09-24)
changeset 6f2cd752db494
parent 5 4c10b7956435
child 7 2e44bc2caefe
Initial Cocoa (Objective-C) API.
Ottoman.xcodeproj/project.pbxproj
bindings/Cocoa/MYOttoman.h
bindings/Cocoa/MYOttoman.mm
bindings/Cocoa/MYOttoman_internal.h
bindings/Cocoa/MYOttoman_test.m
bindings/Cocoa/MYVersionDictionary.h
bindings/Cocoa/MYVersionDictionary.mm
     1.1 --- a/Ottoman.xcodeproj/project.pbxproj	Thu Sep 24 21:46:17 2009 -0700
     1.2 +++ b/Ottoman.xcodeproj/project.pbxproj	Thu Sep 24 21:47:06 2009 -0700
     1.3 @@ -8,6 +8,11 @@
     1.4  
     1.5  /* Begin PBXBuildFile section */
     1.6  		27156CAA104C9C44009EBD39 /* gtest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 27156CA9104C9C44009EBD39 /* gtest.framework */; };
     1.7 +		2754DD87106BCBCF00365FAA /* MYOttoman_test.m in Sources */ = {isa = PBXBuildFile; fileRef = 2754DD86106BCBCF00365FAA /* MYOttoman_test.m */; };
     1.8 +		2754DDC5106BD20A00365FAA /* Test.m in Sources */ = {isa = PBXBuildFile; fileRef = 2754DDC4106BD20A00365FAA /* Test.m */; };
     1.9 +		2754DDEE106BD3BD00365FAA /* Logging.m in Sources */ = {isa = PBXBuildFile; fileRef = 2754DDED106BD3BD00365FAA /* Logging.m */; };
    1.10 +		2754DDF3106BD3D300365FAA /* ExceptionUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = 2754DDF2106BD3D300365FAA /* ExceptionUtils.m */; };
    1.11 +		2754DE2D106BD65600365FAA /* CollectionUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = 2754DE2C106BD65600365FAA /* CollectionUtils.m */; };
    1.12  		27603901105AC81200D931A7 /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 27603900105AC81200D931A7 /* CoreFoundation.framework */; };
    1.13  		276E5BCD1066D13D008A2171 /* Base.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 276E5BC41066D13D008A2171 /* Base.cpp */; };
    1.14  		276E5BCE1066D13D008A2171 /* Chunk.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 276E5BC51066D13D008A2171 /* Chunk.cpp */; };
    1.15 @@ -41,8 +46,21 @@
    1.16  		276E5CC4106731C7008A2171 /* File.h in Headers */ = {isa = PBXBuildFile; fileRef = 276E5BBD1066D135008A2171 /* File.h */; };
    1.17  		276E5CC5106731C8008A2171 /* Ottoman.h in Headers */ = {isa = PBXBuildFile; fileRef = 276E5BC11066D135008A2171 /* Ottoman.h */; };
    1.18  		276E5CC6106731C8008A2171 /* VersionDictionary.h in Headers */ = {isa = PBXBuildFile; fileRef = 276E5BC21066D135008A2171 /* VersionDictionary.h */; };
    1.19 +		276E5D141067D2AD008A2171 /* MYOttoman.mm in Sources */ = {isa = PBXBuildFile; fileRef = 276E5D0A1067D24A008A2171 /* MYOttoman.mm */; };
    1.20 +		276E5D151067D2B2008A2171 /* libOttoman.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 276E5CAE1067315D008A2171 /* libOttoman.a */; };
    1.21 +		276E5D2E1067F86E008A2171 /* MYVersionDictionary.mm in Sources */ = {isa = PBXBuildFile; fileRef = 276E5D2D1067F86E008A2171 /* MYVersionDictionary.mm */; };
    1.22  /* End PBXBuildFile section */
    1.23  
    1.24 +/* Begin PBXContainerItemProxy section */
    1.25 +		276E5D7810692FA7008A2171 /* PBXContainerItemProxy */ = {
    1.26 +			isa = PBXContainerItemProxy;
    1.27 +			containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */;
    1.28 +			proxyType = 1;
    1.29 +			remoteGlobalIDString = 276E5CAD1067315D008A2171;
    1.30 +			remoteInfo = "Static Library";
    1.31 +		};
    1.32 +/* End PBXContainerItemProxy section */
    1.33 +
    1.34  /* Begin PBXCopyFilesBuildPhase section */
    1.35  		8DD76F690486A84900D96B5E /* CopyFiles */ = {
    1.36  			isa = PBXCopyFilesBuildPhase;
    1.37 @@ -57,6 +75,15 @@
    1.38  
    1.39  /* Begin PBXFileReference section */
    1.40  		27156CA9104C9C44009EBD39 /* gtest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = gtest.framework; path = /Library/Frameworks/gtest.framework; sourceTree = "<absolute>"; };
    1.41 +		2754DD86106BCBCF00365FAA /* MYOttoman_test.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MYOttoman_test.m; sourceTree = "<group>"; };
    1.42 +		2754DDC3106BD20A00365FAA /* Test.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Test.h; sourceTree = "<group>"; };
    1.43 +		2754DDC4106BD20A00365FAA /* Test.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Test.m; sourceTree = "<group>"; };
    1.44 +		2754DDEC106BD3BD00365FAA /* Logging.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Logging.h; sourceTree = "<group>"; };
    1.45 +		2754DDED106BD3BD00365FAA /* Logging.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Logging.m; sourceTree = "<group>"; };
    1.46 +		2754DDF1106BD3D300365FAA /* ExceptionUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ExceptionUtils.h; sourceTree = "<group>"; };
    1.47 +		2754DDF2106BD3D300365FAA /* ExceptionUtils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ExceptionUtils.m; sourceTree = "<group>"; };
    1.48 +		2754DE2B106BD65600365FAA /* CollectionUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CollectionUtils.h; sourceTree = "<group>"; };
    1.49 +		2754DE2C106BD65600365FAA /* CollectionUtils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CollectionUtils.m; sourceTree = "<group>"; };
    1.50  		27603900105AC81200D931A7 /* CoreFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreFoundation.framework; path = System/Library/Frameworks/CoreFoundation.framework; sourceTree = SDKROOT; };
    1.51  		276E5BBA1066D135008A2171 /* Base.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Base.h; sourceTree = "<group>"; };
    1.52  		276E5BBB1066D135008A2171 /* Chunk.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Chunk.h; sourceTree = "<group>"; };
    1.53 @@ -83,6 +110,12 @@
    1.54  		276E5BDB1066D142008A2171 /* TestUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TestUtils.h; sourceTree = "<group>"; };
    1.55  		276E5BDC1066D142008A2171 /* VersionDictionary_test.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = VersionDictionary_test.cpp; sourceTree = "<group>"; };
    1.56  		276E5CAE1067315D008A2171 /* libOttoman.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libOttoman.a; sourceTree = BUILT_PRODUCTS_DIR; };
    1.57 +		276E5D091067D24A008A2171 /* MYOttoman.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MYOttoman.h; sourceTree = "<group>"; };
    1.58 +		276E5D0A1067D24A008A2171 /* MYOttoman.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MYOttoman.mm; sourceTree = "<group>"; };
    1.59 +		276E5D101067D27E008A2171 /* OttomanCocoa */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = OttomanCocoa; sourceTree = BUILT_PRODUCTS_DIR; };
    1.60 +		276E5D2C1067F86E008A2171 /* MYVersionDictionary.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MYVersionDictionary.h; sourceTree = "<group>"; };
    1.61 +		276E5D2D1067F86E008A2171 /* MYVersionDictionary.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MYVersionDictionary.mm; sourceTree = "<group>"; };
    1.62 +		276E5D451069246E008A2171 /* MYOttoman_internal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MYOttoman_internal.h; sourceTree = "<group>"; };
    1.63  		8DD76F6C0486A84900D96B5E /* OttomanTest */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = OttomanTest; sourceTree = BUILT_PRODUCTS_DIR; };
    1.64  /* End PBXFileReference section */
    1.65  
    1.66 @@ -94,6 +127,14 @@
    1.67  			);
    1.68  			runOnlyForDeploymentPostprocessing = 0;
    1.69  		};
    1.70 +		276E5D0E1067D27E008A2171 /* Frameworks */ = {
    1.71 +			isa = PBXFrameworksBuildPhase;
    1.72 +			buildActionMask = 2147483647;
    1.73 +			files = (
    1.74 +				276E5D151067D2B2008A2171 /* libOttoman.a in Frameworks */,
    1.75 +			);
    1.76 +			runOnlyForDeploymentPostprocessing = 0;
    1.77 +		};
    1.78  		8DD76F660486A84900D96B5E /* Frameworks */ = {
    1.79  			isa = PBXFrameworksBuildPhase;
    1.80  			buildActionMask = 2147483647;
    1.81 @@ -109,6 +150,7 @@
    1.82  		08FB7794FE84155DC02AAC07 /* BPlusTree */ = {
    1.83  			isa = PBXGroup;
    1.84  			children = (
    1.85 +				276E5D081067D20B008A2171 /* Cocoa */,
    1.86  				276E5BB91066D135008A2171 /* include */,
    1.87  				276E5BC31066D13D008A2171 /* src */,
    1.88  				276E5BD61066D142008A2171 /* test */,
    1.89 @@ -124,10 +166,27 @@
    1.90  			children = (
    1.91  				8DD76F6C0486A84900D96B5E /* OttomanTest */,
    1.92  				276E5CAE1067315D008A2171 /* libOttoman.a */,
    1.93 +				276E5D101067D27E008A2171 /* OttomanCocoa */,
    1.94  			);
    1.95  			name = Products;
    1.96  			sourceTree = "<group>";
    1.97  		};
    1.98 +		2754DDEB106BD38600365FAA /* MYUtilities */ = {
    1.99 +			isa = PBXGroup;
   1.100 +			children = (
   1.101 +				2754DE2B106BD65600365FAA /* CollectionUtils.h */,
   1.102 +				2754DE2C106BD65600365FAA /* CollectionUtils.m */,
   1.103 +				2754DDF1106BD3D300365FAA /* ExceptionUtils.h */,
   1.104 +				2754DDF2106BD3D300365FAA /* ExceptionUtils.m */,
   1.105 +				2754DDEC106BD3BD00365FAA /* Logging.h */,
   1.106 +				2754DDED106BD3BD00365FAA /* Logging.m */,
   1.107 +				2754DDC3106BD20A00365FAA /* Test.h */,
   1.108 +				2754DDC4106BD20A00365FAA /* Test.m */,
   1.109 +			);
   1.110 +			name = MYUtilities;
   1.111 +			path = /Volumes/snoog/Code/MYUtilities;
   1.112 +			sourceTree = "<absolute>";
   1.113 +		};
   1.114  		276E5BB91066D135008A2171 /* include */ = {
   1.115  			isa = PBXGroup;
   1.116  			children = (
   1.117 @@ -173,6 +232,21 @@
   1.118  			path = test;
   1.119  			sourceTree = "<group>";
   1.120  		};
   1.121 +		276E5D081067D20B008A2171 /* Cocoa */ = {
   1.122 +			isa = PBXGroup;
   1.123 +			children = (
   1.124 +				276E5D091067D24A008A2171 /* MYOttoman.h */,
   1.125 +				276E5D0A1067D24A008A2171 /* MYOttoman.mm */,
   1.126 +				276E5D2C1067F86E008A2171 /* MYVersionDictionary.h */,
   1.127 +				276E5D2D1067F86E008A2171 /* MYVersionDictionary.mm */,
   1.128 +				276E5D451069246E008A2171 /* MYOttoman_internal.h */,
   1.129 +				2754DD86106BCBCF00365FAA /* MYOttoman_test.m */,
   1.130 +				2754DDEB106BD38600365FAA /* MYUtilities */,
   1.131 +			);
   1.132 +			name = Cocoa;
   1.133 +			path = bindings/Cocoa;
   1.134 +			sourceTree = "<group>";
   1.135 +		};
   1.136  /* End PBXGroup section */
   1.137  
   1.138  /* Begin PBXHeadersBuildPhase section */
   1.139 @@ -212,6 +286,23 @@
   1.140  			productReference = 276E5CAE1067315D008A2171 /* libOttoman.a */;
   1.141  			productType = "com.apple.product-type.library.static";
   1.142  		};
   1.143 +		276E5D0F1067D27E008A2171 /* Cocoa */ = {
   1.144 +			isa = PBXNativeTarget;
   1.145 +			buildConfigurationList = 276E5D161067D2D1008A2171 /* Build configuration list for PBXNativeTarget "Cocoa" */;
   1.146 +			buildPhases = (
   1.147 +				276E5D0D1067D27E008A2171 /* Sources */,
   1.148 +				276E5D0E1067D27E008A2171 /* Frameworks */,
   1.149 +			);
   1.150 +			buildRules = (
   1.151 +			);
   1.152 +			dependencies = (
   1.153 +				276E5D7910692FA7008A2171 /* PBXTargetDependency */,
   1.154 +			);
   1.155 +			name = Cocoa;
   1.156 +			productName = Cocoa;
   1.157 +			productReference = 276E5D101067D27E008A2171 /* OttomanCocoa */;
   1.158 +			productType = "com.apple.product-type.tool";
   1.159 +		};
   1.160  		8DD76F620486A84900D96B5E /* OttomanTest */ = {
   1.161  			isa = PBXNativeTarget;
   1.162  			buildConfigurationList = 1DEB923108733DC60010E9CD /* Build configuration list for PBXNativeTarget "OttomanTest" */;
   1.163 @@ -244,6 +335,7 @@
   1.164  			targets = (
   1.165  				8DD76F620486A84900D96B5E /* OttomanTest */,
   1.166  				276E5CAD1067315D008A2171 /* Static Library */,
   1.167 +				276E5D0F1067D27E008A2171 /* Cocoa */,
   1.168  			);
   1.169  		};
   1.170  /* End PBXProject section */
   1.171 @@ -265,6 +357,20 @@
   1.172  			);
   1.173  			runOnlyForDeploymentPostprocessing = 0;
   1.174  		};
   1.175 +		276E5D0D1067D27E008A2171 /* Sources */ = {
   1.176 +			isa = PBXSourcesBuildPhase;
   1.177 +			buildActionMask = 2147483647;
   1.178 +			files = (
   1.179 +				276E5D141067D2AD008A2171 /* MYOttoman.mm in Sources */,
   1.180 +				276E5D2E1067F86E008A2171 /* MYVersionDictionary.mm in Sources */,
   1.181 +				2754DD87106BCBCF00365FAA /* MYOttoman_test.m in Sources */,
   1.182 +				2754DDC5106BD20A00365FAA /* Test.m in Sources */,
   1.183 +				2754DDEE106BD3BD00365FAA /* Logging.m in Sources */,
   1.184 +				2754DDF3106BD3D300365FAA /* ExceptionUtils.m in Sources */,
   1.185 +				2754DE2D106BD65600365FAA /* CollectionUtils.m in Sources */,
   1.186 +			);
   1.187 +			runOnlyForDeploymentPostprocessing = 0;
   1.188 +		};
   1.189  		8DD76F640486A84900D96B5E /* Sources */ = {
   1.190  			isa = PBXSourcesBuildPhase;
   1.191  			buildActionMask = 2147483647;
   1.192 @@ -288,6 +394,14 @@
   1.193  		};
   1.194  /* End PBXSourcesBuildPhase section */
   1.195  
   1.196 +/* Begin PBXTargetDependency section */
   1.197 +		276E5D7910692FA7008A2171 /* PBXTargetDependency */ = {
   1.198 +			isa = PBXTargetDependency;
   1.199 +			target = 276E5CAD1067315D008A2171 /* Static Library */;
   1.200 +			targetProxy = 276E5D7810692FA7008A2171 /* PBXContainerItemProxy */;
   1.201 +		};
   1.202 +/* End PBXTargetDependency section */
   1.203 +
   1.204  /* Begin XCBuildConfiguration section */
   1.205  		1DEB923208733DC60010E9CD /* Debug */ = {
   1.206  			isa = XCBuildConfiguration;
   1.207 @@ -320,6 +434,7 @@
   1.208  			buildSettings = {
   1.209  				ARCHS = "$(ARCHS_STANDARD_32_BIT)";
   1.210  				GCC_C_LANGUAGE_STANDARD = c99;
   1.211 +				GCC_INLINES_ARE_PRIVATE_EXTERN = YES;
   1.212  				GCC_OPTIMIZATION_LEVEL = 0;
   1.213  				GCC_PRECOMPILE_PREFIX_HEADER = YES;
   1.214  				GCC_PREPROCESSOR_DEFINITIONS = "";
   1.215 @@ -345,6 +460,7 @@
   1.216  				GCC_C_LANGUAGE_STANDARD = c99;
   1.217  				GCC_DYNAMIC_NO_PIC = YES;
   1.218  				GCC_ENABLE_CPP_RTTI = NO;
   1.219 +				GCC_INLINES_ARE_PRIVATE_EXTERN = YES;
   1.220  				GCC_PRECOMPILE_PREFIX_HEADER = YES;
   1.221  				GCC_PREPROCESSOR_DEFINITIONS = NDEBUG;
   1.222  				GCC_TREAT_WARNINGS_AS_ERRORS = YES;
   1.223 @@ -380,12 +496,43 @@
   1.224  			buildSettings = {
   1.225  				COPY_PHASE_STRIP = YES;
   1.226  				DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
   1.227 -				GENERATE_MASTER_OBJECT_FILE = YES;
   1.228  				INSTALL_PATH = /usr/local/lib;
   1.229  				PRODUCT_NAME = Ottoman;
   1.230  			};
   1.231  			name = Release;
   1.232  		};
   1.233 +		276E5D121067D27F008A2171 /* Debug */ = {
   1.234 +			isa = XCBuildConfiguration;
   1.235 +			buildSettings = {
   1.236 +				COPY_PHASE_STRIP = NO;
   1.237 +				GCC_PRECOMPILE_PREFIX_HEADER = NO;
   1.238 +				GCC_PREFIX_HEADER = "$(SYSTEM_LIBRARY_DIR)/Frameworks/Foundation.framework/Headers/Foundation.h";
   1.239 +				GCC_PREPROCESSOR_DEFINITIONS = DEBUG;
   1.240 +				INSTALL_PATH = /usr/local/bin;
   1.241 +				OTHER_LDFLAGS = (
   1.242 +					"-framework",
   1.243 +					Foundation,
   1.244 +				);
   1.245 +				PRODUCT_NAME = OttomanCocoa;
   1.246 +			};
   1.247 +			name = Debug;
   1.248 +		};
   1.249 +		276E5D131067D27F008A2171 /* Release */ = {
   1.250 +			isa = XCBuildConfiguration;
   1.251 +			buildSettings = {
   1.252 +				COPY_PHASE_STRIP = YES;
   1.253 +				GCC_PRECOMPILE_PREFIX_HEADER = NO;
   1.254 +				GCC_PREFIX_HEADER = "$(SYSTEM_LIBRARY_DIR)/Frameworks/Foundation.framework/Headers/Foundation.h";
   1.255 +				GCC_PREPROCESSOR_DEFINITIONS = DEBUG;
   1.256 +				INSTALL_PATH = /usr/local/bin;
   1.257 +				OTHER_LDFLAGS = (
   1.258 +					"-framework",
   1.259 +					Foundation,
   1.260 +				);
   1.261 +				PRODUCT_NAME = OttomanCocoa;
   1.262 +			};
   1.263 +			name = Release;
   1.264 +		};
   1.265  /* End XCBuildConfiguration section */
   1.266  
   1.267  /* Begin XCConfigurationList section */
   1.268 @@ -416,6 +563,15 @@
   1.269  			defaultConfigurationIsVisible = 0;
   1.270  			defaultConfigurationName = Release;
   1.271  		};
   1.272 +		276E5D161067D2D1008A2171 /* Build configuration list for PBXNativeTarget "Cocoa" */ = {
   1.273 +			isa = XCConfigurationList;
   1.274 +			buildConfigurations = (
   1.275 +				276E5D121067D27F008A2171 /* Debug */,
   1.276 +				276E5D131067D27F008A2171 /* Release */,
   1.277 +			);
   1.278 +			defaultConfigurationIsVisible = 0;
   1.279 +			defaultConfigurationName = Release;
   1.280 +		};
   1.281  /* End XCConfigurationList section */
   1.282  	};
   1.283  	rootObject = 08FB7793FE84155DC02AAC07 /* Project object */;
     2.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     2.2 +++ b/bindings/Cocoa/MYOttoman.h	Thu Sep 24 21:47:06 2009 -0700
     2.3 @@ -0,0 +1,81 @@
     2.4 +//
     2.5 +//  MYOttoman.h
     2.6 +//  Ottoman
     2.7 +//
     2.8 +//  Created by Jens Alfke on 9/21/09.
     2.9 +//  Copyright 2009 Jens Alfke. All rights reserved.
    2.10 +//
    2.11 +
    2.12 +#import <Foundation/Foundation.h>
    2.13 +
    2.14 +@class MYVersionDictionary, MYCurrentVersionDictionary;
    2.15 +
    2.16 +
    2.17 +/** A version-controlled persistent dictionary. 
    2.18 +    Each version is stored as a MYVersionDictionary, 
    2.19 +    unsaved changes as an MYCurrentVersionDictionary. */
    2.20 +@interface MYOttoman : NSObject 
    2.21 +{
    2.22 +    void *_ottoman;
    2.23 +    MYVersionDictionary *_lastVersion;
    2.24 +    MYCurrentVersionDictionary *_currentVersion;
    2.25 +}
    2.26 +
    2.27 +/** Creates an "untitled" Ottoman with no file and no lastVersion.
    2.28 +    After adding values to the currentVersion, use saveAs to save it. */
    2.29 +- (id) init;
    2.30 +
    2.31 +/** Opens an existing Ottoman file. */
    2.32 +- (id) initWithURL: (NSURL*)fileURL writeable: (BOOL)writeable error: (NSError**)outError;
    2.33 +
    2.34 +/** Closes an Ottoman. */
    2.35 +- (void) close;
    2.36 +
    2.37 +/** The current file, or nil if the receiver is still in the "untitled" state. */
    2.38 +@property (readonly) NSURL *URL;
    2.39 +
    2.40 +/** The latest saved version of the dictionary. 
    2.41 +    Earlier versions can be accessed through its previousVersion property.
    2.42 +    This will be nil if the receiver is still in the "untitled" state. */
    2.43 +@property (readonly) MYVersionDictionary* lastVersion;
    2.44 +
    2.45 +/** A mutable overlay representing the current state of the dictionary.
    2.46 +    Changes are made in memory until -save is called.
    2.47 +    This will be nil if the receiver was opened read-only (writeable=NO). */
    2.48 +@property (readonly) MYCurrentVersionDictionary* currentVersion;
    2.49 +
    2.50 +/** Has the on-disk file been updated with newer revisions than what I know about? */
    2.51 +@property (readonly) BOOL needsSync;
    2.52 +
    2.53 +/** Reads any newer versions from disk.
    2.54 +    Returns YES if new versions were read, NO if there were none or on error.
    2.55 +    Afterwards, -lastVersion will return the latest version in the file. 
    2.56 +    (The old lastVersion dictionary is still around, so you can save a pointer to it 
    2.57 +    before the call and use it later to see what changed.)
    2.58 +    Changes made to the -currentVersion dictionary are not lost, but are now relative
    2.59 +    to the new lastVersion. You may want to scan them and resolve conflicts. */
    2.60 +- (BOOL) sync: (NSError**)outError;
    2.61 +
    2.62 +/** Saves the current version to the file, by appending.
    2.63 +    Returns NO if there is a version conflict; in that case you need to call -sync,
    2.64 +    possibly resolve conflicts, and then call -save again. */
    2.65 +- (BOOL) save: (NSError**)outError;
    2.66 +
    2.67 +/** Saves the current version to the file, by writing to a new temporary file and
    2.68 +    then atomically replacing the original. 
    2.69 +    Older versions, and older copies of the data, are not preserved, so this
    2.70 +    will typically shrink the file quite a bit.
    2.71 +    Returns NO if there is a version conflict. */
    2.72 +- (BOOL) saveAndCompact: (NSError**)outError;
    2.73 +
    2.74 +/** Saves the current version to a new file, leaving the new file open, 
    2.75 +    so subsequent saves will be written to it. */
    2.76 +- (BOOL) saveAs: (NSURL*)newFileURL
    2.77 +         overwriteAllowed: (BOOL)overwriteAllowed
    2.78 +         error: (NSError**)outError;
    2.79 +
    2.80 +/** Scans the file for damage. Returns YES if the file is OK, NO if problems are found.
    2.81 +    If the 'repair' flag is set, will truncate the file to the last valid version. */
    2.82 +- (BOOL) scavengeAndRepair: (BOOL)repair error: (NSError**)outError;
    2.83 +
    2.84 +@end
     3.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     3.2 +++ b/bindings/Cocoa/MYOttoman.mm	Thu Sep 24 21:47:06 2009 -0700
     3.3 @@ -0,0 +1,205 @@
     3.4 +//
     3.5 +//  MYOttoman.mm
     3.6 +//  Ottoman
     3.7 +//
     3.8 +//  Created by Jens Alfke on 9/21/09.
     3.9 +//  Copyright 2009 Jens Alfke. All rights reserved.
    3.10 +//
    3.11 +
    3.12 +#import "MYOttoman.h"
    3.13 +#import "MYOttoman_internal.h"
    3.14 +extern "C" {
    3.15 +#import "Test.h"
    3.16 +}
    3.17 +
    3.18 +#include "Ottoman.h"
    3.19 +#include "VersionDictionary.h"
    3.20 +#include "File.h"
    3.21 +
    3.22 +
    3.23 +@interface MYOttoman ()
    3.24 +- (void) _versionsChanged;
    3.25 +@end
    3.26 +
    3.27 +
    3.28 +namespace Mooseyard {
    3.29 +
    3.30 +    class ObjCOwnedOttoman :public Ottoman {
    3.31 +    public:
    3.32 +        ObjCOwnedOttoman (MYOttoman *owner)
    3.33 +        :_owner(owner)
    3.34 +        { }
    3.35 +        
    3.36 +        ObjCOwnedOttoman (MYOttoman *owner, NSURL *fileURL, bool writeable)
    3.37 +        :Ottoman(fileURL.path.fileSystemRepresentation, writeable),
    3.38 +        _owner(owner)
    3.39 +        { }
    3.40 +        
    3.41 +    protected:
    3.42 +        virtual void versionsChanged() {
    3.43 +            [_owner _versionsChanged];
    3.44 +        }
    3.45 +    private:
    3.46 +        MYOttoman *_owner;
    3.47 +    };
    3.48 +    
    3.49 +}
    3.50 +
    3.51 +
    3.52 +using namespace Mooseyard;
    3.53 +
    3.54 +
    3.55 +static BOOL ErrorToNSError (const File::Error &x, NSError **outError) {
    3.56 +    if (outError) {
    3.57 +        *outError = [NSError errorWithDomain: NSPOSIXErrorDomain
    3.58 +                                        code: x.code
    3.59 +                                    userInfo: nil];
    3.60 +    }
    3.61 +    return NO;
    3.62 +}
    3.63 +
    3.64 +
    3.65 +@interface MYOttoman ()
    3.66 +@property (readonly) Ottoman* ottoman;
    3.67 +@end
    3.68 +
    3.69 +
    3.70 +@implementation MYOttoman
    3.71 +
    3.72 +
    3.73 +- (id) init {
    3.74 +    return [self initWithURL: nil writeable: YES error: nil];
    3.75 +}
    3.76 +
    3.77 +- (id) initWithURL: (NSURL*)fileURL
    3.78 +         writeable: (BOOL)writeable
    3.79 +             error: (NSError**)outError
    3.80 +{
    3.81 +    self = [super init];
    3.82 +    if (self) {
    3.83 +        try {
    3.84 +            if (fileURL) {
    3.85 +                NSAssert([fileURL isFileURL], @"MYOttoman only supports file: URLs");
    3.86 +                _ottoman = new ObjCOwnedOttoman(self, fileURL, writeable);
    3.87 +            } else {
    3.88 +                _ottoman = new ObjCOwnedOttoman(self);
    3.89 +            }
    3.90 +        } catch (const File::Error &x) {
    3.91 +            ErrorToNSError(x,outError);
    3.92 +            [self release];
    3.93 +            return nil;
    3.94 +        }
    3.95 +        
    3.96 +        if (writeable)
    3.97 +            _currentVersion = [[MYCurrentVersionDictionary alloc]
    3.98 +                                        _initWithOverlayDictionary: self.ottoman->currentVersion()];
    3.99 +    }
   3.100 +    return self;
   3.101 +}
   3.102 +
   3.103 +- (void) dealloc
   3.104 +{
   3.105 +    [_lastVersion release];
   3.106 +    [_currentVersion release];
   3.107 +    delete (Ottoman*)_ottoman;
   3.108 +    [super dealloc];
   3.109 +}
   3.110 +
   3.111 +- (void) finalize {
   3.112 +    delete (Ottoman*)_ottoman;
   3.113 +    [super finalize];
   3.114 +}
   3.115 +
   3.116 +- (void) close {
   3.117 +    delete (Ottoman*)_ottoman;
   3.118 +    _ottoman = nil;
   3.119 +}
   3.120 +
   3.121 +
   3.122 +- (Ottoman*) ottoman {
   3.123 +    Assert(_ottoman, @"MYOttoman has already been closed");
   3.124 +    return (Ottoman*) _ottoman;
   3.125 +}
   3.126 +
   3.127 +- (NSURL*) URL {
   3.128 +    const char *path = self.ottoman->filename();
   3.129 +    return path ?[NSURL fileURLWithPath: [NSString stringWithUTF8String: path]] :nil;
   3.130 +}
   3.131 +
   3.132 +
   3.133 +- (MYVersionDictionary*) lastVersion {
   3.134 +    if (!_lastVersion)
   3.135 +        _lastVersion = [[MYVersionDictionary alloc] _initWithVersionDictionary: self.ottoman->lastVersion()];
   3.136 +    return _lastVersion;
   3.137 +}
   3.138 +
   3.139 +- (MYCurrentVersionDictionary*) currentVersion {
   3.140 +    return _currentVersion;
   3.141 +}
   3.142 +
   3.143 +- (void) _versionsChanged {
   3.144 +    [_lastVersion autorelease];
   3.145 +    _lastVersion = nil;
   3.146 +}
   3.147 +
   3.148 +
   3.149 +- (BOOL) needsSync {
   3.150 +    return self.ottoman->needsSync();
   3.151 +}
   3.152 +
   3.153 +
   3.154 +- (BOOL) sync: (NSError**)outError {
   3.155 +    if (outError) *outError = nil;
   3.156 +    try {
   3.157 +        return self.ottoman->sync();
   3.158 +    } catch (const File::Error &x) {
   3.159 +        return ErrorToNSError(x,outError);
   3.160 +    }
   3.161 +}
   3.162 +
   3.163 +- (BOOL) save: (NSError**)outError {
   3.164 +    if (outError) *outError = nil;
   3.165 +    try {
   3.166 +        return self.ottoman->save();
   3.167 +    } catch (const File::Error &x) {
   3.168 +        return ErrorToNSError(x,outError);
   3.169 +    }
   3.170 +}
   3.171 +
   3.172 +- (BOOL) saveAndCompact: (NSError**)outError {
   3.173 +    if (outError) *outError = nil;
   3.174 +    try {
   3.175 +        return self.ottoman->saveAndCompact();
   3.176 +    } catch (const File::Error &x) {
   3.177 +        return ErrorToNSError(x,outError);
   3.178 +    }
   3.179 +}
   3.180 +
   3.181 +- (BOOL) saveAs: (NSURL*)newFileURL
   3.182 +         overwriteAllowed: (BOOL)overwriteAllowed
   3.183 +         error: (NSError**)outError
   3.184 +{
   3.185 +    NSParameterAssert(newFileURL!=nil);
   3.186 +    NSAssert([newFileURL isFileURL], @"MYOttoman only supports file: URLs");
   3.187 +    if (outError) *outError = nil;
   3.188 +    try {
   3.189 +        self.ottoman->saveAs(newFileURL.path.fileSystemRepresentation, overwriteAllowed);
   3.190 +        return YES;
   3.191 +    } catch (const File::Error &x) {
   3.192 +        return ErrorToNSError(x,outError);
   3.193 +    }
   3.194 +}
   3.195 +
   3.196 +
   3.197 +- (BOOL) scavengeAndRepair: (BOOL)repair error: (NSError**)outError {
   3.198 +    if (outError) *outError = nil;
   3.199 +    try {
   3.200 +        return self.ottoman->scavenge(repair);
   3.201 +    } catch (const File::Error &x) {
   3.202 +        return ErrorToNSError(x,outError);
   3.203 +    }
   3.204 +}
   3.205 +
   3.206 +
   3.207 +@end
   3.208 +
     4.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     4.2 +++ b/bindings/Cocoa/MYOttoman_internal.h	Thu Sep 24 21:47:06 2009 -0700
     4.3 @@ -0,0 +1,23 @@
     4.4 +//
     4.5 +//  MYOttoman_internal
     4.6 +//  Ottoman
     4.7 +//
     4.8 +//  Created by Jens Alfke on 9/22/09.
     4.9 +//  Copyright 2009 Jens Alfke. All rights reserved.
    4.10 +//
    4.11 +
    4.12 +
    4.13 +#import "MYVersionDictionary.h"
    4.14 +
    4.15 +namespace Mooseyard {
    4.16 +    class OverlayDictionary;
    4.17 +    class VersionDictionary;
    4.18 +}
    4.19 +
    4.20 +@interface MYVersionDictionary ()
    4.21 +- (id) _initWithVersionDictionary: (const Mooseyard::VersionDictionary*)dict;
    4.22 +@end
    4.23 +
    4.24 +@interface MYCurrentVersionDictionary ()
    4.25 +- (id) _initWithOverlayDictionary: (Mooseyard::OverlayDictionary*)dict;
    4.26 +@end
     5.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     5.2 +++ b/bindings/Cocoa/MYOttoman_test.m	Thu Sep 24 21:47:06 2009 -0700
     5.3 @@ -0,0 +1,121 @@
     5.4 +//
     5.5 +//  MYOttoman_test.m
     5.6 +//  Ottoman
     5.7 +//
     5.8 +//  Created by Jens Alfke on 9/24/09.
     5.9 +//  Copyright 2009 Jens Alfke. All rights reserved.
    5.10 +//
    5.11 +
    5.12 +#import "MYOttoman.h"
    5.13 +#import "MYVersionDictionary.h"
    5.14 +
    5.15 +#import "Test.h"
    5.16 +
    5.17 +static NSData* dataOf (NSString *str) {
    5.18 +    return [str dataUsingEncoding: NSUTF8StringEncoding];
    5.19 +}
    5.20 +
    5.21 +/*
    5.22 +static NSString* stringOf (id data) {
    5.23 +    if (!data)
    5.24 +        return nil;
    5.25 +    CAssert([data isKindOfClass: [NSData class]], @"stringOf expected NSData, got %@", [data class]);
    5.26 +    NSString *str = [[[NSString alloc] initWithData: data encoding: NSUTF8StringEncoding] autorelease];
    5.27 +    CAssert(str!=nil, @"Bad data (couldn't decode UTF-8): %@", data);
    5.28 +    return str;
    5.29 +}
    5.30 +*/
    5.31 +
    5.32 +static void checkFile (NSString *path) {
    5.33 +    NSError *error;
    5.34 +    NSDictionary *fileInfo = [[NSFileManager defaultManager] attributesOfItemAtPath: path error: &error];
    5.35 +    CAssert(fileInfo, @"Couldn't get info for %@: %@", path,error);
    5.36 +    CAssert([fileInfo fileSize] >= 512, @"Unexpected file size %lld", [fileInfo fileSize]);
    5.37 +    NSTimeInterval age = - [[fileInfo fileModificationDate] timeIntervalSinceNow];
    5.38 +    CAssert(age < 60.0, @"File %@ is too old: %.1lf sec", path,age);
    5.39 +}
    5.40 +
    5.41 +
    5.42 +TestCase(CreateMYOttoman) {
    5.43 +    MYOttoman *o = [[MYOttoman alloc] init];
    5.44 +    MYCurrentVersionDictionary *cur = o.currentVersion;
    5.45 +    CAssertEq(o.lastVersion,nil);
    5.46 +    
    5.47 +    [cur setObject: dataOf(@"first value") forKey: dataOf(@"first key")];
    5.48 +    [cur setObject: dataOf(@"second value") forKey: dataOf(@"second key")];
    5.49 +    [cur setObject: dataOf(@"third value") forKey: dataOf(@"third key")];
    5.50 +    [cur removeObjectForKey: dataOf(@"third key")];
    5.51 +    [cur removeObjectForKey: dataOf(@"bogus")];
    5.52 +
    5.53 +    CAssertEq(cur.count, 2);
    5.54 +    CAssertEqual([cur objectForKey: dataOf(@"first key")], dataOf(@"first value"));
    5.55 +    CAssertEqual([cur objectForKey: dataOf(@"second key")], dataOf(@"second value"));
    5.56 +    CAssertEqual([cur objectForKey: dataOf(@"third key")], nil);
    5.57 +
    5.58 +    NSError *error;
    5.59 +    if (![o saveAs: [NSURL fileURLWithPath: @"/tmp/myottomantest.ottoman"]
    5.60 +                          overwriteAllowed: YES error: &error])
    5.61 +        CAssert(NO, @"saveAs: failed: %@", error);
    5.62 +    checkFile(@"/tmp/myottomantest.ottoman");
    5.63 +    
    5.64 +    MYVersionDictionary *last = o.lastVersion;
    5.65 +    CAssert(last, @"lastVersion is nil after save");
    5.66 +    CAssertEq(last.generation,0);
    5.67 +    CAssertEqual([last objectForKey: dataOf(@"first key")], dataOf(@"first value"));
    5.68 +    CAssertEqual([last objectForKey: dataOf(@"second key")], dataOf(@"second value"));
    5.69 +    CAssertEqual([last objectForKey: dataOf(@"third key")], nil);
    5.70 +    
    5.71 +    [cur setObject: dataOf(@"fourth value") forKey: dataOf(@"fourth key")];
    5.72 +    [cur setObject: dataOf(@"new first value") forKey: dataOf(@"first key")];
    5.73 +    [cur removeObjectForKey: dataOf(@"second key")];
    5.74 +    CAssertEq(cur.count, 2);
    5.75 +    CAssertEqual([cur objectForKey: dataOf(@"first key")], dataOf(@"new first value"));
    5.76 +    CAssertEqual([cur objectForKey: dataOf(@"second key")], nil);
    5.77 +    CAssertEqual([cur objectForKey: dataOf(@"fourth key")], dataOf(@"fourth value"));
    5.78 +
    5.79 +    if (![o save: &error])
    5.80 +        CAssert(NO, @"save: failed: %@", error);
    5.81 + 
    5.82 +    CAssertEqual([last objectForKey: dataOf(@"first key")], dataOf(@"first value"));
    5.83 +    CAssertEqual([last objectForKey: dataOf(@"second key")], dataOf(@"second value"));
    5.84 +    CAssertEqual([last objectForKey: dataOf(@"third key")], nil);
    5.85 +
    5.86 +    last = o.lastVersion;
    5.87 +    CAssertEq(last.generation,1);
    5.88 +    CAssertEqual([last objectForKey: dataOf(@"first key")], dataOf(@"new first value"));
    5.89 +    CAssertEqual([last objectForKey: dataOf(@"second key")], nil);
    5.90 +    CAssertEqual([last objectForKey: dataOf(@"third key")], nil);
    5.91 +    CAssertEqual([last objectForKey: dataOf(@"fourth key")], dataOf(@"fourth value"));
    5.92 +
    5.93 +    [o close];
    5.94 +    [o release];
    5.95 +    
    5.96 +    o = [[MYOttoman alloc] initWithURL: [NSURL fileURLWithPath: @"/tmp/myottomantest.ottoman"]
    5.97 +                              writeable: NO error: &error];
    5.98 +    CAssert(o, @"Failed to re-open Ottoman: %@", error);
    5.99 +    CAssertEq(o.currentVersion, nil);
   5.100 +    last = o.lastVersion;
   5.101 +    CAssertEq(last.generation,1);
   5.102 +    CAssertEqual([last objectForKey: dataOf(@"first key")], dataOf(@"new first value"));
   5.103 +    CAssertEqual([last objectForKey: dataOf(@"second key")], nil);
   5.104 +    CAssertEqual([last objectForKey: dataOf(@"third key")], nil);
   5.105 +    CAssertEqual([last objectForKey: dataOf(@"fourth key")], dataOf(@"fourth value"));
   5.106 +    
   5.107 +    MYVersionDictionary *prev = last.previousVersion;
   5.108 +    CAssert(prev!=nil);
   5.109 +    CAssertEq(prev.generation,0);
   5.110 +    CAssertEq(prev.previousVersion,nil);
   5.111 +    CAssertEqual([prev objectForKey: dataOf(@"first key")], dataOf(@"first value"));
   5.112 +    CAssertEqual([prev objectForKey: dataOf(@"second key")], dataOf(@"second value"));
   5.113 +    CAssertEqual([prev objectForKey: dataOf(@"third key")], nil);
   5.114 +        
   5.115 +    [o close];
   5.116 +    [o release];
   5.117 +}
   5.118 +
   5.119 +
   5.120 +int main(int argc, const char**argv) {
   5.121 +    const char* testArgs[2] = {"", "Test_All"};
   5.122 +    RunTestCases(2,testArgs);
   5.123 +    return 0;
   5.124 +}
     6.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     6.2 +++ b/bindings/Cocoa/MYVersionDictionary.h	Thu Sep 24 21:47:06 2009 -0700
     6.3 @@ -0,0 +1,55 @@
     6.4 +//
     6.5 +//  MYVersionDictionary.h
     6.6 +//  Ottoman
     6.7 +//
     6.8 +//  Created by Jens Alfke on 9/21/09.
     6.9 +//  Copyright 2009 Jens Alfke. All rights reserved.
    6.10 +//
    6.11 +
    6.12 +#import <Foundation/NSDictionary.h>
    6.13 +
    6.14 +#ifdef __cplusplus
    6.15 +namespace Mooseyard {
    6.16 +    class OverlayDictionary;
    6.17 +    class VersionDictionary;
    6.18 +}
    6.19 +#endif
    6.20 +
    6.21 +
    6.22 +/** A persistent NSDictionary embedded in a memory-mapped file, 
    6.23 +    representing one complete version of a MYOttoman dictionary. */
    6.24 +@interface MYVersionDictionary : NSDictionary
    6.25 +{
    6.26 +#ifdef __cplusplus
    6.27 +    const Mooseyard::VersionDictionary *_dict;
    6.28 +#else
    6.29 +    void *_dict;
    6.30 +#endif
    6.31 +}
    6.32 +
    6.33 +/** The generation number. The first version in a new file is zero. */
    6.34 +@property (readonly) int generation;
    6.35 +
    6.36 +/** The time at which this version was written to the file. */
    6.37 +@property (readonly) NSDate *dateCreated;
    6.38 +
    6.39 +/** The previous version, before this one was saved. */
    6.40 +@property (readonly) MYVersionDictionary *previousVersion;
    6.41 +
    6.42 +/** Is this the latest version in the file? */
    6.43 +@property (readonly) BOOL isLatestVersion;
    6.44 +
    6.45 +@end
    6.46 +
    6.47 +
    6.48 +
    6.49 +@interface MYCurrentVersionDictionary : NSMutableDictionary
    6.50 +{
    6.51 +#ifdef __cplusplus
    6.52 +    Mooseyard::OverlayDictionary *_dict;
    6.53 +#else
    6.54 +    void *_dict;
    6.55 +#endif
    6.56 +}
    6.57 +
    6.58 +@end
     7.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     7.2 +++ b/bindings/Cocoa/MYVersionDictionary.mm	Thu Sep 24 21:47:06 2009 -0700
     7.3 @@ -0,0 +1,187 @@
     7.4 +//
     7.5 +//  MYVersionDictionary.m
     7.6 +//  Ottoman
     7.7 +//
     7.8 +//  Created by Jens Alfke on 9/21/09.
     7.9 +//  Copyright 2009 Jens Alfke. All rights reserved.
    7.10 +//
    7.11 +
    7.12 +#import "MYVersionDictionary.h"
    7.13 +#import "MYOttoman_internal.h"
    7.14 +#import <Foundation/Foundation.h>
    7.15 +
    7.16 +#include "VersionDictionary.h"
    7.17 +
    7.18 +using namespace Mooseyard;
    7.19 +
    7.20 +
    7.21 +@interface MYDictionaryEnumerator : NSEnumerator
    7.22 +{
    7.23 +    Dictionary::Iterator *_iter;
    7.24 +    BOOL _started;
    7.25 +}
    7.26 +- (id) initWithDict: (const Dictionary*)dict;
    7.27 +@end
    7.28 +
    7.29 +
    7.30 +
    7.31 +static id BlobToNSData (const Blob &blob, bool copy =true) {
    7.32 +    if (!blob)
    7.33 +        return nil;
    7.34 +    else if (copy)
    7.35 +        return [NSData dataWithBytes: blob.bytes length: blob.length];
    7.36 +    else
    7.37 +        return [NSData dataWithBytesNoCopy: (void*)blob.bytes
    7.38 +                                    length: blob.length
    7.39 +                              freeWhenDone: NO];
    7.40 +}
    7.41 +
    7.42 +static Blob BlobFromId (id object) {
    7.43 +    if ([object isKindOfClass: [NSData class]])
    7.44 +        return Blob([object bytes], [object length]);
    7.45 +    else 
    7.46 +        return Key();
    7.47 +}
    7.48 +
    7.49 +
    7.50 +
    7.51 +@implementation MYVersionDictionary
    7.52 +
    7.53 +
    7.54 +- (id) _initWithVersionDictionary: (VersionDictionary*)dict
    7.55 +{
    7.56 +    self = [super init];
    7.57 +    if (self != nil) {
    7.58 +        if (!dict) {
    7.59 +            [self release];
    7.60 +            return nil;
    7.61 +        }
    7.62 +        _dict = dict;
    7.63 +    }
    7.64 +    return self;
    7.65 +}
    7.66 +
    7.67 +
    7.68 +- (NSUInteger)count {
    7.69 +    return _dict->count();
    7.70 +}
    7.71 +
    7.72 +- (id)objectForKey:(id)keyObject {
    7.73 +    return BlobToNSData( _dict->get( Key(BlobFromId(keyObject)) ) );
    7.74 +}
    7.75 +
    7.76 +- (NSEnumerator *)keyEnumerator {
    7.77 +    return [[[MYDictionaryEnumerator alloc] initWithDict: _dict] autorelease];
    7.78 +}
    7.79 +
    7.80 +- (int) generation {
    7.81 +    return _dict->generation();
    7.82 +}
    7.83 +
    7.84 +- (NSDate*) dateCreated {
    7.85 +    return [NSDate dateWithTimeIntervalSince1970: _dict->timestamp()];
    7.86 +}
    7.87 +
    7.88 +- (BOOL) isLatestVersion {
    7.89 +    return _dict->isLatestVersion();
    7.90 +}
    7.91 +
    7.92 +- (MYVersionDictionary*) previousVersion {
    7.93 +    const VersionDictionary *prev = _dict->previousVersion();
    7.94 +    if (prev)
    7.95 +        return [[[MYVersionDictionary alloc] _initWithVersionDictionary: prev] autorelease];
    7.96 +    else
    7.97 +        return nil;
    7.98 +}
    7.99 +
   7.100 +@end
   7.101 +
   7.102 +
   7.103 +
   7.104 +
   7.105 +@implementation MYCurrentVersionDictionary
   7.106 +
   7.107 +
   7.108 +- (id) _initWithOverlayDictionary: (OverlayDictionary*)dict
   7.109 +{
   7.110 +    self = [super init];
   7.111 +    if (self != nil) {
   7.112 +        if (!dict) {
   7.113 +            [self release];
   7.114 +            return nil;
   7.115 +        }
   7.116 +        _dict = dict;
   7.117 +    }
   7.118 +    return self;
   7.119 +}
   7.120 +
   7.121 +
   7.122 +- (NSUInteger)count {
   7.123 +    return _dict->count();
   7.124 +}
   7.125 +
   7.126 +- (id)objectForKey:(id)keyObject {
   7.127 +    Blob keyBlob = BlobFromId(keyObject);
   7.128 +    return keyBlob ? BlobToNSData( _dict->get( Key(keyBlob) ) ) :nil;
   7.129 +}
   7.130 +
   7.131 +- (NSEnumerator *)keyEnumerator {
   7.132 +    return [[[MYDictionaryEnumerator alloc] initWithDict: _dict] autorelease];
   7.133 +}
   7.134 +
   7.135 +- (void)setObject:(id)anObject forKey:(id)aKey {
   7.136 +    Key key = Key(BlobFromId(aKey));
   7.137 +    NSAssert1(key, @"Invalid %@ key; only NSData supported", [aKey class]);
   7.138 +    Blob value = BlobFromId(anObject);
   7.139 +    NSAssert1(key, @"Invalid %@ value; only NSData supported", [aKey class]);
   7.140 +    _dict->put(key, value);
   7.141 +}
   7.142 +
   7.143 +- (void)removeObjectForKey:(id)aKey {
   7.144 +    Key key = Key(BlobFromId(aKey));
   7.145 +    NSAssert1(key, @"Invalid key class %@; only NSData supported", [aKey class]);
   7.146 +    _dict->remove(key);
   7.147 +}
   7.148 +
   7.149 +@end
   7.150 +
   7.151 +
   7.152 +
   7.153 +
   7.154 +@implementation MYDictionaryEnumerator
   7.155 +
   7.156 +- (id) initWithDict: (Dictionary*)dict
   7.157 +{
   7.158 +    self = [super init];
   7.159 +    if (self != nil) {
   7.160 +        _iter = dict->iterate();
   7.161 +    }
   7.162 +    return self;
   7.163 +}
   7.164 +
   7.165 +- (void) dealloc
   7.166 +{
   7.167 +    delete _iter;
   7.168 +    [super dealloc];
   7.169 +}
   7.170 +
   7.171 +- (void) finalize {
   7.172 +    delete _iter;
   7.173 +    [super finalize];
   7.174 +}
   7.175 +
   7.176 +- (id)nextObject {
   7.177 +    if (_started) {
   7.178 +        if (!_iter->next())
   7.179 +            return nil;
   7.180 +    } else {
   7.181 +        if (!_iter->hasValue())
   7.182 +            return nil;
   7.183 +        _started = YES;
   7.184 +    }
   7.185 +    Key key = _iter->key();
   7.186 +    return [NSData dataWithBytes: key.bytes length: key.length];
   7.187 +}
   7.188 +            
   7.189 +
   7.190 +@end