-
Notifications
You must be signed in to change notification settings - Fork 134
/
ICUMatcher.m
232 lines (174 loc) · 5.95 KB
/
ICUMatcher.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
//
// ICUMatcher.m
// CocoaICU
//
// Created by Aaron Evans on 11/29/06.
// Copyright 2006 Aaron Evans. All rights reserved.
//
#import "ICUMatcher.h"
#import "NSStringICUAdditions.h"
#import "ICUPattern.h"
size_t InitialGroupSize = 128;
struct URegularExpression;
/**
* Structure represeting a compiled regular rexpression, plus the results
* of a match operation.
* @draft ICU 3.0
*/
typedef struct URegularExpression URegularExpression;
#define U_HIDE_DRAFT_API 1
#define U_DISABLE_RENAMING 1
#import <unicode/uregex.h>
#import <unicode/ustring.h>
#define CheckStatus(status) if(U_FAILURE(status)) { [NSException raise:@"Find Exception" format:@"%s", u_errorName(status)]; }
@interface ICUMatcher (Private)
-(NSString *)performReplacementWithString:(NSString *)aReplacementString replaceAll:(BOOL)replacingAll;
@end
@implementation ICUMatcher
+(ICUMatcher *)matcherWithPattern:(ICUPattern *)p overString:(NSString *)stringToSearchOver {
return [[[ICUMatcher class] alloc] initWithPattern:p overString:stringToSearchOver];
}
-(ICUMatcher *)initWithPattern:(ICUPattern *)p overString:(NSString *)aStringToSearch {
self = [super init];
if (!self) {
return nil;
}
[self setPattern:p];
[[self pattern] setStringToSearch:aStringToSearch];
return self;
}
-(void)setPattern:(ICUPattern *)p {
pattern = p;
}
-(BOOL)matches {
URegularExpression *re = [[self pattern] re];
UErrorCode status = 0;
BOOL matches = uregex_matches(re, 0, &status);
CheckStatus(status);
return matches;
}
/*
Find the first matching substring of the input string that matches the pattern.
The search for a match begins at the specified index. If a match is found, uregex_start(), uregex_end(), and uregex_group() will provide more information regarding the match.
*/
-(BOOL)findNext {
URegularExpression *re = [[self pattern] re];
UErrorCode status = 0;
UBool r = uregex_findNext(re, &status);
CheckStatus(status);
return r;
}
-(BOOL)findFromIndex:(unsigned)idx {
URegularExpression *re = [[self pattern] re];
[self reset];
UErrorCode status = 0;
UBool r = uregex_find(re, idx, &status);
CheckStatus(status);
return r;
}
-(NSString *)group {
NSString *stringToMatch = [[self pattern] stringToSearch];
return [stringToMatch substringWithRange:[self rangeOfMatch]];
}
-(NSString *)groupAtIndex:(unsigned)groupIndex {
size_t groupSize = InitialGroupSize;
URegularExpression *re = [[self pattern] re];
while(YES) {
UErrorCode status = 0;
UChar *dest = (UChar *)NSZoneCalloc(nil, groupSize, sizeof(UChar));
int32_t buffSize = uregex_group(re, groupIndex, dest, (int32_t)groupSize, &status);
if(U_BUFFER_OVERFLOW_ERROR == status) {
groupSize *= 2;
NSZoneFree(nil, dest);
continue;
}
CheckStatus(status);
NSString *result = [[NSString alloc] initWithBytes:dest length:buffSize*sizeof(UChar) encoding:[NSString nativeUTF16Encoding]];
NSZoneFree(nil, dest);
return result;
}
}
-(unsigned)numberOfGroups {
URegularExpression *re = [[self pattern] re];
UErrorCode status = 0;
int numberOfGroups = uregex_groupCount(re, &status);
CheckStatus(status);
return numberOfGroups;
}
-(BOOL)lookingAt:(unsigned)idx {
#pragma unused(idx)
UErrorCode status = 0;
URegularExpression *re = [[self pattern] re];
BOOL matches = uregex_lookingAt(re, 0, &status);
CheckStatus(status);
return matches;
}
-(ICUPattern *)pattern {
return pattern;
}
-(NSString *)performReplacementWithString:(NSString *)aReplacementString replaceAll:(BOOL)replacingAll {
UErrorCode status = 0;
UChar *replacementText = [aReplacementString UTF16String];
URegularExpression *re = [[self pattern] re];
NSUInteger searchTextLength = [[[self pattern] stringToSearch] length];
BOOL replacementCompleted = NO;
int resultLength = 0;
size_t destStringBufferSize = searchTextLength * 2;
UChar *destString = NULL;
while(!replacementCompleted) {
if(!destString) // attempts to increase buffer happen on failure below
destString = NSZoneCalloc(nil, destStringBufferSize, sizeof(UChar));
if(!destString)
[NSException raise:@"Find Exception"
format:@"Could not allocate memory for replacement string"];
status = 0;
if(replacingAll)
resultLength = uregex_replaceAll(re, replacementText, -1, destString, (int32_t)destStringBufferSize, &status);
else
resultLength = uregex_replaceFirst(re, replacementText, -1, destString, (int32_t)destStringBufferSize, &status);
// realloc some more space if possible
if(status == U_BUFFER_OVERFLOW_ERROR) {
destStringBufferSize = resultLength + 1;
UChar *prevString = destString;
destString = NSZoneRealloc(nil, destString, destStringBufferSize*sizeof(UChar));
if(destString == NULL) {
NSZoneFree(nil, prevString);
[NSException raise:@"Find Exception"
format:@"Could not allocate memory for replacement string"];
}
} else if(U_FAILURE(status)) {
NSZoneFree(nil, destString);
[NSException raise:@"Find Exception"
format:@"Could not perform find and replace: %s", u_errorName(status)];
} else {
replacementCompleted = YES;
}
}
NSString *result = [[NSString alloc] initWithBytes:destString
length:resultLength * sizeof(UChar)
encoding:[NSString nativeUTF16Encoding]];
NSZoneFree(nil, destString);
return result;
}
-(NSString *)replaceAllWithString:(NSString *)aReplacementString {
return [self performReplacementWithString:aReplacementString replaceAll:YES];
}
-(NSString *)replaceFirstWithString:(NSString *)aReplacementString {
return [self performReplacementWithString:aReplacementString replaceAll:NO];
}
-(void)reset {
[[self pattern] reset];
}
-(NSRange)rangeOfMatch {
return [self rangeOfMatchGroup:0];
}
-(NSRange)rangeOfMatchGroup:(unsigned)groupNumber {
UErrorCode status = 0;
URegularExpression *re = [[self pattern] re];
int start = uregex_start(re, groupNumber, &status);
CheckStatus(status);
int end = uregex_end(re, groupNumber, &status);
CheckStatus(status);
return NSMakeRange(start == -1 ? NSNotFound : start, end-start);
}
@end