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 <NGObjWeb/WODynamicElement.h>
23 #include "WOElement+private.h"
24 #include <NGExtensions/NSString+misc.h>
27 @interface WORepetition : WODynamicElement
29 // WODynamicElement: extraAttributes
30 // WODynamicElement: otherTagString
38 @end /* WORepetition */
40 @interface _WOComplexRepetition : WORepetition
42 WOAssociation *list; // array of objects to iterate through
43 WOAssociation *item; // current item in the array
44 WOAssociation *index; // current index
45 WOAssociation *identifier; // unique id for element
46 WOAssociation *count; // number of times the contents will be repeated
49 WOAssociation *startIndex;
50 WOAssociation *separator; // string inserted between repetitions
55 @interface _WOSimpleRepetition : WORepetition
57 WOAssociation *list; // array of objects to iterate through
58 WOAssociation *item; // current item in the array
63 @interface _WOTemporaryRepetition : NSObject
66 //#define PROF_REPETITION_CLUSTER 1
68 static int descriptiveIDs = -1;
69 static int debugTakeValues = -1;
71 #if PROF_REPETITION_CLUSTER
72 static int complexCount = 0;
73 static int simpleCount = 0;
76 @implementation _WOTemporaryRepetition
78 static inline Class _classForConfig(NSDictionary *_config) {
82 switch ((c = [_config count])) {
84 repClass = [_WOSimpleRepetition class];
87 if ([_config objectForKey:@"list"])
88 repClass = [_WOSimpleRepetition class];
89 else if ([_config objectForKey:@"item"])
90 repClass = [_WOSimpleRepetition class];
93 if ([_config objectForKey:@"list"] &&
94 [_config objectForKey:@"item"])
95 repClass = [_WOSimpleRepetition class];
98 repClass = [_WOComplexRepetition class];
103 repClass = [_WOComplexRepetition class];
108 - (id)initWithName:(NSString *)_name
109 associations:(NSDictionary *)_config
110 template:(WOElement *)_template
112 Class repClass = Nil;
114 repClass = _classForConfig(_config);
116 return [[repClass alloc]
122 - (id)initWithName:(NSString *)_name
123 associations:(NSDictionary *)_config
124 contentElements:(NSArray *)_contents
126 Class repClass = Nil;
128 repClass = _classForConfig(_config);
130 return [[repClass alloc]
133 contentElements:_contents];
136 @end /* _WOTemporaryRepetition */
138 @implementation WORepetition
141 return [super version] + 1 /* v3 */;
144 NSAssert2([super version] == 2,
145 @"invalid superclass (%@) version %i !",
146 NSStringFromClass([self superclass]), [super version]);
148 if (debugTakeValues == -1) {
150 [[NSUserDefaults standardUserDefaults] boolForKey:@"WODebugTakeValues"]
153 if (debugTakeValues) NSLog(@"WORepetition: WODebugTakeValues on.");
157 + (id)allocWithZone:(NSZone *)zone {
158 static Class WORepetitionClass = Nil;
159 static _WOTemporaryRepetition *temporaryRepetition = nil;
161 if (WORepetitionClass == Nil)
162 WORepetitionClass = [WORepetition class];
163 if (temporaryRepetition == nil)
164 temporaryRepetition = [_WOTemporaryRepetition allocWithZone:zone];
166 return (self == WORepetitionClass)
167 ? (id)temporaryRepetition
168 : (id)NSAllocateObject(self, 0, zone);
171 - (id)initWithName:(NSString *)_name
172 associations:(NSDictionary *)_config
173 template:(WOElement *)_c
175 if (descriptiveIDs == -1) {
176 descriptiveIDs = [[[NSUserDefaults standardUserDefaults]
177 objectForKey:@"WODescriptiveElementIDs"]
182 self->repName = _name ? [_name copy] : (id)@"R";
185 if ((self = [super initWithName:_name associations:_config template:_c])) {
186 self->template = RETAIN(_c);
192 [self->template release];
194 [self->repName release];
199 @end /* WORepetition */
201 @implementation _WOComplexRepetition
203 - (id)initWithName:(NSString *)_name
204 associations:(NSDictionary *)_config
205 template:(WOElement *)_c
207 if ((self = [super initWithName:_name associations:_config template:_c])) {
208 #if PROF_REPETITION_CLUSTER
211 if (complexCount % 10 == 0) {
212 NSLog(@"REPETITION CLUSTER: %i simple, %i complex",
213 simpleCount, complexCount);
216 self->list = OWGetProperty(_config, @"list");
217 self->item = OWGetProperty(_config, @"item");
218 self->index = OWGetProperty(_config, @"index");
219 self->identifier = OWGetProperty(_config, @"identifier");
220 self->count = OWGetProperty(_config, @"count");
221 self->startIndex = OWGetProperty(_config, @"startIndex");
222 self->separator = OWGetProperty(_config, @"separator");
228 [self->separator release];
229 [self->list release];
230 [self->item release];
231 [self->index release];
232 [self->identifier release];
233 [self->count release];
234 [self->startIndex release];
241 return self->template;
247 _applyIdentifier(_WOComplexRepetition *self,
248 WOComponent *sComponent,
255 NSCAssert(self->identifier, @"this method is only to be called on objects "
256 @"with a specified 'identifier' association !");
259 array = [self->list valueInComponent:sComponent];
260 count = [array count];
265 /* find subelement for unique id */
267 for (cnt = 0; cnt < count; cnt++) {
271 [self->index setUnsignedIntValue:cnt inComponent:sComponent];
274 [self->item setValue:[array objectAtIndex:cnt]
275 inComponent:sComponent];
278 ident = [self->identifier stringValueInComponent:sComponent];
280 if ([ident isEqualToString:_idx]) {
281 /* found subelement with unique id */
286 [sComponent logWithFormat:
287 @"WORepetition: array did change, "
288 @"unique-id isn't contained."];
289 [self->item setValue:nil inComponent:sComponent];
290 [self->index setUnsignedIntValue:0 inComponent:sComponent];
295 _applyIndex(_WOComplexRepetition *self, WOComponent *sComponent, unsigned _idx)
299 array = [self->list valueInComponent:sComponent];
302 [self->index setUnsignedIntValue:_idx inComponent:sComponent];
305 unsigned count = [array count];
308 [self->item setValue:[array objectAtIndex:_idx]
309 inComponent:sComponent];
312 [sComponent logWithFormat:
313 @"WORepetition: array did change, index is invalid."];
314 [self->item setValue:nil inComponent:sComponent];
319 - (void)takeValuesFromRequest:(WORequest *)_request
320 inContext:(WOContext *)_ctx
323 WOComponent *sComponent;
330 [_ctx appendElementIDComponent:self->repName];
333 sComponent = [_ctx component];
334 array = [self->list valueInContext:_ctx];
335 aCount = [array count];
337 goCount = self->count
338 ? [self->count unsignedIntValueInComponent:sComponent]
342 unsigned startIdx, goUntil;
346 [self->startIndex unsignedIntValueInComponent:sComponent];
348 if (self->identifier == nil) {
350 [_ctx appendZeroElementIDComponent];
352 [_ctx appendIntElementIDComponent:startIdx];
356 goUntil = (aCount > (startIdx + goCount))
361 goUntil = startIdx + goCount;
364 if (debugTakeValues) {
365 [sComponent debugWithFormat:
366 @"%@: name=%@ id='%@' start walking rep (%i-%i) ..",
367 NSStringFromClass([self class]),
374 for (cnt = startIdx; cnt < goUntil; cnt++) {
375 _applyIndex(self, sComponent, cnt);
377 if (self->identifier) {
380 s = [self->identifier stringValueInComponent:sComponent];
381 [_ctx appendElementIDComponent:s];
384 if (debugTakeValues) {
385 [[_ctx component] debugWithFormat:@"%@<%@>: "
386 @"let template take values ..",
388 NSStringFromClass([self class])];
391 [self->template takeValuesFromRequest:_request inContext:_ctx];
393 if (self->identifier == nil)
394 [_ctx incrementLastElementIDComponent];
396 [_ctx deleteLastElementIDComponent];
399 if (self->identifier == nil)
400 [_ctx deleteLastElementIDComponent]; // Repetition Index
402 else if (debugTakeValues) {
403 [[_ctx component] debugWithFormat:
404 @"%@<%@>: takevalues -> no contents to walk ! ..",
405 [_ctx elementID], NSStringFromClass([self class])];
410 [_ctx deleteLastElementIDComponent];
414 - (id)invokeActionForRequest:(WORequest *)_request
415 inContext:(WOContext *)_ctx
417 WOComponent *sComponent;
421 sComponent = [_ctx component];
424 if (descriptiveIDs) {
425 if (![[_ctx currentElementID] isEqualToString:self->repName]) {
426 [[_ctx session] logWithFormat:@"%s: %@ missing repetition ID 'R' !",
427 __PRETTY_FUNCTION__, self];
430 [_ctx consumeElementID]; // consume 'R'
431 [_ctx appendElementIDComponent:self->repName];
435 if ((idxId = [_ctx currentElementID])) {
436 [_ctx consumeElementID]; // consume index-id
438 /* this updates the element-id path */
439 [_ctx appendElementIDComponent:idxId];
441 if (self->identifier)
442 _applyIdentifier(self, sComponent, idxId);
444 _applyIndex(self, sComponent, [idxId intValue]);
446 result = [self->template invokeActionForRequest:_request inContext:_ctx];
448 [_ctx deleteLastElementIDComponent];
452 logWithFormat:@"%s: %@: MISSING INDEX ID in URL !",
459 [_ctx deleteLastElementIDComponent];
465 - (void)appendToResponse:(WOResponse *)_response inContext:(WOContext *)_ctx {
466 static Class NSAutoreleasePoolClass = Nil;
467 WOComponent *sComponent;
469 unsigned aCount, goCount, startIdx;
470 NSAutoreleasePool *pool;
474 [_ctx appendElementIDComponent:self->repName];
477 if (NSAutoreleasePoolClass == Nil)
478 NSAutoreleasePoolClass = [NSAutoreleasePool class];
480 pool = [[NSAutoreleasePoolClass alloc] init];
482 sComponent = [_ctx component];
483 array = [self->list valueInContext:_ctx];
484 aCount = [array count];
485 startIdx = [self->startIndex unsignedIntValueInComponent:sComponent];
487 goCount = self->count
488 ? [self->count unsignedIntValueInComponent:sComponent]
492 unsigned cnt, goUntil;
495 // append debugging info
496 WOResponse_AddString(_response,
497 [NSString stringWithFormat:
498 @"<!-- WORep. count=%d arraySize=%d -->\n",
499 goCount, [array count]]);
502 if (self->identifier == nil) {
504 [_ctx appendZeroElementIDComponent];
506 [_ctx appendIntElementIDComponent:startIdx];
510 goUntil = (aCount > (startIdx + goCount))
515 goUntil = startIdx + goCount;
517 for (cnt = startIdx; cnt < goUntil; cnt++) {
521 if ((cnt != startIdx) && (self->separator != nil)) {
522 WOResponse_AddString(_response,
523 [self->separator stringValueInComponent:
528 [self->index setUnsignedIntValue:cnt inComponent:sComponent];
530 lItem = [array objectAtIndex:cnt];
533 [self->item setValue:lItem inComponent:sComponent];
536 if (!self->index && self->list) {
537 [_ctx pushCursor:lItem];
541 /* get identifier used for action-links */
543 if (self->identifier) {
544 /* use a unique id for subelement detection */
545 ident = [self->identifier stringValueInComponent:sComponent];
546 ident = [ident stringByEscapingURL];
547 [_ctx appendElementIDComponent:ident];
551 /* append debugging info */
552 WOResponse_AddString(_response, [NSString stringWithFormat:
553 @" <!-- iteration=%d -->\n", cnt]);
556 /* append child elements */
558 [self->template appendToResponse:_response inContext:_ctx];
562 if (self->identifier)
563 [_ctx deleteLastElementIDComponent];
565 [_ctx incrementLastElementIDComponent];
568 if (self->identifier == nil)
569 [_ctx deleteLastElementIDComponent]; /* repetition index */
571 if (!self->item && !self->index &&self->list)
574 //if (self->index) [self->index setUnsignedIntValue:0];
575 //if (self->item) [self->item setValue:nil];
579 WOResponse_AddCString(_response, "<!-- repetition with no contents -->");
587 [_ctx deleteLastElementIDComponent];
593 - (NSString *)associationDescription {
594 NSMutableString *str = [NSMutableString stringWithCapacity:32];
596 if (self->list) [str appendFormat:@" list=%@", self->list];
597 if (self->item) [str appendFormat:@" item=%@", self->item];
598 if (self->index) [str appendFormat:@" index=%@", self->index];
599 if (self->identifier) [str appendFormat:@" id=%@", self->identifier];
600 if (self->count) [str appendFormat:@" count=%@", self->count];
601 if (self->template) [str appendFormat:@" template=%@", self->template];
606 @end /* _WOComplexRepetition */
608 @implementation _WOSimpleRepetition
610 - (id)initWithName:(NSString *)_name
611 associations:(NSDictionary *)_config
612 template:(WOElement *)_c
614 if ((self = [super initWithName:_name associations:_config template:_c])) {
615 #if PROF_REPETITION_CLUSTER
618 if (simpleCount % 10 == 0) {
619 NSLog(@"REPETITION CLUSTER: %i simple, %i complex",
620 simpleCount, complexCount);
623 self->list = OWGetProperty(_config, @"list");
624 self->item = OWGetProperty(_config, @"item");
630 [self->list release];
631 [self->item release];
638 _sapplyIndex(_WOSimpleRepetition *self, WOComponent *sComponent, NSArray *array, unsigned _idx)
641 unsigned count = [array count];
644 [self->item setValue:[array objectAtIndex:_idx]
645 inComponent:sComponent];
648 [sComponent logWithFormat:
649 @"WORepetition: array did change, index is invalid."];
650 [self->item setValue:nil inComponent:sComponent];
655 - (void)takeValuesFromRequest:(WORequest *)_request
656 inContext:(WOContext *)_ctx
659 WOComponent *sComponent;
666 [_ctx appendElementIDComponent:self->repName];
669 sComponent = [_ctx component];
670 array = [self->list valueInContext:_ctx];
671 goCount = aCount = [array count];
676 [_ctx appendZeroElementIDComponent];
679 if (debugTakeValues) {
680 NSLog(@"%s: name=%@ id='%@' start walking rep (count=%i) ..",
681 __PRETTY_FUNCTION__, self->repName, [_ctx elementID], aCount);
685 for (cnt = 0; cnt < aCount; cnt++) {
686 _sapplyIndex(self, sComponent, array, cnt);
689 if (debugTakeValues) {
690 NSLog(@"%s: %@<%@>: idx[%i] let template take values ..",
692 [_ctx elementID], NSStringFromClass([self class]), cnt);
696 [self->template takeValuesFromRequest:_request inContext:_ctx];
698 [_ctx incrementLastElementIDComponent];
701 [_ctx deleteLastElementIDComponent]; // Repetition Index
704 else if (debugTakeValues) {
705 NSLog(@"%s: %@<%@>: takevalues -> no contents to walk:\n"
710 [_ctx elementID], NSStringFromClass([self class]),
711 sComponent, array, goCount);
717 [_ctx deleteLastElementIDComponent];
721 - (id)invokeActionForRequest:(WORequest *)_request
722 inContext:(WOContext *)_ctx
724 WOComponent *sComponent;
728 sComponent = [_ctx component];
731 if (descriptiveIDs) {
732 if (![[_ctx currentElementID] isEqualToString:self->repName]) {
734 logWithFormat:@"%s: %@ missing repetition ID 'R' !",
735 __PRETTY_FUNCTION__, self];
738 [_ctx consumeElementID]; // consume 'R'
739 [_ctx appendElementIDComponent:self->repName];
743 if ((idxId = [_ctx currentElementID])) {
746 idx = [idxId intValue];
747 [_ctx consumeElementID]; // consume index-id
749 /* this updates the element-id path */
750 [_ctx appendElementIDComponent:idxId];
752 _sapplyIndex(self, sComponent,
753 [self->list valueInContext:_ctx], idx);
755 result = [self->template invokeActionForRequest:_request inContext:_ctx];
757 [_ctx deleteLastElementIDComponent];
761 logWithFormat:@"%s: %@: MISSING INDEX ID in URL !",
768 [_ctx deleteLastElementIDComponent];
774 - (void)appendToResponse:(WOResponse *)_response inContext:(WOContext *)_ctx {
775 static Class NSAutoreleasePoolClass = Nil;
776 WOComponent *sComponent;
779 NSAutoreleasePool *pool;
783 [_ctx appendElementIDComponent:self->repName];
786 if (NSAutoreleasePoolClass == Nil)
787 NSAutoreleasePoolClass = [NSAutoreleasePool class];
789 pool = [[NSAutoreleasePoolClass alloc] init];
791 sComponent = [_ctx component];
792 array = [self->list valueInContext:_ctx];
793 aCount = [array count];
799 // append debugging info
800 WOResponse_AddString(_response,
801 [NSString stringWithFormat:
802 @"<!-- WORep. count=%d arraySize=%d -->\n",
803 goCount, [array count]]);
806 [_ctx appendZeroElementIDComponent];
808 for (cnt = 0; cnt < aCount; cnt++) {
810 [self->item setValue:[array objectAtIndex:cnt]
811 inComponent:sComponent];
814 [_ctx pushCursor:[array objectAtIndex:cnt]];
818 // append debugging info
819 WOResponse_AddString(_response, [NSString stringWithFormat:
820 @" <!-- iteration=%d -->\n", cnt]);
823 /* append child elements */
825 [self->template appendToResponse:_response inContext:_ctx];
829 [_ctx incrementLastElementIDComponent];
831 if (self->item == nil)
835 [_ctx deleteLastElementIDComponent]; /* repetition index */
837 //if (self->item) [self->item setValue:nil];
841 WOResponse_AddCString(_response, "<!-- repetition with no contents -->");
849 [_ctx deleteLastElementIDComponent];
855 - (NSString *)associationDescription {
856 NSMutableString *str;
858 str = [NSMutableString stringWithCapacity:24];
859 if (self->list) [str appendFormat:@" list=%@", self->list];
860 if (self->item) [str appendFormat:@" item=%@", self->item];
861 if (self->template) [str appendFormat:@" template=%@", self->template];
865 @end /* _WOSimpleRepetition */