/*
Copyright (C) 2000-2003 SKYRIX Software AG
This file is part of OGo
OGo is free software; you can redistribute it and/or modify it under
the terms of the GNU Lesser General Public License as published by the
Free Software Foundation; either version 2, or (at your option) any
later version.
OGo is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
License for more details.
You should have received a copy of the GNU Lesser General Public
License along with OGo; see the file COPYING. If not, write to the
Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
02111-1307, USA.
*/
// $Id$
/*
description:
Iterates over a list of objects. The current item is stored in the item
binding or, if the binding is not present, as the current cursor.
attributes:
list // array of objects to iterate through
item // current item in the array
index // current index
identifier // unique id for element
count // number of times the contents will be repeated
startIndex // first index
qualifier // limit set
sortkey // sort array using this key
example:
*/
#include
@interface ODR_bind_foreach : ODNodeRenderer
@end
#include "WOContext+Cursor.h"
#include
#include
#include
#include "common.h"
@implementation ODR_bind_foreach
- (NSArray *)_listForNode:(id)_node inContext:(WOContext *)_ctx {
NSMutableArray *sortOrderings;
NSArray *array;
id query;
NSString *sortkey;
/* get list */
array = [self valueFor:@"list" node:_node ctx:_ctx];
if ([array count] == 0)
return array;
/* qualify list */
if ((query = [self valueFor:@"qualifier" node:_node ctx:_ctx])) {
if (![query isKindOfClass:[EOQualifier class]]) {
query = [query stringValue];
if ([query length] > 0)
query = [EOQualifier qualifierWithQualifierFormat:[query stringValue]];
else
query = nil;
}
}
if (query) {
array = [array filteredArrayUsingQualifier:query];
if ([array count] == 0)
return array;
}
/* sort list */
sortOrderings = nil;
if ((sortkey = [self valueFor:@"sortkey" node:_node ctx:_ctx])) {
NSEnumerator *keys;
NSString *key;
sortOrderings = [NSMutableArray arrayWithCapacity:4];
keys = [[sortkey componentsSeparatedByString:@","] objectEnumerator];
while ((key = [keys nextObject])) {
EOSortOrdering *so;
SEL sel = EOCompareAscending;
if ([key hasPrefix:@"-"]) {
key = [key substringFromIndex:1];
sel = EOCompareDescending;
}
if ((so = [EOSortOrdering sortOrderingWithKey:key selector:sel]))
[sortOrderings addObject:so];
}
if ([sortOrderings count] == 0)
sortOrderings = nil;
}
if (sortOrderings)
array = [array sortedArrayUsingKeyOrderArray:sortOrderings];
/* return filtered and sorted list ... */
return array;
}
// OWResponder
static inline void
_applyIdentifier(ODR_bind_foreach *self, id _node, id _ctx, NSString *_idx)
{
NSArray *array;
unsigned count;
array = [self _listForNode:_node inContext:_ctx];
count = [array count];
if (count > 0) {
unsigned i;
BOOL hasSettableIndex;
BOOL hasSettableItem;
BOOL hasItem;
if ((hasItem = [self hasAttribute:@"item" node:_node ctx:_ctx]))
hasSettableItem = [self isSettable:@"item" node:_node ctx:_ctx];
else
hasSettableItem = NO;
hasSettableIndex = [self isSettable:@"index" node:_node ctx:_ctx];
/* find subelement for unique id */
for (i = 0; i < count; i++) {
NSString *ident;
if (hasSettableIndex)
[self setInt:i for:@"index" node:_node ctx:_ctx];
if (hasSettableItem) {
[self setValue:[array objectAtIndex:i]
for:@"item" node:_node ctx:_ctx];
}
else if (!hasItem)
/* push cursor */
[_ctx pushCursor:[array objectAtIndex:i]];
ident = [self stringFor:@"identifier" node:_node ctx:_ctx];
if ([ident isEqualToString:_idx]) {
/* found subelement with unique id */
return;
}
}
if (hasSettableItem)
[self setValue:nil for:@"item" node:_node ctx:_ctx];
if (hasSettableIndex)
[self setInt:0 for:@"index" node:_node ctx:_ctx];
}
}
static inline void
_applyIndex(ODR_bind_foreach *self, id _node, id _ctx, unsigned _i)
{
NSArray *array;
BOOL hasSettableItem;
BOOL hasItem;
if ((hasItem = [self hasAttribute:@"item" node:_node ctx:_ctx]))
hasSettableItem = [self isSettable:@"item" node:_node ctx:_ctx];
else
hasSettableItem = NO;
array = [self _listForNode:_node inContext:_ctx];
if ([self isSettable:@"index" node:_node ctx:_ctx])
[self setInt:_i for:@"index" node:_node ctx:_ctx];
if (hasSettableItem) {
unsigned count = [array count];
if (_i < count)
[self setValue:[array objectAtIndex:_i]
for:@"item" node:_node ctx:_ctx];
else {
[[_ctx component] logWithFormat:
@"ODR_bind_foreach: array did change, index is invalid."];
[self setValue:nil for:@"item" node:_node ctx:_ctx];
}
}
else if (!hasItem) {
/* push cursor */
unsigned count = [array count];
if (_i < count)
[_ctx pushCursor:[array objectAtIndex:_i]];
else {
[[_ctx component] logWithFormat:
@"ODR_bind_foreach: array did change, index is invalid."];
[_ctx pushCursor:nil];
}
}
}
- (void)takeValuesForNode:(id)_node
fromRequest:(WORequest *)_req
inContext:(WOContext *)_ctx
{
NSArray *array;
unsigned aCount;
unsigned goCount;
array = [self _listForNode:_node inContext:_ctx];
aCount = [array count];
goCount = [self hasAttribute:@"count" node:_node ctx:_ctx]
? [self intFor:@"count" node:_node ctx:_ctx]
: (int)aCount;
if (goCount > 0) {
unsigned startIdx, goUntil;
int i;
startIdx = [self intFor:@"startIndex" node:_node ctx:_ctx];
if (![self hasAttribute:@"identifier" node:_node ctx:_ctx]) {
if (startIdx == 0)
[_ctx appendZeroElementIDComponent];
else
[_ctx appendElementIDComponent:[self stringForInt:startIdx]];
}
if ([self hasAttribute:@"list" node:_node ctx:_ctx]) {
goUntil = (aCount > (startIdx + goCount))
? startIdx + goCount
: aCount;
}
else
goUntil = startIdx + goCount;
for (i = startIdx; i < (int)goUntil; i++) {
_applyIndex(self, _node, _ctx, i);
if ([self hasAttribute:@"identifier" node:_node ctx:_ctx]) {
NSString *s;
s = [self stringFor:@"identifier" node:_node ctx:_ctx];
[_ctx appendElementIDComponent:s];
}
[super takeValuesForNode:_node
fromRequest:_req
inContext:_ctx];
[_ctx popCursor];
if (![self hasAttribute:@"identifier" node:_node ctx:_ctx])
[_ctx incrementLastElementIDComponent];
else
[_ctx deleteLastElementIDComponent];
}
if (![self hasAttribute:@"identifier" node:_node ctx:_ctx])
[_ctx deleteLastElementIDComponent]; // Repetition Index
}
}
- (id)invokeActionForNode:(id)_node
fromRequest:(WORequest *)_req
inContext:(WOContext *)_ctx
{
id result = nil;
id idxId;
if ((idxId = [_ctx currentElementID])) {
BOOL hasItem;
int idx;
hasItem = [self hasAttribute:@"item" node:_node ctx:_ctx];
idx = [idxId intValue];
[_ctx consumeElementID]; // consume index-id
/* this updates the element-id path */
[_ctx appendElementIDComponent:idxId];
if ([self hasAttribute:@"identifier" node:_node ctx:_ctx])
_applyIdentifier(self, _node, _ctx, idxId);
else
_applyIndex(self, _node, _ctx, idx);
result = [super invokeActionForNode:_node
fromRequest:_req
inContext:_ctx];
if (!hasItem) [_ctx popCursor];
[_ctx deleteLastElementIDComponent];
}
else {
[[_ctx session]
logWithFormat:@"%s: %@: MISSING INDEX ID in URL !",
__PRETTY_FUNCTION__,
self];
}
return result;
}
- (void)appendNode:(id)_node
toResponse:(WOResponse *)_response
inContext:(WOContext *)_ctx
{
static Class NSAutoreleasePoolClass = Nil;
NSArray *array;
unsigned aCount, goCount, startIdx;
NSAutoreleasePool *pool;
BOOL hasId;
if (NSAutoreleasePoolClass == Nil)
NSAutoreleasePoolClass = [NSAutoreleasePool class];
pool = [[NSAutoreleasePoolClass alloc] init];
hasId = NO;
startIdx = [self intFor:@"startIndex" node:_node ctx:_ctx];
goCount = [self intFor:@"count" node:_node ctx:_ctx];
array = [self _listForNode:_node inContext:_ctx];
aCount = [array count];
goCount = (goCount) ? goCount : aCount;
#if DEBUG_REPETITION
NSLog(@"%s: process %d items ...", __PRETTY_FUNCTION__, goCount);
#endif
hasId = [self hasAttribute:@"identifier" node:_node ctx:_ctx];
if (goCount > 0) {
unsigned i, goUntil;
BOOL hasSettableIndex = NO;
BOOL hasSettableItem = NO;
BOOL hasItem = NO;
if (!hasId) {
if (startIdx == 0)
[_ctx appendZeroElementIDComponent];
else
[_ctx appendElementIDComponent:[self stringForInt:startIdx]];
}
if ([self hasAttribute:@"list" node:_node ctx:_ctx]) {
goUntil = (aCount > (startIdx + goCount))
? startIdx + goCount
: aCount;
}
else
goUntil = startIdx + goCount;
hasItem = [self hasAttribute:@"item" node:_node ctx:_ctx];
hasSettableIndex = [self isSettable:@"index" node:_node ctx:_ctx];
hasSettableItem = [self isSettable:@"item" node:_node ctx:_ctx];
for (i = startIdx; i < goUntil; i++) {
id ident = nil;
if (hasSettableIndex)
[self setInt:i for:@"index" node:_node ctx:_ctx];
if (hasItem) {
if (hasSettableItem) {
id item;
item = [array objectAtIndex:i];
#if DEBUG_REPETITION
NSLog(@"%s: apply item: %@", __PRETTY_FUNCTION__, item);
#endif
[self setValue:item for:@"item"
node:_node ctx:_ctx];
}
}
else {
/* use cursor */
[_ctx pushCursor:[array objectAtIndex:i]];
}
/* get identifier used for action-links */
if (hasId) {
/* use a unique id for subelement detection */
ident = [self stringFor:@"identifier" node:_node ctx:_ctx];
// ident = [ident stringByEscapingURL]; ???
[_ctx appendElementIDComponent:ident];
}
else {
/* use repetition index fo subelement detection */
ident = [self stringForInt:i];
}
/* append child elements */
[super appendNode:(id)_node
toResponse:(WOResponse *)_response
inContext:(WOContext *)_ctx];
/* cleanup */
if (!hasItem)
[_ctx popCursor];
if (hasId)
[_ctx deleteLastElementIDComponent];
else
[_ctx incrementLastElementIDComponent];
}
if (!hasId)
[_ctx deleteLastElementIDComponent]; /* repetition index */
if (hasSettableIndex)
[self setInt:0 for:@"index" node:_node ctx:_ctx];
if (hasSettableItem)
[self setValue:nil for:@"item" node:_node ctx:_ctx];
}
RELEASE(pool);
}
@end /* ODR_bind_foreach */