]> err.no Git - sope/blob - sope-appserver/NGObjWeb/DynamicElements/WOString.m
fixed bundle resource lookup on MacOSX, changed resource lookup in
[sope] / sope-appserver / NGObjWeb / DynamicElements / WOString.m
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 #include "WOHTMLDynamicElement.h"
23 #include "WOElement+private.h"
24 #include "common.h"
25 #import <Foundation/NSNumberFormatter.h>
26 #import <Foundation/NSDateFormatter.h>
27
28 /*
29   Usage:
30     MyString: WOString {
31       value      = myCalendarDate;
32       dateformat = "%B %Y %T";
33       insertBR   = NO;
34       nilString  = "no date is set !";
35       escapeHTML = YES;
36     };
37
38   Hierarchy:
39     WOHTMLDynamicElement
40       WOString
41         _WOSimpleStaticString
42         _WOSimpleStaticASCIIString
43         _WOComplexString
44 */
45
46 @interface WOString : WOHTMLDynamicElement
47 @end /* WOString */
48
49 @interface _WOTemporaryString : NSObject
50 @end
51
52 @interface _WOSimpleStaticString : WOString
53 {
54   NSString *value;
55 }
56 @end /* WOSimpleStaticString */
57
58 @interface _WOSimpleStaticASCIIString : WOString
59 {
60   const unsigned char *value;
61 }
62 @end /* _WOSimpleStaticASCIIString */
63
64 @interface _WOSimpleDynamicString : WOString
65 {
66   WOAssociation *value;
67 }
68 @end /* WOSimpleStaticString */
69
70 @interface _WOComplexString : WOString
71 {
72   WOAssociation *value;          // object
73   WOAssociation *escapeHTML;     // BOOL
74   WOAssociation *numberformat;   // string
75   WOAssociation *dateformat;     // string
76   WOAssociation *formatter;      // WO4: NSFormatter object
77   WOAssociation *valueWhenEmpty; // WO4.5
78   
79   // non-WO attributes
80   WOAssociation *insertBR;   // insert <BR> tags for newlines
81   WOAssociation *nilString;  // string to use if value is nil - DEPRECATED!
82   WOAssociation *style;      // insert surrounding <span class="style">
83 }
84
85 @end /* WOComplexString */
86
87 @implementation WOString
88
89 + (int)version {
90   return [super version] + 1 /* v3 */;
91 }
92 + (void)initialize {
93   NSAssert2([super version] == 2,
94             @"invalid superclass (%@) version %i !",
95             NSStringFromClass([self superclass]), [super version]);
96 }
97
98 + (id)allocWithZone:(NSZone *)zone {
99   static Class WOStringClass = Nil;
100   static _WOTemporaryString *temporaryString = nil;
101   
102   if (WOStringClass == Nil)
103     WOStringClass = [WOString class];
104   if (temporaryString == nil)
105     temporaryString = [_WOTemporaryString allocWithZone:zone];
106   
107   return (self == WOStringClass)
108     ? (id)temporaryString
109     : NSAllocateObject(self, 0, zone);
110 }
111
112 @end /* WOString */
113
114 @implementation _WOSimpleDynamicString
115
116 - (id)initWithName:(NSString *)_name
117   associations:(NSDictionary *)_config
118   template:(WOElement *)_t
119 {
120   if ((self = [super initWithName:_name associations:_config template:_t])) {
121     self->value = OWGetProperty(_config, @"value");
122   }
123   return self;
124 }
125
126 - (void)dealloc {
127   [self->value release];
128   [super dealloc];
129 }
130
131 /* generating response */
132
133 - (void)appendToResponse:(WOResponse *)_response inContext:(WOContext *)_ctx {
134   [_response appendContentHTMLString:[self->value stringValueInContext:_ctx]];
135 }
136
137 /* description */
138
139 - (NSString *)associationDescription {
140   return [NSString stringWithFormat:@" value='%@'", self->value];
141 }
142
143 @end /* _WOSimpleDynamicString */
144
145 @implementation _WOSimpleStaticString
146
147 - (id)initWithValue:(WOAssociation *)_value escapeHTML:(BOOL)_flag {
148   if ((self = [super initWithName:nil associations:nil template:nil])) {
149     // ENCODING, UNICODE
150     NSString *v;
151     unsigned length;
152
153     v = [_value stringValueInComponent:nil];
154     length = [v cStringLength];
155
156     if (length == 0) {
157       v = @"";
158     }
159     else if (_flag) {
160       char buffer[length * 6]; /* longest-encoding: '&quot;' */
161       unsigned clen = [v cStringLength];
162       char     *cbuf;
163       register char *cstr;
164       register unsigned i;
165       register char *bufPtr;
166
167 #if NeXT_Foundation_LIBRARY
168       cbuf = malloc(clen + 1);
169 #else
170       cbuf = NGMallocAtomic(clen + 1);
171 #endif
172       [v getCString:cbuf]; cbuf[clen] = '\0';
173       cstr = cbuf;
174       
175       for (i = 0, bufPtr = buffer; i < length; i++) {
176         switch (cstr[i]) {
177           case '&': /* &amp; */
178             bufPtr[0] = '&'; bufPtr[1] = 'a'; bufPtr[2] = 'm'; bufPtr[3] = 'p';
179             bufPtr[4] = ';';
180             bufPtr += 5;
181             break;
182             
183           case '<': /* &lt;   */
184             bufPtr[0] = '&'; bufPtr[1] = 'l'; bufPtr[2] = 't'; bufPtr[3] = ';';
185             bufPtr += 4;
186             break;
187             
188           case '>': /* &gt;   */
189             bufPtr[0] = '&'; bufPtr[1] = 'g'; bufPtr[2] = 't'; bufPtr[3] = ';';
190             bufPtr += 4;
191             break;
192             
193           case '"': /* &quot; */
194             bufPtr[0] = '&'; bufPtr[1] = 'q'; bufPtr[2] = 'u';
195             bufPtr[3] = 'o'; bufPtr[4] = 't'; bufPtr[5] = ';';
196             bufPtr += 6;
197             break;
198             
199           default:
200             *bufPtr = cstr[i];
201             bufPtr++;
202             break;
203         }
204       }
205 #if NeXT_Foundation_LIBRARY
206       if (cbuf) free(cbuf);
207 #else
208       if (cbuf) NGFree(cbuf);
209 #endif
210       self->value = [[NSString allocWithZone:[self zone]]
211                                initWithCString:&(buffer[0])
212                                length:(bufPtr - &(buffer[0]))];
213     }
214     else {
215       self->value = [v copyWithZone:[self zone]];
216     }
217   }
218   return self;
219 }
220
221 - (id)initWithName:(NSString *)_name
222   associations:(NSDictionary *)_config
223   template:(WOElement *)_t
224 {
225   if ((self = [super initWithName:_name associations:_config template:_t])) {
226     WOAssociation *avalue, *aescape;
227     BOOL doEscape;
228     
229     avalue  = OWGetProperty(_config, @"value");
230     aescape = OWGetProperty(_config, @"escapeHTML");
231     
232     doEscape = (aescape != nil) ? [aescape boolValueInComponent:nil] : YES;
233     self = [self initWithValue:avalue escapeHTML:doEscape];
234   }
235   return self;
236 }
237
238 - (void)dealloc {
239   [self->value release];
240   [super dealloc];
241 }
242
243 /* generating response */
244
245 - (void)appendToResponse:(WOResponse *)_response inContext:(WOContext *)_ctx {
246   WOResponse_AddString(_response, self->value);
247 }
248
249 /* description */
250
251 - (NSString *)associationDescription {
252   return [NSString stringWithFormat:@" value='%@'", self->value];
253 }
254
255 @end /* WOSimpleStaticString */
256
257 @implementation _WOSimpleStaticASCIIString
258
259 - (id)initWithValue:(WOAssociation *)_value escapeHTML:(BOOL)_flag {
260   if ((self = [super initWithName:nil associations:nil template:nil])) {
261     // ENCODING, UNICODE
262     NSString *v;
263     unsigned length;
264     
265     v = [_value stringValueInComponent:nil];
266     length = [v cStringLength];
267     
268     if (length == 0) {
269       self->value = NULL;
270     }
271     else if (_flag) {
272       unsigned char *buffer;
273       register unsigned char *cbuf;
274       register unsigned char *bufPtr;
275       register unsigned      i;
276       unsigned clen;
277       BOOL didEscape = NO;
278       
279       clen = [v cStringLength];
280       cbuf = malloc(clen + 2);
281       [v getCString:cbuf]; cbuf[clen] = '\0';
282       
283       buffer = malloc(clen * 6 + 2); /* longest-encoding: '&quot;' */
284       
285       for (i = 0, bufPtr = buffer; i < length; i++) {
286         switch (cbuf[i]) {
287           case '&': /* &amp; */
288             bufPtr[0] = '&'; bufPtr[1] = 'a'; bufPtr[2] = 'm'; bufPtr[3] = 'p';
289             bufPtr[4] = ';';
290             bufPtr += 5;
291             didEscape = YES;
292             break;
293             
294           case '<': /* &lt;   */
295             bufPtr[0] = '&'; bufPtr[1] = 'l'; bufPtr[2] = 't'; bufPtr[3] = ';';
296             bufPtr += 4;
297             didEscape = YES;
298             break;
299             
300           case '>': /* &gt;   */
301             bufPtr[0] = '&'; bufPtr[1] = 'g'; bufPtr[2] = 't'; bufPtr[3] = ';';
302             bufPtr += 4;
303             didEscape = YES;
304             break;
305             
306           case '"': /* &quot; */
307             bufPtr[0] = '&'; bufPtr[1] = 'q'; bufPtr[2] = 'u';
308             bufPtr[3] = 'o'; bufPtr[4] = 't'; bufPtr[5] = ';';
309             bufPtr += 6;
310             didEscape = YES;
311             break;
312             
313           default:
314             if ((*bufPtr = cbuf[i]) > 127) {
315               NSLog(@"WARNING: string is not ASCII as required for "
316                     @"SimpleStaticASCIIString !!! '%@'", v);
317             }
318             bufPtr++;
319             break;
320         }
321       }
322       if (didEscape) {
323         if (cbuf) free(cbuf);
324         clen = (bufPtr - buffer);
325         self->value = malloc(clen + 2);
326         strncpy((char *)self->value, buffer, clen);
327         ((unsigned char *)self->value)[clen] = '\0';
328       }
329       else {
330         self->value = cbuf;
331       }
332       if (buffer) free(buffer);
333     }
334     else {
335       self->value = malloc(length + 2);
336       [v getCString:(char*)self->value];
337     }
338   }
339   return self;
340 }
341
342 - (id)initWithName:(NSString *)_name
343   associations:(NSDictionary *)_config
344   template:(WOElement *)_t
345 {
346   if ((self = [super initWithName:_name associations:_config template:_t])) {
347     WOAssociation *avalue, *aescape;
348     BOOL doEscape;
349     
350     avalue  = OWGetProperty(_config, @"value");
351     aescape = OWGetProperty(_config, @"escapeHTML");
352     
353     doEscape = (aescape != nil) ? [aescape boolValueInComponent:nil] : YES;
354     self = [self initWithValue:avalue escapeHTML:doEscape];
355   }
356   return self;
357 }
358
359 - (void)dealloc {
360   if (self->value) free((char *)self->value);
361   [super dealloc];
362 }
363
364 /* generating response */
365
366 - (void)appendToResponse:(WOResponse *)_response inContext:(WOContext *)_ctx {
367   if (self->value) WOResponse_AddCString(_response, self->value);
368 }
369
370 /* description */
371
372 - (NSString *)associationDescription {
373   return [NSString stringWithFormat:@" value='%s'", 
374                      self->value ? self->value : (void*)""];
375 }
376
377 @end /* WOSimpleStaticASCIIString */
378
379 @implementation _WOComplexString
380
381 static NSNumber      *yesNum = nil;
382 static WOAssociation *yesAssoc = nil;
383
384 + (void)initialize {
385   if (yesNum   == nil) yesNum = [[NSNumber numberWithBool:YES] retain];
386   if (yesAssoc == nil) 
387     yesAssoc = [[WOAssociation associationWithValue:yesNum] retain];
388 }
389
390 - (id)initWithName:(NSString *)_name
391   associations:(NSDictionary *)_config
392   template:(WOElement *)_t
393 {
394   if ((self = [super initWithName:_name associations:_config template:_t])) {
395     self->value          = OWGetProperty(_config, @"value");
396     self->escapeHTML     = OWGetProperty(_config, @"escapeHTML");
397     self->insertBR       = OWGetProperty(_config, @"insertBR");
398     self->nilString      = OWGetProperty(_config, @"nilString");
399     self->valueWhenEmpty = OWGetProperty(_config, @"valueWhenEmpty");
400     self->style          = OWGetProperty(_config, @"style");
401
402     if (self->nilString != nil && self->valueWhenEmpty != nil) {
403       [self logWithFormat:
404               @"WARNING: 'valueWhenEmpty' AND 'nilString' bindings are set, "
405               @"use only one! ('nilString' is deprecated!)"];
406     }
407     else if (self->nilString != nil) {
408       [self debugWithFormat:
409               @"Note: using deprecated 'nilString' binding, "
410               @"use 'valueWhenEmpty' instead."];
411     }
412     
413     self->formatter    = OWGetProperty(_config, @"formatter");
414     self->numberformat = OWGetProperty(_config, @"numberformat");
415     self->dateformat   = OWGetProperty(_config, @"dateformat");
416     
417     if (self->formatter == nil) {
418       if ([_config objectForKey:@"formatterClass"]) {
419         WOAssociation *assoc;
420         NSString      *className;
421         NSFormatter   *fmt = nil;
422         Class         clazz;
423         
424         assoc = [OWGetProperty(_config, @"formatterClass") autorelease];
425         if (![assoc isValueConstant])
426           [self logWithFormat:@"non-constant 'formatterClass' binding!"];
427         className = [assoc stringValueInComponent:nil];
428         clazz     = NSClassFromString(className);
429         
430         if ((assoc = [OWGetProperty(_config, @"format") autorelease])) {
431           NSString *format = nil;
432           
433           if (![assoc isValueConstant])
434             [self logWithFormat:@"non-constant 'format' binding!"];
435           format = [assoc stringValueInComponent:nil];
436           
437           if ([clazz instancesRespondToSelector:@selector(initWithString:)])
438             fmt = [[clazz alloc] initWithString:format];
439           else {
440             [self logWithFormat:
441                     @"cannot instantiate formatter with format: '%@'", format];
442             fmt = [[clazz alloc] init];
443           }
444         }
445         else
446           fmt = [[clazz alloc] init];
447         
448         self->formatter = [[WOAssociation associationWithValue:fmt] retain];
449         [fmt release];
450       }
451     }
452
453     if (self->escapeHTML == nil)
454       self->escapeHTML = [yesAssoc retain];
455     
456     /* check formats */
457     {
458       int num = 0;
459       if (self->formatter)    num++;
460       if (self->numberformat) num++;
461       if (self->dateformat)   num++;
462       if (num > 1)
463         NSLog(@"WARNING: more than one formats specified in element %@", self);
464     }
465   }
466   return self;
467 }
468
469 - (id)initWithValue:(WOAssociation *)_value escapeHTML:(BOOL)_flag {
470   if ((self = [super initWithName:nil associations:nil template:nil])) {
471     self->value      = [_value retain];
472     self->escapeHTML = _flag
473       ? yesAssoc
474       : [WOAssociation associationWithValue:[NSNumber numberWithBool:NO]];
475     self->escapeHTML = [self->escapeHTML retain];
476   }
477   return self;
478 }
479
480 - (void)dealloc {
481   [self->numberformat   release];
482   [self->dateformat     release];
483   [self->formatter      release];
484   [self->nilString      release];
485   [self->valueWhenEmpty release];
486   [self->value          release];
487   [self->escapeHTML     release];
488   [self->insertBR       release];
489   [self->style          release];
490   [super dealloc];
491 }
492
493 /* response generation */
494
495 - (void)_appendStringLines:(NSString *)_s withSeparator:(NSString *)_br
496   contentSelector:(SEL)_sel
497   toResponse:(WOResponse *)_response inContext:(WOContext *)_ctx
498 {
499   NSArray *lines;
500   unsigned i, count;
501   
502   lines = [_s componentsSeparatedByString:@"\n"];
503
504   for (i = 0, count = [lines count]; i < count; i++) {
505     NSString *line;
506
507     line = [lines objectAtIndex:i];
508     if (i != 0) WOResponse_AddString(_response, _br);
509     
510     [_response performSelector:_sel withObject:line];
511   }
512 }
513
514 - (NSFormatter *)_formatterInContext:(WOContext *)_ctx {
515   if (self->numberformat) {
516     NSNumberFormatter *fmt;
517
518     fmt = [[[NSNumberFormatter alloc] init] autorelease];
519     [fmt setFormat:[self->numberformat valueInComponent:[_ctx component]]];
520     return fmt;
521   }
522
523   if (self->dateformat) {
524     NSDateFormatter *fmt;
525     NSString *s;
526     
527     s = [self->dateformat valueInComponent:[_ctx component]];
528     fmt = [[NSDateFormatter alloc] initWithDateFormat:s
529                                    allowNaturalLanguage:NO];
530     return [fmt autorelease];
531   }
532   
533   if (self->formatter)
534     return [self->formatter valueInComponent:[_ctx component]];
535   
536   return nil;
537 }
538
539 - (void)appendToResponse:(WOResponse *)_response inContext:(WOContext *)_ctx {
540   WOComponent *sComponent = [_ctx component];
541   NSFormatter *fmt;
542   id          obj    = nil;
543   SEL         addSel = NULL;
544   NSString    *styleName;
545
546   fmt = [self _formatterInContext:_ctx];
547 #if DEBUG
548   if (fmt!=nil && ![fmt respondsToSelector:@selector(stringForObjectValue:)]) {
549     [sComponent logWithFormat:
550                   @"invalid formatter determined by keypath %@: %@",
551                   self->formatter, fmt];
552   }
553 #endif
554   
555   obj    = [self->value valueInContext:_ctx];
556   addSel = [self->escapeHTML boolValueInComponent:sComponent]
557     ? @selector(appendContentHTMLString:)
558     : @selector(appendContentString:);
559
560   if (fmt) {
561     NSString *formattedObj;
562     
563     formattedObj = [fmt stringForObjectValue:obj];
564 #if 0
565     if (formattedObj == nil) {
566       NSLog(@"WARNING: formatter %@ returned nil string for object %@",
567             fmt, obj);
568     }
569 #endif
570     
571     obj = formattedObj;
572   }
573   
574   obj = [obj stringValue];
575
576   styleName = [self->style stringValueInContext:_ctx];
577
578   /* handling of empty and nil values */
579
580   if (self->valueWhenEmpty) {
581     if (obj == nil || [obj length] == 0) {
582       NSString *s;
583       
584       s = [self->valueWhenEmpty stringValueInComponent:sComponent];
585       
586       /* Note: the missing escaping is intentional for WO 4.5 compatibility */
587       if (s != nil) {
588         if (styleName) {
589           [_response appendContentString:@"<span class=\""];
590           [_response appendContentHTMLAttributeValue:styleName];
591           [_response appendContentString:@"\">"];
592         }
593         [_response appendContentString:s];
594       }
595       goto closeSpan;
596     }
597   }
598   else if (self->nilString != nil && obj == nil) {
599     NSString *s;
600
601     if (styleName) {
602       [_response appendContentString:@"<span class=\""];
603       [_response appendContentHTMLAttributeValue:styleName];
604       [_response appendContentString:@"\">"];
605     }
606     s = [self->nilString stringValueInComponent:sComponent];
607     [_response performSelector:addSel withObject:s];
608     goto closeSpan;
609   }
610   else if (obj == nil)
611     return;
612
613   /* handling of non-empty values */
614   
615   if (styleName) {
616     [_response appendContentString:@"<span class=\""];
617     [_response appendContentHTMLAttributeValue:styleName];
618     [_response appendContentString:@"\">"];
619   }
620
621   if (![self->insertBR boolValueInComponent:sComponent]) {
622     [_response performSelector:addSel withObject:obj];
623   }
624   else {
625     [self _appendStringLines:obj withSeparator:@"<br />"
626           contentSelector:addSel toResponse:_response inContext:_ctx];
627   }
628   
629 closeSpan:
630   if (styleName) {
631     [_response appendContentString:@"</span>"];
632   }
633 }
634
635 /* description */
636
637 - (NSString *)associationDescription {
638   NSMutableString *str;
639   
640   str = [NSMutableString stringWithCapacity:64];
641   
642   if (self->value)      [str appendFormat:@" value=%@",      self->value];
643   if (self->escapeHTML) [str appendFormat:@" escape=%@",     self->escapeHTML];
644   if (self->insertBR)   [str appendFormat:@" insertBR=%@",   self->insertBR];
645   if (self->formatter)  [str appendFormat:@" formatter=%@",  self->formatter];
646   if (self->dateformat) [str appendFormat:@" dateformat=%@", self->dateformat];
647   if (self->numberformat)
648     [str appendFormat:@" numberformat=%@", self->numberformat];
649
650   if (self->valueWhenEmpty) 
651     [str appendFormat:@" valueWhenEmpty=%@", self->valueWhenEmpty];
652   if (self->style) 
653       [str appendFormat:@" style=%@", self->style];
654   
655   return str;
656 }
657
658 @end /* _WOComplexString */
659
660 @implementation _WOTemporaryString
661
662 static Class ComplexStringClass     = Nil;
663 static Class SimpleStringClass      = Nil;
664 static Class SimpleASCIIStringClass = Nil;
665 static Class SimpleDynStringClass   = Nil;
666
667 #define ENSURE_CACHE \
668   if (ComplexStringClass == Nil)\
669     ComplexStringClass = [_WOComplexString class];\
670   if (SimpleStringClass == Nil)\
671     SimpleStringClass = [_WOSimpleStaticString class];\
672   if (SimpleASCIIStringClass == Nil)\
673     SimpleASCIIStringClass = [_WOSimpleStaticASCIIString class];\
674   if (SimpleDynStringClass == Nil)\
675     SimpleDynStringClass = [_WOSimpleDynamicString class];
676
677 static inline Class _classForConfig(NSDictionary *_config) {
678   Class         sClass = Nil;
679   WOAssociation *assoc, *assoc2;
680   unsigned      c;
681   
682   switch ((c = [_config count])) {
683     case 0:
684       sClass = SimpleStringClass;
685       break;
686       
687     case 1:
688       if ((assoc = [_config objectForKey:@"value"])) {
689         if ([assoc isValueConstant])
690           sClass = SimpleStringClass;
691         else
692           sClass = SimpleDynStringClass;
693         break;
694       }
695       if ((assoc = [_config objectForKey:@"escapeHTML"])) {
696         if ([assoc isValueConstant])
697           sClass = SimpleStringClass;
698         break;
699       }
700       break;
701       
702     case 2:
703       if ((assoc = [_config objectForKey:@"value"])) {
704         if ((assoc2 = [_config objectForKey:@"escapeHTML"])) {
705           if ([assoc isValueConstant] && [assoc2 isValueConstant])
706             sClass = SimpleStringClass;
707
708           break;
709         }
710       }
711       break;
712       
713     default:
714       sClass = ComplexStringClass;
715       break;
716   }
717   
718   return sClass ? sClass : ComplexStringClass;
719 }
720
721 - (id)initWithName:(NSString *)_n
722   associations:(NSDictionary *)_config
723   template:(WOElement *)_t
724 {
725   Class stringClass;
726   ENSURE_CACHE;
727
728 #if DEBUG
729   if (_t != nil) {
730     NSLog(@"WARNING: WOString '%@' has contents !", _n);
731     abort();
732   }
733 #endif
734
735   stringClass = _classForConfig(_config);
736   
737   return [[stringClass alloc]
738                        initWithName:_n associations:_config template:nil];
739 }
740 - (id)initWithName:(NSString *)_name
741   associations:(NSDictionary *)_associations
742   contentElements:(NSArray *)_contents
743 {
744   Class stringClass;
745   ENSURE_CACHE;
746
747 #if DEBUG
748   if ([_contents count] > 0) {
749     NSLog(@"WARNING: WOString has contents !");
750     abort();
751   }
752 #endif
753   
754   stringClass = _classForConfig(_associations);
755   
756   return [[stringClass alloc]
757                        initWithName:_name
758                        associations:_associations
759                        contentElements:nil];
760 }
761
762 - (id)initWithValue:(WOAssociation *)_value escapeHTML:(BOOL)_flag {
763   Class stringClass;
764   ENSURE_CACHE;
765   
766   if ((_value == nil) || [_value isValueConstant])
767     stringClass = SimpleStringClass;
768   else
769     stringClass = ComplexStringClass;
770     
771   return [[stringClass alloc] initWithValue:_value escapeHTML:_flag];
772 }
773
774 - (void)dealloc {
775   [self errorWithFormat:@"called dealloc on %@", self];
776 #if DEBUG
777   abort();
778 #endif
779   return;
780 }
781
782 @end /* _WOTemporaryString */