]> err.no Git - sope/blob - sope-xml/ChangeLogSaxDriver/ChangeLogSaxDriver.m
lF fixes
[sope] / sope-xml / ChangeLogSaxDriver / ChangeLogSaxDriver.m
1 /*
2   Copyright (C) 2004 Marcus Mueller <znek@mulle-kybernetik.com>
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 "ChangeLogSaxDriver.h"
23 #include "NSString+Extensions.h"
24 #include "NSCalendarDate+Extensions.h"
25 #include "common.h"
26
27 @interface ChangeLogSaxDriver(PrivateAPI)
28 - (NSString *)_namespace;
29 - (void)_writeString:(NSString *)_s;
30 - (void)_processLine:(NSString *)_line;
31 - (void)_parseFromString:(NSString *)_str systemId:(NSString *)_sysId;
32
33 - (void)_beginEntryWithDate:(NSCalendarDate *)_date;
34 - (void)_endEntry;
35 - (void)_beginLogs;
36 - (void)_endLogs;
37 - (void)_beginLog;
38 - (void)_appendLog:(NSString *)_s;
39 - (void)_endLog;
40 @end
41
42 @implementation ChangeLogSaxDriver
43
44 static NSCharacterSet *wsSet   = nil;
45 static NSCharacterSet *wsnlSet = nil;
46
47 + (void)initialize {
48   static BOOL didInit = NO;
49   
50   if(didInit) return;
51   didInit = YES;
52   wsSet   = [[NSCharacterSet whitespaceCharacterSet] retain];
53   wsnlSet = [[NSCharacterSet whitespaceAndNewlineCharacterSet] retain];
54 }
55
56 - (void)dealloc {
57   [self->contentHandler release];
58   [self->errorHandler release];
59   [self->namespace release];
60   [super dealloc];
61 }
62
63 /* properties */
64
65 - (void)setProperty:(NSString *)_name to:(id)_value {
66   return;
67   [SaxNotRecognizedException raise:@"PropertyException"
68                             format:@"don't know property %@", _name];
69 }
70 - (id)property:(NSString *)_name {
71   return nil;
72   [SaxNotRecognizedException raise:@"PropertyException"
73                             format:@"don't know property %@", _name];
74   return nil;
75 }
76
77
78 /* features */
79
80 - (void)setFeature:(NSString *)_name to:(BOOL)_value {
81   if ([_name isEqualToString:@"http://xml.org/sax/features/namespaces"]) {
82     self->fNamespaces = _value;
83     return;
84   }
85   
86   if ([_name isEqualToString:
87                       @"http://xml.org/sax/features/namespace-prefixes"]) {
88     self->fNamespacePrefixes = _value;
89     return;
90   }
91   
92   [SaxNotRecognizedException raise:@"FeatureException"
93                             format:@"don't know feature %@", _name];
94 }
95 - (BOOL)feature:(NSString *)_name {
96   if ([_name isEqualToString:@"http://xml.org/sax/features/namespaces"])
97     return self->fNamespaces;
98   
99   if ([_name isEqualToString:
100                       @"http://xml.org/sax/features/namespace-prefixes"])
101     return self->fNamespacePrefixes;
102   
103   if ([_name isEqualToString:
104                       @"http://www.skyrix.com/sax/features/predefined-namespaces"])
105     return NO;
106   
107   [SaxNotRecognizedException raise:@"FeatureException"
108                             format:@"don't know feature %@", _name];
109   return NO;
110 }
111
112
113 /* handlers */
114
115 /*
116  - (void)setDocumentHandler:(id<NSObject,SaxDocumentHandler>)_handler {
117    SaxDocumentHandlerAdaptor *a;
118    
119    a = [[SaxDocumentHandlerAdaptor alloc] initWithDocumentHandler:_handler];
120    [self setContentHandler:a];
121    [a release];
122  }
123  */
124
125 - (void)setDTDHandler:(id<NSObject,SaxDTDHandler>)_handler {
126 }
127 - (id<NSObject,SaxDTDHandler>)dtdHandler {
128   return nil;
129 }
130
131 - (void)setErrorHandler:(id<NSObject,SaxErrorHandler>)_handler {
132   ASSIGN(self->errorHandler, _handler);
133 }
134 - (id<NSObject,SaxErrorHandler>)errorHandler {
135   return self->errorHandler;
136 }
137
138 - (void)setEntityResolver:(id<NSObject,SaxEntityResolver>)_handler {
139 }
140 - (id<NSObject,SaxEntityResolver>)entityResolver {
141   return nil;
142 }
143
144 - (void)setContentHandler:(id<NSObject,SaxContentHandler>)_handler {
145   ASSIGN(self->contentHandler, _handler);
146 }
147 - (id<NSObject,SaxContentHandler>)contentHandler {
148   return self->contentHandler;
149 }
150
151
152 /* parsing ... */
153
154 - (void)parseFromSource:(id)_source systemId:(NSString *)_sysId {
155   NSAutoreleasePool *pool;
156   
157   pool = [[NSAutoreleasePool alloc] init];
158   
159   if ([_source isKindOfClass:[NSData class]]) {
160     NSString *s;
161     
162     s = [[NSString alloc] initWithData:_source
163                               encoding:[NSString defaultCStringEncoding]];
164     [self _parseFromString:s systemId:_sysId];
165     [s release];
166   }
167   else if ([_source isKindOfClass:[NSURL class]]) {
168     [self parseFromSystemId:_source];
169   }
170   else if ([_source isKindOfClass:[NSString class]]) {
171     if (_sysId == nil) _sysId = @"<string>";
172     [self _parseFromString:_source systemId:_sysId];
173   }
174   else {
175     SaxParseException *e;
176     NSDictionary      *ui;
177     
178     ui = [NSDictionary dictionaryWithObjectsAndKeys:
179                                  _source ? _source : @"<nil>", @"source",
180       self,                         @"parser",
181       nil];
182     
183     e = (id)[SaxParseException exceptionWithName:@"SaxIOException"
184                                           reason:@"can't handle data-source"
185                                         userInfo:ui];
186     
187     [self->errorHandler fatalError:e];
188   }
189   
190   [pool release];
191 }
192 - (void)parseFromSource:(id)_source {
193   [self parseFromSource:_source systemId:@"<memory>"];
194 }
195
196 - (void)parseFromSystemId:(NSString *)_sysId {
197   NSAutoreleasePool *pool;
198   NSString *str;
199   NSURL    *url;
200   
201   pool = [[NSAutoreleasePool alloc] init];
202   
203   url = [NSURL URLWithString:_sysId];
204   str = [NSString stringWithContentsOfURL:url];
205   
206   [self _parseFromString:str systemId:_sysId];
207   
208   [pool release];
209 }
210
211
212 /* Private API */
213
214 - (void)_parseFromString:(NSString *)_s systemId:(NSString *)_sysId {
215   static SaxAttributes *versionAttr = nil;
216   NSArray *lines;
217   unsigned i, count;
218   
219   self->currentLog     = [[NSMutableString alloc] initWithCapacity:200];
220   self->flags.hasLog   = NO;
221   self->flags.hasEntry = NO;
222
223   if (versionAttr == nil) {
224     versionAttr = [[SaxAttributes alloc] init];
225     [versionAttr addAttribute:@"version"
226                  uri:[self _namespace]
227                  rawName:@"version"
228                  type:@"CDATA"
229                  value:@"1.0"];
230   }
231   
232   lines = [_s componentsSeparatedByString:@"\n"];
233   count = [lines count];
234   
235   [self->contentHandler startDocument];
236   [self->contentHandler startElement:@"changelog"
237                         namespace:[self _namespace]
238                         rawName:@"changelog"
239                         attributes:versionAttr];
240
241   for(i = 0; i < count; i++) {
242     [self _processLine:[lines objectAtIndex:i]];
243   }
244   [self _endEntry];
245   [self->contentHandler endElement:@"changelog"
246                         namespace:[self _namespace]
247                         rawName:@"changelog"];
248
249   [self->contentHandler endDocument];
250   [self->currentLog     release];
251 }
252
253 - (void)_processLine:(NSString *)_line {
254   if([_line length] > 0) {
255     unichar        first;
256     NSCalendarDate *date;
257     NSString       *author;
258
259     first = [_line characterAtIndex:0];
260     if(!(first == '*' ||
261          first == '-' ||
262          [wsSet characterIsMember:first]) &&
263        [_line parseDate:&date andAuthor:&author])
264     {
265       SaxAttributes *authorAttrs;
266       NSString      *realName, *email;
267
268       /* entry start */
269       [self _beginEntryWithDate:date];
270       /* author */
271       [author getRealName:&realName andEmail:&email];
272       if(!email)
273         email = @"";
274       authorAttrs = [[SaxAttributes alloc] init];
275       [authorAttrs addAttribute:@"email"
276                    uri:[self _namespace]
277                    rawName:@"email"
278                    type:@"CDATA"
279                    value:email];
280       [self->contentHandler startElement:@"author"
281                             namespace:[self _namespace]
282                             rawName:@"author"
283                             attributes:authorAttrs];
284       [authorAttrs release];
285       [self _writeString:realName];
286       [self->contentHandler endElement:@"author"
287                             namespace:[self _namespace]
288                             rawName:@"author"];
289     }
290     else {
291       /* strip leading whitespace and "*" from line */
292       _line = [_line stringByTrimmingLeadSpaces];
293       if([_line length] > 0) {
294         first = [_line characterAtIndex:0];
295
296         if(first == '*') {
297           /* new log line */
298           [self _beginLog];
299           _line = [_line substringFromIndex:1];
300           _line = [_line stringByTrimmingLeadSpaces];
301         }
302         [self _appendLog:_line];
303       }
304     }
305   }
306 }
307
308 - (void)_beginEntryWithDate:(NSCalendarDate *)_date {
309   SaxAttributes *entryAttrs;
310
311   [self _endEntry];
312
313   /* date */
314   entryAttrs = [[SaxAttributes alloc] init];
315   [entryAttrs addAttribute:@"date"
316               uri:[self _namespace]
317               rawName:@"date"
318               type:@"CDATA"
319               value:[_date w3OrgDateTimeRepresentation]];
320   [self->contentHandler startElement:@"entry"
321                         namespace:[self _namespace]
322                         rawName:@"entry"
323                         attributes:entryAttrs];
324   [entryAttrs release];
325   self->flags.hasEntry = YES;
326 }
327
328 - (void)_endEntry {
329   if(self->flags.hasEntry) {
330     [self _endLogs];
331     [self->contentHandler endElement:@"entry"
332                           namespace:[self _namespace]
333                           rawName:@"entry"];
334     self->flags.hasEntry = NO;
335   }
336 }
337
338 - (void)_beginLogs {
339   if(!self->flags.hasLog) {
340     [self->contentHandler startElement:@"logs"
341                           namespace:[self _namespace]
342                           rawName:@"logs"
343                           attributes:nil];
344     self->flags.hasLog = YES;
345   }
346 }
347
348 - (void)_endLogs {
349   if(self->flags.hasLog) {
350     [self _endLog];
351     [self->contentHandler endElement:@"logs"
352                           namespace:[self _namespace]
353                           rawName:@"logs"];
354     self->flags.hasLog = NO;
355   }
356 }
357
358
359 - (void)_beginLog {
360   [self _beginLogs];
361   [self _endLog];
362 }
363
364 - (void)_appendLog:(NSString *)_s {
365   unsigned loc;
366
367   if([_s length] == 0)
368     return;
369   loc = [self->currentLog length];
370   if(loc > 0) {
371     unichar last;
372     
373     last = [self->currentLog characterAtIndex:loc - 1];
374     if(![wsnlSet characterIsMember:last]) {
375       [self->currentLog appendString:@" "];
376     }
377   }
378   [self->currentLog appendString:_s];
379 }
380
381 - (void)_endLog {
382   NSRange r;
383
384   r = NSMakeRange(0, [self->currentLog length]);
385   if(r.length == 0)
386     return;
387
388   [self _beginLogs];
389   [self->contentHandler startElement:@"log"
390                         namespace:[self _namespace]
391                         rawName:@"log"
392                         attributes:nil];
393   [self _writeString:self->currentLog];
394   [self->contentHandler endElement:@"log"
395                         namespace:[self _namespace]
396                         rawName:@"log"];
397   [self->currentLog deleteCharactersInRange:r];
398 }
399
400 - (NSString *)_namespace {
401   if(!self->namespace)
402     return @"";
403   return self->namespace;
404 }
405
406 - (void)_writeString:(NSString *)_s {
407   unsigned len;
408   
409   if ((len = [_s length]) == 0) return;
410   
411   if (len == 1) {
412     unichar c[2];
413     [_s getCharacters:&(c[0])];
414     c[1] = '\0';
415     [self->contentHandler characters:&(c[0]) length:1];
416   }
417   else {
418     unichar *ca;
419     
420     ca = calloc(len + 1, sizeof(unichar));
421     [_s getCharacters:ca];
422     ca[len] = 0;
423     [self->contentHandler characters:ca length:len];
424     if (ca) free(ca);
425   }
426 }
427
428 @end /* ChangeLogSaxDriver */