2 Copyright (C) 2002-2004 SKYRIX Software AG
4 This file is part of OpenGroupware.org.
6 OGo 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 OGo 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 OGo; see the file COPYING. If not, write to the
18 Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
22 #include "SoObjectDataSource.h"
23 #include "SoObjectResultEntry.h"
24 #include "SoObject+SoDAV.h"
26 #include "EOFetchSpecification+SoDAV.h"
27 #include <NGObjWeb/WEClientCapabilities.h>
28 #include <NGObjWeb/WOContext.h>
29 #include <EOControl/EOQualifier.h>
30 #include <EOControl/EOSortOrdering.h>
33 @implementation SoObjectDataSource
35 static BOOL debugOn = NO;
40 ud = [NSUserDefaults standardUserDefaults];
41 debugOn = [ud boolForKey:@"SoObjectDataSourceDebugEnabled"];
44 - (id)initWithObject:(id)_object inContext:(id)_ctx {
45 if ((self = [super init])) {
46 self->object = [_object retain];
52 return [self initWithObject:nil inContext:nil];
56 [self->object release];
63 - (void)setFetchSpecification:(EOFetchSpecification *)_fetchSpec {
64 if ([_fetchSpec isEqual:self->fspec]) return;
66 ASSIGN(self->fspec, _fetchSpec);
67 [self postDataSourceChangedNotification];
69 - (EOFetchSpecification *)fetchSpecification {
80 - (EOClassDescription *)classDescriptionForObjects {
81 if ([self->object respondsToSelector:_cmd])
82 /* forward to datasource owner if possible */
83 return [self->object performSelector:_cmd];
90 - (NSArray *)davFlatQuery:(EOFetchSpecification *)_fs inContext:(id)_ctx {
92 If you have a datasource for your children, consider to override this
93 method and use the DS to perform the query.
95 id<EOQualifierEvaluation> q;
98 NSEnumerator *childKeys;
99 NSArray *queriedAttrNames;
104 BOOL isBrief = YES; // do not encode "sub-errors", just omit the item
106 [self debugWithFormat:@"performing flat query: %@", _fs];
110 entityURL = [_fs entityName];
111 if (![entityURL hasSuffix:@"/"])
112 entityURL = [entityURL stringByAppendingString:@"/"];
116 /* retrieve child names (calls -toOneRelationshipKeys and -toManyRel...) */
118 childKeys = [self->object davChildKeysInContext:_ctx];
122 [self debugWithFormat:@"query keys: %@", childKeys];
124 q = (void *)[_fs qualifier];
125 ma = [NSMutableArray arrayWithCapacity:16];
127 queriedAttrNames = [_fs selectedWebDAVPropertyNames];
129 while ((childKey = [[childKeys nextObject] stringValue]) != nil) {
136 TODO: question of the week, is it faster to filter on the record
137 dictionary or to filter on the object itself? Do WebDAV clients
138 always select all items they sort or filter? Hm.
142 if ((e = [self->object validateName:childKey inContext:_ctx])) {
143 /* security problem */
144 [self debugWithFormat:@" child key '%@' did not validate:\n %@",
147 if (isBrief) // when the brief header is set, forget errors
150 else if ((child = [self->object lookupName:childKey inContext:_ctx
153 [self debugWithFormat:@" did not find key '%@'", childKey];
155 if (!isBrief) { // when the brief header is not set, encode status
158 ui = [NSDictionary dictionaryWithObject:@"404" /* not found */
159 forKey:@"http-status"];
160 e = [NSException exceptionWithName:@"KeyError"
161 reason:@"failed to lookup a WebDAV child resource"
165 else if ([child isKindOfClass:[NSException class]]) {
170 /* found a valid child */
171 //[self debugWithFormat:@" found child for key '%@'", childKey];
174 if (![q evaluateWithObject:child]) {
175 /* object does not match the filter */
177 [self debugWithFormat:@" object did not match qualifier: %@",
185 // TODO: child can be SoHTTPException ?
186 rec = (queriedAttrNames == nil)
188 : [child valuesForKeys:queriedAttrNames];
190 [self logWithFormat:@"got values: %@ for keys: %@ from object: %@",
191 rec, queriedAttrNames, child];
198 Note: we cannot use NSPathUtilities, those will reformat the string on
199 Cocoa Foundation! (eg http://a => http:/a, remove double slashes)
201 childHref = doEscape ? [childKey stringByEscapingURL] : childKey;
202 childHref = [entityURL stringByAppendingString:childHref];
205 // TODO: this happens if we access using Goliath
206 if ([childHref hasPrefix:@"http:/"] &&
207 ![childHref hasPrefix:@"http://"]) {
208 [self logWithFormat:@"BROKEN CHILD URL: %@ (entity=%@,key=%@)",
209 childHref, [_fs entityName], childKey];
214 /* add errors if required */
216 if ((rec == nil) && (e != nil) && !isBrief) {
217 rec = [[SoObjectErrorEntry alloc]
218 initWithURI:childHref object:child error:e];
220 else if (rec == nil) {
221 /* either brief or no error found */
224 else if (queriedAttrNames) {
225 rec = [[SoObjectResultEntry alloc]
226 initWithURI:childHref object:child values:rec];
232 /* add record to result */
237 /* sort result (note: you must select anything you want to sort ...) */
238 if ((orderings = [_fs sortOrderings]))
239 [ma sortUsingKeyOrderArray:orderings];
243 [self debugWithFormat:@" got %i results\n%@", [result count], result];
249 - (NSArray *)fetchObjects {
250 NSAutoreleasePool *pool;
253 pool = [[NSAutoreleasePool alloc] init];
257 if ((keys = [[self fetchSpecification] davBulkTargetKeys])) {
258 [self logWithFormat:@"SoObjectDataSource cannot handle bulk queries !"];
262 result = [self davFlatQuery:[self fetchSpecification]
263 inContext:[self context]];
265 result = [result retain];
268 return [result autorelease];;
273 - (NSString *)loggingPrefix {
274 return @"[object-datasource]";
276 - (BOOL)isDebuggingEnabled {
280 @end /* SoObjectDataSource */