2 Copyright (C) 2000-2005 SKYRIX Software AG
4 This file is part of SOPE.
6 SOPE is free software; you can redistribute it and/or modify it under
7 the terms of the GNU Lesser General Public License as published by the
8 Free Software Foundation; either version 2, or (at your option) any
11 SOPE is distributed in the hope that it will be useful, but WITHOUT ANY
12 WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
14 License for more details.
16 You should have received a copy of the GNU Lesser General Public
17 License along with SOPE; see the file COPYING. If not, write to the
18 Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
22 #include "OFSPropertyListObject.h"
23 #include "OFSFactoryContext.h"
24 #include <WebDAV/SoObject+SoDAV.h>
27 @interface OFSPropertyListObjectClassDescription : NSClassDescription
30 OFSPropertyListObject *object;
35 @implementation OFSPropertyListObject
37 static int debugOn = 0;
40 return [super version] + 0 /* v1 */;
43 static BOOL didInit = NO;
45 NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
47 NSAssert2([super version] == 1,
48 @"invalid superclass (%@) version %i !",
49 NSStringFromClass([self superclass]), [super version]);
51 debugOn = [ud boolForKey:@"SoOFSDebugPlistObject"] ? 1 : 0;
56 [self->recordKeys release];
57 [self->record release];
63 - (NSStringEncoding)stringEncoding {
64 return [NSString defaultCStringEncoding];
67 - (void)setSoResourceClassName:(NSString *)_name {} // ignore
68 - (void)setSoClassName:(NSString *)_name {} // ignore
69 - (NSString *)soResourceClassName { // deprecated
70 return [[self soClass] className];
72 - (NSString *)soClassName {
73 return [[self soClass] className];
76 - (NSArray *)attributeKeys {
77 [[self restoreObject] raise];
78 return self->recordKeys;
81 - (NSArray *)allKeys {
82 return [self attributeKeys];
85 - (NSClassDescription *)soClassDescription {
86 OFSPropertyListObjectClassDescription *cd;
87 cd = [[OFSPropertyListObjectClassDescription alloc] init];
88 cd->object = [self retain];
89 return [cd autorelease];
92 - (NSString *)contentAsString {
93 /* do not allow access to raw contents ! */
97 - (void)removeSpecialKeysFromRestoreDictionary:(NSMutableDictionary *)_md {
98 /* remove special storage keys like SoClassName to plist */
99 [_md removeObjectForKey:@"SoClassName"];
100 [_md removeObjectForKey:@"SoResourceClassName"];
102 - (void)addSpecialKeysToSaveDictionary:(NSMutableDictionary *)_md {
103 /* add special storage keys like SoClassName to plist */
104 [_md setObject:[self soClassName] forKey:@"SoClassName"];
107 - (NSException *)restoreObject {
108 NSMutableDictionary *plist = nil;
114 if (self->flags.isLoaded) return nil;
115 if ((fm = [self fileManager]) == nil) {
116 e = [NSException exceptionWithHTTPStatus:500
117 reason:@"plist object has no filemanager ??"];
120 s = [self storagePath];
121 if ([s length] == 0) {
122 e = [NSException exceptionWithHTTPStatus:500
123 reason:@"plist object has no storage path ??"];
127 self->flags.isLoading = 1;
128 self->flags.isLoaded = 1;
131 /* load file, convert into string, then into a property list */
133 if ((content = [fm contentsAtPath:s])==nil) {
134 if (([fm respondsToSelector:@selector(lastException)])) {
135 e = [fm lastException];
139 e = [NSException exceptionWithHTTPStatus:404 /* not found */
140 reason:@"failed to load property list file ..."];
144 s = [[NSString alloc] initWithData:content encoding:[self stringEncoding]];
146 e = [NSException exceptionWithHTTPStatus:500
147 reason:@"failed to create string from file ..."];
150 plist = [[s propertyList] mutableCopy];
153 e = [NSException exceptionWithHTTPStatus:500
154 reason:@"failed to create property list from file ..."];
158 [self removeSpecialKeysFromRestoreDictionary:plist];
160 self->recordKeys = [[plist allKeys] copy];
162 [self debugWithFormat:@"taking values of: %@", plist];
163 [self takeValuesFromDictionary:plist];
167 self->flags.isEdited = 0;
168 self->flags.isLoading = 0;
173 if (!self->flags.isLoading)
174 self->flags.isEdited = 1;
177 return self->flags.isLoaded ? YES : NO;
180 - (NSException *)saveObject {
181 NSMutableDictionary *d;
187 e = (self->flags.isNew)
188 ? [self validateForInsert]
189 : [self validateForSave];
192 if (!self->flags.isNew) {
193 if ((e = [self restoreObject]))
197 d = (self->recordKeys)
198 ? [[self valuesForKeys:self->recordKeys] mutableCopy]
199 : [self->record mutableCopy];
202 [self logWithFormat:@"got no dict to save ..."];
203 return [NSException exceptionWithHTTPStatus:500
204 reason:@"got no record to save ..."];
207 [self addSpecialKeysToSaveDictionary:d];
212 content = [s dataUsingEncoding:[self stringEncoding]];
214 fm = [self fileManager];
216 if (![fm writeContents:content atPath:[self storagePath]]) {
217 [self logWithFormat:@"failed to update file: %@", [self storagePath]];
219 if (([fm respondsToSelector:@selector(lastException)]))
220 return [fm lastException];
222 return [NSException exceptionWithHTTPStatus:500
223 reason:@"failed to update property list file ..."];
227 self->flags.isNew = 0;
233 - (void)handleTakeValue:(id)_value forUnboundKey:(NSString *)_key {
236 if ((oldValue = [self->record objectForKey:_key]) == nil) {
237 if (_value == nil) return;
239 else if (![_value isNotNull]) {
241 [self->record removeObjectForKey:_key];
244 else if (oldValue == _value) {
247 else if ([oldValue isEqual:_value])
252 if (self->record == nil)
253 self->record = [[NSMutableDictionary alloc] initWithCapacity:16];
255 if (!self->flags.isLoading && debugOn)
256 [self debugWithFormat:@"set unbound key: %@", _key];
258 if (![self->recordKeys containsObject:_key]) {
261 rk = [self->recordKeys mutableCopy];
263 [self->recordKeys release];
264 self->recordKeys = rk;
267 [self->record setObject:_value?_value:@"" forKey:_key];
270 - (BOOL)isStoredKey:(NSString *)_key {
271 /* says whether we need to restore the object to access the key */
272 if ([_key hasPrefix:@"NS"]) {
273 if ([_key isEqualToString:@"NSFileSubject"])
280 - (void)takeValue:(id)_value forKey:(NSString *)_name {
281 if (!self->flags.isLoaded && !self->flags.isLoading) {
282 if ([self isStoredKey:_name])
283 [[self restoreObject] raise];
286 [super takeValue:_value forKey:_name];
289 - (id)valueForKey:(NSString *)_name {
292 if ([_name hasPrefix:@"NS"]) {
293 if ([_name isEqualToString:@"NSFileSize"])
294 v = [self davContentLength];
295 else if ([_name isEqualToString:@"NSFileSubject"])
296 v = [self davDisplayName];
298 /* this implies that stored keys never begin with NS ! (good ?) */
299 v = [super valueForKey:_name];
301 else if ([self isStoredKey:_name]) {
302 if ((v = [self restoreObject]))
303 /* v is the restoration exception, do not want to raise */;
304 else if ((v = [self->record objectForKey:_name]))
305 /* a record value */;
307 /* stored-key doesn't say *where* it is stored ! */
308 v = [super valueForKey:_name];
311 v = [super valueForKey:_name];
318 - (id)GETAction:(WOContext *)_ctx {
321 if ((e = [self restoreObject]))
324 /* let the renderer deal with our representation ... */
328 - (id)PUTAction:(id)_ctx {
329 return [NSException exceptionWithHTTPStatus:405 /* method not allowed */
330 reason:@"HTTP PUT not yet allowed on plist objects"];
335 - (NSString *)davDisplayName {
336 return [[self nameInContainer] stringByDeletingPathExtension];
338 - (id)davContentLength {
339 static NSNumber *zero = nil;
340 if (zero == nil) zero = [[NSNumber numberWithInt:0] retain];
344 - (NSException *)davSetProperties:(NSDictionary *)_setProps
345 removePropertiesNamed:(NSArray *)_delProps
351 [self debugWithFormat:@"patch: %@, del: %@", _setProps, _delProps];
353 if ((e = [self restoreObject]))
356 if ([_setProps count] > 0)
357 [self takeValuesFromDictionary:_setProps];
359 if ([_delProps count] > 0) {
362 [self->record removeObjectsForKeys:_delProps];
363 rk = [self->recordKeys mutableCopy];
364 [rk removeObjectsInArray:_delProps];
365 [self->recordKeys release];
366 self->recordKeys = rk;
369 if ((e = [self saveObject])) {
370 [self logWithFormat:@"update failed ..."];
379 + (id)instantiateInFactoryContext:(OFSFactoryContext *)_ctx {
380 /* look into plist for class */
382 OFSPropertyListObject *object;
385 if ([_ctx isNewObject]) {
386 /* create a new object in the storage */
387 clazz = [self soClass];
391 [self debugWithFormat:@"instantiate child %@ from class %@",
392 [_ctx nameInContainer], clazz];
395 object = [clazz instantiateObject];
396 [object takeStorageInfoFromContext:_ctx];
398 if ([object isKindOfClass:[OFSPropertyListObject class]])
399 object->flags.isNew = 1;
401 if ((e = [object saveObject])) {
402 [self debugWithFormat:@" save failed: %@", e];
407 /* restore object from storage */
413 content = [[_ctx fileManager] contentsAtPath:[_ctx storagePath]];
415 /* hm, file doesn't exist ? */
416 return [super instantiateInFactoryContext:_ctx];
418 /* parse the existing plist file */
420 string = [[NSString alloc] initWithData:content
421 encoding:[NSString defaultCStringEncoding]];
423 [self logWithFormat:@"could not make string for stored data."];
424 return [NSException exceptionWithHTTPStatus:500
425 reason:@"stored property list is corrupted"];
428 if ((plist = [string propertyList]) == nil) {
430 [self logWithFormat:@"could not make plist for stored data."];
431 return [NSException exceptionWithHTTPStatus:500
433 @"stored property list is corrupted "
434 @"(not in plist format)"];
438 /* lookup the classname in plist */
440 className = [plist objectForKey:@"SoClassName"];
441 if ([className length] == 0)
442 /* no special class assigned, use default */
443 clazz = [self soClass];
445 clazz = [[SoClassRegistry sharedClassRegistry] soClassWithName:className];
447 [self logWithFormat:@"did not find SoClass: %@", className];
455 [self debugWithFormat:@"instantiate child %@ from class %@",
456 [_ctx nameInContainer], clazz];
459 object = [clazz instantiateObject];
460 [object takeStorageInfoFromContext:_ctx];
465 [self debugWithFormat:@"restore child %@: %@",
466 [_ctx nameInContainer], object];
469 if ((e = [object restoreObject])) {
470 [self debugWithFormat:@" restore failed: %@", e];
479 - (BOOL)isDebuggingEnabled {
480 return debugOn ? YES : NO;
483 @end /* OFSPropertyListObject */
485 @implementation OFSPropertyListObjectClassDescription
488 [self->object release];
492 - (NSArray *)attributeKeys {
493 return [self->object attributeKeys];
496 @end /* OFSPropertyListObjectClassDescription */