Adium

Changeset 24146

Show
Ignore:
Timestamp:
07/01/2008 03:02:10 PM (5 months ago)
Author:
sholt
Message:

Many enhancements to AutoHyperlinks.framework:

  • Thread Safety:
    • All global variables have been removed.
    • Flex generated FSM and all calling functions are now reentrant.
  • API Overhaul:
    • AHHyperlinkScanner now operates similar to NSScanner:
      • A scanner is created by one of:
        • +[AHHyperlinkScanner hyperlinkScannerWithString:]
        • +[AHHyperlinkScanner strictHyperlinkScannerWithString:]
        • +[AHHyperlinkScanner hyperlinkScannerWithAttributedString:]
        • +[AHHyperlinkScanner strictHyperlinkScannerWithAttributedString:]
      • Link scanning is now accomplished through:
        • -[AHHyperlinkScanner nextURI]
        • -[AHHyperlinkScanner allURIs]
        • -[AHHyperlinkScanner linkifiedString]
      • You may get and set the current scanner index
  • Performance Improvements:
    • All instances of NSLock are now unnecessary and have been removed.
    • Delayed construction of several NSMutableArrays until they're necessary
    • -[AHHyperlinkScanner linkifiedString] bypasses -[AHHyperlinkScanner allURIs], saving potentially many NSMutableArray operations in a critical loop.

Also:

  • Updated all calling methods within Adium.
  • Fixed build error for LinkDriver.app in AutoHyperlinks.framework.xcodeproj
Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • trunk/Frameworks/AutoHyperlinks Framework/AutoHyperlinks.framework.xcodeproj/project.pbxproj

    r24137 r24146  
    979979                                GCC_PRECOMPILE_PREFIX_HEADER = YES; 
    980980                                GCC_PREFIX_HEADER = "$(SYSTEM_LIBRARY_DIR)/Frameworks/AppKit.framework/Headers/AppKit.h"; 
    981                                 INFOPLIST_FILE = "LinkDriver-Info.plist"; 
     981                                INFOPLIST_FILE = "LinkDriver/LinkDriver-Info.plist"; 
    982982                                INSTALL_PATH = "$(HOME)/Applications"; 
    983983                                OTHER_LDFLAGS = ( 
     
    10031003                                GCC_PRECOMPILE_PREFIX_HEADER = YES; 
    10041004                                GCC_PREFIX_HEADER = "$(SYSTEM_LIBRARY_DIR)/Frameworks/AppKit.framework/Headers/AppKit.h"; 
    1005                                 INFOPLIST_FILE = "LinkDriver-Info.plist"; 
     1005                                INFOPLIST_FILE = "LinkDriver/LinkDriver-Info.plist"; 
    10061006                                INSTALL_PATH = "$(HOME)/Applications"; 
    10071007                                OTHER_LDFLAGS = ( 
     
    10251025                                GCC_PRECOMPILE_PREFIX_HEADER = YES; 
    10261026                                GCC_PREFIX_HEADER = "$(SYSTEM_LIBRARY_DIR)/Frameworks/AppKit.framework/Headers/AppKit.h"; 
    1027                                 INFOPLIST_FILE = "LinkDriver-Info.plist"; 
     1027                                INFOPLIST_FILE = "LinkDriver/LinkDriver-Info.plist"; 
    10281028                                INSTALL_PATH = "$(HOME)/Applications"; 
    10291029                                OTHER_LDFLAGS = ( 
     
    10471047                                GCC_PRECOMPILE_PREFIX_HEADER = YES; 
    10481048                                GCC_PREFIX_HEADER = "$(SYSTEM_LIBRARY_DIR)/Frameworks/AppKit.framework/Headers/AppKit.h"; 
    1049                                 INFOPLIST_FILE = "LinkDriver-Info.plist"; 
     1049                                INFOPLIST_FILE = "LinkDriver/LinkDriver-Info.plist"; 
    10501050                                INSTALL_PATH = "$(HOME)/Applications"; 
    10511051                                OTHER_LDFLAGS = ( 
  • trunk/Frameworks/AutoHyperlinks Framework/LinkDriver/LinkDriverWindowController.m

    r24114 r24146  
    44// 
    55//  Created by Stephen Holt on 5/15/08. 
    6 //  Copyright 2008 __MyCompanyName__. All rights reserved. 
    76// 
    87 
     
    2524{ 
    2625        NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 
    27         AHHyperlinkScanner      *scanner = [[AHHyperlinkScanner alloc] initWithStrictChecking:NO]; 
    28         [scanner linkifyTextView:inView]; 
     26        AHHyperlinkScanner      *scanner = [AHHyperlinkScanner hyperlinkScannerWithAttributedString:[inView textStorage]]; 
     27        [[inView textStorage] setAttributedString:[scanner linkifiedString]]; 
    2928        [pool release]; 
    3029} 
  • trunk/Frameworks/AutoHyperlinks Framework/Source/AHHyperlinkScanner.h

    r24137 r24146  
    2828#import "AHLinkLexer.h" 
    2929 
    30 extern long AHleng; 
    31 extern long AHlex(); 
     30typedef void* yyscan_t; 
     31 
     32extern long AHlex( yyscan_t yyscanner ); 
     33extern long AHlex_init( yyscan_t * ptr_yy_globals ); 
     34extern long AHlex_destroy ( yyscan_t yyscanner ); 
     35extern long AHget_leng ( yyscan_t scanner ); 
     36extern void AHset_in ( FILE * in_str , yyscan_t scanner ); 
     37 
    3238typedef struct AH_buffer_state *AH_BUFFER_STATE; 
    33 void AH_switch_to_buffer(AH_BUFFER_STATE); 
    34 AH_BUFFER_STATE AH_scan_string (const char *); 
    35 void AH_delete_buffer(AH_BUFFER_STATE); 
     39extern void AH_switch_to_buffer(AH_BUFFER_STATE, yyscan_t scanner); 
     40extern AH_BUFFER_STATE AH_scan_string (const char *, yyscan_t scanner); 
     41extern void AH_delete_buffer(AH_BUFFER_STATE, yyscan_t scanner); 
    3642 
    3743@class AHMarkedHyperlink; 
     
    3945@interface AHHyperlinkScanner : NSObject 
    4046{ 
    41         NSDictionary                            *urlSchemes; 
    42         BOOL                                             strictChecking; 
    43         unsigned long                            stringOffset; 
     47        NSDictionary                            *m_urlSchemes; 
     48        NSString                                        *m_scanString; 
     49        NSAttributedString                      *m_scanAttrString; 
     50        BOOL                                             m_strictChecking; 
     51        unsigned long                            m_scanLocation; 
     52        unsigned long                            m_scanStringLength; 
    4453} 
    45  
    46 /*! 
    47  * @brief Init 
    48  * 
    49  * Defaults to strict URL checking (only links with schemes are matched). 
    50  * 
    51  * @return A new AHHyperlinkScanner. 
    52  */ 
    53 - (id)init; 
    54  
    55 /*! 
    56  * @brief Init 
    57  * 
    58  * @param flag Sets strict checking preference. 
    59  * @return A new AHHyperlinkScanner. 
    60  */ 
    61  - (id)initWithStrictChecking:(BOOL)flag; 
    6254 
    6355 
    6456/*! 
    65  * @brief Determine the validity of a given string using the default strictness 
     57 * @brief Allocs and inits a new lax AHHyperlinkScanner with the given NSString 
    6658 * 
    67  * @param inString The string to be verified 
    68  * @return Boolean 
     59 * @param inString the scanner's string 
     60 * @return a new AHHyperlinkScanner 
    6961 */ 
    70 - (BOOL)isStringValidURL:(NSString *)inString; 
     62+ (id)hyperlinkScannerWithString:(NSString *)inString; 
     63 
     64/*! 
     65 * @brief Allocs and inits a new strict AHHyperlinkScanner with the given NSString 
     66 * 
     67 * @param inString the scanner's string 
     68 * @return a new AHHyperlinkScanner 
     69 */ 
     70+ (id)strictHyperlinkScannerWithString:(NSString *)inString; 
     71 
     72/*! 
     73 * @brief Allocs and inits a new lax AHHyperlinkScanner with the given attributed string 
     74 * 
     75 * @param inString the scanner's string 
     76 * @return a new AHHyperlinkScanner 
     77 */ 
     78+ (id)hyperlinkScannerWithAttributedString:(NSAttributedString *)inString; 
     79 
     80/*! 
     81 * @brief Allocs and inits a new strict AHHyperlinkScanner with the given attributed string 
     82 * 
     83 * @param inString the scanner's string 
     84 * @return a new AHHyperlinkScanner 
     85 */ 
     86+ (id)strictHyperlinkScannerWithAttributedString:(NSAttributedString *)inString; 
    7187 
    7288/*! 
     
    7692 * @param useStrictChecking Use strict rules or not 
    7793 * @param index a pointer to the index the string starts at, for easy incrementing. 
    78  * @param validStatus a pointer to an AH_URI_VERIFICATION_STATUS which will be set to the status of the URL on return. Pass NULL if this information is not needed. 
    7994 * @return Boolean 
    8095 */ 
    81 + (BOOL)isStringValidURL:(NSString *)inString usingStrict:(BOOL)useStrictChecking fromIndex:(unsigned long *)index withStatus:(AH_URI_VERIFICATION_STATUS *)validStatus; 
     96+ (BOOL)isStringValidURI:(NSString *)inString usingStrict:(BOOL)useStrictChecking fromIndex:(unsigned long *)index withStatus:(AH_URI_VERIFICATION_STATUS *)validStatus; 
    8297 
    8398/*! 
    84  * @brief Fetches all the URLs from a string 
    85  * @param inString The NSString with potential URLs in it 
     99 * @brief Init 
     100 * 
     101 * Inits a new AHHyperlinkScanner object for a NSString with the set strict checking option. 
     102 * 
     103 * @param inString the NSString to be scanned. 
     104 * @param flag Sets strict checking preference. 
     105 * @return A new AHHyperlinkScanner. 
     106 */ 
     107- (id)initWithString:(NSString *)inString usingStrictChecking:(BOOL)flag; 
     108 
     109/*! 
     110 * @brief Init 
     111 * 
     112 * Inits a new AHHyperlinkScanner object for a NSAttributedString with the set strict checking option. 
     113 * 
     114 * param inString the NSString to be scanned. 
     115 * @param flag Sets strict checking preference. 
     116 * @return A new AHHyperlinkScanner. 
     117 */ 
     118 - (id)initWithAttributedString:(NSAttributedString *)inString usingStrictChecking:(BOOL)flag; 
     119 
     120 
     121/*! 
     122 * @brief Determine the validity of the scanner's string using the set strictness 
     123 * 
     124 * @return Boolean 
     125 */ 
     126- (BOOL)isValidURI; 
     127 
     128/*! 
     129 * @brief Returns a AHMarkedHyperlink representing the next URI in the scanner's string 
     130 * 
     131 * @return A new AHMarkedHyperlink. 
     132 */ 
     133- (AHMarkedHyperlink *)nextURI; 
     134 
     135/*! 
     136 * @brief Fetches all the URIs from the scanner's string 
     137 * 
    86138 * @return An array of AHMarkedHyperlinks representing each matched URL in the string or nil if no matches. 
    87139 */ 
    88 - (NSArray *)allURLsFromString:(NSString *)inString
     140- (NSArray *)allURIs
    89141 
    90142/*! 
    91  * @brief Fetches all the URLs from a NSTextView 
    92  * @param inView The NSTextView with potential URLs in it 
    93  * @return An array of AHMarkedHyperlinks representing each matched URL in the textView or nil if no matches. 
    94  */ 
    95 - (NSArray *)allURLsFromTextView:(NSTextView *)inView; 
    96  
    97 /*! 
    98  * @brief Scans an attributed string for URLs then adds the link attribs and objects. 
     143 * @brief Scans an attributed string for URIs then adds the link attribs and objects. 
    99144 * @param inString The NSAttributedString to be linkified 
    100145 * @return An autoreleased NSAttributedString. 
    101146 */ 
    102 - (NSAttributedString *)linkifyString:(NSAttributedString *)inString; 
     147- (NSAttributedString *)linkifiedString; 
    103148 
    104 /*! 
    105  * @brief Scans a NSTextView's text store for URLs then adds the link attribs and objects. 
    106  *  
    107  * This scan happens in place: the origional NSTextView is modified, and nothing is returned. 
    108  * @param inView The NSTextView to be linkified. 
    109  */ 
    110 - (void)linkifyTextView:(NSTextView *)inView; 
     149- (unsigned long)scanLocation; 
     150- (void)setScanLocation:(unsigned int)location; 
    111151 
    112152@end 
  • trunk/Frameworks/AutoHyperlinks Framework/Source/AHHyperlinkScanner.m

    r24137 r24146  
    3232#define DEFAULT_URL_SCHEME      @"http://" 
    3333 
    34 @interface AHHyperlinkScanner (PRIVATE) 
    35 - (AHMarkedHyperlink *)nextURLFromString:(NSString *)inString; 
    36 @end 
    37  
    3834@implementation AHHyperlinkScanner 
    3935 
    40 #pragma mark Init 
    41  
    42  
    43 - (id)init 
    44 
    45         return [self initWithStrictChecking:YES]; 
    46 
    47  
    48 - (id)initWithStrictChecking:(BOOL)flag 
     36#pragma mark Class Methods 
     37+ (id)hyperlinkScannerWithString:(NSString *)inString 
     38
     39        return [[[[self class] alloc] initWithString:inString usingStrictChecking:NO] autorelease]; 
     40
     41 
     42+ (id)strictHyperlinkScannerWithString:(NSString *)inString 
     43
     44        return [[[[self class] alloc] initWithString:inString usingStrictChecking:YES] autorelease]; 
     45
     46 
     47+ (id)hyperlinkScannerWithAttributedString:(NSAttributedString *)inString 
     48
     49        return [[[[self class] alloc] initWithAttributedString:inString usingStrictChecking:NO] autorelease]; 
     50
     51 
     52+ (id)strictHyperlinkScannerWithAttributedString:(NSAttributedString *)inString 
     53
     54        return [[[[self class] alloc] initWithAttributedString:inString usingStrictChecking:NO] autorelease]; 
     55
     56 
     57#pragma mark Init/Dealloc 
     58 
     59 
     60- (id)initWithString:(NSString *)inString usingStrictChecking:(BOOL)flag 
    4961{ 
    5062        if((self = [super init])){ 
    51                 urlSchemes = [[NSDictionary alloc] initWithObjectsAndKeys: 
     63                m_scanString = [inString retain]; 
     64                m_scanAttrString = nil; 
     65                m_urlSchemes = [[NSDictionary alloc] initWithObjectsAndKeys: 
    5266                        @"ftp://", @"ftp", 
    5367                        nil]; 
    54                 strictChecking = flag; 
    55                 stringOffset = 0; 
     68                m_strictChecking = flag; 
     69                m_scanLocation = 0; 
     70                m_scanStringLength = [m_scanString length]; 
    5671        } 
    5772 
     
    5974} 
    6075 
     76- (id)initWithAttributedString:(NSAttributedString *)inString usingStrictChecking:(BOOL)flag 
     77{ 
     78        if((self = [super init])){ 
     79                m_scanString = [[inString string] retain]; 
     80                m_scanAttrString = [inString retain]; 
     81                m_urlSchemes = [[NSDictionary alloc] initWithObjectsAndKeys: 
     82                        @"ftp://", @"ftp", 
     83                        nil]; 
     84                m_strictChecking = flag; 
     85                m_scanLocation = 0; 
     86                m_scanStringLength = [m_scanString length]; 
     87        } 
     88 
     89        return self; 
     90} 
     91 
    6192- (void)dealloc 
    6293{ 
    63         [urlSchemes release]; 
     94        [m_scanString release]; 
     95        [m_urlSchemes release]; 
     96        if(m_scanAttrString) [m_scanAttrString release]; 
    6497        [super dealloc]; 
    6598} 
    6699 
    67 #pragma mark primitive methods 
    68  
    69 - (BOOL)isStringValidURL:(NSString *)inString 
    70 
    71         return [AHHyperlinkScanner isStringValidURL:inString usingStrict:strictChecking fromIndex:nil withStatus:nil]; 
    72 
    73  
    74 + (BOOL)isStringValidURL:(NSString *)inString usingStrict:(BOOL)useStrictChecking fromIndex:(unsigned long *)index withStatus:(AH_URI_VERIFICATION_STATUS *)validStatus 
    75 
    76     AH_BUFFER_STATE buf;  // buffer for flex to scan from 
     100#pragma mark URI Verification 
     101 
     102- (BOOL)isValidURI 
     103
     104        return [AHHyperlinkScanner isStringValidURI:m_scanString usingStrict:m_strictChecking fromIndex:nil withStatus:nil]; 
     105
     106 
     107+ (BOOL)isStringValidURI:(NSString *)inString usingStrict:(BOOL)useStrictChecking fromIndex:(unsigned long *)index withStatus:(AH_URI_VERIFICATION_STATUS *)validStatus 
     108
     109    AH_BUFFER_STATE      buf;  // buffer for flex to scan from 
     110        yyscan_t                 scanner; // pointer to the flex scanner opaque type 
    77111        const char              *inStringEnc; 
    78112    unsigned long        encodedLength; 
    79         static NSLock   *linkLock = nil; 
    80          
    81          
    82         if(!linkLock) 
    83                 linkLock = [[NSLock alloc] init]; 
    84         [linkLock lock]; 
    85          
     113 
    86114        if(!validStatus){ 
    87115                AH_URI_VERIFICATION_STATUS newStatus = AH_URL_INVALID; 
     
    97125 
    98126        if (!(inStringEnc = [inString cStringUsingEncoding:stringEnc])) { 
    99                 [linkLock unlock]; 
    100127                return NO; 
    101128        } 
     
    105132     
    106133        // initialize the buffer (flex automatically switches to the buffer in this function) 
    107     buf = AH_scan_string(inStringEnc); 
     134        AHlex_init(&scanner); 
     135    buf = AH_scan_string(inStringEnc, scanner); 
    108136 
    109137    // call flex to parse the input 
    110     *validStatus = AHlex(); 
    111         if(index) *index += AHleng
     138    *validStatus = AHlex(scanner); 
     139        if(index) *index += AHget_leng(scanner)
    112140         
    113141    // condition for valid URI's 
    114142    if(*validStatus == AH_URL_VALID || *validStatus == AH_MAILTO_VALID || *validStatus == AH_FILE_VALID){ 
    115         AH_delete_buffer(buf); //remove the buffer from flex. 
     143        AH_delete_buffer(buf, scanner); //remove the buffer from flex. 
    116144        buf = NULL; //null the buffer pointer for safty's sake. 
    117145         
    118146        // check that the whole string was matched by flex. 
    119147        // this prevents silly things like "blah...com" from being seen as links 
    120         if(AHleng == encodedLength){ 
    121                         [linkLock unlock]
     148        if(AHget_leng(scanner) == encodedLength){ 
     149                        AHlex_destroy(scanner)
    122150            return YES; 
    123151        } 
    124152    // condition for degenerate URL's (A.K.A. URI's sans specifiers), requres strict checking to be NO. 
    125153    }else if((*validStatus == AH_URL_DEGENERATE || *validStatus == AH_MAILTO_DEGENERATE) && !useStrictChecking){ 
    126         AH_delete_buffer(buf); 
     154        AH_delete_buffer(buf, scanner); 
    127155        buf = NULL; 
    128         if(AHleng == encodedLength){ 
    129                         [linkLock unlock]
     156        if(AHget_leng(scanner) == encodedLength){ 
     157                        AHlex_destroy(scanner)
    130158            return YES; 
    131159        } 
    132160    // if it ain't vaild, and it ain't degenerate, then it's invalid. 
    133161    }else{ 
    134         AH_delete_buffer(buf); 
     162        AH_delete_buffer(buf, scanner); 
    135163        buf = NULL; 
    136                 [linkLock unlock]
     164                AHlex_destroy(scanner)
    137165        return NO; 
    138166    } 
    139167    // default case, if the range checking above fails. 
    140         [linkLock unlock]
     168        AHlex_destroy(scanner)
    141169    return NO; 
    142170} 
    143171 
    144 /*! 
    145  * @brief Retreives the next URL from the given string 
    146  *  
    147  * Private to AHHyperlinkScanner.  Calling on this externally could create some weird results. 
    148  * 
    149  * @return a AHMarkedHyperlink representing the given URL or nil, if there are no more hyperlinks.  
    150  */ 
    151 - (AHMarkedHyperlink *)nextURLFromString:(NSString *)inString fromIndex:(unsigned long)index 
     172#pragma mark Accessors 
     173 
     174- (AHMarkedHyperlink *)nextURI 
    152175{ 
    153176    NSString    *scanString = nil; 
     
    207230    // scan upto the next whitespace char so that we don't unnecessarity confuse flex 
    208231    // otherwise we end up validating urls that look like this "http://www.adiumx.com/ <--cool" 
    209     NSScanner *preScanner = [[[NSScanner alloc] initWithString:inString] autorelease]; 
     232    NSScanner *preScanner = [[[NSScanner alloc] initWithString:m_scanString] autorelease]; 
    210233    [preScanner setCharactersToBeSkipped:skipSet]; 
    211     [preScanner setScanLocation:index]; 
     234    [preScanner setScanLocation:m_scanLocation]; 
    212235 
    213236    [preScanner scanCharactersFromSet:startSet intoString:nil]; 
     
    232255                 
    233256                // Find balanced enclosure chars 
    234                 NSMutableArray  *enclosureStack = [NSMutableArray arrayWithCapacity:2]; // totally arbitrary. 
    235                 NSMutableArray  *enclosureArray = [NSMutableArray arrayWithCapacity:2]; 
     257                NSMutableArray  *enclosureStack = nil, *enclosureArray = nil; 
    236258                NSString  *matchChar = nil; 
    237259                NSScanner *enclosureScanner = [[[NSScanner alloc] initWithString:scanString] autorelease]; 
     
    245267                         
    246268                        if(encScanLocation >= [[enclosureScanner string] length]) break; 
     269                         
    247270                        matchChar = [scanString substringWithRange:NSMakeRange(encScanLocation, 1)]; 
     271                         
    248272                        if([enclosureStartArray containsObject:matchChar]) { 
    249273                                encDict = [NSDictionary dictionaryWithObjects:[NSArray arrayWithObjects:[NSNumber numberWithUnsignedLong:encScanLocation], matchChar, nil] 
    250274                                                                                                          forKeys:encKeys]; 
     275                                if(!enclosureStack) enclosureStack = [NSMutableArray arrayWithCapacity:1]; 
    251276                                [enclosureStack addObject:encDict]; 
    252277                        }else if([enclosureStopArray containsObject:matchChar]) { 
     
    257282                                        if([enclosureStopArray indexOfObjectIdenticalTo:matchChar] == encStartIndex) { 
    258283                                                NSRange encRange = NSMakeRange(encTagIndex, encScanLocation - encTagIndex); 
     284                                                if(!enclosureStack) enclosureStack = [NSMutableArray arrayWithCapacity:1]; 
     285                                                if(!enclosureArray) enclosureArray = [NSMutableArray arrayWithCapacity:1]; 
    259286                                                [enclosureStack removeObject:encDict]; 
    260287                                                [enclosureArray addObject:NSStringFromRange(encRange)]; 
     
    267294                } 
    268295                NSRange lastEnclosureRange = NSMakeRange(0, 0); 
    269                 if([enclosureArray count]) lastEnclosureRange = NSRangeFromString([enclosureArray lastObject]); 
     296                if(enclosureArray && [enclosureArray count]) lastEnclosureRange = NSRangeFromString([enclosureArray lastObject]); 
    270297                while (finalStringLen > 2 && [endSet characterIsMember:[scanString characterAtIndex:finalStringLen - 1]]) { 
    271298                        if((lastEnclosureRange.location + lastEnclosureRange.length + 1) < finalStringLen){ 
     
    274301                        }else break; 
    275302                } 
    276  
    277         stringOffset = scannedLocation - finalStringLen; 
     303                 
     304        m_scanLocation = scannedLocation - finalStringLen; 
    278305 
    279306        // if we have a valid URL then save the scanned string, and make a SHMarkedHyperlink out of it. 
     
    281308        // parent string, its validation status (valid, file, degenerate, etc), and its range in the parent string 
    282309                AH_URI_VERIFICATION_STATUS validStatus; 
    283         if((finalStringLen > 0) && [AHHyperlinkScanner isStringValidURL:scanString usingStrict:strictChecking fromIndex:&stringOffset withStatus:&validStatus]){ 
     310        if((finalStringLen > 0) && [[self class] isStringValidURI:scanString usingStrict:m_strictChecking fromIndex:&m_scanLocation withStatus:&validStatus]){ 
    284311            AHMarkedHyperlink   *markedLink; 
    285312                        NSRange                         urlRange; 
     
    299326 
    300327                    if(firstComponent) { 
    301                         NSString *hostnameScheme = [urlSchemes objectForKey:firstComponent]; 
     328                        NSString *hostnameScheme = [m_urlSchemes objectForKey:firstComponent]; 
    302329                        if(hostnameScheme) scheme = hostnameScheme; 
    303330                    } 
     
    320347            markedLink = [[AHMarkedHyperlink alloc] initWithString:scanString 
    321348                                                                                          withValidationStatus:validStatus 
    322                                                                                                           parentString:inString 
     349                                                                                                          parentString:m_scanString 
    323350                                                                                                                  andRange:urlRange]; 
    324351            return [markedLink autorelease]; 
     
    328355                NSRange startRange = [scanString rangeOfCharacterFromSet:startSet]; 
    329356                if (startRange.location != NSNotFound) { 
    330                         index += startRange.location + 1
    331                         if(index >= [inString length]
    332                                 index--; 
     357                        m_scanLocation += startRange.length
     358                        if(m_scanLocation >= m_scanStringLength
     359                                m_scanLocation--; 
    333360                }else{ 
    334                         index += [scanString length]; 
    335                         if(index >= [inString length]
    336                                 index--; 
     361                        m_scanLocation += [scanString length]; 
     362                        if(m_scanLocation >= m_scanStringLength
     363                                m_scanLocation--; 
    337364                } 
    338                 [preScanner setScanLocation:index++]; 
     365                [preScanner setScanLocation:m_scanLocation]; 
    339366    } 
    340367         
    341368    // if we're here, then NSScanner hit the end of the string 
    342369    // set AHStringOffset to the string length here so we avoid potential infinite looping with many trailing spaces. 
    343     stringOffset = [inString length]
     370    m_scanLocation = m_scanStringLength
    344371    return nil; 
    345372} 
    346373 
    347 #pragma mark string and textview handleing 
    348  
    349  
    350 -(NSArray *)allURLsFromString:(NSString *)inString 
    351 
    352     NSMutableArray              *rangeArray = nil; 
     374-(NSArray *)allURIs 
     375
     376    NSMutableArray              *rangeArray = [NSMutableArray array]; 
    353377    AHMarkedHyperlink   *markedLink; 
    354         stringOffset = 0; //set the offset to 0. 
     378        unsigned long            _holdOffset = m_scanLocation; // store location for later restoration; 
     379        m_scanLocation = 0; //set the offset to 0. 
    355380     
    356381    //build an array of marked links. 
    357     while([inString length] > stringOffset){ 
    358         if((markedLink = [self nextURLFromString:inString fromIndex:stringOffset])){ 
    359                         if(!rangeArray) rangeArray = [NSMutableArray array]; 
    360             [rangeArray addObject:markedLink]; 
    361         } 
    362     } 
    363      
     382        while((markedLink = [self nextURI])){ 
     383                [rangeArray addObject:markedLink]; 
     384        } 
     385    m_scanLocation = _holdOffset; // reset scanLocation 
    364386        return rangeArray; 
    365387} 
    366388 
    367  
    368 -(NSArray *)allURLsFromTextView:(NSTextView *)inView 
    369 
    370     // since a NSTextView is really just a glorified NSMutableAttributedString, 
    371     // we can take the string and send it out to allURLsFromString: 
    372     return [self allURLsFromString:[inView string]]; 
    373 
    374  
    375  
    376 -(NSAttributedString *)linkifyString:(NSAttributedString *)inString 
    377 
    378     //build an array from the input string and get its obj. enumerator 
    379     NSArray                             *rangeArray = [self allURLsFromString:[inString string]]; 
    380  
    381         if([rangeArray count]){ 
    382                 NSMutableAttributedString       *linkifiedString; 
    383                 NSEnumerator                            *enumerator; 
    384                 AHMarkedHyperlink                       *markedLink; 
    385                  
    386                 linkifiedString = [[inString mutableCopy] autorelease]; 
    387  
    388                 //for each SHMarkedHyperlink, add the proper URL to the proper range in the string. 
    389                 enumerator = [rangeArray objectEnumerator]; 
    390                 while((markedLink = [enumerator nextObject])){ 
    391                         NSURL *markedLinkURL; 
    392                          
    393                         if((markedLinkURL = [markedLink URL])){ 
    394                                 [linkifiedString addAttribute:NSLinkAttributeName 
    395                                                                                 value:markedLinkURL 
    396                                                                                 range:[markedLink range]]; 
    397                         } 
     389-(NSAttributedString *)linkifiedString 
     390
     391        NSMutableAttributedString       *linkifiedString; 
     392        AHMarkedHyperlink                       *markedLink; 
     393        BOOL                                            _didFindLinks = NO; 
     394        unsigned long                           _holdOffset = m_scanLocation; // store location for later restoration; 
     395         
     396        m_scanLocation = 0; 
     397 
     398        if(m_scanAttrString) { 
     399                linkifiedString = [[m_scanAttrString mutableCopy] autorelease]; 
     400        } else { 
     401                linkifiedString = [[[NSMutableAttributedString alloc] initWithString:m_scanString] autorelease]; 
     402        } 
     403 
     404        //for each SHMarkedHyperlink, add the proper URL to the proper range in the string. 
     405        while((markedLink = [self nextURI])) { 
     406                NSURL *markedLinkURL; 
     407                _didFindLinks = YES; 
     408                if((markedLinkURL = [markedLink URL])){ 
     409                        [linkifiedString addAttribute:NSLinkAttributeName 
     410                                                                        value:markedLinkURL 
     411                                                                        range:[markedLink range]]; 
    398412                } 
    399                  
    400                 return linkifiedString; 
    401  
    402     }else{ 
    403                 //If no links were found, just return the string we were passed 
    404                 return [[inString retain] autorelease]; 
    405         } 
    406 
    407  
    408  
    409 - (void)linkifyTextView:(NSTextView *)inView 
    410 
    411         NSAttributedString *newAttributedString; 
    412  
    413         // like allURLsFromTextView before it, we can just call the linkifyString: method here 
    414         // then replace the NSTextView's contents with it. 
    415         newAttributedString = [self linkifyString:[inView attributedSubstringFromRange:NSMakeRange(0,[[inView string] length])]]; 
    416  
    417         [[inView textStorage] setAttributedString:newAttributedString]; 
    418 
    419  
     413        } 
     414         
     415        m_scanLocation = _holdOffset; // reset scanLocation 
     416                 
     417        return _didFindLinks? linkifiedString : 
     418                                                  m_scanAttrString ? [[m_scanAttrString retain] autorelease] : [[[NSMutableAttributedString alloc] initWithString:m_scanString] autorelease]; 
     419
     420 
     421-(unsigned long)scanLocation 
     422
     423        return m_scanLocation; 
     424
     425 
     426- (void)setScanLocation:(unsigned int)location 
     427
     428        m_scanLocation = location; 
     429
    420430@end 
  • trunk/Frameworks/AutoHyperlinks Framework/Source/AHLinkLexer.h

    r23471 r24146  
    3434    AH_MAILTO_DEGENERATE 
    3535} AH_URI_VERIFICATION_STATUS; 
     36 
     37#define YY_EXTRA_TYPE unsigned int 
  • trunk/Frameworks/AutoHyperlinks Framework/Source/AHLinkLexer.l

    r23905 r24146  
    4343 *                                                                                        without a costly call to yymore(). 
    4444 */ 
    45 long AHValidShift = 0; 
    4645#include "AHLinkLexer.h" 
    4746%} 
     
    8988ignoreable      (b\.sc|m\.in) 
    9089 
    91 %option noyywrap nounput 8bit caseless never-interactive prefix="AH" 
     90%option noyywrap nounput 8bit caseless never-interactive reentrant warn prefix="AH" 
    9291 
    9392%x CANONICAL 
    9493%% 
    9594 
    96 <CANONICAL>({userAndPass}@)?{urlCSpec}|{ipURL}|{ipv6URL} {AHleng += AHValidShift
     95<CANONICAL>({userAndPass}@)?{urlCSpec}|{ipURL}|{ipv6URL} {yyleng += yyextra
    9796                                                          BEGIN INITIAL; 
    9897                                                          return AH_URL_VALID;} 
     
    124123notes:\/\/              | 
    125124gopher:\/\/             | 
    126 x-man-page:\/\/         {AHValidShift = AHleng; BEGIN CANONICAL;} 
     125x-man-page:\/\/         {yyextra = yyleng; BEGIN CANONICAL;} 
    127126 
    128127mailto:{mailSpec}       {return AH_MAILTO_VALID;} 
  • trunk/Frameworks/AutoHyperlinks Framework/UnitTests/HyperlinkContextTest.m

    r24112 r24146  
    88 
    99@implementation HyperlinkContextTest 
    10 - (void)testLaxContext:(NSString *)linkString withURI:(NSString *)URIString { 
    11         AHHyperlinkScanner      *scanner = [[AHHyperlinkScanner alloc] initWithStrictChecking:NO]; 
     10- (void)testLaxContext:(NSString *)linkString withURI:(NSString *)URIString 
     11
    1212        NSString                        *testString = [NSString stringWithFormat:linkString, URIString]; 
    13         AHMarkedHyperlink       *link = [[scanner allURLsFromString:testString] objectAtIndex:0]; 
    14          
    15         STAssertNotNil(link, @"-[SHHyperlinkScanner nextURLFromString:] found no URI in \"%@\"", testString); 
     13        AHHyperlinkScanner      *scanner = [AHHyperlinkScanner hyperlinkScannerWithString:testString]; 
     14        AHMarkedHyperlink       *link = [scanner nextURI]; 
     15         
     16        STAssertNotNil(link, @"-[SHHyperlinkScanner nextURL] found no URI in \"%@\"", testString); 
    1617        STAssertEqualObjects([[link parentString] substringWithRange:[link range]], URIString, @"in context: '%@'", testString); 
    1718} 
     
    1920- (void)testNegativeContext:(NSString *)linkString withURI:(NSString *)URIString 
    2021{ 
    21         AHHyperlinkScanner      *scanner = [[AHHyperlinkScanner alloc] initWithStrictChecking:NO]; 
    2222        NSString                        *testString = [NSString stringWithFormat:linkString, URIString]; 
    23         AHMarkedHyperlink       *link = [[scanner allURLsFromString:testString] objectAtIndex:0]; 
     23        AHHyperlinkScanner      *scanner = [AHHyperlinkScanner hyperlinkScannerWithString:testString]; 
     24        AHMarkedHyperlink       *link = [scanner nextURI]; 
    2425         
    2526        STAssertNil(link, @"-[SHHyperlinkScanner nextURLFromString:] found no URI in \"%@\"", testString); 
  • trunk/Frameworks/AutoHyperlinks Framework/UnitTests/NegativeURLTest.h

    r23421 r24146  
    77#import "AutoHyperlinks.h" 
    88 
    9 #define testHyperlink(x) STAssertFalse([scanner isStringValidURL: x ], @"%@ is a valid URI and should not be", x) 
     9#define testHyperlink(x) STAssertFalse([AHHyperlinkScanner isStringValidURI: x usingStrict:YES fromIndex:0 withStatus:nil], @"%@ is a valid URI and should not be", x) 
     10 
    1011 
    1112@interface NegativeURLTest : SenTestCase { 
    12         AHHyperlinkScanner      *scanner; 
    1313} 
    1414 
  • trunk/Frameworks/AutoHyperlinks Framework/UnitTests/NegativeURLTest.m

    r23863 r24146  
    77 
    88@implementation NegativeURLTest 
    9 - (void)setUp { 
    10         scanner = [[AHHyperlinkScanner alloc] initWithStrictChecking:NO]; 
    11 } 
    12  
    13 - (void)tearDown { 
    14         [scanner release]; 
    15 } 
    16  
    179- (void)testInvalidURI { 
    1810        testHyperlink(@"adium"); 
  • trunk/Frameworks/AutoHyperlinks Framework/UnitTests/SimpleURLTest.h

    r23925 r24146  
    77#import "AutoHyperlinks.h" 
    88 
    9 #define testHyperlink(x) STAssertTrue([scanner isStringValidURL: x ],\ 
     9#define testHyperlink(x) STAssertTrue([AHHyperlinkScanner isStringValidURI: x usingStrict:NO fromIndex:0 withStatus:nil],\ 
    1010                                        @"\"%@\" Should be a valid URI.", x ) 
    1111 
  • trunk/Frameworks/AutoHyperlinks Framework/UnitTests/SimpleURLTest.m

    r23863 r24146  
    77 
    88@implementation SimpleURLTest 
    9  
    10 - (void)setUp { 
    11         scanner = [[AHHyperlinkScanner alloc] initWithStrictChecking:NO]; 
    12 } 
    13  
    14 - (void)tearDown { 
    15         [scanner release]; 
    16 } 
    179 
    1810- (void)testURLOnly { 
  • trunk/Frameworks/AutoHyperlinks Framework/UnitTests/StressTest.m

    r23513 r24146  
    1717        STAssertNil(error, @"stringWithContentsOfFile:encoding:error: could not read file at path '%s': %@", TEST_URIS_FILE_PATHNAME, error); 
    1818 
    19         AHHyperlinkScanner      *scanner = [[AHHyperlinkScanner alloc] initWithStrictChecking:NO]; 
     19        AHHyperlinkScanner      *scanner = [AHHyperlinkScanner hyperlinkScannerWithString:stressString]; 
    2020        NSAttributedString      *attrString; 
    2121         
    2222        int i = 5; 
    2323        while(i > 0) { 
    24                 attrString = [scanner linkifyString:[[NSAttributedString alloc] initWithString:stressString]]; 
     24                attrString = [scanner linkifiedString]; 
    2525                i--; 
    2626        } 
  • trunk/Frameworks/AutoHyperlinks Framework/UnitTests/ThreadedStressTest.m

    r23923 r24146  
    1111 
    1212#define LOOP_COUNT 10 
    13 #define THREAD_COUNT 8 
     13#define THREAD_COUNT 10 
    1414 
    1515@implementation ThreadedStressTest 
    1616-(void) threadedStressTest 
    1717{ 
    18         AHHyperlinkScanner      *scanner = [[AHHyperlinkScanner alloc] initWithStrictChecking:NO]; 
    1918         
    20         NSThread        *thread1 = [[NSThread alloc] initWithTarget:self selector:@selector(performTestWithScanner:) object:scanner]; 
    21         NSThread        *thread2 = [[NSThread alloc] initWithTarget:self selector:@selector(performTestWithScanner:) object:scanner]; 
    22         NSThread        *thread3 = [[NSThread alloc] initWithTarget:self selector:@selector(performTestWithScanner:) object:scanner]; 
    23         NSThread        *thread4 = [[NSThread alloc] initWithTarget:self selector:@selector(performTestWithScanner:) object:scanner]; 
    24         NSThread        *thread5 = [[NSThread alloc] initWithTarget:self selector:@selector(performTestWithScanner:) object:scanner]; 
    25         NSThread        *thread6 = [[NSThread alloc] initWithTarget:self selector:@selector(performTestWithScanner:) object:scanner]; 
    26         NSThread        *thread7 = [[NSThread alloc] initWithTarget:self selector:@selector(performTestWithScanner:) object:scanner]; 
    27         NSThread        *thread8 = [[NSThread alloc] initWithTarget:self selector:@selector(performTestWithScanner:) object:scanner]; 
     19        NSThread        *thread1 = [[NSThread alloc] initWithTarget:self selector:@selector(performLinkTest:) object:nil]; 
     20        NSThread        *thread2 = [[NSThread alloc] initWithTarget:self s