Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
branch: master
Fetching contributors…

Octocat-spinner-32-eaf2f5

Cannot retrieve contributors at this time

file 192 lines (158 sloc) 6.843 kb
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
//
// 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 = [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
Something went wrong with that request. Please try again.