root/trunk/Models/JVChatEvent.m

Revision 3450, 9.2 kB (checked in by timothy, 2 years ago)

Code clean up for JavaScript? exception reporting. Fixes a leak also.

Line 
1 #import "JVChatEvent.h"
2 #import "NSAttributedStringMoreAdditions.h"
3
4 #import <libxml/tree.h>
5
6 @implementation JVChatEvent
7 - (void) dealloc {
8         [_eventIdentifier release];
9         [_date release];
10         [_name release];
11         [_message release];
12         [_attributes release];
13
14         _eventIdentifier = nil;
15         _date = nil;
16         _name = nil;
17         _message = nil;
18         _attributes = nil;
19
20         _transcript = nil; // weak reference
21         _node = NULL;
22
23         if( _doc ) xmlFreeDoc( _doc );
24         _doc = NULL;
25
26         [super dealloc];
27 }
28
29 #pragma mark -
30
31 - (void) loadSmall {
32         if( _loadedSmall || ! _node ) return;
33
34         @synchronized( _transcript ) {
35                 xmlChar *prop = xmlGetProp( (xmlNode *) _node, (xmlChar *) "name" );
36                 _name = ( prop ? [[NSString allocWithZone:[self zone]] initWithUTF8String:(char *) prop] : nil );
37                 xmlFree( prop );
38
39                 prop = xmlGetProp( (xmlNode *) _node, (xmlChar *) "occurred" );
40                 _date = ( prop ? [[NSDate allocWithZone:[self zone]] initWithString:[NSString stringWithUTF8String:(char *) prop]] : nil );
41                 xmlFree( prop );
42         }
43
44         _loadedSmall = YES;
45 }
46
47 - (void) loadMessage {
48         if( _loadedMessage || ! _node ) return;
49
50         @synchronized( _transcript ) {
51                 xmlNode *subNode = ((xmlNode *) _node) -> children;
52
53                 do {
54                         if( subNode -> type == XML_ELEMENT_NODE && ! strcmp( "message", (char *) subNode -> name ) ) {
55                                 _message = [[NSTextStorage allocWithZone:[self zone]] initWithXHTMLTree:subNode baseURL:nil defaultAttributes:nil];
56                                 break;
57                         }
58                 } while( ( subNode = subNode -> next ) );
59         }
60
61         _loadedMessage = YES;
62 }
63
64 - (void) loadAttributes {
65         if( _loadedAttributes || ! _node ) return;
66
67         @synchronized( _transcript ) {
68                 xmlNode *subNode = ((xmlNode *) _node) -> children;
69                 NSMutableDictionary *attributes = [NSMutableDictionary dictionary];
70
71                 do {
72                         if( subNode -> type == XML_ELEMENT_NODE && strcmp( "message", (char *) subNode -> name ) ) { // everything but "message"
73                                 NSMutableDictionary *properties = [NSMutableDictionary dictionary];
74                                 xmlAttrPtr prop = NULL;
75                                 for( prop = subNode -> properties; prop; prop = prop -> next ) {
76                                         xmlChar *value = xmlGetProp( subNode, prop -> name );
77                                         if( value ) {
78                                                 [properties setObject:[NSString stringWithUTF8String:(char *) value] forKey:[NSString stringWithUTF8String:(char *) prop -> name]];
79                                                 xmlFree( value );
80                                         }
81                                 }
82
83                                 xmlNode *cnode = subNode -> children;
84                                 unsigned count = 0;
85
86                                 do {
87                                         if( cnode && cnode -> type == XML_ELEMENT_NODE ) count++;
88                                 } while( cnode && ( cnode = cnode -> next ) );
89
90                                 id value = nil;
91                                 if( count > 0 ) {
92                                         value = [NSTextStorage attributedStringWithXHTMLTree:subNode baseURL:nil defaultAttributes:nil];
93                                 } else {
94                                         xmlChar *content = xmlNodeGetContent( subNode );
95                                         value = [NSString stringWithUTF8String:(char *) content];
96                                         xmlFree( content );
97                                 }
98
99                                 if( [properties count] ) {
100                                         [properties setObject:value forKey:@"value"];
101                                         [attributes setObject:properties forKey:[NSString stringWithUTF8String:(char *) subNode -> name]];
102                                 } else {
103                                         [attributes setObject:value forKey:[NSString stringWithUTF8String:(char *) subNode -> name]];
104                                 }
105                         }
106                 } while( ( subNode = subNode -> next ) );
107         }
108
109         _loadedAttributes = YES;
110 }
111
112 #pragma mark -
113
114 - (void *) node {
115         if( ! _node ) {
116                 if( _doc ) xmlFreeDoc( _doc );
117                 _doc = xmlNewDoc( (xmlChar *) "1.0" );
118
119                 xmlNodePtr root = xmlNewNode( NULL, (xmlChar *) "event" );
120                 xmlSetProp( root, (xmlChar *) "id", (xmlChar *) [[self eventIdentifier] UTF8String] );
121                 xmlSetProp( root, (xmlChar *) "name", (xmlChar *) [[self name] UTF8String] );
122                 xmlSetProp( root, (xmlChar *) "occurred", (xmlChar *) [[[self date] description] UTF8String] );
123                 xmlDocSetRootElement( _doc, root );
124
125                 xmlDocPtr msgDoc = NULL;
126                 xmlNodePtr child = NULL;
127                 const char *msgStr = NULL;
128
129                 if( [self message] ) {
130                         NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:YES], @"IgnoreFonts", [NSNumber numberWithBool:YES], @"IgnoreFontSizes", nil];
131                         NSString *msgValue = [[self message] HTMLFormatWithOptions:options];
132                         msgValue = [msgValue stringByStrippingIllegalXMLCharacters];
133
134                         msgStr = [[NSString stringWithFormat:@"<message>%@</message>", msgValue] UTF8String];
135
136                         msgDoc = xmlParseMemory( msgStr, strlen( msgStr ) );
137                         child = xmlDocCopyNode( xmlDocGetRootElement( msgDoc ), _doc, 1 );
138                         xmlAddChild( root, child );
139                         xmlFreeDoc( msgDoc );
140                 }
141
142                 NSEnumerator *kenumerator = [[self attributes] keyEnumerator];
143                 NSEnumerator *enumerator = [[self attributes] objectEnumerator];
144                 NSString *key = nil;
145                 id value = nil;
146
147                 while( ( key = [kenumerator nextObject] ) && ( value = [enumerator nextObject] ) ) {
148                         msgStr = NULL;
149
150                         if( [value respondsToSelector:@selector( xmlDescriptionWithTagName: )] ) {
151                                 msgStr = [(NSString *)[value performSelector:@selector( xmlDescriptionWithTagName: ) withObject:key] UTF8String];
152                         } else if( [value isKindOfClass:[NSAttributedString class]] ) {
153                                 NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:YES], @"IgnoreFonts", [NSNumber numberWithBool:YES], @"IgnoreFontSizes", nil];
154                                 value = [value HTMLFormatWithOptions:options];
155                                 value = [value stringByStrippingIllegalXMLCharacters];
156                                 if( [(NSString *)value length] )
157                                         msgStr = [[NSString stringWithFormat:@"<%@>%@</%@>", key, value, key] UTF8String];
158                         } else if( [value isKindOfClass:[NSString class]] ) {
159                                 value = [value stringByEncodingXMLSpecialCharactersAsEntities];
160                                 value = [value stringByStrippingIllegalXMLCharacters];
161                                 if( [(NSString *)value length] )
162                                         msgStr = [[NSString stringWithFormat:@"<%@>%@</%@>", key, value, key] UTF8String];
163                         } else if( [value isKindOfClass:[NSData class]] ) {
164                                 value = [value base64EncodingWithLineLength:0];
165                                 if( [(NSString *)value length] )
166                                         msgStr = [[NSString stringWithFormat:@"<%@ encoding=\"base64\">%@</%@>", key, value, key] UTF8String];
167                         }
168
169                         if( ! msgStr ) msgStr = [[NSString stringWithFormat:@"<%@ />", key] UTF8String];
170
171                         msgDoc = xmlParseMemory( msgStr, strlen( msgStr ) );
172                         child = xmlDocCopyNode( xmlDocGetRootElement( msgDoc ), _doc, 1 );
173                         xmlAddChild( root, child );
174                         xmlFreeDoc( msgDoc );
175                 }
176
177                 _node = root;
178         }
179
180         return _node;
181 }
182
183 - (void) _setNode:(xmlNode *) node {
184         if( _doc ) {
185                 xmlFreeDoc( _doc );
186                 _doc = NULL;
187         }
188
189         _node = node;
190 }
191
192 #pragma mark -
193
194 - (JVChatTranscript *) transcript {
195         return _transcript;
196 }
197
198 - (NSString *) eventIdentifier {
199         return _eventIdentifier;
200 }
201
202 #pragma mark -
203
204 - (NSDate *) date {
205         [self loadSmall];
206         return _date;
207 }
208
209 - (NSString *) name {
210         [self loadSmall];
211         return _name;
212 }
213
214 #pragma mark -
215
216 - (NSTextStorage *) message {
217         [self loadMessage];
218         return _message;
219 }
220
221 - (NSString *) messageAsPlainText {
222         return [[self message] string];
223 }
224
225 - (NSString *) messageAsHTML {
226         NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:YES], @"IgnoreFonts", [NSNumber numberWithBool:YES], @"IgnoreFontSizes", nil];
227         return [[self message] HTMLFormatWithOptions:options];
228 }
229
230 #pragma mark -
231
232 - (NSDictionary *) attributes {
233         [self loadAttributes];
234         return _attributes;
235 }
236 @end
237
238 #pragma mark -
239
240 @implementation JVMutableChatEvent
241 + (id) chatEventWithName:(NSString *) name andMessage:(id) message {
242         return [[[self alloc] initWithName:name andMessage:message] autorelease];
243 }
244
245 #pragma mark -
246
247 - (id) init {
248         if( ( self = [super init] ) ) {
249                 _loadedMessage = YES;
250                 _loadedAttributes = YES;
251                 _loadedSmall = YES;
252                 [self setDate:[NSDate date]];
253                 [self setEventIdentifier:[NSString locallyUniqueString]];
254         }
255
256         return self;
257 }
258
259 - (id) initWithName:(NSString *) name andMessage:(id) message {
260         if( ( self = [self init] ) ) {
261                 [self setName:name];
262                 [self setMessage:message];
263         }
264
265         return self;
266 }
267
268 #pragma mark -
269
270 - (void) setDate:(NSDate *) date {
271         [self _setNode:NULL];
272         [_date autorelease];
273         _date = [date copyWithZone:[self zone]];
274 }
275
276 - (void) setName:(NSString *) name {
277         [self _setNode:NULL];
278         [_name autorelease];
279         _name = [name copyWithZone:[self zone]];
280 }
281
282 #pragma mark -
283
284 - (void) setMessage:(id) message {
285         [self _setNode:NULL];
286         if( ! _message ) {
287                 if( [message isKindOfClass:[NSTextStorage class]] ) _message = [message retain];
288                 else if( [message isKindOfClass:[NSAttributedString class]] ) _message = [[NSTextStorage alloc] initWithAttributedString:message];
289                 else if( [message isKindOfClass:[NSString class]] ) _message = [[NSTextStorage alloc] initWithXHTMLFragment:(NSString *)message baseURL:nil defaultAttributes:nil];
290         } else if( _message && [message isKindOfClass:[NSAttributedString class]] ) {
291                 [_message setAttributedString:message];
292         } else if( _message && [message isKindOfClass:[NSString class]] ) {
293                 id string = [NSAttributedString attributedStringWithXHTMLFragment:(NSString *)message baseURL:nil defaultAttributes:nil];
294                 [_message setAttributedString:string];
295         }
296 }
297
298 - (void) setMessageAsPlainText:(NSString *) message {
299         [self setMessage:[[[NSAttributedString alloc] initWithString:message] autorelease]];
300 }
301
302 - (void) setMessageAsHTML:(NSString *) message {
303         [self setMessage:message];
304 }
305
306 #pragma mark -
307
308 - (void) setAttributes:(NSDictionary *) attributes {
309         [self _setNode:NULL];
310         [_attributes autorelease];
311         _attributes = [attributes copyWithZone:[self zone]];
312 }
313
314 #pragma mark -
315
316 - (void) setEventIdentifier:(NSString *) identifier {
317         [self _setNode:NULL];
318         [_eventIdentifier autorelease];
319         _eventIdentifier = [identifier copyWithZone:[self zone]];
320 }
321 @end
Note: See TracBrowser for help on using the browser.