]> err.no Git - sope/blob - Recycler/NGObjDOM/ODNodeRenderer.m
added maintenance directory
[sope] / Recycler / NGObjDOM / ODNodeRenderer.m
1 /*
2   Copyright (C) 2000-2004 SKYRIX Software AG
3
4   This file is part of OpenGroupware.org.
5
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
9   later version.
10
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.
15
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
19   02111-1307, USA.
20 */
21 // $Id$
22
23 #include <NGObjDOM/ODNodeRenderer.h>
24 #include <NGObjDOM/ODNodeRendererFactory.h>
25 #include <NGObjDOM/ODNamespaces.h>
26 #include "common.h"
27
28 //#define WITH_EXCEPTION_HANDLER 1
29 #if NeXT_RUNTIME || APPLE_RUNTIME
30 #  define sel_get_name sel_getName
31 #endif
32
33 @implementation ODNodeRenderer
34
35 static int    profileRenderers = -1;
36 static double profRenderMin = 0.0009;
37 static Class NSDateClass = Nil;
38
39 + (void)initialize {
40   if (NSDateClass == Nil)
41     NSDateClass = [NSDate class];
42   
43   if (profileRenderers == -1) {
44     id tmp;
45     
46     profileRenderers = [[[NSUserDefaults standardUserDefaults]
47                                           objectForKey:@"ODProfileRenderer"]
48                                           boolValue] ? 1 : 0;
49     
50     if ((tmp = [[NSUserDefaults standardUserDefaults]
51                                 objectForKey:@"ODProfileRendererMin"])) {
52       double d = [tmp doubleValue];
53       
54       if (d > profRenderMin) {
55         profRenderMin = d;
56         if (profileRenderers)
57           NSLog(@"profRenderMin: %0.3fs", profRenderMin);
58       }
59     }
60   }
61 }
62
63 + (int)version {
64   return 1;
65 }
66
67 - (NSString *)_idForNode:(id)_node {
68   if ([_node nodeType] == DOM_ELEMENT_NODE) {
69     NSString *s;
70
71     if ((s = [_node attribute:@"id" namespaceURI:@"*"]))
72       return s;
73     
74     return [_node tagName];
75   }
76   
77   return [_node nodeName];
78 }
79
80 /* renderer lookup */
81
82 - (ODNodeRenderer *)rendererForNode:(id)_domNode inContext:(WOContext *)_ctx {
83   id<ODNodeRendererFactory> factory;
84   ODNodeRenderer *renderer = nil;
85   NSTimeInterval st = 0.0;
86
87   if (profileRenderers)
88     st = [[NSDateClass date] timeIntervalSince1970];
89   
90   if ((factory = [_ctx objectForKey:@"domRenderFactory"]))
91     renderer = [factory rendererForNode:_domNode inContext:_ctx];
92   
93   if (profileRenderers) {
94     NSTimeInterval diff;
95     diff = [[NSDateClass date] timeIntervalSince1970] - st;
96     if (diff > profRenderMin) {
97       printf("[renderer lookup: %s %s]: %0.3fs id='%s'\n",
98              [[_domNode nodeName] cString],
99              sel_get_name(_cmd), diff,
100              [[self _idForNode:_domNode] cString]);
101     }
102   }
103   
104   return renderer;
105 }
106
107 /* request phase operations on children */
108
109 - (void)takeValuesForChildNodes:(id)_nodeList
110   fromRequest:(WORequest *)_request
111   inContext:(WOContext *)_ctx
112 {
113   id children;
114   id child;
115   NSTimeInterval st = 0.0;
116   static int profDepth = 0;
117
118   if (profileRenderers) {
119     st = [[NSDateClass date] timeIntervalSince1970];
120     profDepth++;
121   }
122   
123   if ([_nodeList count] == 0) {
124     profDepth--;
125     return;
126   }
127   
128   [_ctx appendZeroElementIDComponent];
129   
130   children = [_nodeList objectEnumerator];
131   while ((child = [children nextObject])) {
132     if ([self includeChildNode:child ofNode:[child parentNode] inContext:_ctx]) {
133       ODNodeRenderer *renderer;
134       
135       if ((renderer = [self rendererForNode:child inContext:_ctx])) {
136         [renderer takeValuesForNode:child
137                   fromRequest:_request
138                   inContext:_ctx];
139       }
140     }    
141     [_ctx incrementLastElementIDComponent];
142   }
143   
144   [_ctx deleteLastElementIDComponent];
145   
146   if (profileRenderers) {
147     NSTimeInterval diff;
148     int i;
149     diff = [[NSDateClass date] timeIntervalSince1970] - st;
150     if (diff > profRenderMin) {
151       id dn;
152
153       dn = [[_nodeList lastObject] parentNode];
154       
155       for (i = profDepth; i >= 0; i--)
156         printf("  ");
157       printf("[list: %s %s]: %0.3fs '%s'\n",
158              [[dn nodeName] cString],
159              sel_get_name(_cmd), diff,
160              [[self _idForNode:dn] cString]);
161     }
162     profDepth--;
163   }
164 }
165
166 - (id)invokeActionForChildNodes:(id)_nodeList
167   fromRequest:(WORequest *)_request
168   inContext:(WOContext *)_ctx
169 {
170   NSString *idxId;
171   id result;
172   NSTimeInterval st = 0.0;
173   static int profDepth = 0;
174
175   if (profileRenderers) {
176     st = [[NSDateClass date] timeIntervalSince1970];
177     profDepth++;
178   }
179   
180   if ([_nodeList count] == 0) {
181     profDepth--;
182 #if DEBUG && 0
183     NSLog(@"%s: no child nodes to invoke upon ..", __PRETTY_FUNCTION__);
184 #endif
185     return nil;
186   }
187   
188   if ((idxId  = [_ctx currentElementID])) {
189     id node;
190     unsigned count;
191     
192     count = [_nodeList count];
193     
194     if (isdigit([idxId characterAtIndex:0])) {
195       /* lookup by index */
196       int idx;
197       idx = [idxId intValue];
198       
199       if (idx >= count) {
200         NSLog(@"%s: ERROR, click index is out of range "
201               @"(idx=%i, count=%d, eid=%@, sid=%@)",
202               __PRETTY_FUNCTION__, idx, count,
203               [_ctx elementID], [_ctx senderID]);
204         node = nil;
205       }
206       else if ((node = [_nodeList objectAtIndex:idx]) == nil) {
207 #if DEBUG
208         NSLog(@"%s: ERROR, did not find node at index %i (tag=%@, list=%@) ..",
209               __PRETTY_FUNCTION__, idx,
210               [[_nodeList parentNode] nodeName], _nodeList);
211 #endif
212       }
213     }
214     else {
215       /* lookup by name */
216       NSLog(@"%s: ERROR, LOOK FOR NAME %@ ..", __PRETTY_FUNCTION__, idxId);
217       node = nil;
218     }
219     
220     [_ctx consumeElementID]; // consume index-id
221     
222     /* this updates the element-id path */
223     [_ctx appendElementIDComponent:idxId];
224
225 #if DEBUG && 0
226     NSLog(@"%s: invoke action on node:\n  %@\n  id=%@", __PRETTY_FUNCTION__,
227           node, [_ctx currentElementID]);
228 #endif
229     
230     result = [[self rendererForNode:node inContext:_ctx]
231                     invokeActionForNode:node
232                     fromRequest:_request
233                     inContext:_ctx];
234     
235     [_ctx deleteLastElementIDComponent];
236   }
237   else {
238     [[_ctx session]
239            logWithFormat:
240              @"%s: %@: \n"
241              @"    MISSING INDEX ID in URL\n    eid: %@\n    nodelist: %@ !",
242              __PRETTY_FUNCTION__,
243              self, [_ctx elementID], _nodeList];
244     result = nil;
245   }
246   
247   if (profileRenderers) {
248     NSTimeInterval diff;
249     int i;
250     diff = [[NSDateClass date] timeIntervalSince1970] - st;
251     if (diff > profRenderMin) {
252       for (i = profDepth; i >= 0; i--)
253         printf("  ");
254       printf("[list: %s %s]: %0.3fs\n",
255              [[[[_nodeList lastObject] parentNode] nodeName] cString],
256              sel_get_name(_cmd), diff);
257     }
258     profDepth--;
259   }
260   
261   return result;
262 }
263
264 - (void)appendChildNodes:(id)_nodeList
265   toResponse:(WOResponse *)_response
266   inContext:(WOContext *)_ctx
267 {
268   id children;
269   id child, parent;
270   static unsigned profDepth = 0;
271   NSTimeInterval st = 0.0;
272
273   if (profileRenderers) {
274     st = [[NSDateClass date] timeIntervalSince1970];
275     profDepth++;
276   }
277   
278   if ([_nodeList count] == 0) {
279     profDepth--;
280     return;
281   }
282   
283   [_ctx appendZeroElementIDComponent];
284
285   parent   = nil;
286   children = [_nodeList objectEnumerator];
287   while ((child = [children nextObject])) {
288     if (parent == nil) parent = [child parentNode];
289     
290     if ([self includeChildNode:child ofNode:parent inContext:_ctx]) {
291       ODNodeRenderer *renderer;
292       NSTimeInterval st = 0.0;
293
294       if (profileRenderers) {
295         st = [[NSDateClass date] timeIntervalSince1970];
296         profDepth++;
297       }
298       
299       renderer = [self rendererForNode:child inContext:_ctx];
300       
301       [renderer appendNode:child
302                 toResponse:_response
303                 inContext:_ctx];
304       
305       if (profileRenderers) {
306         NSTimeInterval diff;
307         int i;
308         diff = [[NSDateClass date] timeIntervalSince1970] - st;
309         if (diff > profRenderMin) {
310           for (i = profDepth; i >= 0; i--)
311             printf("  ");
312           printf("[child: %s %s]: %0.3fs '%s'\n",
313                  [[child nodeName] cString],
314                  sel_get_name(_cmd), diff,
315                  [[self _idForNode:child] cString]);
316         }
317         profDepth--;
318       }
319     }
320     [_ctx incrementLastElementIDComponent];
321   }
322   
323   [_ctx deleteLastElementIDComponent];
324
325   
326   if (profileRenderers) {
327     NSTimeInterval diff;
328     int i;
329     diff = [[NSDateClass date] timeIntervalSince1970] - st;
330     if (diff > profRenderMin) {
331       id dn;
332       
333       dn = [[_nodeList lastObject] parentNode];
334       
335       for (i = profDepth; i >= 0; i--)
336         printf("  ");
337       printf("[list: %s %s]: %0.3fs '%s'\n",
338              [[dn nodeName] cString],
339              sel_get_name(_cmd), diff,
340              [[self _idForNode:dn] cString]);
341     }
342     profDepth--;
343   }
344 }
345
346 /* the three WO request phase operations */
347
348 - (void)takeValuesForNode:(id)_domNode
349   fromRequest:(WORequest *)_request
350   inContext:(WOContext *)_context
351 {
352 #if DEBUG && 0
353   NSLog(@"take values for node: %@", _domNode);
354 #endif
355
356 #if WITH_EXCEPTION_HANDLER
357   NS_DURING {
358     if ([_domNode hasChildNodes]) {
359       [self takeValuesForChildNodes:[_domNode childNodes]
360             fromRequest:_request
361             inContext:_context];
362     }
363   }
364   NS_HANDLER {
365     fprintf(stderr, "%s\n", [[localException description] cString]);
366     abort();
367   }
368   NS_ENDHANDLER;
369 #else
370   if ([_domNode hasChildNodes]) {
371     [self takeValuesForChildNodes:[_domNode childNodes]
372           fromRequest:_request
373           inContext:_context];
374   }
375 #endif
376 }
377
378 - (id)invokeActionForNode:(id)_domNode
379   fromRequest:(WORequest *)_request
380   inContext:(WOContext *)_context
381 {
382   if ([_domNode hasChildNodes]) {
383     return [self invokeActionForChildNodes:[_domNode childNodes]
384                  fromRequest:_request
385                  inContext:_context];
386   }
387   else {
388 #if DEBUG
389     NSLog(@"%s: node %@ has no child nodes:\n  sid=%@\n  eid=%@",
390           __PRETTY_FUNCTION__, _domNode,
391           [_context senderID], [_context elementID]);
392 #endif
393     return nil;
394   }
395 }
396
397 - (void)appendNode:(id)_domNode
398   toResponse:(WOResponse *)_response
399   inContext:(WOContext *)_context
400 {
401   if ([_domNode hasChildNodes]) {
402     [self appendChildNodes:[_domNode childNodes]
403           toResponse:_response
404           inContext:_context];
405   }
406 }
407
408 /* requires HTML form */
409
410 - (BOOL)requiresFormForChildNodes:(id)_nodeList inContext:(WOContext *)_ctx {
411   id children;
412   id child;
413   
414   if ([_nodeList count] == 0)
415     return NO;
416   
417   if ([_ctx isInForm])
418     return NO;
419   
420   children = [_nodeList objectEnumerator];
421   
422   while ((child = [children nextObject])) {
423     if ([self includeChildNode:child ofNode:[child parentNode] inContext:_ctx]) {
424       ODNodeRenderer *renderer;
425       
426       if ((renderer = [self rendererForNode:child inContext:_ctx])) {
427         if ([renderer requiresFormForNode:child inContext:_ctx])
428           return YES;
429       }
430     }
431   }
432   return NO;
433 }
434
435 - (BOOL)requiresFormForNode:(id)_domNode inContext:(WOContext *)_ctx {
436   if ([_domNode hasChildNodes]) {
437     return [self requiresFormForChildNodes:[_domNode childNodes]
438                  inContext:_ctx];
439   }
440   else
441     return NO;
442 }
443
444 /* generating node ids unique in DOM tree */
445
446 - (NSString *)uniqueIDForNode:(id)_node inContext:(WOContext *)_ctx {
447   NSMutableArray  *nodePath;
448   NSMutableString *uid;
449   NSEnumerator    *topDown;
450   id   node, parent;
451   BOOL isFirst;
452   
453   if (_node == nil) return nil;
454   
455   nodePath = [NSMutableArray arrayWithCapacity:16];
456   
457   /* collect all parent nodes in bottom-up form */
458   
459   for (node = _node; node; node = [node parentNode])
460     [nodePath addObject:node];
461   
462   /* generate ID */
463   
464   uid     = [NSMutableString stringWithCapacity:64];
465   topDown = [nodePath reverseObjectEnumerator];
466   isFirst = YES;
467   parent  = nil;
468
469   for (isFirst = YES; (node = [topDown nextObject]); parent = node) {
470     if (!isFirst) {
471       NSArray  *children;
472       unsigned i, count;
473       
474       [uid appendString:@"."];
475       
476       /* determine index of _node */
477
478       children = (NSArray *)[parent childNodes];
479       for (i = 0, count = [children count]; i < count; i++) {
480         if ([children objectAtIndex:i] == node)
481           break;
482       }
483       [uid appendFormat:@"%d", i];
484     }
485     else {
486       [uid appendString:@"R"];
487       isFirst = NO;
488     }
489   }
490   
491   return [[uid copy] autorelease];
492 }
493
494 /* selecting children */
495
496 - (BOOL)includeChildNode:(id)_childNode
497   ofNode:(id)_domNode
498   inContext:(WOContext *)_ctx
499 {
500   /* check 'if' attribute .. */
501
502   if ([_childNode nodeType] != DOM_ELEMENT_NODE)
503      return YES;
504   
505   if ([_childNode hasAttribute:@"if" namespaceURI:@"*"])
506     return [self boolFor:@"if" node:_childNode ctx:_ctx];
507   
508   if ([_childNode hasAttribute:@"ifnot" namespaceURI:@"*"])
509     return [self boolFor:@"ifnot" node:_childNode ctx:_ctx] ? NO : YES;
510   
511   return YES;
512 }
513
514 @end /* ODNodeRenderer */