-
Notifications
You must be signed in to change notification settings - Fork 644
/
XMLBasedOutputGenerator.m
639 lines (548 loc) · 24.1 KB
/
XMLBasedOutputGenerator.m
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
//
// XMLBasedOutputGenerator.m
// appledoc
//
// Created by Tomaz Kragelj on 28.5.09.
// Copyright (C) 2009, Tomaz Kragelj. All rights reserved.
//
#import "OutputGenerator.h"
#import "XMLBasedOutputGenerator+GeneralParsingAPI.h"
#import "XMLBasedOutputGenerator+ObjectParsingAPI.h"
#import "XMLBasedOutputGenerator+ObjectSubclassAPI.h"
#import "XMLBasedOutputGenerator+IndexParsingAPI.h"
#import "XMLBasedOutputGenerator+IndexSubclassAPI.h"
#import "XMLBasedOutputGenerator+HierarchyParsingAPI.h"
#import "XMLBasedOutputGenerator+HierarchySubclassAPI.h"
#import "CommandLineParser.h"
#import "LoggingProvider.h"
#import "Systemator.h"
#import "Constants.h"
//////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////
/** Defines private methods for use within @c XMLBasedOutputGenerator class only.
These are helper methods to make the main @c generateOutput() less cluttered. The methods
should only be used internaly by the @c XMLBasedOutputGenerator, they are not intended to
be used by the subclasses. Therefore the parameters are closely coupled to the underlying
clean object markup which is XML.
*/
@interface XMLBasedOutputGenerator ()
//////////////////////////////////////////////////////////////////////////////////////////
/// @name Object generation helpers
//////////////////////////////////////////////////////////////////////////////////////////
/** Generates the object info section if necessary.
From here the following messages are sent to the subclass:
- @c appendObjectInfoHeaderToData:()
- @c appendObjectInfoFooterToData:()
@param data The @c NSMutableData to append to.
@exception NSException Thrown if generation fails.
@see generateOutputForObject
@see generateOverviewSectionToData:
@see generateTasksSectionToData:
@see generateObjectMembersSectionToData:
@see generateObjectInfoSectionToData:fromNodes:index:type:
*/
- (void) generateObjectInfoSectionToData:(NSMutableData*) data;
/** Generates the given object info section if necessary.
This is sent from @c generateObjectInfoSectionToData:() for each info section item. From here
the following message is sent to the subclass.
- @c appendObjectInfoItemToData:fromItems:index:type:()
The message is only send if the given @c nodes array is not empty. The @c type parameter
can be one of the following:
- @c kTKObjectInfoItemInherits: The @c nodes contain inherit from information.
- @c kTKObjectInfoItemConforms: The @c nodes contain conforms to information.
- @c kTKObjectInfoItemDeclared: The @c nodex contain declared in information.
@param data The @c NSMutableData to append to.
@param nodes The array of @c NSXMLElement instances to append to.
@param index Pointer to zero based index of the section item. The method will increment
if the given @c nodes is not empty.
@param type Type of the section item.
@exception NSException Thrown if generation fails.
@see generateObjectInfoSectionToData:
*/
- (void) generateObjectInfoSectionToData:(NSMutableData*) data
fromNodes:(NSArray*) nodes
index:(int*) index
type:(int) type;
/** Generates the object overview data if necessary.
This is where the following messages are sent to the subclass:
- @c appendObjectOverviewToData:fromItem:()
@param data The @c NSMutableData to append to.
@exception NSException Thrown if generation fails.
@see generateOutputForObject
@see generateObjectInfoSectionToData:
@see generateTasksSectionToData:
@see generateObjectMembersSectionToData:
*/
- (void) generateObjectOverviewSectionToData:(NSMutableData*) data;
/** Generates the tasks section data if necessary.
This is where the following messages are sent to the subclass:
- @c appendObjectTasksHeaderToData:()
- @c appendObjectTaskHeaderToData:fromItem:index:()
- @c appendObjectTaskMemberToData:fromItem:index:()
- @c appendObjectTaskFooterToData:fromItem:index:()
- @c appendObjectTasksFooterToData:()
@param data The @c NSMutableData to append to.
@exception NSException Thrown if generation fails.
@see generateOutputForObject
@see generateObjectInfoSectionToData:
@see generateOverviewSectionToData:
@see generateObjectMembersSectionToData:
*/
- (void) generateObjectTasksSectionToData:(NSMutableData*) data;
/** Generates the main members documentation section if necessary.
This is where the following messages are sent to the subclass:
- @c appendObjectMembersHeaderToData:()
- @c appendObjectMembersFooterToData:()
@param data The @c NSMutableData to append to.
@exception NSException Thrown if generation fails.
@see generateOutputForObject
@see generateObjectInfoSectionToData:
@see generateOverviewSectionToData:
@see generateTasksSectionToData:
@see generateMemberSectionToData:fromItems:type:
*/
- (void) generateObjectMembersSectionToData:(NSMutableData*) data;
/** Generates the given main members documentation section.
This is sent from @c generateObjectMembersSectionToData:() for each group of members that
has at least one documented entry. This is where the following messages are sent to the
subclass:
- @c appendIndexGroupHeaderToData:type:()
- @c appendIndexGroupItemToData:fromItem:index:type:()
- @c appendIndexGroupFooterToData:type:()
The @c type parameter can be one of the following:
- @c kTKIndexGroupClasses: This group will append all classes.
- @c kTKIndexGroupProtocols: This group will append all protocols.
- @c kTKIndexGroupCategories: This group will append all categories.
@param data The @c NSMutableData to append to.
@param nodes The array of @c NSXMLElement instances representing individual members.
@param type The type of the instances.
@exception NSException Thrown if generation fails.
@see generateObjectMembersSectionToData:
*/
- (void) generateObjectMemberSectionToData:(NSMutableData*) data
fromNodes:(NSArray*) nodes
type:(int) type;
//////////////////////////////////////////////////////////////////////////////////////////
/// @name Index generation helpers
//////////////////////////////////////////////////////////////////////////////////////////
/** Generates the index groups documentation sections.
This is sent from @c generateIndexGroupSectionsToData:(). It collects the group data
and then sends @c generateIndexGroupSectionToData:fromNodes:type:() for each detected
group.
@param data The @c NSMutableData to append to.
@exception NSException Thrown if generation fails.
@see generateOutputForIndex
@see generateIndexGroupSectionToData:fromNodes:type:
*/
- (void) generateIndexGroupSectionsToData:(NSMutableData*) data;
/** Generates the given main members documentation section.
This is sent from @c generateIndexGroupSectionsToData:() for each group that has at
least one member. This is where the following messages are sent to the subclass:
- @c appendIndexGroupHeaderToData:type:()
- @c appendIndexGroupItemToData:fromItem:index:type:()
- @c appendIndexGroupFooterToData:type:()
The @c type parameter can be one of the following:
- @c kTKObjectMemberTypeClass: The @c nodes describes class members.
- @c kTKObjectMemberTypeInstance: The @c nodes describes instance members.
- @c kTKObjectMemberTypeProperty: The @c nodes describes properties.
@param data The @c NSMutableData to append to.
@param nodes The array of @c NSXMLElement instances representing individual members.
@param type The type of the instances.
@exception NSException Thrown if generation fails.
@see generateObjectMembersSectionToData:
*/
- (void) generateIndexGroupSectionToData:(NSMutableData*) data
fromNodes:(NSArray*) nodes
type:(int) type;
//////////////////////////////////////////////////////////////////////////////////////////
/// @name Various helper methods
//////////////////////////////////////////////////////////////////////////////////////////
/** Converts all common placeholders in the given clean XML.
The method eventually returns new @c NSXMLDocument instance which can be used for
output generation.
@param document The original clean XML.
@return Returns new autoreleased @c NSXMLDocument with all common placeholders replaced.
@exception NSException Thrown if convertion fails.
*/
- (NSXMLDocument*) markupByConvertingPlaceholdersInTemplate:(NSXMLDocument*) document;
@end
//////////////////////////////////////////////////////////////////////////////////////////
#pragma mark -
//////////////////////////////////////////////////////////////////////////////////////////
@implementation XMLBasedOutputGenerator
//////////////////////////////////////////////////////////////////////////////////////////
#pragma mark Output generation entry points
//////////////////////////////////////////////////////////////////////////////////////////
//----------------------------------------------------------------------------------------
- (void) outputGenerationStarting
{
outputFileWasCreated = NO;
}
//----------------------------------------------------------------------------------------
- (void) generateSpecificOutput
{
NSAutoreleasePool* loopAutoreleasePool = nil;
@try
{
// Convert the objects markup to final product.
NSDictionary* objects = [database objectForKey:kTKDataMainObjectsKey];
for (NSString* objectName in objects)
{
[loopAutoreleasePool drain];
loopAutoreleasePool = [[NSAutoreleasePool alloc] init];
NSDictionary* data = [objects objectForKey:objectName];
[self generateOutputForObject:data];
}
// Convert the index and hierarchy markup to final product.
[self generateOutputForIndex];
[self generateOutputForHierarchy];
}
@finally
{
[loopAutoreleasePool drain];
}
}
//////////////////////////////////////////////////////////////////////////////////////////
#pragma mark Objects output generation
//////////////////////////////////////////////////////////////////////////////////////////
//----------------------------------------------------------------------------------------
- (void) generateOutputForObject:(NSDictionary*) data
{
NSParameterAssert(data != nil);
@try
{
// Assign the parameters and replace all placeholder templates before starting
// parsing. Note that this will generate a new NSXMLDocument which we retain
// through the parsing loop and release at the end.
objectData = data;
objectMarkup = [objectData objectForKey:kTKDataObjectMarkupKey];
objectMarkup = [[self markupByConvertingPlaceholdersInTemplate:objectMarkup] retain];
// Generate the data.
logVerbose(@"- Generating output for object '%@'...", self.objectName);
NSMutableData* result = [NSMutableData data];
[self appendObjectHeaderToData:result];
[self generateObjectInfoSectionToData:result];
[self generateObjectOverviewSectionToData:result];
[self generateObjectTasksSectionToData:result];
[self generateObjectMembersSectionToData:result];
[self appendObjectFooterToData:result];
// Save the data.
if (result && [result length] > 0)
{
NSString* relativePath = [objectData objectForKey:kTKDataObjectRelPathKey];
NSString* filename = [[self outputBasePath] stringByAppendingPathComponent:relativePath];
NSString* extension = [self outputFilesExtension];
filename = [filename stringByReplacingOccurrencesOfString:kTKPlaceholderExtension withString:extension];
logDebug(@" - Saving object output to '%@'...", filename);
if (![result writeToFile:filename atomically:NO])
{
NSString* message = [NSString stringWithFormat:@"Failed saving object output to '%@'!", filename];
logError(@"Failed saving clean object output!");
[Systemator throwExceptionWithName:kTKConverterException withDescription:message];
}
outputFileWasCreated = YES;
}
}
@finally
{
[objectMarkup release];
objectData = nil;
}
}
//----------------------------------------------------------------------------------------
- (void) generateObjectInfoSectionToData:(NSMutableData*) data
{
// Get all nodes that describe the object information.
NSArray* baseNodes = [self.objectMarkup nodesForXPath:@"object/base" error:nil];
NSArray* protocolNodes = [self.objectMarkup nodesForXPath:@"object/protocol" error:nil];
NSArray* fileNodes = [self.objectMarkup nodesForXPath:@"object/file" error:nil];
// If at least one is present, continue.
if ([baseNodes count] > 0 || [protocolNodes count] > 0 || [fileNodes count] > 0)
{
int index = 0;
[self appendObjectInfoHeaderToData:data];
[self generateObjectInfoSectionToData:data fromNodes:baseNodes index:&index type:kTKObjectInfoItemInherits];
[self generateObjectInfoSectionToData:data fromNodes:protocolNodes index:&index type:kTKObjectInfoItemConforms];
[self generateObjectInfoSectionToData:data fromNodes:fileNodes index:&index type:kTKObjectInfoItemDeclared];
[self appendObjectInfoFooterToData:data];
}
}
//----------------------------------------------------------------------------------------
- (void) generateObjectInfoSectionToData:(NSMutableData*) data
fromNodes:(NSArray*) items
index:(int*) index
type:(int) type;
{
if ([items count] > 0)
{
[self appendObjectInfoItemToData:data fromItems:items index:*index type:type];
(*index)++;
}
}
//----------------------------------------------------------------------------------------
- (void) generateObjectOverviewSectionToData:(NSMutableData*) data
{
// Append the object overview if the object is documented. Note that we only take
// the first description node if there are more (shouldn't happen, but just in case).
NSArray* overviewNodes = [self.objectMarkup nodesForXPath:@"object/description" error:nil];
if ([overviewNodes count] > 0)
{
NSXMLElement* overviewNode = [overviewNodes objectAtIndex:0];
[self appendObjectOverviewToData:data fromItem:overviewNode];
}
}
//----------------------------------------------------------------------------------------
- (void) generateObjectTasksSectionToData:(NSMutableData*) data
{
// Appends the tasks with short method descriptions if at least one section is
// found with at least one member. Note that the sections are only handled if there's
// at least one member, otherwise these messages are not sent, even if (empty)
// sections are encountered.
NSArray* sectionNodes = [self.objectMarkup nodesForXPath:@"object/sections/section" error:nil];
if ([sectionNodes count] > 0)
{
BOOL sectionsHandled = NO;
for (int i = 0; i < [sectionNodes count]; i++)
{
NSXMLElement* sectionNode = [sectionNodes objectAtIndex:i];
NSArray* memberNodes = [sectionNode nodesForXPath:@"member" error:nil];
if ([memberNodes count] > 0)
{
// Append the sections header if not yet.
if (!sectionsHandled)
{
[self appendObjectTasksHeaderToData:data];
sectionsHandled = YES;
}
// Append section header.
[self appendObjectTaskHeaderToData:data fromItem:sectionNode index:i];
// Process all section members.
for (int n = 0; n < [memberNodes count]; n++)
{
NSXMLElement* memberNode = [memberNodes objectAtIndex:n];
[self appendObjectTaskMemberToData:data fromItem:memberNode index:n];
}
// Append section footer.
[self appendObjectTaskFooterToData:data fromItem:sectionNode index:i];
}
}
// Append sections footer.
if (sectionsHandled) [self appendObjectTasksFooterToData:data];
}
}
//----------------------------------------------------------------------------------------
- (void) generateObjectMembersSectionToData:(NSMutableData*) data
{
// Get the lists of all member nodes for each member type.
NSArray* classMethodNodes = [self.objectMarkup
nodesForXPath:@"object/sections/section/member[@kind='class-method']"
error:nil];
NSArray* instanceMethodNodes = [self.objectMarkup
nodesForXPath:@"object/sections/section/member[@kind='instance-method']"
error:nil];
NSArray* propertyNodes = [self.objectMarkup
nodesForXPath:@"object/sections/section/member[@kind='property']"
error:nil];
// Append the main members documentation descriptions if at least one member group
// is found with at least one documented member.
if ([classMethodNodes count] > 0 ||
[instanceMethodNodes count] > 0 ||
[propertyNodes count] > 0)
{
// Ask the subclass to append members documentation header.
[self appendObjectMembersHeaderToData:data];
// Process all lists.
[self generateObjectMemberSectionToData:data fromNodes:classMethodNodes type:kTKObjectMemberTypeClass];
[self generateObjectMemberSectionToData:data fromNodes:instanceMethodNodes type:kTKObjectMemberTypeInstance];
[self generateObjectMemberSectionToData:data fromNodes:propertyNodes type:kTKObjectMemberTypeProperty];
// Ask the subclass to append members documentation footer.
[self appendObjectMembersFooterToData:data];
}
}
//----------------------------------------------------------------------------------------
- (void) generateObjectMemberSectionToData:(NSMutableData*) data
fromNodes:(NSArray*) nodes
type:(int) type
{
if ([nodes count] > 0)
{
// Ask the subclass to append members group header.
[self appendObjectMemberGroupHeaderToData:data type:type];
// Ask the subclass to document all members of this group.
for (int i = 0; i < [nodes count]; i++)
{
NSXMLElement* node = [nodes objectAtIndex:i];
[self appendObjectMemberToData:data fromItem:node index:i];
}
// Ask the subclass to append members group footer.
[self appendObjectMemberGroupFooterToData:data type:type];
}
}
//////////////////////////////////////////////////////////////////////////////////////////
#pragma mark Index output generation handling
//////////////////////////////////////////////////////////////////////////////////////////
//----------------------------------------------------------------------------------------
- (void) generateOutputForIndex
{
@try
{
// Assign the parameters and replace all placeholder templates.
indexMarkup = [database objectForKey:kTKDataMainIndexKey];
indexMarkup = [[self markupByConvertingPlaceholdersInTemplate:indexMarkup] retain];
// Generate the data.
logVerbose(@"- Generating output for index...");
NSMutableData* result = [NSMutableData data];
[self appendIndexHeaderToData:result];
[self generateIndexGroupSectionsToData:result];
[self appendIndexFooterToData:result];
// Save the data.
if (result && [result length] > 0)
{
NSString* filename = [[self outputBasePath] stringByAppendingPathComponent:@"index"];
filename = [filename stringByAppendingString:[self outputFilesExtension]];
logDebug(@" - Saving index output to '%@'...", filename);
if (![result writeToFile:filename atomically:NO])
{
NSString* message = [NSString stringWithFormat:@"Failed saving index output to '%@'!", filename];
logError(@"Failed saving clean index output!");
[Systemator throwExceptionWithName:kTKConverterException withDescription:message];
}
outputFileWasCreated = YES;
}
}
@finally
{
[indexMarkup release];
}
}
//----------------------------------------------------------------------------------------
- (void) generateIndexGroupSectionsToData:(NSMutableData*) data
{
NSArray* classNodes = [self.indexMarkup nodesForXPath:@"project/object[@kind='class']" error:nil];
NSArray* categoryNodes = [self.indexMarkup nodesForXPath:@"project/object[@kind='category']" error:nil];
NSArray* protocolNodes = [self.indexMarkup nodesForXPath:@"project/object[@kind='protocol']" error:nil];
[self generateIndexGroupSectionToData:data
fromNodes:classNodes
type:kTKIndexGroupClasses];
[self generateIndexGroupSectionToData:data
fromNodes:protocolNodes
type:kTKIndexGroupProtocols];
[self generateIndexGroupSectionToData:data
fromNodes:categoryNodes
type:kTKIndexGroupCategories];
}
//----------------------------------------------------------------------------------------
- (void) generateIndexGroupSectionToData:(NSMutableData*) data
fromNodes:(NSArray*) nodes
type:(int) type
{
if ([nodes count] > 0)
{
[self appendIndexGroupHeaderToData:data type:type];
for (int i = 0; i < [nodes count]; i++)
{
NSXMLElement* node = [nodes objectAtIndex:i];
[self appendIndexGroupItemToData:data
fromItem:node
index:i
type:type];
}
[self appendIndexGroupFooterToData:data type:type];
}
}
//////////////////////////////////////////////////////////////////////////////////////////
#pragma mark Hierarchy output generation handling
//////////////////////////////////////////////////////////////////////////////////////////
//----------------------------------------------------------------------------------------
- (void) generateOutputForHierarchy
{
@try
{
// Assign the parameters and replace all placeholder templates.
hierarchyMarkup = [database objectForKey:kTKDataMainHierarchyKey];
hierarchyMarkup = [[self markupByConvertingPlaceholdersInTemplate:hierarchyMarkup] retain];
// Generate the data. Note that we could use the hierarchies data directly
// instead of relying on the generated XML and it would probably be more optimized
// too. However using XML ensures consistent parsing code with the rest of the
// generation methods and perhaps even more important - it uses the same data
// that external utilities might use if they are only interested in generating
// the XML from appledoc.
logVerbose(@"- Generating output for hierarchy...");
NSMutableData* result = [NSMutableData data];
NSArray* rootNodes = [self.hierarchyMarkup nodesForXPath:@"project/object" error:nil];
if ([rootNodes count] > 0)
{
// Generate the data.
[self appendHierarchyHeaderToData:result];
[self appendHierarchyGroupHeaderToData:result];
for (int i = 0; i < [rootNodes count]; i++)
{
NSXMLElement* rootNode = [rootNodes objectAtIndex:i];
[self appendHierarchyGroupItemToData:result
fromItem:rootNode
index:i];
}
[self appendHierarchyGroupFooterToData:result];
[self appendHierarchyFooterToData:result];
// Save the data.
if (result && [result length] > 0)
{
NSString* filename = [[self outputBasePath] stringByAppendingPathComponent:@"hierarchy"];
filename = [filename stringByAppendingString:[self outputFilesExtension]];
logDebug(@" - Saving hierarchy output to '%@'...", filename);
if (![result writeToFile:filename atomically:NO])
{
NSString* message = [NSString stringWithFormat:@"Failed saving hierarchy output to '%@'!", filename];
logError(@"Failed saving clean hierarchy output!");
[Systemator throwExceptionWithName:kTKConverterException withDescription:message];
}
outputFileWasCreated = YES;
}
}
}
@finally
{
[hierarchyMarkup release];
}
}
//////////////////////////////////////////////////////////////////////////////////////////
#pragma mark Common helper methods
//////////////////////////////////////////////////////////////////////////////////////////
//----------------------------------------------------------------------------------------
- (NSXMLDocument*) markupByConvertingPlaceholdersInTemplate:(NSXMLDocument*) document
{
// The handling is simple, but resource intensive - convert the XML to string,
// replace all placeholder occurences and re-create the new document from the
// resulting string.
NSString* string = [document XMLString];
string = [self pathByReplacingTemplatePlaceholdersInPath:string];
// Create the new XMl document.
NSError* error = nil;
NSXMLDocument* result = [[[NSXMLDocument alloc] initWithXMLString:string
options:0
error:&error] autorelease];
if (error)
{
logError(@"Converting placeholders XML failed!");
[Systemator throwExceptionWithName:kTKConverterException basedOnError:error];
}
// Return the new document.
return result;
}
//////////////////////////////////////////////////////////////////////////////////////////
#pragma mark Properties
//////////////////////////////////////////////////////////////////////////////////////////
//----------------------------------------------------------------------------------------
- (NSString*) projectName
{
return cmd.projectName;
}
//----------------------------------------------------------------------------------------
- (NSString*) lastUpdated
{
NSCalendarDate* now = [NSCalendarDate date];
return [now descriptionWithCalendarFormat:cmd.dateTimeTemplate];
}
//----------------------------------------------------------------------------------------
@synthesize outputFileWasCreated;
@end