1 /* ***** BEGIN LICENSE BLOCK *****
2 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 * The contents of this file are subject to the Mozilla Public License Version
5 * 1.1 (the "License"); you may not use this file except in compliance with
6 * the License. You may obtain a copy of the License at
7 * http://www.mozilla.org/MPL/
9 * Software distributed under the License is distributed on an "AS IS" basis,
10 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
11 * for the specific language governing rights and limitations under the
14 * The Original Code is Chimera code.
16 * The Initial Developer of the Original Code is
17 * Netscape Communications Corporation.
18 * Portions created by the Initial Developer are Copyright (C) 2002
19 * the Initial Developer. All Rights Reserved.
22 * Simon Fraser <sfraser@netscape.com>
23 * David Haas <haasd@cae.wisc.edu>
25 * Alternatively, the contents of this file may be used under the terms of
26 * either the GNU General Public License Version 2 or later (the "GPL"), or
27 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28 * in which case the provisions of the GPL or the LGPL are applicable instead
29 * of those above. If you wish to allow use of your version of this file only
30 * under the terms of either the GPL or the LGPL, and not to allow others to
31 * use your version of this file under the terms of the MPL, indicate your
32 * decision by deleting the provisions above and replace them with the notice
33 * and other provisions required by the GPL or the LGPL. If you do not delete
34 * the provisions above, a recipient may use your version of this file under
35 * the terms of any one of the MPL, the GPL or the LGPL.
37 * ***** END LICENSE BLOCK ***** */
39 #import <AppKit/AppKit.h> // for NSStringDrawing.h
41 #import "NSString+Utils.h"
45 @implementation NSString (ChimeraStringUtils)
49 static NSString* sEllipsisString = nil;
50 if (!sEllipsisString) {
51 unichar ellipsisChar = 0x2026;
52 sEllipsisString = [[NSString alloc] initWithCharacters:&ellipsisChar length:1];
55 return sEllipsisString;
58 + (NSString*)stringWithUUID
60 NSString* uuidString = nil;
61 CFUUIDRef newUUID = CFUUIDCreate(kCFAllocatorDefault);
63 uuidString = (NSString *)CFUUIDCreateString(kCFAllocatorDefault, newUUID);
66 return [uuidString autorelease];
69 - (BOOL)isEqualToStringIgnoringCase:(NSString*)inString
71 return ([self compare:inString options:NSCaseInsensitiveSearch] == NSOrderedSame);
74 - (BOOL)hasCaseInsensitivePrefix:(NSString*)inString
76 if ([self length] < [inString length])
78 return ([self compare:inString options:NSCaseInsensitiveSearch range:NSMakeRange(0, [inString length])] == NSOrderedSame);
81 - (BOOL)isLooselyValidatedURI
83 return ([self hasCaseInsensitivePrefix:@"javascript:"] || [self hasCaseInsensitivePrefix:@"data:"]);
86 - (BOOL)isPotentiallyDangerousURI
88 return ([self hasCaseInsensitivePrefix:@"javascript:"] || [self hasCaseInsensitivePrefix:@"data:"]);
93 // isValid() will only be true for valid, well-formed URI strings
94 GURL testURL([self UTF8String]);
96 // |javascript:| and |data:| URIs might not have passed the test,
97 // but spaces will work OK, so evaluate them separately.
98 if ((testURL.is_valid()) || [self isLooselyValidatedURI]) {
104 - (NSString *)stringByRemovingCharactersInSet:(NSCharacterSet*)characterSet
106 NSScanner* cleanerScanner = [NSScanner scannerWithString:self];
107 NSMutableString* cleanString = [NSMutableString stringWithCapacity:[self length]];
108 // Make sure we don't skip whitespace, which NSScanner does by default
109 [cleanerScanner setCharactersToBeSkipped:[NSCharacterSet characterSetWithCharactersInString:@""]];
111 while (![cleanerScanner isAtEnd]) {
112 NSString* stringFragment;
113 if ([cleanerScanner scanUpToCharactersFromSet:characterSet intoString:&stringFragment])
114 [cleanString appendString:stringFragment];
116 [cleanerScanner scanCharactersFromSet:characterSet intoString:nil];
122 - (NSString *)stringByReplacingCharactersInSet:(NSCharacterSet*)characterSet
123 withString:(NSString*)string
125 NSScanner* cleanerScanner = [NSScanner scannerWithString:self];
126 NSMutableString* cleanString = [NSMutableString stringWithCapacity:[self length]];
127 // Make sure we don't skip whitespace, which NSScanner does by default
128 [cleanerScanner setCharactersToBeSkipped:[NSCharacterSet characterSetWithCharactersInString:@""]];
130 while (![cleanerScanner isAtEnd])
132 NSString* stringFragment;
133 if ([cleanerScanner scanUpToCharactersFromSet:characterSet intoString:&stringFragment])
134 [cleanString appendString:stringFragment];
136 if ([cleanerScanner scanCharactersFromSet:characterSet intoString:nil])
137 [cleanString appendString:string];
143 - (NSString*)stringByTruncatingTo:(unsigned int)maxCharacters at:(ETruncationType)truncationType
145 if ([self length] > maxCharacters)
147 NSMutableString *mutableCopy = [self mutableCopy];
148 [mutableCopy truncateTo:maxCharacters at:truncationType];
149 return [mutableCopy autorelease];
155 - (NSString *)stringByTruncatingToWidth:(float)inWidth at:(ETruncationType)truncationType
156 withAttributes:(NSDictionary *)attributes
158 if ([self sizeWithAttributes:attributes].width > inWidth)
160 NSMutableString *mutableCopy = [self mutableCopy];
161 [mutableCopy truncateToWidth:inWidth at:truncationType withAttributes:attributes];
162 return [mutableCopy autorelease];
168 - (NSString *)stringByTrimmingWhitespace
170 return [self stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
173 -(NSString *)stringByRemovingAmpEscapes
175 NSMutableString* dirtyStringMutant = [NSMutableString stringWithString:self];
176 [dirtyStringMutant replaceOccurrencesOfString:@"&"
178 options:NSLiteralSearch
179 range:NSMakeRange(0,[dirtyStringMutant length])];
180 [dirtyStringMutant replaceOccurrencesOfString:@"""
182 options:NSLiteralSearch
183 range:NSMakeRange(0,[dirtyStringMutant length])];
184 [dirtyStringMutant replaceOccurrencesOfString:@"<"
186 options:NSLiteralSearch
187 range:NSMakeRange(0,[dirtyStringMutant length])];
188 [dirtyStringMutant replaceOccurrencesOfString:@">"
190 options:NSLiteralSearch
191 range:NSMakeRange(0,[dirtyStringMutant length])];
192 [dirtyStringMutant replaceOccurrencesOfString:@"—"
194 options:NSLiteralSearch
195 range:NSMakeRange(0,[dirtyStringMutant length])];
196 [dirtyStringMutant replaceOccurrencesOfString:@"'"
198 options:NSLiteralSearch
199 range:NSMakeRange(0,[dirtyStringMutant length])];
200 // fix import from old Firefox versions, which exported ' instead of a plain apostrophe
201 [dirtyStringMutant replaceOccurrencesOfString:@"'"
203 options:NSLiteralSearch
204 range:NSMakeRange(0,[dirtyStringMutant length])];
205 return [dirtyStringMutant stringByRemovingCharactersInSet:[NSCharacterSet controlCharacterSet]];
208 -(NSString *)stringByAddingAmpEscapes
210 NSMutableString* dirtyStringMutant = [NSMutableString stringWithString:self];
211 [dirtyStringMutant replaceOccurrencesOfString:@"&"
213 options:NSLiteralSearch
214 range:NSMakeRange(0,[dirtyStringMutant length])];
215 [dirtyStringMutant replaceOccurrencesOfString:@"\""
217 options:NSLiteralSearch
218 range:NSMakeRange(0,[dirtyStringMutant length])];
219 [dirtyStringMutant replaceOccurrencesOfString:@"<"
221 options:NSLiteralSearch
222 range:NSMakeRange(0,[dirtyStringMutant length])];
223 [dirtyStringMutant replaceOccurrencesOfString:@">"
225 options:NSLiteralSearch
226 range:NSMakeRange(0,[dirtyStringMutant length])];
227 return [NSString stringWithString:dirtyStringMutant];
233 @implementation NSMutableString (ChimeraMutableStringUtils)
235 - (void)truncateTo:(unsigned)maxCharacters at:(ETruncationType)truncationType
237 if ([self length] <= maxCharacters)
240 NSRange replaceRange;
241 replaceRange.length = [self length] - maxCharacters;
243 switch (truncationType) {
244 case kTruncateAtStart:
245 replaceRange.location = 0;
248 case kTruncateAtMiddle:
249 replaceRange.location = maxCharacters / 2;
253 replaceRange.location = maxCharacters;
258 NSLog(@"Unknown truncation type in stringByTruncatingTo::");
260 replaceRange.location = maxCharacters;
264 [self replaceCharactersInRange:replaceRange withString:[NSString ellipsisString]];
268 - (void)truncateToWidth:(float)maxWidth
269 at:(ETruncationType)truncationType
270 withAttributes:(NSDictionary *)attributes
272 // First check if we have to truncate at all.
273 if ([self sizeWithAttributes:attributes].width <= maxWidth)
276 // Essentially, we perform a binary search on the string length
277 // which fits best into maxWidth.
279 float width = maxWidth;
281 int hi = [self length];
284 // Make a backup copy of the string so that we can restore it if we fail low.
285 NSMutableString *backup = [self mutableCopy];
290 // Cut to mid chars and calculate the resulting width
291 [self truncateTo:mid at:truncationType];
292 width = [self sizeWithAttributes:attributes].width;
294 if (width > maxWidth) {
295 // Fail high - string is still to wide. For the next cut, we can simply
296 // work on the already cut string, so we don't restore using the backup.
299 else if (width == maxWidth) {
300 // Perfect match, abort the search.
304 // Fail low - we cut off too much. Restore the string before cutting again.
306 [self setString:backup];
309 // Perform the final cut (unless this was already a perfect match).
310 if (width != maxWidth)
311 [self truncateTo:hi at:truncationType];
317 @implementation NSString (ChimeraFilePathStringUtils)
319 - (NSString*)volumeNamePathComponent
321 // if the file doesn't exist, then componentsToDisplayForPath will return nil,
322 // so back up to the nearest existing dir
323 NSString* curPath = self;
324 while (![[NSFileManager defaultManager] fileExistsAtPath:curPath])
326 NSString* parentDirPath = [curPath stringByDeletingLastPathComponent];
327 if ([parentDirPath isEqualToString:curPath])
328 break; // avoid endless loop
329 curPath = parentDirPath;
332 NSArray* displayComponents = [[NSFileManager defaultManager] componentsToDisplayForPath:curPath];
333 if ([displayComponents count] > 0)
334 return [displayComponents objectAtIndex:0];
339 - (NSString*)displayNameOfLastPathComponent
341 return [[NSFileManager defaultManager] displayNameAtPath:self];
346 @implementation NSString (CaminoURLStringUtils)
350 return ([self isEqualToString:@"about:blank"] || [self isEqualToString:@""]);
353 // Excluded character list comes from RFC2396 and by examining Safari's behaviour
354 - (NSString*)unescapedURI
356 NSString *unescapedURI = (NSString*)CFURLCreateStringByReplacingPercentEscapesUsingEncoding(kCFAllocatorDefault,
358 CFSTR(" \"\';/?:@&=+$,#"),
359 kCFStringEncodingUTF8);
360 return unescapedURI ? [unescapedURI autorelease] : self;