mirror of
https://github.com/gnustep/libs-base.git
synced 2025-04-22 16:33:29 +00:00
Merge pull request #416 from gnustep/enumerateSubstringInRange
Fix broken enumerateSubstringsInRange:options:usingBlock: implementation
This commit is contained in:
commit
3bcb484691
2 changed files with 264 additions and 103 deletions
|
@ -6404,7 +6404,7 @@ static NSFileManager *fm = nil;
|
|||
else if (substringType == NSStringEnumerationByWords
|
||||
|| substringType == NSStringEnumerationBySentences)
|
||||
{
|
||||
#if GS_USE_ICU
|
||||
#if GS_USE_ICU
|
||||
// These macros may be useful elsewhere.
|
||||
#define GS_U_HANDLE_ERROR(errorCode, description) do { \
|
||||
if (U_FAILURE(errorCode)) { \
|
||||
|
@ -6422,6 +6422,7 @@ static NSFileManager *fm = nil;
|
|||
UErrorCode errorCode = U_ZERO_ERROR;
|
||||
const char *locale;
|
||||
UBreakIterator *breakIterator;
|
||||
int32_t start, end;
|
||||
|
||||
[self getCharacters: characters range: range];
|
||||
/* @ss=standard will use lists of common abbreviations,
|
||||
|
@ -6438,55 +6439,42 @@ static NSFileManager *fm = nil;
|
|||
length, // textLength
|
||||
&errorCode);
|
||||
GS_U_HANDLE_ERROR(errorCode, @"opening ICU break iterator");
|
||||
ubrk_first(breakIterator);
|
||||
while (YES)
|
||||
{
|
||||
// Make sure it's a valid substring.
|
||||
BOOL isValidSubstring = YES;
|
||||
int32_t nextPosition;
|
||||
NSUInteger nextLocation;
|
||||
NSRange enclosingRange;
|
||||
|
||||
if (byWords)
|
||||
{
|
||||
int32_t ruleStatus = ubrk_getRuleStatus(breakIterator);
|
||||
/* From ICU User Guide:
|
||||
* A status value UBRK_WORD_NONE indicates that the boundary
|
||||
* does not start a word or number.
|
||||
* However, valid words seem to be UBRK_WORD_NONE, and invalid
|
||||
* words seem to be UBRK_WORD_NONE_LIMIT.
|
||||
*/
|
||||
isValidSubstring = ruleStatus != UBRK_WORD_NONE_LIMIT;
|
||||
// NSLog(@"Status for position %d (%d): %d", (int)currentLocation, (int)ubrk_current(breakIterator), (int) ruleStatus);
|
||||
}
|
||||
|
||||
nextPosition = ubrk_next(breakIterator);
|
||||
if (nextPosition == UBRK_DONE) break;
|
||||
|
||||
nextLocation = range.location + nextPosition;
|
||||
// Same as substringRange
|
||||
enclosingRange
|
||||
= NSMakeRange(currentLocation, nextLocation - currentLocation);
|
||||
// FIXME: Implement reverse enumeration by using ubrk_last and ubrk_previous
|
||||
start = ubrk_first(breakIterator);
|
||||
for (end = ubrk_next(breakIterator); end != UBRK_DONE; start = end, end = ubrk_next(breakIterator))
|
||||
{
|
||||
BOOL isValidSubstring = YES;
|
||||
NSUInteger nextLocation;
|
||||
NSRange enclosingRange;
|
||||
|
||||
if (isValidSubstring)
|
||||
{
|
||||
if (byWords)
|
||||
{
|
||||
int32_t ruleStatus;
|
||||
|
||||
ruleStatus = ubrk_getRuleStatus(breakIterator);
|
||||
isValidSubstring = ruleStatus != UBRK_WORD_NONE;
|
||||
}
|
||||
|
||||
nextLocation = range.location + end;
|
||||
enclosingRange = NSMakeRange(currentLocation, end - start);
|
||||
currentLocation = nextLocation;
|
||||
|
||||
if (isValidSubstring)
|
||||
{
|
||||
CALL_BLOCK(block,
|
||||
substringNotRequired
|
||||
? nil
|
||||
: [self substringWithRange: enclosingRange],
|
||||
substringNotRequired ? nil : [self substringWithRange: enclosingRange],
|
||||
enclosingRange,
|
||||
enclosingRange,
|
||||
&stop);
|
||||
if (stop) break;
|
||||
}
|
||||
|
||||
currentLocation = nextLocation;
|
||||
}
|
||||
#else
|
||||
}
|
||||
}
|
||||
#else
|
||||
NSWarnLog(@"NSStringEnumerationByWords and NSStringEnumerationBySentences"
|
||||
@" are not supported when GNUstep-base is compiled without ICU.");
|
||||
return;
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
else if (substringType == NSStringEnumerationByCaretPositions)
|
||||
{
|
||||
|
|
|
@ -1,97 +1,270 @@
|
|||
#import "ObjectTesting.h"
|
||||
#import <Foundation/NSAutoreleasePool.h>
|
||||
#import <Foundation/NSString.h>
|
||||
#import <Foundation/NSArray.h>
|
||||
|
||||
#if defined(__has_extension) && __has_extension(blocks)
|
||||
int main (int argc, const char * argv[])
|
||||
|
||||
void
|
||||
testMutationAffectingSubsequentCall()
|
||||
{
|
||||
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
|
||||
NSMutableString *mutableString;
|
||||
NSMutableArray *results;
|
||||
NSArray *expectedResults;
|
||||
NSRange range;
|
||||
|
||||
BOOL correctResults;
|
||||
BOOL correctCallCount;
|
||||
__block NSUInteger callCount = 0;
|
||||
|
||||
mutableString = [NSMutableString stringWithString:@"Hello World"];
|
||||
results = [NSMutableArray array];
|
||||
range = NSMakeRange(0, mutableString.length);
|
||||
expectedResults = @[ @"Hello", @"World" ];
|
||||
|
||||
[mutableString
|
||||
enumerateSubstringsInRange:range
|
||||
options:NSStringEnumerationByWords
|
||||
usingBlock:^(NSString *substring, NSRange substringRange,
|
||||
NSRange enclosingRange, BOOL *stop) {
|
||||
[results addObject:substring];
|
||||
callCount++;
|
||||
|
||||
if ([substring isEqualToString:@"Hello"])
|
||||
{
|
||||
// Simulate a mutation that affects subsequent
|
||||
// enumeration "Hello " is changed to "Hello"
|
||||
[mutableString
|
||||
deleteCharactersInRange:NSMakeRange(
|
||||
substringRange.location
|
||||
+ substringRange.length,
|
||||
1)];
|
||||
*stop = YES;
|
||||
}
|
||||
}];
|
||||
|
||||
[mutableString
|
||||
enumerateSubstringsInRange:NSMakeRange(5, mutableString.length - 5)
|
||||
options:NSStringEnumerationByWords
|
||||
usingBlock:^(NSString *substring, NSRange substringRange,
|
||||
NSRange enclosingRange, BOOL *stop) {
|
||||
[results addObject:substring];
|
||||
}];
|
||||
|
||||
correctResults = [results isEqualToArray:expectedResults];
|
||||
correctCallCount = (callCount == 1); // Ensure only one call before stopping
|
||||
|
||||
PASS(correctResults && correctCallCount,
|
||||
"Enumeration should adjust correctly after string mutation and handle "
|
||||
"subsequent calls appropriately.");
|
||||
}
|
||||
|
||||
void
|
||||
testBasicFunctionality()
|
||||
{
|
||||
NSString *string;
|
||||
NSMutableArray *results;
|
||||
NSArray *expected;
|
||||
NSRange range;
|
||||
BOOL result;
|
||||
|
||||
string = @"Hello World";
|
||||
results = [NSMutableArray array];
|
||||
range = NSMakeRange(0, string.length);
|
||||
expected = @[ @"Hello", @"World" ];
|
||||
|
||||
[string
|
||||
enumerateSubstringsInRange:range
|
||||
options:NSStringEnumerationByWords
|
||||
usingBlock:^(NSString *substring, NSRange substringRange,
|
||||
NSRange enclosingRange, BOOL *stop) {
|
||||
[results addObject:substring];
|
||||
}];
|
||||
|
||||
PASS_EQUAL(results, expected, "Should correctly enumerate words.");
|
||||
}
|
||||
|
||||
void
|
||||
testEmptyRange()
|
||||
{
|
||||
NSString *string;
|
||||
NSMutableArray *results;
|
||||
NSRange range;
|
||||
|
||||
string = @"Hello World";
|
||||
results = [NSMutableArray array];
|
||||
range = NSMakeRange(0, 0);
|
||||
|
||||
[string
|
||||
enumerateSubstringsInRange:range
|
||||
options:NSStringEnumerationByWords
|
||||
usingBlock:^(NSString *substring, NSRange substringRange,
|
||||
NSRange enclosingRange, BOOL *stop) {
|
||||
[results addObject:substring];
|
||||
}];
|
||||
|
||||
PASS(results.count == 0,
|
||||
"No substrings should be enumerated for an empty range.");
|
||||
}
|
||||
|
||||
void testLocationOffset() {
|
||||
NSString *string;
|
||||
NSMutableArray *results;
|
||||
NSArray *expected;
|
||||
NSRange range;
|
||||
|
||||
string = @"Hello World Continued";
|
||||
results = [NSMutableArray array];
|
||||
range = NSMakeRange(6, [string length] - 6);
|
||||
expected = @[ @"World", @"Continued"];
|
||||
|
||||
[string
|
||||
enumerateSubstringsInRange:range
|
||||
options:NSStringEnumerationByWords
|
||||
usingBlock:^(NSString *substring, NSRange substringRange,
|
||||
NSRange enclosingRange, BOOL *stop) {
|
||||
[results addObject:substring];
|
||||
}];
|
||||
|
||||
PASS_EQUAL(results, expected, "Should correctly enumerate words with location offset.");
|
||||
}
|
||||
|
||||
void
|
||||
testStoppingEnumeration()
|
||||
{
|
||||
NSString *string;
|
||||
NSMutableArray *results;
|
||||
NSRange range;
|
||||
|
||||
string = @"Hello World";
|
||||
results = [NSMutableArray array];
|
||||
range = NSMakeRange(0, [string length]);
|
||||
|
||||
__block BOOL didStop = NO;
|
||||
|
||||
[string
|
||||
enumerateSubstringsInRange:range
|
||||
options:NSStringEnumerationByWords
|
||||
usingBlock:^(NSString *substring, NSRange substringRange,
|
||||
NSRange enclosingRange, BOOL *stop) {
|
||||
if ([substring isEqualToString:@"Hello"])
|
||||
{
|
||||
*stop = YES;
|
||||
didStop = YES;
|
||||
}
|
||||
[results addObject:substring];
|
||||
}];
|
||||
|
||||
PASS(didStop && [results count] == 1 && [results[0] isEqualToString:@"Hello"],
|
||||
"Enumeration should stop after 'Hello'.");
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, const char *argv[])
|
||||
{
|
||||
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
|
||||
START_SET("Enumerate substrings by lines");
|
||||
|
||||
NSString* s1 = @"Line 1\nLine 2";
|
||||
NSString *s1 = @"Line 1\nLine 2";
|
||||
__block NSUInteger currentIteration = 0;
|
||||
[s1 enumerateSubstringsInRange:(NSRange){
|
||||
.location = 0,
|
||||
.length = [s1 length]
|
||||
} options: NSStringEnumerationByLines
|
||||
usingBlock: ^(NSString *substring, NSRange substringRange, NSRange enclosingRange, BOOL *stop) {
|
||||
NSLog(@"Substring range: {.location=%ld, .length=%ld}", substringRange.location, substringRange.length);
|
||||
NSLog(@"Enclosing range: {.location=%ld, .length=%ld}", enclosingRange.location, enclosingRange.length);
|
||||
NSLog(@"Substring: %@", substring);
|
||||
// *stop = YES;
|
||||
if(currentIteration == 0) PASS([substring isEqual: @"Line 1"], "First line of \"Line 1\\nLine 2\" is \"Line 1\"");
|
||||
if(currentIteration == 1) PASS([substring isEqual: @"Line 2"], "Second line of \"Line 1\\nLine 2\" is \"Line 2\"");
|
||||
currentIteration++;
|
||||
}];
|
||||
PASS(currentIteration == 2, "There are only two lines in \"Line 1\\nLine 2\"");
|
||||
[s1
|
||||
enumerateSubstringsInRange:(NSRange){.location = 0, .length = [s1 length]}
|
||||
options:NSStringEnumerationByLines
|
||||
usingBlock:^(NSString *substring, NSRange substringRange,
|
||||
NSRange enclosingRange, BOOL *stop) {
|
||||
// *stop = YES;
|
||||
if (currentIteration == 0)
|
||||
PASS([substring isEqual:@"Line 1"],
|
||||
"First line of \"Line 1\\nLine 2\" is \"Line 1\"");
|
||||
if (currentIteration == 1)
|
||||
PASS(
|
||||
[substring isEqual:@"Line 2"],
|
||||
"Second line of \"Line 1\\nLine 2\" is \"Line 2\"");
|
||||
currentIteration++;
|
||||
}];
|
||||
PASS(currentIteration == 2,
|
||||
"There are only two lines in \"Line 1\\nLine 2\"");
|
||||
END_SET("Enumerate substrings by lines");
|
||||
|
||||
START_SET("Enumerate substrings by paragraphs");
|
||||
|
||||
NSString* s1 = @"Paragraph 1\nParagraph 2";
|
||||
NSString *s1 = @"Paragraph 1\nParagraph 2";
|
||||
__block NSUInteger currentIteration = 0;
|
||||
[s1 enumerateSubstringsInRange:(NSRange){
|
||||
.location = 0,
|
||||
.length = [s1 length]
|
||||
} options: NSStringEnumerationByParagraphs
|
||||
usingBlock: ^(NSString *substring, NSRange substringRange, NSRange enclosingRange, BOOL *stop) {
|
||||
NSLog(@"Substring range: {.location=%ld, .length=%ld}", substringRange.location, substringRange.length);
|
||||
NSLog(@"Enclosing range: {.location=%ld, .length=%ld}", enclosingRange.location, enclosingRange.length);
|
||||
NSLog(@"Substring: %@", substring);
|
||||
// *stop = YES;
|
||||
if(currentIteration == 0) PASS([substring isEqual: @"Paragraph 1"], "First paragraph of \"Paragraph 1\\nParagraph 2\" is \"Paragraph 1\"");
|
||||
if(currentIteration == 1) PASS([substring isEqual: @"Paragraph 2"], "Second paragraph of \"Paragraph 1\\nParagraph 2\" is \"Paragraph 2\"");
|
||||
currentIteration++;
|
||||
}];
|
||||
PASS(currentIteration == 2, "There are only two paragraphs in \"Paragraph 1\\nParagraph 2\"");
|
||||
[s1 enumerateSubstringsInRange:(NSRange){.location = 0, .length = [s1 length]}
|
||||
options:NSStringEnumerationByParagraphs
|
||||
usingBlock:^(NSString *substring, NSRange substringRange,
|
||||
NSRange enclosingRange, BOOL *stop) {
|
||||
// *stop = YES;
|
||||
if (currentIteration == 0)
|
||||
PASS([substring isEqual:@"Paragraph 1"],
|
||||
"First paragraph of \"Paragraph 1\\nParagraph "
|
||||
"2\" is \"Paragraph 1\"");
|
||||
if (currentIteration == 1)
|
||||
PASS([substring isEqual:@"Paragraph 2"],
|
||||
"Second paragraph of \"Paragraph 1\\nParagraph "
|
||||
"2\" is \"Paragraph 2\"");
|
||||
currentIteration++;
|
||||
}];
|
||||
PASS(currentIteration == 2,
|
||||
"There are only two paragraphs in \"Paragraph 1\\nParagraph 2\"");
|
||||
END_SET("Enumerate substrings by paragraphs");
|
||||
|
||||
START_SET("Enumerate substrings by words");
|
||||
|
||||
NSString* s1 = @"Word1 word2.";
|
||||
testBasicFunctionality();
|
||||
testEmptyRange();
|
||||
testLocationOffset();
|
||||
testStoppingEnumeration();
|
||||
testMutationAffectingSubsequentCall();
|
||||
|
||||
NSString *s1 = @"Word1 word2.";
|
||||
__block NSUInteger currentIteration = 0;
|
||||
[s1 enumerateSubstringsInRange:(NSRange){
|
||||
.location = 0,
|
||||
.length = [s1 length]
|
||||
} options: NSStringEnumerationByWords
|
||||
usingBlock: ^(NSString *substring, NSRange substringRange, NSRange enclosingRange, BOOL *stop) {
|
||||
NSLog(@"Substring range: {.location=%ld, .length=%ld}", substringRange.location, substringRange.length);
|
||||
NSLog(@"Enclosing range: {.location=%ld, .length=%ld}", enclosingRange.location, enclosingRange.length);
|
||||
NSLog(@"Substring: %@", substring);
|
||||
// *stop = YES;
|
||||
if(currentIteration == 0) PASS([substring isEqual: @"Word1"], "First word of \"Word1 word2.\" is \"Word1\"");
|
||||
if(currentIteration == 1) PASS([substring isEqual: @"word2"], "Second word of \"Word1 word2.\" is \"word2\"");
|
||||
currentIteration++;
|
||||
}];
|
||||
[s1 enumerateSubstringsInRange:(NSRange){.location = 0, .length = [s1 length]}
|
||||
options:NSStringEnumerationByWords
|
||||
usingBlock:^(NSString *substring, NSRange substringRange,
|
||||
NSRange enclosingRange, BOOL *stop) {
|
||||
// *stop = YES;
|
||||
if (currentIteration == 0)
|
||||
PASS([substring isEqual:@"Word1"],
|
||||
"First word of \"Word1 word2.\" is \"Word1\"");
|
||||
if (currentIteration == 1)
|
||||
PASS([substring isEqual:@"word2"],
|
||||
"Second word of \"Word1 word2.\" is \"word2\"");
|
||||
currentIteration++;
|
||||
}];
|
||||
PASS(currentIteration == 2, "There are only two words in \"Word1 word2.\"");
|
||||
END_SET("Enumerate substrings by words");
|
||||
|
||||
START_SET("Enumerate substrings by sentences");
|
||||
|
||||
NSString* s1 = @"Sentence 1. Sentence 2.";
|
||||
NSString *s1 = @"Sentence 1. Sentence 2.";
|
||||
__block NSUInteger currentIteration = 0;
|
||||
[s1 enumerateSubstringsInRange:(NSRange){
|
||||
.location = 0,
|
||||
.length = [s1 length]
|
||||
} options: NSStringEnumerationBySentences
|
||||
usingBlock: ^(NSString *substring, NSRange substringRange, NSRange enclosingRange, BOOL *stop) {
|
||||
NSLog(@"Substring range: {.location=%ld, .length=%ld}", substringRange.location, substringRange.length);
|
||||
NSLog(@"Enclosing range: {.location=%ld, .length=%ld}", enclosingRange.location, enclosingRange.length);
|
||||
NSLog(@"Substring: %@", substring);
|
||||
// *stop = YES;
|
||||
if(currentIteration == 0) PASS([substring isEqual: @"Sentence 1. "], "First sentence of \"Sentence 1. Sentence 2.\" is \"Sentence 1. \"");
|
||||
if(currentIteration == 1) PASS([substring isEqual: @"Sentence 2."], "Second sentence of \"Sentence 1. Sentence 2.\" is \"Sentence 2.\"");
|
||||
currentIteration++;
|
||||
}];
|
||||
PASS(currentIteration == 2, "There are only two sentences in \"Sentence 1. Sentence 2.");
|
||||
[s1 enumerateSubstringsInRange:(NSRange){.location = 0, .length = [s1 length]}
|
||||
options:NSStringEnumerationBySentences
|
||||
usingBlock:^(NSString *substring, NSRange substringRange,
|
||||
NSRange enclosingRange, BOOL *stop) {
|
||||
// *stop = YES;
|
||||
if (currentIteration == 0)
|
||||
PASS([substring isEqual:@"Sentence 1. "],
|
||||
"First sentence of \"Sentence 1. Sentence 2.\" "
|
||||
"is \"Sentence 1. \"");
|
||||
if (currentIteration == 1)
|
||||
PASS([substring isEqual:@"Sentence 2."],
|
||||
"Second sentence of \"Sentence 1. Sentence 2.\" "
|
||||
"is \"Sentence 2.\"");
|
||||
currentIteration++;
|
||||
}];
|
||||
PASS(currentIteration == 2,
|
||||
"There are only two sentences in \"Sentence 1. Sentence 2.");
|
||||
END_SET("Enumerate substrings by sentences");
|
||||
|
||||
[pool drain];
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
int main (int argc, const char * argv[])
|
||||
int
|
||||
main(int argc, const char *argv[])
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue