1/*
2  Copyright (C) 2000-2005 SKYRIX Software AG
3
4  This file is part of SOPE.
5
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
9  later version.
10
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.
15
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
19  02111-1307, USA.
20*/
21
22/*
23  WESwitch      { selection | selections };
24  WECase        { key | keys };
25  WEDefaultCase {};
26
27  Warning: The DefaultCase must appear at the last position!!!
28*/
29
30/*
31  example:
32
33  // wod:
34  Switch:      WESwitch { selection = selection;           };
35  FirstCase:   WECase   { key       = "first";             };
36  SecondCase:  WECase   { keys      = ("second", "third"); };
37  DefaultCase: WEDefaultCase {};
38
39  // html:
40  <#Switch>
41    <#FirstCase>content of first case</#FirstCase>
42    <#SecondCase>content of second case</#SecondCase>
43    <#DefaultCase>content of default case</#SecondCase>
44  </#Switch>
45
46*/
47#include <NGObjWeb/WODynamicElement.h>
48
49@class WOAssociation;
50
51@interface WESwitch : WODynamicElement
52{
53@protected
54  WOAssociation *selection;     // string -> single switch
55  WOAssociation *selections;    // array  -> multi  switch
56  WOElement     *template;
57}
58@end
59
60@interface WECase : WODynamicElement
61{
62  WOAssociation *key;          // string -> unique identifier
63  WOAssociation *keys;         // array of unique identifiers
64
65  WOAssociation *defaultCase;  // emulates a WEDefaultCase DEPRECATED!!!
66
67  WOElement     *template;
68}
69@end
70
71@interface WEDefaultCase : WODynamicElement
72{
73  WOElement *template;
74}
75@end
76
77#include "common.h"
78
79#if DEBUG
80static NSString *WESwitch_DefaultCaseFound = @"WESwitch_DefaultCaseFound";
81#endif
82static NSString *WESwitch_CaseDidMatch     = @"WESwitch_CaseDidMatch";
83static NSString *WESwitchSelection         = @"WESwitchSelection";
84static NSString *WESwitchSelections        = @"WESwitchSelections";
85static NSString *WESwitchDict              = @"WESwitchDict";
86
87@implementation WESwitch
88
89- (id)initWithName:(NSString *)_name
90  associations:(NSDictionary *)_config
91  template:(WOElement *)_subs
92{
93  if ((self = [super initWithName:_name associations:_config template:_subs])) {
94    self->selection  = WOExtGetProperty(_config, @"selection");
95    self->selections = WOExtGetProperty(_config, @"selections");
96
97    self->template   = [_subs retain];
98  }
99  return self;
100}
101
102- (void)dealloc {
103  [self->template   release];
104  [self->selection  release];
105  [self->selections release];
106  [super dealloc];
107}
108
109/* processing requests */
110
111- (void)takeValuesFromRequest:(WORequest *)_req inContext:(WOContext *)_ctx {
112  WOComponent *cmp   = nil;
113  NSArray     *array = nil;
114  NSString    *k     = nil;
115  unsigned    i, cnt;
116  BOOL        doLazy = YES;
117
118
119  cmp   = [_ctx component];
120  array = [self->selections valueInComponent:cmp];
121  k     = [self->selection  valueInComponent:cmp];
122  cnt   = [array count];
123
124  if (_req == nil) {
125    [self->template takeValuesFromRequest:_req inContext:_ctx];
126  }
127  else if (k) {
128    [_ctx setObject:k forKey:WESwitchSelection];
129    [self->template takeValuesFromRequest:_req inContext:_ctx];
130    [_ctx removeObjectForKey:WESwitchSelection];
131  }
132  else if (doLazy) {
133    for (i = 0; i < cnt; i++) {
134      [_ctx setObject:[array objectAtIndex:i] forKey:WESwitchSelection];
135      [self->template takeValuesFromRequest:_req inContext:_ctx];
136    }
137    if (cnt == 0) {
138      [_ctx setObject:array forKey:WESwitchSelections];
139      [self->template takeValuesFromRequest:_req inContext:_ctx];
140      [_ctx removeObjectForKey:WESwitchSelections];
141    }
142    [_ctx removeObjectForKey:WESwitchSelection];
143  }
144  else if (cnt > 0) {
145    NSLog(@"Warning(%s):This case is not implemented!!!", __PRETTY_FUNCTION__);
146    [self->template takeValuesFromRequest:_req inContext:_ctx];
147  }
148
149#if DEBUG
150  else {
151    [cmp logWithFormat:
152         @"Warning! WESwitch: Neither 'selection' nor 'selections' set!!!"];
153  }
154#endif
155
156  [_ctx removeObjectForKey:WESwitch_CaseDidMatch];
157}
158
159- (id)invokeActionForRequest:(WORequest *)_request inContext:(WOContext *)_ctx {
160  return [self->template invokeActionForRequest:_request inContext:_ctx];
161}
162
163/* generating response */
164
165- (void)appendToResponse:(WOResponse *)_response inContext:(WOContext *)_ctx {
166  WOComponent *cmp   = nil;
167  NSArray     *array = nil;
168  NSString    *k     = nil;
169  unsigned    i, cnt;
170  BOOL        doLazy = YES;
171
172
173  cmp   = [_ctx component];
174  array = [self->selections valueInComponent:cmp];
175  k     = [self->selection  valueInComponent:cmp];
176  cnt   = [array count];
177
178  if (_response == nil) {
179    [self->template appendToResponse:_response inContext:_ctx];
180  }
181  else if (k) {
182    [_ctx setObject:k forKey:WESwitchSelection];
183    [self->template appendToResponse:_response inContext:_ctx];
184    [_ctx removeObjectForKey:WESwitchSelection];
185  }
186  else if (doLazy) {
187    for (i=0; i<cnt; i++) {
188      [_ctx setObject:[array objectAtIndex:i] forKey:WESwitchSelection];
189      [self->template appendToResponse:_response inContext:_ctx];
190    }
191    if (cnt == 0) {
192      [_ctx setObject:array forKey:WESwitchSelections];
193      [self->template appendToResponse:_response inContext:_ctx];
194      [_ctx removeObjectForKey:WESwitchSelections];
195    }
196    [_ctx removeObjectForKey:WESwitchSelection];
197  }
198  else if (cnt > 0) {
199    NSMutableDictionary *dict = nil;
200
201    // get subcontent of WECases
202    [_ctx setObject:array forKey:WESwitchSelections];
203    [self->template appendToResponse:_response inContext:_ctx];
204
205    dict = [_ctx objectForKey:WESwitchDict];
206
207    // append subcontent
208    if (dict) {
209      for (i=0; i<cnt; i++) {
210        NSString *k = [array objectAtIndex:i];
211        NSData   *c = [dict objectForKey:k];   // subcontent of WECase
212
213        if (c)
214          [_response appendContentData:c];
215      }
216    }
217
218    [_ctx removeObjectForKey:WESwitchDict];
219    [_ctx removeObjectForKey:WESwitchSelections];
220  }
221
222#if DEBUG
223  else {
224    [cmp logWithFormat:
225         @"Warning! WESwitch: Neither 'selection' nor 'selections' set!!!"];
226  }
227#endif
228
229  [_ctx removeObjectForKey:WESwitch_CaseDidMatch];
230}
231
232@end /* WESwitch */
233
234@implementation WECase
235
236- (id)initWithName:(NSString *)_name
237  associations:(NSDictionary *)_config
238  template:(WOElement *)_t
239{
240  if ((self = [super initWithName:_name associations:_config template:_t])) {
241    self->key  = WOExtGetProperty(_config, @"key");
242    self->keys = WOExtGetProperty(_config, @"keys");
243
244    // DEPRECATED!!!
245    self->defaultCase  = WOExtGetProperty(_config, @"default");
246
247    self->template = [_t retain];
248  }
249  return self;
250}
251
252- (void)dealloc {
253  [self->template    release];
254  [self->key         release];
255  [self->keys        release];
256  [self->defaultCase release];
257  [super dealloc];
258}
259
260/* processing requests */
261
262- (void)takeValuesFromRequest:(WORequest *)_req inContext:(WOContext *)_ctx {
263  NSArray     *selections = nil;
264  NSString    *selection  = nil;
265  NSString    *k          = nil;
266  NSArray     *ks         = nil;
267
268  k   = [self->key  stringValueInComponent:[_ctx component]];
269  ks  = [self->keys valueInComponent:[_ctx component]];
270
271  selections = [_ctx objectForKey:WESwitchSelections];
272  selection  = [_ctx objectForKey:WESwitchSelection];
273
274  if ([self->defaultCase boolValueInComponent:[_ctx component]]) {
275    if ([_ctx objectForKey:WESwitch_CaseDidMatch] == nil)
276      [self->template takeValuesFromRequest:_req inContext:_ctx];
277    return;
278  }
279
280  if ((k == nil) && (ks == nil)) {
281#if DEBUG
282    [[_ctx component] logWithFormat:
283                      @"Warning! WECase: Neither 'key' nor 'keys' set!!!"];
284#endif
285    return;
286  }
287  if ((k != nil) && (ks != nil)) {
288#if DEBUG
289    [[_ctx component] logWithFormat:
290                      @"Warning! WECase: Both, 'key' and 'keys' are set!!!"];
291#endif
292    return;
293  }
294
295  if (_req == nil) {
296    [self->template takeValuesFromRequest:nil inContext:_ctx];
297  }
298  if (selection) {
299    if (k && [k isEqualToString:selection]) {
300       [self->template takeValuesFromRequest:_req inContext:_ctx];
301       [_ctx setObject:@"YES" forKey:WESwitch_CaseDidMatch];
302    }
303    else if (ks && [ks containsObject:selection]) {
304      [self->template takeValuesFromRequest:_req inContext:_ctx];
305      [_ctx setObject:@"YES" forKey:WESwitch_CaseDidMatch];
306    }
307  }
308  else if (selections && [selections count] > 0) {
309    NSLog(@"Warning(%s): This case is not implemented!", __PRETTY_FUNCTION__);
310    [self->template takeValuesFromRequest:_req inContext:_ctx];
311  }
312}
313
314- (id)invokeActionForRequest:(WORequest *)_req inContext:(WOContext *)_ctx {
315  return [self->template invokeActionForRequest:_req inContext:_ctx];
316}
317
318/* generating response */
319
320- (void)appendToResponse:(WOResponse *)_response inContext:(WOContext *)_ctx {
321  NSArray     *selections = nil;
322  NSString    *selection  = nil;
323  NSString    *k          = nil;
324  NSArray     *ks         = nil;
325
326  k   = [self->key  stringValueInComponent:[_ctx component]];
327  ks  = [self->keys valueInComponent:[_ctx component]];
328
329  selections = [_ctx objectForKey:WESwitchSelections];
330  selection  = [_ctx objectForKey:WESwitchSelection];
331
332  if ([self->defaultCase boolValueInComponent:[_ctx component]]) {
333    if ([_ctx objectForKey:WESwitch_CaseDidMatch] == nil)
334      [self->template appendToResponse:_response inContext:_ctx];
335    return;
336  }
337
338  if ((k == nil) && (ks == nil)) {
339#if DEBUG
340    [[_ctx component] warnWithFormat:
341                      @"WECase: Neither 'key' nor 'keys' set!!!"];
342#endif
343    return;
344  }
345  if ((k != nil) && (ks != nil)) {
346#if DEBUG
347    [[_ctx component] warnWithFormat:
348                      @"WECase: Both, 'key' and 'keys' are set!!!"];
349#endif
350    return;
351  }
352
353  if (_response == nil) {
354    [self->template appendToResponse:nil inContext:_ctx];
355  }
356  if (selection) {
357    if (k && [k isEqualToString:selection]) {
358       [self->template appendToResponse:_response inContext:_ctx];
359       [_ctx setObject:@"YES" forKey:WESwitch_CaseDidMatch];
360    }
361    else if (ks && [ks containsObject:selection]) {
362      [self->template appendToResponse:_response inContext:_ctx];
363      [_ctx setObject:@"YES" forKey:WESwitch_CaseDidMatch];
364    }
365  }
366  else if (selections && [selections count] > 0) {
367    if ([selections containsObject:k]) {
368      static NSData *emptyData = nil;
369      NSMutableDictionary *dict       = nil;
370      NSData              *oldContent = nil;
371
372      if (emptyData == nil)
373        emptyData = [[NSData alloc] init];
374
375      // get subcontent dictionary
376      dict = [_ctx objectForKey:WESwitchDict];
377      if (dict == nil)
378        dict = [NSMutableDictionary dictionaryWithCapacity:[selections count]];
379
380      // set new content
381      oldContent = [_response content];
382      RETAIN(oldContent);
383      [_response setContent:emptyData];
384
385      // append template to new content
386      [self->template appendToResponse:_response inContext:_ctx];
387
388      // save new content in dict
389      if ([_response content])
390        [dict setObject:[_response content] forKey:k];
391      [_ctx setObject:dict forKey:WESwitchDict];
392
393      // restore old content
394      [_response setContent:oldContent];
395      [oldContent release]; oldContent = nil;
396
397      // TODO: use NSNumber here?
398      [_ctx setObject:@"YES" forKey:WESwitch_CaseDidMatch];
399    }
400  }
401}
402
403@end /* WECase */
404
405@implementation WEDefaultCase
406
407- (id)initWithName:(NSString *)_name
408  associations:(NSDictionary *)_config
409  template:(WOElement *)_t
410{
411  if ((self = [super initWithName:_name associations:_config template:_t])) {
412    self->template = [_t retain];
413  }
414  return self;
415}
416
417- (void)dealloc {
418  [self->template release];
419  [super dealloc];
420}
421
422/* processing requests */
423
424- (void)takeValuesFromRequest:(WORequest *)_req inContext:(WOContext *)_ctx {
425  if (([_ctx objectForKey:WESwitch_CaseDidMatch] == nil))
426    [self->template takeValuesFromRequest:_req inContext:_ctx];
427}
428
429- (id)invokeActionForRequest:(WORequest *)_req inContext:(WOContext *)_ctx {
430  return [self->template invokeActionForRequest:_req inContext:_ctx];
431}
432
433/* generating response */
434
435- (void)appendToResponse:(WOResponse *)_response inContext:(WOContext *)_ctx {
436  if (([_ctx objectForKey:WESwitch_CaseDidMatch] == nil))
437    [self->template appendToResponse:_response inContext:_ctx];
438
439#if DEBUG
440  [_ctx setObject:@"Yes" forKey:WESwitch_DefaultCaseFound];
441#endif
442}
443
444@end /* WEDefaultCase */
445