#import "GHGherkinLine.h" #import "GHGherkinLanguageConstants.h" #import "GHGherkinLineSpan.h" #import "NSString+Trim.h" @interface GHGherkinLine () @property (nonatomic, strong) NSString * lineText; @property (nonatomic, assign) NSUInteger lineNumber; - (NSMutableArray *> *)splitCellsFromRow:(NSString *)theRow; - (NSString *)trim:(NSString *)theString trimmedCharsCount:(NSUInteger *)theTrimmedCharsCount; + (BOOL)startsWithFrom:(NSString *)theText index:(NSInteger)theTextIndex value:(NSString *)theValue; @end @implementation GHGherkinLine { NSString * trimmedLineText; } @synthesize lineText; @synthesize lineNumber; - (id)initWithLine:(NSString *)theLine lineNumber:(NSUInteger)theLineNumber { if (self = [super init]) { lineNumber = theLineNumber; lineText = theLine; trimmedLineText = [theLine stringByTrimmingStartWithCharactersInSet: [NSCharacterSet whitespaceAndNewlineCharacterSet]]; } return self; } - (void)detach { //nop } - (NSUInteger)indent { return [lineText length] - [trimmedLineText length]; } - (BOOL)empty { return trimmedLineText && ![trimmedLineText length]; } - (BOOL)hasPrefix:(NSString *)theText { return [trimmedLineText hasPrefix: theText]; } - (BOOL)hasTitleKeywordPrefix:(NSString *)theText { NSUInteger textLength = [theText length]; return [trimmedLineText length] > textLength && [trimmedLineText hasPrefix: theText] && [GHGherkinLine startsWithFrom: trimmedLineText index: textLength value: GHTitleKeywordSeparator]; } + (BOOL)startsWithFrom:(NSString *)theText index:(NSInteger)theTextIndex value:(NSString *)theValue { NSUInteger valueLength = [theValue length]; NSString * textSubstring = [theText substringWithRange: NSMakeRange(theTextIndex, valueLength)]; NSString * valueSubstring = [theValue substringWithRange: NSMakeRange(0, valueLength)]; return [textSubstring compare: valueSubstring options: NSLiteralSearch] == NSOrderedSame; } - (NSString *)lineTextByRemovingIndent:(NSInteger)theIndentToRemove { if (theIndentToRemove < 0 || theIndentToRemove > [self indent]) return trimmedLineText; return [lineText substringFromIndex: theIndentToRemove]; } - (NSString *)trimmedRest:(NSUInteger)theLength { return [[trimmedLineText substringFromIndex: theLength] stringByTrimmingCharactersInSet: [NSCharacterSet whitespaceAndNewlineCharacterSet]]; } - (NSArray *)tags { NSInteger position = [self indent]; NSMutableArray * tags = [[NSMutableArray alloc] init]; for (NSString * item in [trimmedLineText componentsSeparatedByCharactersInSet: [NSCharacterSet whitespaceAndNewlineCharacterSet]]) { if ([item length]) { [tags addObject: [[GHGherkinLineSpan alloc] initWithColumn: position + 1 text: item]]; position += [item length]; } position++; // separator } return [[NSArray alloc] initWithArray: tags]; } - (NSArray *)tableCells { NSMutableArray *> * items = [self splitCellsFromRow: trimmedLineText]; [items removeLastObject]; NSMutableArray * tableCells = [[NSMutableArray alloc] init]; BOOL isBeforeFirst = YES; for (NSArray * item in items) { if (!isBeforeFirst) { NSUInteger trimmedStart = 0; NSString * cellText = [self trim: [item firstObject] trimmedCharsCount: &trimmedStart]; NSUInteger cellPosition = [[item lastObject] integerValue] + trimmedStart; if (![cellText length]) cellPosition = [[item lastObject] integerValue]; [tableCells addObject: [[GHGherkinLineSpan alloc] initWithColumn: [self indent] + cellPosition + 1 text: cellText]]; } isBeforeFirst = NO; } return tableCells; } - (NSMutableArray *> *)splitCellsFromRow:(NSString *)theRow { NSMutableString * cell = [[NSMutableString alloc] init]; NSInteger startPos = 0; NSMutableArray *> * items = [[NSMutableArray alloc] init]; NSUInteger rowLength = [theRow length]; for (NSUInteger i = 0; i < rowLength; i++) { NSString * stringCharacter = [theRow substringWithRange: NSMakeRange(i, 1)]; if ([stringCharacter isEqualToString: GHTableCellSeparator]) { [items addObject: @[ cell, @(startPos)]]; cell = [[NSMutableString alloc] init]; startPos = i + 1; } else if ([stringCharacter isEqualToString: GHTableCellEscape]) { stringCharacter = i + 1 < rowLength ? [theRow substringWithRange: NSMakeRange(++i, 1)] : nil; if ([stringCharacter isEqualToString: GHTableCellNewlineEscape]) [cell appendString: @"\n"]; else { if (![stringCharacter isEqualToString: GHTableCellSeparator] && ![stringCharacter isEqualToString: GHTableCellEscape]) [cell appendString: GHTableCellEscape]; [cell appendString: stringCharacter]; } } else [cell appendString: stringCharacter]; } [items addObject: @[ cell, @(startPos)]]; return items; } - (NSString *)trim:(NSString *)theString trimmedCharsCount:(NSUInteger *)theTrimmedCharsCount { NSUInteger trimmedCharsCount = 0; NSCharacterSet * whitespaceAndNewLineCharacterSet = [NSCharacterSet whitespaceAndNewlineCharacterSet]; while (trimmedCharsCount < [theString length] && [whitespaceAndNewLineCharacterSet characterIsMember: [theString characterAtIndex: trimmedCharsCount]]) trimmedCharsCount++; *theTrimmedCharsCount = trimmedCharsCount; return [theString stringByTrimmingCharactersInSet: whitespaceAndNewLineCharacterSet]; } @end