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 /* ZNeK: due to a bug in gstep-base 1.12.0 KVC, we need to add this */
271 - (void)setValue:(id)_value forUndefinedKey:(NSString *)_key {
272 [self handleTakeValue:_value forUnboundKey:_key];
275 - (BOOL)isStoredKey:(NSString *)_key {
276 /* says whether we need to restore the object to access the key */
277 if ([_key hasPrefix:@"NS"]) {
278 if ([_key isEqualToString:@"NSFileSubject"])
285 - (void)takeValue:(id)_value forKey:(NSString *)_name {
286 if (!self->flags.isLoaded && !self->flags.isLoading) {
287 if ([self isStoredKey:_name])
288 [[self restoreObject] raise];
291 [super takeValue:_value forKey:_name];
294 - (id)valueForKey:(NSString *)_name {
297 if ([_name hasPrefix:@"NS"]) {
298 if ([_name isEqualToString:@"NSFileSize"])
299 v = [self davContentLength];
300 else if ([_name isEqualToString:@"NSFileSubject"])
301 v = [self davDisplayName];
303 /* this implies that stored keys never begin with NS ! (good ?) */
304 v = [super valueForKey:_name];
306 else if ([self isStoredKey:_name]) {
307 if ((v = [self restoreObject]))
308 /* v is the restoration exception, do not want to raise */;
309 else if ((v = [self->record objectForKey:_name]))
310 /* a record value */;
312 /* stored-key doesn't say *where* it is stored ! */
313 v = [super valueForKey:_name];
316 v = [super valueForKey:_name];
323 - (id)GETAction:(WOContext *)_ctx {
326 if ((e = [self restoreObject]))
329 /* let the renderer deal with our representation ... */
333 - (id)PUTAction:(WOContext *)_ctx {
334 return [NSException exceptionWithHTTPStatus:405 /* method not allowed */
335 reason:@"HTTP PUT not yet allowed on plist objects"];
340 - (NSString *)davDisplayName {
341 return [[self nameInContainer] stringByDeletingPathExtension];
343 - (id)davContentLength {
344 static NSNumber *zero = nil;
345 if (zero == nil) zero = [[NSNumber numberWithInt:0] retain];
349 - (NSException *)davSetProperties:(NSDictionary *)_setProps
350 removePropertiesNamed:(NSArray *)_delProps
356 [self debugWithFormat:@"patch: %@, del: %@", _setProps, _delProps];
358 if ((e = [self restoreObject]))
361 if ([_setProps count] > 0)
362 [self takeValuesFromDictionary:_setProps];
364 if ([_delProps count] > 0) {
367 [self->record removeObjectsForKeys:_delProps];
368 rk = [self->recordKeys mutableCopy];
369 [rk removeObjectsInArray:_delProps];
370 [self->recordKeys release];
371 self->recordKeys = rk;
374 if ((e = [self saveObject])) {
375 [self logWithFormat:@"update failed ..."];
384 + (id)instantiateInFactoryContext:(OFSFactoryContext *)_ctx {
385 /* look into plist for class */
387 OFSPropertyListObject *object;
390 if ([_ctx isNewObject]) {
391 /* create a new object in the storage */
392 clazz = [self soClass];
396 [self debugWithFormat:@"instantiate child %@ from class %@",
397 [_ctx nameInContainer], clazz];
400 object = [clazz instantiateObject];
401 [object takeStorageInfoFromContext:_ctx];
403 if ([object isKindOfClass:[OFSPropertyListObject class]])
404 object->flags.isNew = 1;
406 if ((e = [object saveObject])) {
407 [self debugWithFormat:@" save failed: %@", e];
412 /* restore object from storage */
418 content = [[_ctx fileManager] contentsAtPath:[_ctx storagePath]];
420 /* hm, file doesn't exist ? */
421 return [super instantiateInFactoryContext:_ctx];
423 /* parse the existing plist file */
425 string = [[NSString alloc] initWithData:content
426 encoding:[NSString defaultCStringEncoding]];
428 [self logWithFormat:@"could not make string for stored data."];
429 return [NSException exceptionWithHTTPStatus:500
430 reason:@"stored property list is corrupted"];
433 if ((plist = [string propertyList]) == nil) {
435 [self logWithFormat:@"could not make plist for stored data."];
436 return [NSException exceptionWithHTTPStatus:500
438 @"stored property list is corrupted "
439 @"(not in plist format)"];
443 /* lookup the classname in plist */
445 className = [plist objectForKey:@"SoClassName"];
446 if ([className length] == 0)
447 /* no special class assigned, use default */
448 clazz = [self soClass];
450 clazz = [[SoClassRegistry sharedClassRegistry] soClassWithName:className];
452 [self logWithFormat:@"did not find SoClass: %@", className];
460 [self debugWithFormat:@"instantiate child %@ from class %@",
461 [_ctx nameInContainer], clazz];
464 object = [clazz instantiateObject];
465 [object takeStorageInfoFromContext:_ctx];
470 [self debugWithFormat:@"restore child %@: %@",
471 [_ctx nameInContainer], object];
474 if ((e = [object restoreObject])) {
475 [self debugWithFormat:@" restore failed: %@", e];
484 - (BOOL)isDebuggingEnabled {
485 return debugOn ? YES : NO;
488 @end /* OFSPropertyListObject */
490 @implementation OFSPropertyListObjectClassDescription
493 [self->object release];
497 - (NSArray *)attributeKeys {
498 return [self->object attributeKeys];
501 @end /* OFSPropertyListObjectClassDescription */