Skip to content
Browse files

Copied all the changes from the old generating branch.

Something broke while copying the project over from the old computer, so the simplest way to fix this was to pull from GitHub and copy all the files from the local backup...
  • Loading branch information...
1 parent 9a499e4 commit 7d6a323e579ea6d44f1059a41e2f049daa2145bc @tomaz committed
Showing with 9,649 additions and 39 deletions.
  1. +2 −4 Application/GBAppledocApplication.h
  2. +16 −11 Application/GBAppledocApplication.m
  3. +25 −0 Application/GBApplicationSettingsProvider.h
  4. +186 −0 Application/GBApplicationSettingsProvider.m
  5. +101 −0 Application/GBApplicationSettingsProviding.h
  6. +44 −0 Application/GBApplicationStringsProvider.h
  7. +80 −0 Application/GBApplicationStringsProvider.m
  8. +3 −2 Common/GBLog.h
  9. +36 −8 Common/NSString+GBString.h
  10. +51 −10 Common/NSString+GBString.m
  11. 0 Common/ThirdParty/DDLog/DDLog.h
  12. 0 Common/ThirdParty/DDLog/DDLog.m
  13. +10 −0 Common/ThirdParty/MGTemplateEngine/DeepMutableCopy.h
  14. +44 −0 Common/ThirdParty/MGTemplateEngine/ICUTemplateMatcher.h
  15. +192 −0 Common/ThirdParty/MGTemplateEngine/ICUTemplateMatcher.m
  16. +105 −0 Common/ThirdParty/MGTemplateEngine/MGTemplateEngine.h
  17. +676 −0 Common/ThirdParty/MGTemplateEngine/MGTemplateEngine.m
  18. +14 −0 Common/ThirdParty/MGTemplateEngine/MGTemplateFilter.h
  19. +41 −0 Common/ThirdParty/MGTemplateEngine/MGTemplateMarker.h
  20. +15 −0 Common/ThirdParty/MGTemplateEngine/MGTemplateStandardFilters.h
  21. +56 −0 Common/ThirdParty/MGTemplateEngine/MGTemplateStandardFilters.m
  22. +24 −0 Common/ThirdParty/MGTemplateEngine/MGTemplateStandardMarkers.h
  23. +634 −0 Common/ThirdParty/MGTemplateEngine/MGTemplateStandardMarkers.m
  24. +12 −0 Common/ThirdParty/MGTemplateEngine/NSArray_DeepMutableCopy.h
  25. +42 −0 Common/ThirdParty/MGTemplateEngine/NSArray_DeepMutableCopy.m
  26. +12 −0 Common/ThirdParty/MGTemplateEngine/NSDictionary_DeepMutableCopy.h
  27. +43 −0 Common/ThirdParty/MGTemplateEngine/NSDictionary_DeepMutableCopy.m
  28. +56 −0 Generating/GBGenerator.h
  29. +156 −0 Generating/GBGenerator.m
  30. +98 −0 Generating/GBTemplateReader.h
  31. +136 −0 Generating/GBTemplateReader.m
  32. +87 −0 Generating/GBTemplateVariablesProvider.h
  33. +255 −0 Generating/GBTemplateVariablesProvider.m
  34. +64 −0 Generating/GBTemplateWriter.h
  35. +130 −0 Generating/GBTemplateWriter.m
  36. +0 −1 Libraries/GHUnit.framework/GHUnit
  37. BIN Libraries/GHUnit.framework/GHUnit
  38. +0 −1 Libraries/GHUnit.framework/Headers
  39. +0 −1 Libraries/GHUnit.framework/Resources
  40. 0 Libraries/GHUnit.framework/Versions/A/GHUnit
  41. +0 −1 Libraries/GHUnit.framework/Versions/Current
  42. BIN Libraries/GHUnit.framework/Versions/Current/GHUnit
  43. +43 −0 Libraries/GHUnit.framework/Versions/Current/Headers/BWSplitView.h
  44. +139 −0 Libraries/GHUnit.framework/Versions/Current/Headers/GHAsyncTestCase.h
  45. +46 −0 Libraries/GHUnit.framework/Versions/Current/Headers/GHMockNSHTTPURLResponse.h
  46. +162 −0 Libraries/GHUnit.framework/Versions/Current/Headers/GHMockNSURLConnection.h
  47. +132 −0 Libraries/GHUnit.framework/Versions/Current/Headers/GHNSInvocation+Utils.h
  48. +133 −0 Libraries/GHUnit.framework/Versions/Current/Headers/GHNSInvocationProxy.h
  49. +59 −0 Libraries/GHUnit.framework/Versions/Current/Headers/GHNSLocale+Mock.h
  50. +100 −0 Libraries/GHUnit.framework/Versions/Current/Headers/GHNSObject+Invocation.h
  51. +40 −0 Libraries/GHUnit.framework/Versions/Current/Headers/GHTest+JUnitXML.h
  52. +181 −0 Libraries/GHUnit.framework/Versions/Current/Headers/GHTest.h
  53. +23 −0 Libraries/GHUnit.framework/Versions/Current/Headers/GHTestApp.h
  54. +141 −0 Libraries/GHUnit.framework/Versions/Current/Headers/GHTestCase.h
  55. +38 −0 Libraries/GHUnit.framework/Versions/Current/Headers/GHTestGroup+JUnitXML.h
  56. +151 −0 Libraries/GHUnit.framework/Versions/Current/Headers/GHTestGroup.h
  57. +1,010 −0 Libraries/GHUnit.framework/Versions/Current/Headers/GHTestMacros.h
  58. +42 −0 Libraries/GHUnit.framework/Versions/Current/Headers/GHTestOperation.h
  59. +26 −0 Libraries/GHUnit.framework/Versions/Current/Headers/GHTestOutlineViewModel.h
  60. +159 −0 Libraries/GHUnit.framework/Versions/Current/Headers/GHTestRunner.h
  61. +114 −0 Libraries/GHUnit.framework/Versions/Current/Headers/GHTestSuite.h
  62. +103 −0 Libraries/GHUnit.framework/Versions/Current/Headers/GHTestViewController.h
  63. +163 −0 Libraries/GHUnit.framework/Versions/Current/Headers/GHTestViewModel.h
  64. +41 −0 Libraries/GHUnit.framework/Versions/Current/Headers/GHTestWindowController.h
  65. +144 −0 Libraries/GHUnit.framework/Versions/Current/Headers/GHTesting.h
  66. +36 −0 Libraries/GHUnit.framework/Versions/Current/Headers/GHUNSObject+Swizzle.h
  67. +51 −0 Libraries/GHUnit.framework/Versions/Current/Headers/GHUnit.h
  68. +105 −0 Libraries/GHUnit.framework/Versions/Current/Headers/GTMStackTrace.h
  69. +88 −0 Libraries/GHUnit.framework/Versions/Current/Headers/NSException+GHTestFailureExceptions.h
  70. +67 −0 Libraries/GHUnit.framework/Versions/Current/Headers/NSValue+GHValueFormatter.h
  71. BIN Libraries/GHUnit.framework/Versions/Current/Resources/English.lproj/InfoPlist.strings
  72. BIN Libraries/GHUnit.framework/Versions/Current/Resources/GHTestApp.nib
  73. BIN Libraries/GHUnit.framework/Versions/Current/Resources/GHTestView.nib
  74. BIN Libraries/GHUnit.framework/Versions/Current/Resources/GHTestWindow.nib
  75. +4 −0 Libraries/GHUnit.framework/Versions/Current/Resources/GHUnit-Debug.xcconfig
  76. +15 −0 Libraries/GHUnit.framework/Versions/Current/Resources/GHUnit.xcconfig
  77. BIN Libraries/GHUnit.framework/Versions/Current/Resources/GradientSplitViewDimpleBitmap.tif
  78. BIN Libraries/GHUnit.framework/Versions/Current/Resources/GradientSplitViewDimpleVector.pdf
  79. +22 −0 Libraries/GHUnit.framework/Versions/Current/Resources/Info.plist
  80. +43 −0 Libraries/GHUnit.framework/headers/BWSplitView.h
  81. +139 −0 Libraries/GHUnit.framework/headers/GHAsyncTestCase.h
  82. +46 −0 Libraries/GHUnit.framework/headers/GHMockNSHTTPURLResponse.h
  83. +162 −0 Libraries/GHUnit.framework/headers/GHMockNSURLConnection.h
  84. +132 −0 Libraries/GHUnit.framework/headers/GHNSInvocation+Utils.h
  85. +133 −0 Libraries/GHUnit.framework/headers/GHNSInvocationProxy.h
  86. +59 −0 Libraries/GHUnit.framework/headers/GHNSLocale+Mock.h
  87. +100 −0 Libraries/GHUnit.framework/headers/GHNSObject+Invocation.h
  88. +40 −0 Libraries/GHUnit.framework/headers/GHTest+JUnitXML.h
  89. +181 −0 Libraries/GHUnit.framework/headers/GHTest.h
  90. +23 −0 Libraries/GHUnit.framework/headers/GHTestApp.h
  91. +141 −0 Libraries/GHUnit.framework/headers/GHTestCase.h
  92. +38 −0 Libraries/GHUnit.framework/headers/GHTestGroup+JUnitXML.h
  93. +151 −0 Libraries/GHUnit.framework/headers/GHTestGroup.h
  94. +1,010 −0 Libraries/GHUnit.framework/headers/GHTestMacros.h
  95. +42 −0 Libraries/GHUnit.framework/headers/GHTestOperation.h
  96. +26 −0 Libraries/GHUnit.framework/headers/GHTestOutlineViewModel.h
  97. +159 −0 Libraries/GHUnit.framework/headers/GHTestRunner.h
Sorry, we could not display the entire diff because it was too big.
View
6 Application/GBAppledocApplication.h
@@ -14,12 +14,10 @@
This is the principal tool class. It represents the entry point for the application. The main promises of the class are parsing and validating of command line arguments and initiating documentation generation. Generation is divided into several distinct phases:
- 1. Parsing data from source files: This is the initial phase where input directories and files are parsed into a memory representation (i.e. objects) suitable for subsequent handling. This is where the source code files are parsed and validated for possible file or object-level incosistencies. This step is driven by `GBParser` class.
-
+ 1. Parsing data from source files: This is the initial phase where input directories and files are parsed into a memory representation (i.e. objects) suitable for subsequent handling. This is where the source code files are parsed and validated for possible file or object-level incosistencies. This step is driven by `GBParser` class.
2. Post-processing of the data parsed in the previous step: At this phase, we already have in-memory representation of all source code objects, so we can post-process and validate things such as links to other objects etc. We can also update in-memory representation with this data and therefore prepare everything for the final phase. This step is driven by `GBProcessor` class.
-
3. Generating output: This is the final phase where we use in-memory data to generate output. This step is driven by `GBGenerator` class.
*/
-@interface GBAppledocApplication : NSObject <GBApplicationSettingsProviding, DDCliApplicationDelegate>
+@interface GBAppledocApplication : NSObject <DDCliApplicationDelegate>
@end
View
27 Application/GBAppledocApplication.m
@@ -12,6 +12,8 @@
#import "GBStore.h"
#import "GBParser.h"
#import "GBProcessor.h"
+#import "GBGenerator.h"
+#import "GBApplicationSettingsProvider.h"
#import "GBAppledocApplication.h"
static NSString *kGBArgIgnorePath = @"ignore";
@@ -26,6 +28,7 @@ @interface GBAppledocApplication ()
- (void)initializeLoggingSystem;
- (void)validateArguments:(NSArray *)arguments;
+@property (retain) GBApplicationSettingsProvider *settings;
@property (assign) NSString *logformat;
@property (assign) NSString *verbose;
@property (assign) BOOL version;
@@ -52,10 +55,9 @@ @implementation GBAppledocApplication
- (id)init {
self = [super init];
if (self) {
+ self.settings = [GBApplicationSettingsProvider provider];
self.logformat = @"1";
self.verbose = @"2";
- self.ignoredPaths = [NSMutableSet set];
- self.commentComponents = [GBCommentComponentsProvider provider];
}
return self;
}
@@ -81,22 +83,25 @@ - (int)application:(DDCliApplication *)app runWithArguments:(NSArray *)arguments
GBAbsoluteTime startTime = GetCurrentTime();
GBLogNormal(@"Parsing source files...");
- GBParser *parser = [[GBParser alloc] initWithSettingsProvider:self];
+ GBParser *parser = [GBParser parserWithSettingsProvider:self.settings];
[parser parseObjectsFromPaths:arguments toStore:store];
GBAbsoluteTime parseTime = GetCurrentTime();
NSUInteger timeForParsing = SubtractTime(parseTime, startTime) * 1000.0;
+ GBLogInfo(@"Finished parsing in %ldms.\n", timeForParsing);
GBLogNormal(@"Processing parsed data...");
- GBProcessor *processor = [[GBProcessor alloc] initWithSettingsProvider:self];
+ GBProcessor *processor = [GBProcessor processorWithSettingsProvider:self.settings];
[processor processObjectsFromStore:store];
GBAbsoluteTime processTime = GetCurrentTime();
NSUInteger timeForProcessing = SubtractTime(processTime, parseTime) * 1000.0;
+ GBLogInfo(@"Finished processing in %ldms.\n", timeForProcessing);
-// GBLogNormal(@"Generating output...");
-// GBGenerator *generator = [[GBGenerator alloc] init];
-// [generator generateOutputFromProcessor:processor];
+ GBLogNormal(@"Generating output...");
+ GBGenerator *generator = [GBGenerator generatorWithSettingsProvider:self.settings];
+ [generator generateOutputFromStore:store];
GBAbsoluteTime generateTime = GetCurrentTime();
NSUInteger timeForGeneration = SubtractTime(generateTime, processTime) * 1000.0;
+ GBLogInfo(@"Finished generating in %ldms.\n", timeForGeneration);
NSUInteger timeForEverything = timeForParsing + timeForProcessing + timeForGeneration;
GBLogNormal(@"Finished in %ldms.", timeForEverything);
@@ -153,7 +158,7 @@ - (NSString *)description {
- (void)setIgnore:(NSString *)path {
if ([path hasPrefix:@"*"]) path = [path substringFromIndex:1];
- [self.ignoredPaths addObject:path];
+ [self.settings.ignoredPaths addObject:path];
}
@synthesize logformat;
@@ -161,10 +166,9 @@ - (void)setIgnore:(NSString *)path {
@synthesize version;
@synthesize help;
-#pragma mark Application settings provider implementation
+#pragma mark Properties
-@synthesize ignoredPaths;
-@synthesize commentComponents;
+@synthesize settings;
@end
@@ -193,6 +197,7 @@ - (void)printHelp {
ddprintf(@"- ParseKit by Todd Ditchendorf\n");
ddprintf(@"- RegexKitLite by John Engelhart\n");
ddprintf(@"- Timing functions from Apple examples\n");
+ ddprintf(@"- MGTemplateEnding by Matt Legend Gemmell\n");
ddprintf(@"\n");
ddprintf(@"We'd like to thank all authors for their contribution!");
}
View
25 Application/GBApplicationSettingsProvider.h
@@ -0,0 +1,25 @@
+//
+// GBApplicationSettingsProvider.h
+// appledoc
+//
+// Created by Tomaz Kragelj on 3.10.10.
+// Copyright (C) 2010, Gentle Bytes. All rights reserved.
+//
+
+#import "GBApplicationSettingsProviding.h"
+
+/** Main application settings provider.
+
+ This object implements `GBApplicationStringsProviding` interface and is used by `GBAppledocApplication` to prepare application-wide settings including factory defaults, global and session values. The main purpose of the class is to simplify `GBAppledocApplication` class by decoupling it from the actual settings providing implementation.
+ */
+@interface GBApplicationSettingsProvider : NSObject <GBApplicationSettingsProviding>
+
+///---------------------------------------------------------------------------------------
+/// @name Initialization & disposal
+///---------------------------------------------------------------------------------------
+
+/** Returns autoreleased instance of the class.
+ */
++ (id)provider;
+
+@end
View
186 Application/GBApplicationSettingsProvider.m
@@ -0,0 +1,186 @@
+//
+// GBApplicationSettingsProvider.m
+// appledoc
+//
+// Created by Tomaz Kragelj on 3.10.10.
+// Copyright (C) 2010, Gentle Bytes. All rights reserved.
+//
+
+#import "GBDataObjects.h"
+#import "GBApplicationSettingsProvider.h"
+
+@interface GBApplicationSettingsProvider ()
+
+- (BOOL)isTopLevelStoreObject:(id)object;
+- (NSString *)outputPathForObject:(id)object withExtension:(NSString *)extension;
+- (NSString *)relativePathPrefixFromObject:(GBModelBase *)source toObject:(GBModelBase *)destination;
+- (NSString *)htmlReferenceForTopLevelObject:(GBModelBase *)object fromTopLevelObject:(GBModelBase *)source;
+- (NSString *)htmlReferenceForMember:(GBModelBase *)member prefixedWith:(NSString *)prefix;
+- (NSString *)htmlExtension;
+
+@end
+
+#pragma mark -
+
+@implementation GBApplicationSettingsProvider
+
+#pragma mark Initialization & disposal
+
++ (id)provider {
+ return [[[self alloc] init] autorelease];
+}
+
+- (id)init {
+ self = [super init];
+ if (self) {
+ self.outputPath = @"~/Downloads/examples/objc/AppledocHtml";
+ self.templatesPath = @"~/Downloads/examples/objc/AppledocHtml";
+ self.ignoredPaths = [NSMutableSet set];
+ self.commentComponents = [GBCommentComponentsProvider provider];
+ self.stringTemplates = [GBApplicationStringsProvider provider];
+ }
+ return self;
+}
+
+#pragma mark Template paths handling
+
+- (NSString *)cssClassTemplatePath {
+ return @"../../styles.css";
+}
+
+- (NSString *)cssCategoryTemplatePath {
+ return @"../../styles.css";
+}
+
+- (NSString *)cssProtocolTemplatePath {
+ return @"../../styles.css";
+}
+
+#pragma mark HTML paths and references handling
+
+- (NSString *)htmlOutputPath {
+ return [self.outputPath stringByAppendingPathComponent:@"html"];
+}
+
+- (NSString *)htmlOutputPathForObject:(GBModelBase *)object {
+ NSParameterAssert(object != nil);
+ NSParameterAssert([self isTopLevelStoreObject:object]);
+ NSString *inner = [self outputPathForObject:object withExtension:[self htmlExtension]];
+ return [self.htmlOutputPath stringByAppendingPathComponent:inner];
+}
+
+- (NSString *)htmlReferenceNameForObject:(GBModelBase *)object {
+ NSParameterAssert(object != nil);
+ if ([self isTopLevelStoreObject:object])
+ return [self htmlReferenceForObject:object fromSource:object];
+ return [self htmlReferenceForMember:object prefixedWith:@""];
+}
+
+- (NSString *)htmlReferenceForObject:(GBModelBase *)object fromSource:(GBModelBase *)source {
+ NSParameterAssert(object != nil);
+ NSParameterAssert(source != nil);
+
+ // Generate hrefs from member to other objects:
+ if (![self isTopLevelStoreObject:source]) {
+ GBModelBase *sourceParent = source.parentObject;
+
+ // To the parent or another top-level object.
+ if ([self isTopLevelStoreObject:object]) return [self htmlReferenceForObject:object fromSource:sourceParent];
+
+ // To same or another member of the same parent.
+ if (object.parentObject == sourceParent) return [self htmlReferenceForMember:object prefixedWith:@"#"];
+
+ // To a member of another top-level object.
+ NSString *path = [self htmlReferenceForObject:object.parentObject fromSource:sourceParent];
+ NSString *memberReference = [self htmlReferenceForMember:object prefixedWith:@"#"];
+ return [NSString stringWithFormat:@"%@%@", path, memberReference];
+ }
+
+ // From top-level object to samo or another top level object.
+ if (object == source || [self isTopLevelStoreObject:object]) {
+ return [self htmlReferenceForTopLevelObject:object fromTopLevelObject:source];
+ }
+
+ // From top-level object to another top-level object member.
+ NSString *memberPath = [self htmlReferenceForMember:object prefixedWith:@"#"];
+ if (object.parentObject != source) {
+ NSString *objectPath = [self htmlReferenceForTopLevelObject:object.parentObject fromTopLevelObject:source];
+ return [NSString stringWithFormat:@"%@%@", objectPath, memberPath];
+ }
+
+ // From top-level object to one of it's members.
+ return memberPath;
+}
+
+- (NSString *)htmlReferenceForTopLevelObject:(GBModelBase *)object fromTopLevelObject:(GBModelBase *)source {
+ NSString *path = [self outputPathForObject:object withExtension:[self htmlExtension]];
+ if ([object isKindOfClass:[source class]]) return [path lastPathComponent];
+ NSString *prefix = [self relativePathPrefixFromObject:source toObject:object];
+ return [prefix stringByAppendingPathComponent:path];
+}
+
+- (NSString *)htmlReferenceForMember:(GBModelBase *)member prefixedWith:(NSString *)prefix {
+ NSParameterAssert(member != nil);
+ NSParameterAssert(prefix != nil);
+ if ([member isKindOfClass:[GBMethodData class]]) {
+ GBMethodData *method = (GBMethodData *)member;
+ return [NSString stringWithFormat:@"%@//api/name/%@", prefix, method.methodSelector];
+ }
+ return @"";
+}
+
+- (NSString *)htmlExtension {
+ return @"html";
+}
+
+#pragma mark Paths helper methods
+
+- (NSString *)outputPathForObject:(id)object withExtension:(NSString *)extension {
+ NSString *basePath = nil;
+ NSString *name = nil;
+ if ([object isKindOfClass:[GBClassData class]]) {
+ basePath = @"Classes";
+ name = [object nameOfClass];
+ }
+ else if ([object isKindOfClass:[GBCategoryData class]]) {
+ basePath = @"Categories";
+ name = [object idOfCategory];
+ }
+ else if ([object isKindOfClass:[GBProtocolData class]]) {
+ basePath = @"Protocols";
+ name = [object nameOfProtocol];
+ }
+
+ if (basePath == nil || name == nil) return nil;
+ basePath = [basePath stringByAppendingPathComponent:name];
+ return [basePath stringByAppendingPathExtension:extension];
+}
+
+- (NSString *)relativePathPrefixFromObject:(GBModelBase *)source toObject:(GBModelBase *)destination {
+ if ([source isKindOfClass:[destination class]]) return @"";
+ return @"../";
+}
+
+#pragma mark Helper methods
+
+- (BOOL)isTopLevelStoreObject:(id)object {
+ if ([object isKindOfClass:[GBClassData class]] || [object isKindOfClass:[GBCategoryData class]] || [object isKindOfClass:[GBProtocolData class]])
+ return YES;
+ return NO;
+}
+
+#pragma mark Overriden methods
+
+- (NSString *)description {
+ return [self className];
+}
+
+#pragma mark Properties
+
+@synthesize outputPath;
+@synthesize templatesPath;
+@synthesize ignoredPaths;
+@synthesize commentComponents;
+@synthesize stringTemplates;
+
+@end
View
101 Application/GBApplicationSettingsProviding.h
@@ -8,6 +8,9 @@
#import <Foundation/Foundation.h>
#import "GBCommentComponentsProvider.h"
+#import "GBApplicationStringsProvider.h"
+
+@class GBModelBase;
/** Defines the requirements for application-level settings providers.
@@ -15,13 +18,111 @@
*/
@protocol GBApplicationSettingsProviding
+///---------------------------------------------------------------------------------------
+/// @name Template paths handling
+///---------------------------------------------------------------------------------------
+
+/** The base path to template files used for generating various output files. */
+@property (copy) NSString *templatesPath;
+
+/** The path to the CSS template file, relative from class html files.
+
+ @see cssCategoryTemplatePath
+ @see cssProtocolTemplatePath
+ */
+@property (readonly) NSString *cssClassTemplatePath;
+
+/** The path to the CSS template file, relative from category html files.
+
+ @see cssClassTemplatePath
+ @see cssProtocolTemplatePath
+ */
+@property (readonly) NSString *cssCategoryTemplatePath;
+
+/** The path to the CSS template file, relative from protocol html files.
+
+ @see cssClassTemplatePath
+ @see cssCategoryTemplatePath
+ */
+@property (readonly) NSString *cssProtocolTemplatePath;
+
+///---------------------------------------------------------------------------------------
+/// @name Output paths handling
+///---------------------------------------------------------------------------------------
+
+/** The base path of the generated files. */
+@property (copy) NSString *outputPath;
+
+/** The base path of the HTML generated files.
+
+ This value depends on `outputPath` and is automatically calculated.
+
+ @see htmlOutputPathForObject:
+ @see outputPath
+ */
+@property (readonly) NSString *htmlOutputPath;
+
+/** Returns file name including full path for HTML file representing the given top-level object.
+
+ This works for any top-level object: class, category or protocol. The path is automatically determined regarding to the object class.
+
+ @param object The object for which to return the path.
+ @return Returns the path.
+ @exception NSException Thrown if the given object is `nil` or not top-level object.
+ @see htmlReferenceForObject:fromSource:
+ @see htmlOutputPath
+ */
+- (NSString *)htmlOutputPathForObject:(GBModelBase *)object;
+
+/** Returns HTML reference name for the given object.
+
+ This should only be used for creating anchors that need to be referenced from other parts of the same HTML file. The method works for top-level objects as well as their members.
+
+ @param object The object for which to return reference name.
+ @return Returns the reference name of the object.
+ @exception NSException Thrown if the given object is `nil`.
+ @see htmlReferenceForObject:fromSource:
+ */
+- (NSString *)htmlReferenceNameForObject:(GBModelBase *)object;
+
+/** Returns relative HTML reference to the given object from the context of the given source object.
+
+ This is useful for generating hrefs from one object HTML file to another. This is the swiss army knife king of a method for all hrefs generation. It works for any kind of links:
+
+ - Top-level object to same top-level object.
+ - Top-level object to a different top-level object.
+ - Top-level object to one of it's members.
+ - Member object to it's top-level object.
+ - Member object to another top-level object.
+ - Member object to another member of the same top-level object.
+ - Member object to a member of another top-level object.
+
+ @param object The object for which to generate the reference to.
+ @param source The source object from which to generate the reference from.
+ @return Returns the reference string.
+ @exception NSException Thrown if any of the given objects is `nil`.
+ @see htmlReferenceNameForObject:
+ */
+- (NSString *)htmlReferenceForObject:(GBModelBase *)object fromSource:(GBModelBase *)source;
+
+///---------------------------------------------------------------------------------------
+/// @name Other paths handling
+///---------------------------------------------------------------------------------------
+
/** The list of all full or partial paths to be ignored.
It's recommended to check if a path string ends with any of the given paths before processing it. This should catch directory and file names properly as directories are processed first.
*/
@property (retain) NSMutableArray *ignoredPaths;
+///---------------------------------------------------------------------------------------
+/// @name Helper classes
+///---------------------------------------------------------------------------------------
+
/** Returns the `GBCommentComponentsProvider` object that identifies comment components. */
@property (retain) GBCommentComponentsProvider *commentComponents;
+/** Returns the `GBApplicationStringsProvider` object that specifies all string templates used for output generation. */
+@property (retain) GBApplicationStringsProvider *stringTemplates;
+
@end
View
44 Application/GBApplicationStringsProvider.h
@@ -0,0 +1,44 @@
+//
+// GBApplicationStringsProvider.h
+// appledoc
+//
+// Created by Tomaz Kragelj on 1.10.10.
+// Copyright (C) 2010, Gentle Bytes. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+
+/** Provides static strings and string templates for the rest of the application.
+
+ The main purpose of this class is to serve as an entry point for static strings used for output generation. This allows us to provide simple way of handling translations. The class is intended to be used as is - just pass it over to `GBTemplateWriter` and make sure template files use proper key paths.
+ */
+@interface GBApplicationStringsProvider : NSObject
+
+///---------------------------------------------------------------------------------------
+/// @name Initialization & disposal
+///---------------------------------------------------------------------------------------
+
+/** Returns a new autoreleased `GBApplicationStringsProvider` instance.
+ */
++ (id)provider;
+
+///---------------------------------------------------------------------------------------
+/// @name Object output strings
+///---------------------------------------------------------------------------------------
+
+/** Strings used for generating common page strings for objects. */
+@property (readonly) NSDictionary *objectPage;
+
+/** Strings used for generating specification sections for objects. */
+@property (readonly) NSDictionary *objectSpecifications;
+
+/** Strings used for generating overview section for objects. */
+@property (readonly) NSDictionary *objectOverview;
+
+/** Strings used for generating tasks section for objects. */
+@property (readonly) NSDictionary *objectTasks;
+
+/** Strings used for generating methods sections for objects. */
+@property (readonly) NSDictionary *objectMethods;
+
+@end
View
80 Application/GBApplicationStringsProvider.m
@@ -0,0 +1,80 @@
+//
+// GBApplicationStringsProvider.m
+// appledoc
+//
+// Created by Tomaz Kragelj on 1.10.10.
+// Copyright (C) 2010, Gentle Bytes. All rights reserved.
+//
+
+#import "GBApplicationStringsProvider.h"
+
+@implementation GBApplicationStringsProvider
+
+#pragma mark Initialization & disposal
+
++ (id)provider {
+ return [[[self alloc] init] autorelease];
+}
+
+#pragma mark Object output strings
+
+- (NSDictionary *)objectPage {
+ static NSMutableDictionary *result = nil;
+ if (!result) {
+ result = [[NSMutableDictionary alloc] init];
+ [result setObject:@"%@ Class Reference" forKey:@"classTitle"];
+ [result setObject:@"%@(%@) Category Reference" forKey:@"categoryTitle"];
+ [result setObject:@"%@ Protocol Reference" forKey:@"protocolTitle"];
+ }
+ return result;
+}
+
+- (NSDictionary *)objectSpecifications {
+ static NSMutableDictionary *result = nil;
+ if (!result) {
+ result = [[NSMutableDictionary alloc] init];
+ [result setObject:@"Inherits from" forKey:@"inheritsFrom"];
+ [result setObject:@"Conforms to" forKey:@"conformsTo"];
+ [result setObject:@"Declared in" forKey:@"declaredIn"];
+ }
+ return result;
+}
+
+- (NSDictionary *)objectOverview {
+ static NSMutableDictionary *result = nil;
+ if (!result) {
+ result = [[NSMutableDictionary alloc] init];
+ [result setObject:@"Overview" forKey:@"title"];
+ }
+ return result;
+}
+
+- (NSDictionary *)objectTasks {
+ static NSMutableDictionary *result = nil;
+ if (!result) {
+ result = [[NSMutableDictionary alloc] init];
+ [result setObject:@"Tasks" forKey:@"title"];
+ [result setObject:@"Other Methods" forKey:@"otherMethodsSectionName"];
+ [result setObject:@"required method" forKey:@"requiredMethod"];
+ }
+ return result;
+}
+
+- (NSDictionary *)objectMethods {
+ static NSMutableDictionary *result = nil;
+ if (!result) {
+ result = [[NSMutableDictionary alloc] init];
+ [result setObject:@"Class Methods" forKey:@"classMethodsTitle"];
+ [result setObject:@"Instance Methods" forKey:@"instanceMethodsTitle"];
+ [result setObject:@"Properties" forKey:@"propertiesTitle"];
+ [result setObject:@"Parameters" forKey:@"parametersTitle"];
+ [result setObject:@"Return Value" forKey:@"resultTitle"];
+ [result setObject:@"Discussion" forKey:@"discussionTitle"];
+ [result setObject:@"Exceptions" forKey:@"exceptionsTitle"];
+ [result setObject:@"See Also" forKey:@"seeAlsoTitle"];
+ [result setObject:@"Declared In" forKey:@"declaredInTitle"];
+ }
+ return result;
+}
+
+@end
View
5 Common/GBLog.h
@@ -73,6 +73,7 @@ extern NSUInteger kGBLogLevel;
#define GBLogInfo(frmt, ...) SYNC_LOG_OBJC_MAYBE(kGBLogLevel, LOG_FLAG_INFO, frmt, ##__VA_ARGS__)
#define GBLogVerbose(frmt, ...) SYNC_LOG_OBJC_MAYBE(kGBLogLevel, LOG_FLAG_VERBOSE, frmt, ##__VA_ARGS__)
#define GBLogDebug(frmt, ...) SYNC_LOG_OBJC_MAYBE(kGBLogLevel, LOG_FLAG_DEBUG, frmt, ##__VA_ARGS__)
+#define GBLogIsEnabled(level) ((kGBLogLevel & level) > 0)
// Helper macros for logging exceptions. Note that we don't use formatting here as it would make the output unreadable
// in higher level log formats. The information is already verbose enough!
@@ -90,8 +91,8 @@ extern NSUInteger kGBLogLevel;
}
#define GBLogNSError(error,frmt,...) { \
if (frmt) GBLogExceptionLine(frmt, ##__VA_ARGS__); \
- GBLogExceptionLine(@"%@", [error localizedDescription]); \
- GBLogExceptionLine(@"%@", [error localizedFailureReason]); \
+ if ([error localizedDescription]) GBLogExceptionLine(@"%@", [error localizedDescription]); \
+ if ([error localizedFailureReason]) GBLogExceptionLine(@"%@", [error localizedFailureReason]); \
}
#pragma mark Application wide logging helpers
View
44 Common/NSString+GBString.h
@@ -11,6 +11,10 @@
/** Provides string extensions that make the rest of parsing code simpler. */
@interface NSString (GBString)
+///---------------------------------------------------------------------------------------
+/// @name Simplifying strings
+///---------------------------------------------------------------------------------------
+
/** Trims all characters from the given set from the string end.
Works the same way as `[NSString stringByTrimmingCharactersInSetFromEnd:]` except it trims from end only.
@@ -27,29 +31,53 @@
*/
- (NSString *)stringByWordifyingWithSpaces;
+///---------------------------------------------------------------------------------------
+/// @name Preparing nice descriptions
+///---------------------------------------------------------------------------------------
+
/** Returns normalized description from the receiver.
The main purpose of this method is to strip and wordifiy long descriptions by making them suitable for logging and debug messages.
@return Returns stripped description.
- @see normalizedDescriptionFromString:
+ @see normalizedDescriptionWithMaxLength:
@see maxNormalizedDescriptionLength
*/
- (NSString *)normalizedDescription;
-/** Returns normalized description from the given string.
+/** Returns normalized description from the receiver.
The main purpose of this method is to strip and wordifiy long descriptions by making them suitable for logging and debug messages.
-
- @param string String to strip.
+
+ @param length Maximum length of the description.
@return Returns stripped description.
- @see normalizedDescription
+ @see normalizedDescriptionWithMaxLength:
@see maxNormalizedDescriptionLength
*/
-+ (NSString *)normalizedDescriptionFromString:(NSString *)string;
+- (NSString *)normalizedDescriptionWithMaxLength:(NSUInteger)length;
+
+/** Returns default maximum length of normalized string.
+ */
++ (NSUInteger)defaultNormalizedDescriptionLength;
-/** Returns maximum length of normalized string.
+///---------------------------------------------------------------------------------------
+/// @name Getting information
+///---------------------------------------------------------------------------------------
+
+/** Returns the number of all lines in the receiver.
+
+ @return Returns the number of all lines in the receiver.
+ @see numberOfLinesInRange:
+ */
+- (NSUInteger)numberOfLines;
+
+/** Calculates the numer of lines in the given range of the receiver.
+
+ @param range The range to use for calculation.
+ @return Returns the number of lines in the given range.
+ @exception NSException Thrown if the given range is invalid.
+ @see numberOfLines
*/
-+ (NSUInteger)maxNormalizedDescriptionLength;
+- (NSUInteger)numberOfLinesInRange:(NSRange)range;
@end
View
61 Common/NSString+GBString.m
@@ -18,12 +18,18 @@ @interface NSString (GBPrivateAPI)
*/
- (unichar)lastCharacter;
+/** Returns the array contaiing all words of the receiver.
+ */
+- (NSArray *)arrayOfWords;
+
@end
#pragma mark -
@implementation NSString (GBString)
+#pragma mark Simplifying string
+
- (NSString *)stringByTrimmingCharactersInSetFromEnd:(NSCharacterSet *)set {
NSParameterAssert(set != nil);
NSMutableString *result = [self mutableCopy];
@@ -36,7 +42,7 @@ - (NSString *)stringByTrimmingCharactersInSetFromEnd:(NSCharacterSet *)set {
- (NSString *)stringByWordifyingWithSpaces {
if ([self length] == 0) return self;
NSMutableString *result = [NSMutableString stringWithCapacity:[self length]];
- NSArray *words = [self componentsSeparatedByRegex:@"\\s+"];
+ NSArray *words = [self arrayOfWords];
[words enumerateObjectsUsingBlock:^(NSString *word, NSUInteger idx, BOOL *stop) {
if ([word length] == 0) return;
if ([result length] > 0) [result appendString:@" "];
@@ -45,20 +51,51 @@ - (NSString *)stringByWordifyingWithSpaces {
return result;
}
+#pragma mark Preparing nice descriptions
+
- (NSString *)normalizedDescription {
- return [[self class] normalizedDescriptionFromString:self];
+ return [self normalizedDescriptionWithMaxLength:[[self class] defaultNormalizedDescriptionLength]];
+}
+
+- (NSString *)normalizedDescriptionWithMaxLength:(NSUInteger)length {
+ NSString *extract = [self stringByReplacingOccurrencesOfRegex:@"\\s+" withString:@" "];
+ if ([extract length] <= length) return [extract stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
+
+ NSArray *words = [extract arrayOfWords];
+ length /= 2;
+
+ NSMutableString *prefix = [NSMutableString stringWithCapacity:[extract length] / 2];
+ [words enumerateObjectsUsingBlock:^(NSString *word, NSUInteger idx, BOOL *stop) {
+ if ([prefix length] > 0) [prefix appendString:@" "];
+ [prefix appendString:word];
+ if ([prefix length] >= length) *stop = YES;
+ }];
+
+ NSMutableString *suffix = [NSMutableString stringWithCapacity:[prefix length]];
+ [words enumerateObjectsWithOptions:NSEnumerationReverse usingBlock:^(NSString *word, NSUInteger idx, BOOL *stop) {
+ if ([suffix length] > 0) [suffix insertString:@" " atIndex:0];
+ [suffix insertString:word atIndex:0];
+ if ([suffix length] >= length) *stop = YES;
+ }];
+
+ return [NSString stringWithFormat:@"%@%@", prefix, suffix];
}
-+ (NSString *)normalizedDescriptionFromString:(NSString *)string {
- NSString *extract = [string stringByReplacingOccurrencesOfRegex:@"\\s+" withString:@" "];
- extract = [extract substringToIndex:MIN([self maxNormalizedDescriptionLength],[extract length])];
- BOOL missing = ([extract length] < [string length]);
- extract = [extract stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
- return [NSString stringWithFormat:@"%@%@", extract, missing ? @"*" : @""];
++ (NSUInteger)defaultNormalizedDescriptionLength {
+ return 35;
}
-+ (NSUInteger)maxNormalizedDescriptionLength {
- return 25;
+#pragma mark Getting information
+
+- (NSUInteger)numberOfLines {
+ if ([self length] == 0) return 0;
+ return [self numberOfLinesInRange:NSMakeRange(0, [self length])];
+}
+
+- (NSUInteger)numberOfLinesInRange:(NSRange)range {
+ NSString *substring = [self substringWithRange:range];
+ NSArray *lines = [substring componentsSeparatedByCharactersInSet:[NSCharacterSet newlineCharacterSet]];
+ return [lines count];
}
@end
@@ -71,5 +108,9 @@ - (unichar)lastCharacter {
return [self characterAtIndex:[self length] - 1];
}
+- (NSArray *)arrayOfWords {
+ return [self componentsSeparatedByRegex:@"\\s+"];
+}
+
@end
View
0 Common/ThirdParty/DDLog/DDLog.h 100755 → 100644
File mode changed.
View
0 Common/ThirdParty/DDLog/DDLog.m 100755 → 100644
File mode changed.
View
10 Common/ThirdParty/MGTemplateEngine/DeepMutableCopy.h
@@ -0,0 +1,10 @@
+/*
+ * DeepMutableCopy.h
+ *
+ * Created by Matt Gemmell on 02/05/2008.
+ * Copyright 2008 Instinctive Code. All rights reserved.
+ *
+ */
+
+#import "NSArray_DeepMutableCopy.h"
+#import "NSDictionary_DeepMutableCopy.h"
View
44 Common/ThirdParty/MGTemplateEngine/ICUTemplateMatcher.h
@@ -0,0 +1,44 @@
+//
+// ICUTemplateMatcher.h
+//
+// Created by Matt Gemmell on 19/05/2008.
+// Copyright 2008 Instinctive Code. All rights reserved.
+//
+
+#import "MGTemplateEngine.h"
+
+/*
+ This is an example Matcher for MGTemplateEngine, implemented using libicucore on Leopard,
+ via the RegexKitLite library: http://regexkit.sourceforge.net/#RegexKitLite
+
+ This project includes everything you need, as long as you're building on Mac OS X 10.5 or later.
+
+ Other matchers can easily be implemented using the MGTemplateEngineMatcher protocol,
+ if you prefer to use another regex framework, or use another matching method entirely.
+ */
+
+@interface ICUTemplateMatcher : NSObject <MGTemplateEngineMatcher> {
+ MGTemplateEngine *engine;
+ NSString *markerStart;
+ NSString *markerEnd;
+ NSString *exprStart;
+ NSString *exprEnd;
+ NSString *filterDelimiter;
+ NSString *templateString;
+ NSString *regex;
+}
+
+@property(assign) MGTemplateEngine *engine; // weak ref
+@property(retain) NSString *markerStart;
+@property(retain) NSString *markerEnd;
+@property(retain) NSString *exprStart;
+@property(retain) NSString *exprEnd;
+@property(retain) NSString *filterDelimiter;
+@property(retain) NSString *templateString;
+@property(retain) NSString *regex;
+
++ (ICUTemplateMatcher *)matcherWithTemplateEngine:(MGTemplateEngine *)theEngine;
+
+- (NSArray *)argumentsFromString:(NSString *)argString;
+
+@end
View
192 Common/ThirdParty/MGTemplateEngine/ICUTemplateMatcher.m
@@ -0,0 +1,192 @@
+//
+// ICUTemplateMatcher.m
+//
+// Created by Matt Gemmell on 19/05/2008.
+// Copyright 2008 Instinctive Code. All rights reserved.
+//
+
+#import "ICUTemplateMatcher.h"
+#import "RegexKitLite.h"
+
+
+@implementation ICUTemplateMatcher
+
+
++ (ICUTemplateMatcher *)matcherWithTemplateEngine:(MGTemplateEngine *)theEngine
+{
+ return [[[ICUTemplateMatcher alloc] initWithTemplateEngine:theEngine] autorelease];
+}
+
+
+- (id)initWithTemplateEngine:(MGTemplateEngine *)theEngine
+{
+ if ((self = [super init])) {
+ self.engine = theEngine; // weak ref
+ }
+
+ return self;
+}
+
+
+- (void)dealloc
+{
+ self.engine = nil;
+ self.templateString = nil;
+ self.markerStart = nil;
+ self.markerEnd = nil;
+ self.exprStart = nil;
+ self.exprEnd = nil;
+ self.filterDelimiter = nil;
+ self.regex = nil;
+
+ [super dealloc];
+}
+
+
+- (void)engineSettingsChanged
+{
+ // This method is a good place to cache settings from the engine.
+ self.markerStart = engine.markerStartDelimiter;
+ self.markerEnd = engine.markerEndDelimiter;
+ self.exprStart = engine.expressionStartDelimiter;
+ self.exprEnd = engine.expressionEndDelimiter;
+ self.filterDelimiter = engine.filterDelimiter;
+ self.templateString = engine.templateContents;
+
+ // Note: the \Q ... \E syntax causes everything inside it to be treated as literals.
+ // This help us in the case where the marker/filter delimiters have special meaning
+ // in regular expressions; notably the "$" character in the default marker start-delimiter.
+ // Note: the (?m) syntax makes ICU enable multiline matching.
+ NSString *basePattern = @"(\\Q%@\\E)(?:\\s+)?(.*?)(?:(?:\\s+)?\\Q%@\\E(?:\\s+)?(.*?))?(?:\\s+)?\\Q%@\\E";
+ NSString *mrkrPattern = [NSString stringWithFormat:basePattern, self.markerStart, self.filterDelimiter, self.markerEnd];
+ NSString *exprPattern = [NSString stringWithFormat:basePattern, self.exprStart, self.filterDelimiter, self.exprEnd];
+ self.regex = [NSString stringWithFormat:@"(?m)(?:%@|%@)", mrkrPattern, exprPattern];
+}
+
+
+- (NSDictionary *)firstMarkerWithinRange:(NSRange)range
+{
+ NSRange matchRange = [self.templateString rangeOfRegex:self.regex options:RKLNoOptions inRange:range capture:0 error:NULL];
+ NSMutableDictionary *markerInfo = nil;
+ if (matchRange.length > 0) {
+ markerInfo = [NSMutableDictionary dictionary];
+ [markerInfo setObject:[NSValue valueWithRange:matchRange] forKey:MARKER_RANGE_KEY];
+
+ // Found a match. Obtain marker string.
+ NSString *matchString = [self.templateString substringWithRange:matchRange];
+ NSRange localRange = NSMakeRange(0, [matchString length]);
+ //NSLog(@"mtch: \"%@\"", matchString);
+
+ // Find type of match
+ NSString *matchType = nil;
+ NSRange mrkrSubRange = [matchString rangeOfRegex:regex options:RKLNoOptions inRange:localRange capture:1 error:NULL];
+ BOOL isMarker = (mrkrSubRange.length > 0); // only matches if match has marker-delimiters
+ int offset = 0;
+ if (isMarker) {
+ matchType = MARKER_TYPE_MARKER;
+ } else {
+ matchType = MARKER_TYPE_EXPRESSION;
+ offset = 3;
+ }
+ [markerInfo setObject:matchType forKey:MARKER_TYPE_KEY];
+
+ // Split marker string into marker-name and arguments.
+ NSRange markerRange = NSMakeRange(0, [matchString length]);
+ markerRange = [matchString rangeOfRegex:regex options:RKLNoOptions inRange:localRange capture:2 + offset error:NULL];
+
+ if (markerRange.length > 0) {
+ NSString *markerString = [matchString substringWithRange:markerRange];
+ NSArray *markerComponents = [self argumentsFromString:markerString];
+ if (markerComponents && [markerComponents count] > 0) {
+ [markerInfo setObject:[markerComponents objectAtIndex:0] forKey:MARKER_NAME_KEY];
+ int count = [markerComponents count];
+ if (count > 1) {
+ [markerInfo setObject:[markerComponents subarrayWithRange:NSMakeRange(1, count - 1)]
+ forKey:MARKER_ARGUMENTS_KEY];
+ }
+ }
+
+ // Check for filter.
+ NSRange filterRange = [matchString rangeOfRegex:regex options:RKLNoOptions inRange:localRange capture:3 + offset error:NULL];
+ if (filterRange.length > 0) {
+ // Found a filter. Obtain filter string.
+ NSString *filterString = [matchString substringWithRange:filterRange];
+
+ // Convert first : plus any immediately-following whitespace into a space.
+ localRange = NSMakeRange(0, [filterString length]);
+ NSString *space = @" ";
+ NSRange filterArgDelimRange = [filterString rangeOfRegex:@":(?:\\s+)?" options:RKLNoOptions inRange:localRange
+ capture:0 error:NULL];
+ if (filterArgDelimRange.length > 0) {
+ // Replace found text with space.
+ filterString = [NSString stringWithFormat:@"%@%@%@",
+ [filterString substringWithRange:NSMakeRange(0, filterArgDelimRange.location)],
+ space,
+ [filterString substringWithRange:NSMakeRange(NSMaxRange(filterArgDelimRange),
+ localRange.length - NSMaxRange(filterArgDelimRange))]];
+ }
+
+ // Split into filter-name and arguments.
+ NSArray *filterComponents = [self argumentsFromString:filterString];
+ if (filterComponents && [filterComponents count] > 0) {
+ [markerInfo setObject:[filterComponents objectAtIndex:0] forKey:MARKER_FILTER_KEY];
+ int count = [filterComponents count];
+ if (count > 1) {
+ [markerInfo setObject:[filterComponents subarrayWithRange:NSMakeRange(1, count - 1)]
+ forKey:MARKER_FILTER_ARGUMENTS_KEY];
+ }
+ }
+ }
+ }
+ }
+
+ return markerInfo;
+}
+
+
+- (NSArray *)argumentsFromString:(NSString *)argString
+{
+ // Extract arguments from argString, taking care not to break single- or double-quoted arguments,
+ // including those containing \-escaped quotes.
+ NSString *argsPattern = @"\"(.*?)(?<!\\\\)\"|'(.*?)(?<!\\\\)'|(\\S+)";
+ NSMutableArray *args = [NSMutableArray array];
+
+ NSUInteger location = 0;
+ while (location != NSNotFound) {
+ NSRange searchRange = NSMakeRange(location, [argString length] - location);
+ NSRange entireRange = [argString rangeOfRegex:argsPattern options:RKLNoOptions
+ inRange:searchRange capture:0 error:NULL];
+ NSRange matchedRange = [argString rangeOfRegex:argsPattern options:RKLNoOptions
+ inRange:searchRange capture:1 error:NULL];
+ if (matchedRange.length == 0) {
+ matchedRange = [argString rangeOfRegex:argsPattern options:RKLNoOptions
+ inRange:searchRange capture:2 error:NULL];
+ if (matchedRange.length == 0) {
+ matchedRange = [argString rangeOfRegex:argsPattern options:RKLNoOptions
+ inRange:searchRange capture:3 error:NULL];
+ }
+ }
+
+ location = NSMaxRange(entireRange) + ((entireRange.length == 0) ? 1 : 0);
+ if (matchedRange.length > 0) {
+ [args addObject:[argString substringWithRange:matchedRange]];
+ } else {
+ location = NSNotFound;
+ }
+ }
+
+ return args;
+}
+
+
+@synthesize engine;
+@synthesize markerStart;
+@synthesize markerEnd;
+@synthesize exprStart;
+@synthesize exprEnd;
+@synthesize filterDelimiter;
+@synthesize templateString;
+@synthesize regex;
+
+
+@end
View
105 Common/ThirdParty/MGTemplateEngine/MGTemplateEngine.h
@@ -0,0 +1,105 @@
+//
+// MGTemplateEngine.h
+//
+// Created by Matt Gemmell on 11/05/2008.
+// Copyright 2008 Instinctive Code. All rights reserved.
+//
+
+// Keys in blockInfo dictionaries passed to delegate methods.
+#define BLOCK_NAME_KEY @"name" // NSString containing block name (first word of marker)
+#define BLOCK_END_NAMES_KEY @"endNames" // NSArray containing names of possible ending-markers for block
+#define BLOCK_ARGUMENTS_KEY @"args" // NSArray of further arguments in block start marker
+#define BLOCK_START_MARKER_RANGE_KEY @"startMarkerRange" // NSRange (as NSValue) of block's starting marker
+#define BLOCK_END_MARKER_RANGE_KEY @"endMarkerRage" // NSRange (as NSValue) of block's ending marker
+#define BLOCK_VARIABLES_KEY @"vars" // NSDictionary of variables
+
+#define TEMPLATE_ENGINE_ERROR_DOMAIN @"MGTemplateEngineErrorDomain"
+
+@class MGTemplateEngine;
+@protocol MGTemplateEngineDelegate
+@optional
+- (id)templateEngine:(MGTemplateEngine *)engine blockStarted:(NSDictionary *)blockInfo;
+- (id)templateEngine:(MGTemplateEngine *)engine blockEnded:(NSDictionary *)blockInfo;
+- (void)templateEngineFinishedProcessingTemplate:(MGTemplateEngine *)engine;
+- (void)templateEngine:(MGTemplateEngine *)engine encounteredError:(NSError *)error isContinuing:(BOOL)continuing;
+@end
+
+// Keys in marker dictionaries returned from Matcher methods.
+#define MARKER_NAME_KEY @"name" // NSString containing marker name (first word of marker)
+#define MARKER_TYPE_KEY @"type" // NSString, either MARKER_TYPE_EXPRESSION or MARKER_TYPE_MARKER
+#define MARKER_TYPE_MARKER @"marker"
+#define MARKER_TYPE_EXPRESSION @"expression"
+#define MARKER_ARGUMENTS_KEY @"args" // NSArray of further arguments in marker, if any
+#define MARKER_FILTER_KEY @"filter" // NSString containing name of filter attached to marker, if any
+#define MARKER_FILTER_ARGUMENTS_KEY @"filterArgs" // NSArray of filter arguments, if any
+#define MARKER_RANGE_KEY @"range" // NSRange (as NSValue) of marker's range
+
+@protocol MGTemplateEngineMatcher
+@required
+- (id)initWithTemplateEngine:(MGTemplateEngine *)engine;
+- (void)engineSettingsChanged; // always called at least once before beginning to process a template.
+- (NSDictionary *)firstMarkerWithinRange:(NSRange)range;
+@end
+
+#import "MGTemplateMarker.h"
+#import "MGTemplateFilter.h"
+
+@interface MGTemplateEngine : NSObject {
+@public
+ NSString *markerStartDelimiter; // default: {%
+ NSString *markerEndDelimiter; // default: %}
+ NSString *expressionStartDelimiter; // default: {{
+ NSString *expressionEndDelimiter; // default: }}
+ NSString *filterDelimiter; // default: | example: {{ myVar|uppercase }}
+ NSString *literalStartMarker; // default: literal
+ NSString *literalEndMarker; // default: /literal
+@private
+ NSMutableArray *_openBlocksStack;
+ NSMutableDictionary *_globals;
+ int _outputDisabledCount;
+ int _templateLength;
+ NSMutableDictionary *_filters;
+ NSMutableDictionary *_markers;
+ NSMutableDictionary *_templateVariables;
+ BOOL _literal;
+@public
+ NSRange remainingRange;
+ id <MGTemplateEngineDelegate> delegate;
+ id <MGTemplateEngineMatcher> matcher;
+ NSString *templateContents;
+}
+
+@property(retain) NSString *markerStartDelimiter;
+@property(retain) NSString *markerEndDelimiter;
+@property(retain) NSString *expressionStartDelimiter;
+@property(retain) NSString *expressionEndDelimiter;
+@property(retain) NSString *filterDelimiter;
+@property(retain) NSString *literalStartMarker;
+@property(retain) NSString *literalEndMarker;
+@property(assign, readonly) NSRange remainingRange;
+@property(assign) id <MGTemplateEngineDelegate> delegate; // weak ref
+@property(retain) id <MGTemplateEngineMatcher> matcher;
+@property(retain, readonly) NSString *templateContents;
+
+// Creation.
++ (NSString *)version;
++ (MGTemplateEngine *)templateEngine;
+
+// Managing persistent values.
+- (void)setObject:(id)anObject forKey:(id)aKey;
+- (void)addEntriesFromDictionary:(NSDictionary *)dict;
+- (id)objectForKey:(id)aKey;
+
+// Configuration and extensibility.
+- (void)loadMarker:(NSObject <MGTemplateMarker> *)marker;
+- (void)loadFilter:(NSObject <MGTemplateFilter> *)filter;
+
+// Utilities.
+- (NSObject *)resolveVariable:(NSString *)var;
+- (NSDictionary *)templateVariables;
+
+// Processing templates.
+- (NSString *)processTemplate:(NSString *)templateString withVariables:(NSDictionary *)variables;
+- (NSString *)processTemplateInFileAtPath:(NSString *)templatePath withVariables:(NSDictionary *)variables;
+
+@end
View
676 Common/ThirdParty/MGTemplateEngine/MGTemplateEngine.m
@@ -0,0 +1,676 @@
+//
+// MGTemplateEngine.m
+//
+// Created by Matt Gemmell on 11/05/2008.
+// Copyright 2008 Instinctive Code. All rights reserved.
+//
+
+#import "MGTemplateEngine.h"
+#import "MGTemplateStandardMarkers.h"
+#import "MGTemplateStandardFilters.h"
+#import "DeepMutableCopy.h"
+
+
+#define DEFAULT_MARKER_START @"{%"
+#define DEFAULT_MARKER_END @"%}"
+#define DEFAULT_EXPRESSION_START @"{{" // should always be different from marker-start
+#define DEFAULT_EXPRESSION_END @"}}"
+#define DEFAULT_FILTER_START @"|"
+#define DEFAULT_LITERAL_START @"literal"
+#define DEFAULT_LITERAL_END @"/literal"
+// example: {% markername arg1 arg2|filter:arg1 arg2 %}
+
+#define GLOBAL_ENGINE_GROUP @"engine" // name of dictionary in globals containing engine settings
+#define GLOBAL_ENGINE_DELIMITERS @"delimiters" // name of dictionary in GLOBAL_ENGINE_GROUP containing delimiters
+#define GLOBAL_DELIM_MARKER_START @"markerStart" // name of key in GLOBAL_ENGINE_DELIMITERS containing marker start delimiter
+#define GLOBAL_DELIM_MARKER_END @"markerEnd"
+#define GLOBAL_DELIM_EXPR_START @"expressionStart"
+#define GLOBAL_DELIM_EXPR_END @"expressionEnd"
+#define GLOBAL_DELIM_FILTER @"filter"
+
+@interface MGTemplateEngine (PrivateMethods)
+
+- (NSObject *)valueForVariable:(NSString *)var parent:(NSObject **)parent parentKey:(NSString **)parentKey;
+- (void)setValue:(NSObject *)newValue forVariable:(NSString *)var forceCurrentStackFrame:(BOOL)inStackFrame;
+- (void)reportError:(NSString *)errorStr code:(int)code continuing:(BOOL)continuing;
+- (id)reportBlockBoundaryStarted:(BOOL)started;
+- (void)reportTemplateProcessingFinished;
+
+@end
+
+
+@implementation MGTemplateEngine
+
+
+#pragma mark Creation and destruction
+
+
++ (NSString *)version
+{
+ // 1.0.0 20 May 2008
+ return @"1.0.0";
+}
+
+
++ (MGTemplateEngine *)templateEngine
+{
+ return [[[MGTemplateEngine alloc] init] autorelease];
+}
+
+
+- (id)init
+{
+ if ((self = [super init])) {
+ _openBlocksStack = [[NSMutableArray alloc] init];
+ _globals = [[NSMutableDictionary alloc] init];
+ _markers = [[NSMutableDictionary alloc] init];
+ _filters = [[NSMutableDictionary alloc] init];
+ _templateVariables = [[NSMutableDictionary alloc] init];
+ _outputDisabledCount = 0; // i.e. not disabled.
+ self.markerStartDelimiter = DEFAULT_MARKER_START;
+ self.markerEndDelimiter = DEFAULT_MARKER_END;
+ self.expressionStartDelimiter = DEFAULT_EXPRESSION_START;
+ self.expressionEndDelimiter = DEFAULT_EXPRESSION_END;
+ self.filterDelimiter = DEFAULT_FILTER_START;
+ self.literalStartMarker = DEFAULT_LITERAL_START;
+ self.literalEndMarker = DEFAULT_LITERAL_END;
+
+ // Load standard markers and filters.
+ [self loadMarker:[[[MGTemplateStandardMarkers alloc] initWithTemplateEngine:self] autorelease]];
+ [self loadFilter:[[[MGTemplateStandardFilters alloc] init] autorelease]];
+ }
+
+ return self;
+}
+
+
+- (void)dealloc
+{
+ [_openBlocksStack release];
+ _openBlocksStack = nil;
+ [_globals release];
+ _globals = nil;
+ [_filters release];
+ _filters = nil;
+ [_markers release];
+ _markers = nil;
+ self.delegate = nil;
+ [templateContents release];
+ templateContents = nil;
+ [_templateVariables release];
+ _templateVariables = nil;
+ self.markerStartDelimiter = nil;
+ self.markerEndDelimiter = nil;
+ self.expressionStartDelimiter = nil;
+ self.expressionEndDelimiter = nil;
+ self.filterDelimiter = nil;
+ self.literalStartMarker = nil;
+ self.literalEndMarker = nil;
+
+ [super dealloc];
+}
+
+
+#pragma mark Managing persistent values.
+
+
+- (void)setObject:(id)anObject forKey:(id)aKey
+{
+ [_globals setObject:anObject forKey:aKey];
+}
+
+
+- (void)addEntriesFromDictionary:(NSDictionary *)dict
+{
+ [_globals addEntriesFromDictionary:dict];
+}
+
+
+- (id)objectForKey:(id)aKey
+{
+ return [_globals objectForKey:aKey];
+}
+
+
+#pragma mark Configuration and extensibility.
+
+
+- (void)loadMarker:(NSObject <MGTemplateMarker> *)marker
+{
+ if (marker) {
+ // Obtain claimed markers.
+ NSArray *markers = [marker markers];
+ if (markers) {
+ for (NSString *markerName in markers) {
+ NSObject *existingHandler = [_markers objectForKey:markerName];
+ if (!existingHandler) {
+ // Set this MGTemplateMaker instance as the handler for markerName.
+ [_markers setObject:marker forKey:markerName];
+ }
+ }
+ }
+ }
+}
+
+
+- (void)loadFilter:(NSObject <MGTemplateFilter> *)filter
+{
+ if (filter) {
+ // Obtain claimed filters.
+ NSArray *filters = [filter filters];
+ if (filters) {
+ for (NSString *filterName in filters) {
+ NSObject *existingHandler = [_filters objectForKey:filterName];
+ if (!existingHandler) {
+ // Set this MGTemplateFilter instance as the handler for filterName.
+ [_filters setObject:filter forKey:filterName];
+ }
+ }
+ }
+ }
+}
+
+
+#pragma mark Delegate
+
+
+- (void)reportError:(NSString *)errorStr code:(int)code continuing:(BOOL)continuing
+{
+ if (delegate) {
+ NSString *errStr = NSLocalizedString(errorStr, nil);
+ if (!continuing) {
+ errStr = [NSString stringWithFormat:@"%@: %@", NSLocalizedString(@"Fatal Error", nil), errStr];
+ }
+ SEL selector = @selector(templateEngine:encounteredError:isContinuing:);
+ if ([(NSObject *)delegate respondsToSelector:selector]) {
+ NSError *error = [NSError errorWithDomain:TEMPLATE_ENGINE_ERROR_DOMAIN
+ code:code
+ userInfo:[NSDictionary dictionaryWithObject:errStr
+ forKey:NSLocalizedDescriptionKey]];
+ [(NSObject <MGTemplateEngineDelegate> *)delegate templateEngine:self
+ encounteredError:error
+ isContinuing:continuing];
+ }
+ }
+}
+
+
+- (id)reportBlockBoundaryStarted:(BOOL)started
+{
+ if (delegate) {
+ SEL selector = (started) ? @selector(templateEngine:blockStarted:) : @selector(templateEngine:blockEnded:);
+ if ([(NSObject *)delegate respondsToSelector:selector]) {
+ return [(NSObject *)delegate performSelector:selector withObject:self withObject:[_openBlocksStack lastObject]];
+ }
+ }
+ return nil;
+}
+
+
+- (void)reportTemplateProcessingFinished
+{
+ if (delegate) {
+ SEL selector = @selector(templateEngineFinishedProcessingTemplate:);
+ if ([(NSObject *)delegate respondsToSelector:selector]) {
+ [(NSObject *)delegate performSelector:selector withObject:self];
+ }
+ }
+}
+
+
+#pragma mark Utilities.
+
+
+- (NSObject *)valueForVariable:(NSString *)var parent:(NSObject **)parent parentKey:(NSString **)parentKey
+{
+ // Returns value for given variable-path, and returns by reference the parent object the variable
+ // is contained in, and the key used on that parent object to access the variable.
+ // e.g. for var "thing.stuff.2", where thing = NSDictionary and stuff = NSArray,
+ // parent would be a pointer to the "stuff" array, and parentKey would be "2".
+
+ NSString *dot = @".";
+ NSArray *dotBits = [var componentsSeparatedByString:dot];
+ NSObject *result = nil;
+ NSObject *currObj = nil;
+
+ // Check to see if there's a top-level entry for first part of var in templateVariables.
+ NSString *firstVar = [dotBits objectAtIndex:0];
+
+ if ([_templateVariables objectForKey:firstVar]) {
+ currObj = _templateVariables;
+ } else if ([_globals objectForKey:firstVar]) {
+ currObj = _globals;
+ } else {
+ // Attempt to find firstVar in stack variables.
+ NSEnumerator *stack = [_openBlocksStack reverseObjectEnumerator];
+ NSDictionary *stackFrame = nil;
+ while ((stackFrame = [stack nextObject])) {
+ NSDictionary *vars = [stackFrame objectForKey:BLOCK_VARIABLES_KEY];
+ if (vars && [vars objectForKey:firstVar]) {
+ currObj = vars;
+ break;
+ }
+ }
+ }
+
+ if (!currObj) {
+ return nil;
+ }
+
+ // Try raw KVC.
+ @try {
+ result = [currObj valueForKeyPath:var];
+ }
+ @catch (NSException *exception) {
+ // do nothing
+ }
+
+ if (result) {
+ // Got it with regular KVC. Work out parent and parentKey if necessary.
+ if (parent || parentKey) {
+ if ([dotBits count] > 1) {
+ if (parent) {
+ *parent = [currObj valueForKeyPath:[[dotBits subarrayWithRange:NSMakeRange(0, [dotBits count] - 1)]
+ componentsJoinedByString:dot]];
+ }
+ if (parentKey) {
+ *parentKey = [dotBits lastObject];
+ }
+ } else {
+ if (parent) {
+ *parent = currObj;
+ }
+ if (parentKey) {
+ *parentKey = var;
+ }
+ }
+ }
+ } else {
+ // Try iterative checking for array indices.
+ int numKeys = [dotBits count];
+ if (numKeys > 1) { // otherwise no point in checking
+ NSObject *thisParent = currObj;
+ NSString *thisKey = nil;
+ for (int i = 0; i < numKeys; i++) {
+ thisKey = [dotBits objectAtIndex:i];
+ NSObject *newObj = nil;
+ @try {
+ newObj = [currObj valueForKeyPath:thisKey];
+ }
+ @catch (NSException *e) {
+ // do nothing
+ }
+ // Check to see if this is an array which we can index into.
+ if (!newObj && [currObj isKindOfClass:[NSArray class]]) {
+ NSCharacterSet *numbersSet = [NSCharacterSet decimalDigitCharacterSet];
+ NSScanner *scanner = [NSScanner scannerWithString:thisKey];
+ NSString *digits;
+ BOOL scanned = [scanner scanCharactersFromSet:numbersSet intoString:&digits];
+ if (scanned && digits && [digits length] > 0) {
+ int index = [digits intValue];
+ if (index >= 0 && index < [((NSArray *)currObj) count]) {
+ newObj = [((NSArray *)currObj) objectAtIndex:index];
+ }
+ }
+ }
+ thisParent = currObj;
+ currObj = newObj;
+ if (!currObj) {
+ break;
+ }
+ }
+ result = currObj;
+ if (parent || parentKey) {
+ if (parent) {
+ *parent = thisParent;
+ }
+ if (parentKey) {
+ *parentKey = thisKey;
+ }
+ }
+ }
+ }
+
+ return result;
+}
+
+
+- (void)setValue:(NSObject *)newValue forVariable:(NSString *)var forceCurrentStackFrame:(BOOL)inStackFrame
+{
+ NSObject *parent = nil;
+ NSString *parentKey = nil;
+ NSObject *currValue;
+ currValue = [self valueForVariable:var parent:&parent parentKey:&parentKey];
+ if (!inStackFrame && currValue && (currValue != newValue)) {
+ // Set new value appropriately.
+ if ([parent isKindOfClass:[NSMutableArray class]]) {
+ [(NSMutableArray *)parent replaceObjectAtIndex:[parentKey intValue] withObject:newValue];
+ } else {
+ // Try using setValue:forKey:
+ @try {
+ [parent setValue:newValue forKey:parentKey];
+ }
+ @catch (NSException *e) {
+ // do nothing
+ }
+ }
+ } else if (!currValue || inStackFrame) {
+ // Put the variable into the current block-stack frame, or _templateVariables otherwise.
+ NSMutableDictionary *vars;
+ if ([_openBlocksStack count] > 0) {
+ vars = [[_openBlocksStack lastObject] objectForKey:BLOCK_VARIABLES_KEY];
+ } else {
+ vars = _templateVariables;
+ }
+ if ([vars respondsToSelector:@selector(setValue:forKey:)]) {
+ [vars setValue:newValue forKey:var];
+ }
+ }
+}
+
+
+- (NSObject *)resolveVariable:(NSString *)var
+{
+ NSObject *parent = nil;
+ NSString *key = nil;
+ NSObject *result = [self valueForVariable:var parent:&parent parentKey:&key];
+ //NSLog(@"var: %@, parent: %@, key: %@, result: %@", var, parent, key, result);
+ return result;
+}
+
+
+- (NSDictionary *)templateVariables
+{
+ return [NSDictionary dictionaryWithDictionary:_templateVariables];
+}
+
+
+#pragma mark Processing templates.
+
+
+- (NSString *)processTemplate:(NSString *)templateString withVariables:(NSDictionary *)variables
+{
+ // Set up environment.
+ [_openBlocksStack release];
+ _openBlocksStack = [[NSMutableArray alloc] init];
+ [_globals setObject:[NSDictionary dictionaryWithObjectsAndKeys:
+ [NSDictionary dictionaryWithObjectsAndKeys:
+ self.markerStartDelimiter, GLOBAL_DELIM_MARKER_START,
+ self.markerEndDelimiter, GLOBAL_DELIM_MARKER_END,
+ self.expressionStartDelimiter, GLOBAL_DELIM_EXPR_START,
+ self.expressionEndDelimiter, GLOBAL_DELIM_EXPR_END,
+ self.filterDelimiter, GLOBAL_DELIM_FILTER,
+ nil], GLOBAL_ENGINE_DELIMITERS,
+ nil]
+ forKey:GLOBAL_ENGINE_GROUP];
+ [_globals setObject:[NSNumber numberWithBool:YES] forKey:@"true"];
+ [_globals setObject:[NSNumber numberWithBool:NO] forKey:@"false"];
+ [_globals setObject:[NSNumber numberWithBool:YES] forKey:@"YES"];
+ [_globals setObject:[NSNumber numberWithBool:NO] forKey:@"NO"];
+ [_globals setObject:[NSNumber numberWithBool:YES] forKey:@"yes"];
+ [_globals setObject:[NSNumber numberWithBool:NO] forKey:@"no"];
+ _outputDisabledCount = 0;
+ [templateContents release];
+ templateContents = [templateString retain];
+ _templateLength = [templateString length];
+ [_templateVariables release];
+ _templateVariables = [variables deepMutableCopy];
+ remainingRange = NSMakeRange(0, [templateString length]);
+ _literal = NO;
+
+ // Ensure we have a matcher.
+ if (!matcher) {
+ [self reportError:@"No matcher has been configured for the template engine" code:7 continuing:NO];
+ return nil;
+ }
+
+ // Tell our matcher to take note of our settings.
+ [matcher engineSettingsChanged];
+ NSMutableString *output = [NSMutableString string];
+
+ while (remainingRange.location != NSNotFound) {
+ NSDictionary *matchInfo = [matcher firstMarkerWithinRange:remainingRange];
+ if (matchInfo) {
+ // Append output before marker if appropriate.
+ NSRange matchRange = [[matchInfo objectForKey:MARKER_RANGE_KEY] rangeValue];
+ if (_outputDisabledCount == 0) {
+ NSRange preMarkerRange = NSMakeRange(remainingRange.location, matchRange.location - remainingRange.location);
+ [output appendFormat:@"%@", [templateContents substringWithRange:preMarkerRange]];
+ }
+
+ // Adjust remainingRange.
+ remainingRange.location = NSMaxRange(matchRange);
+ remainingRange.length = _templateLength - remainingRange.location;
+
+ // Process the marker we found.
+ //NSLog(@"Match: %@", matchInfo);
+ NSString *matchMarker = [matchInfo objectForKey:MARKER_NAME_KEY];
+
+ // Deal with literal mode.
+ if ([matchMarker isEqualToString:self.literalStartMarker]) {
+ if (_literal && _outputDisabledCount == 0) {
+ // Output this tag literally.
+ [output appendFormat:@"%@", [templateContents substringWithRange:matchRange]];
+ } else {
+ // Enable literal mode.
+ _literal = YES;
+ }
+ continue;
+ } else if ([matchMarker isEqualToString:self.literalEndMarker]) {
+ // Disable literal mode.
+ _literal = NO;
+ continue;
+ } else if (_literal && _outputDisabledCount == 0) {
+ [output appendFormat:@"%@", [templateContents substringWithRange:matchRange]];
+ continue;
+ }
+
+ // Check to see if the match is a marker.
+ BOOL isMarker = [[matchInfo objectForKey:MARKER_TYPE_KEY] isEqualToString:MARKER_TYPE_MARKER];
+ NSObject <MGTemplateMarker> *markerHandler = nil;
+ NSObject *val = nil;
+ if (isMarker) {
+ markerHandler = [_markers objectForKey:matchMarker];
+
+ // Process marker with handler.
+ BOOL blockStarted = NO;
+ BOOL blockEnded = NO;
+ BOOL outputEnabled = (_outputDisabledCount == 0);
+ BOOL outputWasEnabled = outputEnabled;
+ NSRange nextRange = remainingRange;
+ NSDictionary *newVariables = nil;
+ NSDictionary *blockInfo = nil;
+
+ // If markerHandler is same as that of current block, send blockInfo.
+ if ([_openBlocksStack count] > 0) {
+ NSDictionary *currBlock = [_openBlocksStack lastObject];
+ NSString *currBlockStartMarker = [currBlock objectForKey:BLOCK_NAME_KEY];
+ if ([_markers objectForKey:currBlockStartMarker] == markerHandler) {
+ blockInfo = currBlock;
+ }
+ }
+
+ // Call marker's handler.
+ val = [markerHandler markerEncountered:matchMarker
+ withArguments:[matchInfo objectForKey:MARKER_ARGUMENTS_KEY]
+ inRange:matchRange
+ blockStarted:&blockStarted blockEnded:&blockEnded
+ outputEnabled:&outputEnabled nextRange:&nextRange
+ currentBlockInfo:blockInfo newVariables:&newVariables];
+
+ if (outputEnabled != outputWasEnabled) {
+ if (outputEnabled) {
+ _outputDisabledCount--;
+ } else {
+ _outputDisabledCount++;
+ }
+ }
+ remainingRange = nextRange;
+
+ // Check to see if remainingRange is valid.
+ if (NSMaxRange(remainingRange) > [self.templateContents length]) {
+ [self reportError:[NSString stringWithFormat:@"Marker handler \"%@\" specified an invalid range to resume processing from",
+ matchMarker]
+ code:5 continuing:NO];
+ break;
+ }
+
+ BOOL forceVarsToStack = NO;
+ if (blockStarted && blockEnded) {
+ // This is considered an error on the part of the marker-handler. Report to delegate.
+ [self reportError:[NSString stringWithFormat:@"Marker \"%@\" reported that a block simultaneously began and ended",
+ matchMarker]
+ code:0 continuing:YES];
+ } else if (blockStarted) {
+ NSArray *endMarkers = [markerHandler endMarkersForMarker:matchMarker];
+ if (!endMarkers) {
+ // Report error to delegate.
+ [self reportError:[NSString stringWithFormat:@"Marker \"%@\" started a block but did not supply any suitable end-markers",
+ matchMarker]
+ code:4 continuing:YES];
+ continue;
+ }
+
+ // A block has begun. Create relevant stack frame.
+ NSMutableDictionary *frame = [NSMutableDictionary dictionary];
+ [frame setObject:matchMarker forKey:BLOCK_NAME_KEY];
+ [frame setObject:endMarkers forKey:BLOCK_END_NAMES_KEY];
+ NSArray *arguments = [matchInfo objectForKey:MARKER_ARGUMENTS_KEY];
+ if (!arguments) {
+ arguments = [NSArray array];
+ }
+ [frame setObject:arguments forKey:BLOCK_ARGUMENTS_KEY];
+ [frame setObject:[matchInfo objectForKey:MARKER_RANGE_KEY] forKey:BLOCK_START_MARKER_RANGE_KEY];
+ [frame setObject:[NSMutableDictionary dictionary] forKey:BLOCK_VARIABLES_KEY];
+ [_openBlocksStack addObject:frame];
+
+ forceVarsToStack = YES;
+
+ // Report block start to delegate.
+ if (outputEnabled) val = [self reportBlockBoundaryStarted:YES];
+ } else if (blockEnded) {
+ if (!blockInfo ||
+ ([_openBlocksStack count] > 0 &&
+ ![(NSArray *)[[_openBlocksStack lastObject] objectForKey:BLOCK_END_NAMES_KEY] containsObject:matchMarker])) {
+ // The marker-handler just told us a block ended, but the current block was not
+ // started by that marker-handler. This means a syntax error exists in the template,
+ // specifically an unterminated block (the current block).
+ // This is considered an unrecoverable error.
+ NSString *errMsg;
+ if ([_openBlocksStack count] == 0) {
+ errMsg = [NSString stringWithFormat:@"Marker \"%@\" reported that a non-existent block ended",
+ matchMarker];
+ } else {
+ NSString *currBlockName = [[_openBlocksStack lastObject] objectForKey:BLOCK_NAME_KEY];
+ errMsg = [NSString stringWithFormat:@"Marker \"%@\" reported that a block ended, \
+but current block was started by \"%@\" marker",
+ matchMarker, currBlockName];
+ }
+ [self reportError:errMsg code:1 continuing:YES];
+ break;
+ }
+
+ // Report block end to delegate before removing stack frame, so we can send info dict.
+ NSMutableDictionary *frame = [_openBlocksStack lastObject];
+ [frame setObject:[matchInfo objectForKey:MARKER_RANGE_KEY] forKey:BLOCK_END_MARKER_RANGE_KEY];
+ if (outputEnabled) val = [self reportBlockBoundaryStarted:NO];
+
+ // Remove relevant stack frame.
+ if ([_openBlocksStack count] > 0) {
+ [_openBlocksStack removeLastObject];
+ }
+ }
+
+ // Process newVariables
+ if (newVariables) {
+ //NSLog(@"new vars %@", newVariables);
+ for (NSString *key in newVariables) {
+ [self setValue:[newVariables objectForKey:key] forVariable:key forceCurrentStackFrame:forceVarsToStack];
+ }
+ }
+
+ } else {
+ // Check to see if the first word of the match is a variable.
+ val = [self resolveVariable:matchMarker];
+ }
+
+ // Prepare result for output, if we have a result.
+ if (val && _outputDisabledCount == 0) {
+ // Process filter if specified.
+ NSString *filter = [matchInfo objectForKey:MARKER_FILTER_KEY];
+ if (filter) {
+ NSObject <MGTemplateFilter> *filterHandler = [_filters objectForKey:filter];
+ if (filterHandler) {
+ val = [filterHandler filterInvoked:filter
+ withArguments:[matchInfo objectForKey:MARKER_FILTER_ARGUMENTS_KEY] onValue:val];
+ }
+ }
+
+ // Output result.
+ [output appendFormat:@"%@", val];
+ } else if ((!val && !isMarker && _outputDisabledCount == 0) || (isMarker && !markerHandler)) {
+ // Call delegate's error-reporting method, if implemented.
+ [self reportError:[NSString stringWithFormat:@"\"%@\" is not a valid %@",
+ matchMarker, (isMarker) ? @"marker" : @"variable"]
+ code:((isMarker) ? 2 : 3) continuing:YES];
+ }
+ } else {
+ // Append output to end of template.
+ if (_outputDisabledCount == 0) {
+ [output appendFormat:@"%@", [templateContents substringWithRange:remainingRange]];
+ }
+
+ // Check to see if there are open blocks left over.
+ int openBlocks = [_openBlocksStack count];
+ if (openBlocks > 0) {
+ NSString *errMsg = [NSString stringWithFormat:@"Finished processing template, but %d %@ left open (%@).",
+ openBlocks,
+ (openBlocks == 1) ? @"block was" : @"blocks were",
+ [[_openBlocksStack valueForKeyPath:BLOCK_NAME_KEY] componentsJoinedByString:@", "]];
+ [self reportError:errMsg code:6 continuing:YES];
+ }
+
+ // Ensure we terminate the loop.
+ remainingRange.location = NSNotFound;
+ }
+ }
+
+ // Tell all marker-handlers we're done.
+ [[_markers allValues] makeObjectsPerformSelector:@selector(engineFinishedProcessingTemplate)];
+
+ // Inform delegate we're done.
+ [self reportTemplateProcessingFinished];
+
+ return output;
+}
+
+
+- (NSString *)processTemplateInFileAtPath:(NSString *)templatePath withVariables:(NSDictionary *)variables
+{
+ NSString *result = nil;
+ NSStringEncoding enc;
+ NSString *templateString = [NSString stringWithContentsOfFile:templatePath usedEncoding:&enc error:NULL];
+ if (templateString) {
+ result = [self processTemplate:templateString withVariables:variables];
+ }
+ return result;
+}
+
+
+#pragma mark Properties
+
+
+@synthesize markerStartDelimiter;
+@synthesize markerEndDelimiter;
+@synthesize expressionStartDelimiter;
+@synthesize expressionEndDelimiter;
+@synthesize filterDelimiter;
+@synthesize literalStartMarker;
+@synthesize literalEndMarker;
+@synthesize remainingRange;
+@synthesize delegate;
+@synthesize matcher;
+@synthesize templateContents;
+
+
+@end
View
14 Common/ThirdParty/MGTemplateEngine/MGTemplateFilter.h
@@ -0,0 +1,14 @@
+/*
+ * MGTemplateFilter.h
+ *
+ * Created by Matt Gemmell on 12/05/2008.
+ * Copyright 2008 Instinctive Code. All rights reserved.
+ *
+ */
+
+@protocol MGTemplateFilter
+
+- (NSArray *)filters;
+- (NSObject *)filterInvoked:(NSString *)filter withArguments:(NSArray *)args onValue:(NSObject *)value;
+
+@end
View
41 Common/ThirdParty/MGTemplateEngine/MGTemplateMarker.h
@@ -0,0 +1,41 @@
+/*
+ * MGTemplateMarker.h
+ *
+ * Created by Matt Gemmell on 12/05/2008.
+ * Copyright 2008 Instinctive Code. All rights reserved.
+ *
+ */
+
+#import "MGTemplateEngine.h"
+
+@protocol MGTemplateMarker
+@required
+- (id)initWithTemplateEngine:(MGTemplateEngine *)engine; // to avoid retain cycles, use a weak reference for engine.
+- (NSArray *)markers; // array of markers (each unique across all markers) this object handles.
+- (NSArray *)endMarkersForMarker:(NSString *)marker; // returns the possible corresponding end-markers for a marker which has just started a block.
+- (NSObject *)markerEncountered:(NSString *)marker withArguments:(NSArray *)args inRange:(NSRange)markerRange
+ blockStarted:(BOOL *)blockStarted blockEnded:(BOOL *)blockEnded
+ outputEnabled:(BOOL *)outputEnabled nextRange:(NSRange *)nextRange
+ currentBlockInfo:(NSDictionary *)blockInfo newVariables:(NSDictionary **)newVariables;
+/* Notes for -markerEncountered:... method
+ Arguments:
+ marker: marker encountered by the template engine
+ args: arguments to the marker, in order
+ markerRange: the range of the marker encountered in the engine's templateString
+ blockStarted: pointer to BOOL. Set it to YES if the marker just started a block.
+ blockEnded: pointer to BOOL. Set it to YES if the marker just ended a block.
+ Note: you should never set both blockStarted and blockEnded in the same call.
+ outputEnabled: pointer to BOOL, indicating whether the engine is currently outputting. Can be changed to switch output on/off.
+ nextRange: the next range in the engine's templateString which will be searched. Can be modified if necessary.
+ currentBlockInfo: information about the current block, if the block was started by this handler; otherwise nil.
+ Note: if supplied, will include a dictionary of variables set for the current block.
+ newVariables: variables to set in the template context. If blockStarted is YES, these will be scoped only within the new block.
+ Note: if currentBlockInfo was specified, variables set in the return dictionary will override/update any variables of
+ the same name in currentBlockInfo's variables. This is for ease of updating loop-counters or such.
+ Returns:
+ A return value to insert into the template output, or nil if nothing should be inserted.
+ */
+
+- (void)engineFinishedProcessingTemplate;
+
+@end
View
15 Common/ThirdParty/MGTemplateEngine/MGTemplateStandardFilters.h
@@ -0,0 +1,15 @@
+//
+// MGTemplateStandardFilters.h
+//
+// Created by Matt Gemmell on 13/05/2008.
+// Copyright 2008 Instinctive Code. All rights reserved.
+//
+
+#import "MGTemplateFilter.h"
+
+
+@interface MGTemplateStandardFilters : NSObject <MGTemplateFilter> {
+
+}
+
+@end
View
56 Common/ThirdParty/MGTemplateEngine/MGTemplateStandardFilters.m
@@ -0,0 +1,56 @@
+//
+// MGTemplateStandardFilters.m
+//
+// Created by Matt Gemmell on 13/05/2008.
+// Copyright 2008 Instinctive Code. All rights reserved.
+//
+
+#import "MGTemplateStandardFilters.h"
+
+
+#define UPPERCASE @"uppercase"
+#define LOWERCASE @"lowercase"
+#define CAPITALIZED @"capitalized"
+#define DATE_FORMAT @"date_format"
+
+
+@implementation MGTemplateStandardFilters
+
+
+- (NSArray *)filters
+{
+ return [NSArray arrayWithObjects:
+ UPPERCASE, LOWERCASE, CAPITALIZED,
+ DATE_FORMAT,
+ nil];
+}
+
+
+- (NSObject *)filterInvoked:(NSString *)filter withArguments:(NSArray *)args onValue:(NSObject *)value
+{
+ if ([filter isEqualToString:UPPERCASE]) {
+ return [[NSString stringWithFormat:@"%@", value] uppercaseString];
+
+ } else if ([filter isEqualToString:LOWERCASE]) {
+ return [[NSString stringWithFormat:@"%@", value] lowercaseString];
+
+ } else if ([filter isEqualToString:CAPITALIZED]) {
+ return [[NSString stringWithFormat:@"%@", value] capitalizedString];
+
+ } else if ([filter isEqualToString:DATE_FORMAT]) {
+ // Formats NSDates according to Unicode syntax:
+ // http://unicode.org/reports/tr35/tr35-4.html#Date_Format_Patterns
+ // e.g. "dd MM yyyy" etc.
+ if ([value isKindOfClass:[NSDate class]] && [args count] == 1) {
+ NSDateFormatter *dateFormatter = [[[NSDateFormatter alloc] init] autorelease];
+ [dateFormatter setFormatterBehavior:NSDateFormatterBehavior10_4];
+ NSString *format = [args objectAtIndex:0];
+ [dateFormatter setDateFormat:format];
+ return [dateFormatter stringFromDate:(NSDate *)value];
+ }
+
+ }
+ return value;
+}
+
+@end
View
24 Common/ThirdParty/MGTemplateEngine/MGTemplateStandardMarkers.h
@@ -0,0 +1,24 @@
+//
+// MGTemplateStandardMarkers.h
+//
+// Created by Matt Gemmell on 13/05/2008.
+// Copyright 2008 Instinctive Code. All rights reserved.
+//
+
+#import "MGTemplateEngine.h"
+#import "MGTemplateMarker.h"
+
+@interface MGTemplateStandardMarkers : NSObject <MGTemplateMarker> {
+ MGTemplateEngine *engine; // weak ref
+ NSMutableArray *forStack;
+ NSMutableArray *sectionStack;
+ NSMutableArray *ifStack;
+ NSMutableArray *commentStack;
+ NSMutableDictionary *cycles;
+}
+
+- (BOOL)currentBlock:(NSDictionary *)blockInfo matchesTopOfStack:(NSMutableArray *)stack;
+- (BOOL)argIsNumeric:(NSString *)arg intValue:(int *)val checkVariables:(BOOL)checkVars;
+- (BOOL)argIsTrue:(NSString *)arg;
+
+@end
View
634 Common/ThirdParty/MGTemplateEngine/MGTemplateStandardMarkers.m
@@ -0,0 +1,634 @@
+//
+// MGTemplateStandardMarkers.m
+//
+// Created by Matt Gemmell on 13/05/2008.
+// Copyright 2008 Instinctive Code. All rights reserved.
+//
+
+#import "MGTemplateStandardMarkers.h"
+#import "MGTemplateFilter.h"
+
+//==============================================================================
+
+#define FOR_START @"for"
+#define FOR_END @"/for"
+
+#define FOR_TYPE_ENUMERATOR @"in" // e.g. for thing in things
+#define FOR_TYPE_RANGE @"to" // e.g. for 1 to 5
+#define FOR_REVERSE @"reversed"
+
+#define FOR_LOOP_VARS @"currentLoop"
+#define FOR_LOOP_CURR_INDEX @"currentIndex"
+#define FOR_LOOP_START_INDEX @"startIndex"
+#define FOR_LOOP_END_INDEX @"endIndex"
+#define FOR_PARENT_LOOP @"parentLoop"
+
+#define STACK_START_MARKER_RANGE @"markerRange"
+#define STACK_START_REMAINING_RANGE @"remainingRange"
+#define FOR_STACK_ENUMERATOR @"enumerator"
+#define FOR_STACK_ENUM_VAR @"enumeratorVariable"
+#define FOR_STACK_DISABLED_OUTPUT @"disabledOutput"
+
+//==============================================================================
+
+#define SECTION_START @"section"
+#define SECTION_END @"/section"
+
+//==============================================================================
+
+#define IF_START @"if"
+#define ELSE @"else"
+#define IF_END @"/if"
+
+#define IF_VARS @"currentIf"
+#define DISABLE_OUTPUT @"shouldDisableOutput"
+#define IF_ARG_TRUE @"argumentTrue"
+#define IF_ELSE_SEEN @"elseEncountered"
+
+//==============================================================================
+
+#define NOW @"now"
+
+//==============================================================================
+
+#define COMMENT_START @"comment"
+#define COMMENT_END @"/comment"
+
+//==============================================================================
+
+#define LOAD @"load"
+
+//==============================================================================
+
+#define CYCLE @"cycle"
+#define CYCLE_INDEX @"lastIndex"
+#define CYCLE_VALUES @"value"
+
+//==============================================================================
+
+#define SET @"set"
+
+//==============================================================================
+
+
+@implementation MGTemplateStandardMarkers
+
+
+- (id)initWithTemplateEngine:(MGTemplateEngine *)theEngine
+{
+ if ((self = [super init])) {
+ engine = theEngine;
+ forStack = [[NSMutableArray alloc] init];
+ sectionStack = [[NSMutableArray alloc] init];
+ ifStack = [[NSMutableArray alloc] init];
+ commentStack = [[NSMutableArray alloc] init];
+ cycles = [[NSMutableDictionary alloc] init];
+ }
+ return self;
+}
+
+
+- (void)dealloc
+{
+ engine = nil;
+ [forStack release];
+ forStack = nil;
+ [sectionStack release];
+ sectionStack = nil;
+ [ifStack release];
+ ifStack = nil;
+ [commentStack release];
+ commentStack = nil;
+ [cycles release];
+ cycles = nil;
+
+ [super dealloc];
+}
+
+
+- (NSArray *)markers
+{
+ return [NSArray arrayWithObjects:
+ FOR_START, FOR_END,
+ SECTION_START, SECTION_END,
+ IF_START, ELSE, IF_END,
+ NOW,
+ COMMENT_START, COMMENT_END,
+ LOAD,
+ CYCLE,
+ SET,
+ nil];
+}
+
+
+- (NSArray *)endMarkersForMarker:(NSString *)marker
+{
+ if ([marker isEqualToString:FOR_START]) {
+ return [NSArray arrayWithObjects:FOR_END, nil];
+ } else if ([marker isEqualToString:SECTION_START]) {
+ return [NSArray arrayWithObjects:SECTION_END, nil];
+ } else if ([marker isEqualToString:IF_START]) {
+ return [NSArray arrayWithObjects:IF_END, ELSE, nil];
+ } else if ([marker isEqualToString:COMMENT_START]) {
+ return [NSArray arrayWithObjects:COMMENT_END, nil];
+ }
+ return nil;
+}
+
+
+- (NSObject *)markerEncountered:(NSString *)marker withArguments:(NSArray *)args inRange:(NSRange)markerRange
+ blockStarted:(BOOL *)blockStarted blockEnded:(BOOL *)blockEnded
+ outputEnabled:(BOOL *)outputEnabled nextRange:(NSRange *)nextRange
+ currentBlockInfo:(NSDictionary *)blockInfo newVariables:(NSDictionary **)newVariables
+{
+ if ([marker isEqualToString:FOR_START]) {
+ if (args && [args count] >= 3) {
+ // Determine which type of loop this is.
+ BOOL isRange = YES;
+ if ([[args objectAtIndex:1] isEqualToString:FOR_TYPE_ENUMERATOR]) {
+ isRange = NO;
+ }
+ BOOL reversed = NO;
+ if ([args count] == 4 && [[args objectAtIndex:3] isEqualToString:FOR_REVERSE]) {
+ reversed = YES;
+ }