Back to index

lightning-sunbird  0.9+nobinonly
NSString+Utils.mm
Go to the documentation of this file.
00001 /* ***** BEGIN LICENSE BLOCK *****
00002  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00003  *
00004  * The contents of this file are subject to the Mozilla Public License Version
00005  * 1.1 (the "License"); you may not use this file except in compliance with
00006  * the License. You may obtain a copy of the License at
00007  * http://www.mozilla.org/MPL/
00008  *
00009  * Software distributed under the License is distributed on an "AS IS" basis,
00010  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00011  * for the specific language governing rights and limitations under the
00012  * License.
00013  *
00014  * The Original Code is Chimera code.
00015  *
00016  * The Initial Developer of the Original Code is
00017  * Netscape Communications Corporation.
00018  * Portions created by the Initial Developer are Copyright (C) 2002
00019  * the Initial Developer. All Rights Reserved.
00020  *
00021  * Contributor(s):
00022  *   Simon Fraser <sfraser@netscape.com>
00023  *
00024  * Alternatively, the contents of this file may be used under the terms of
00025  * either the GNU General Public License Version 2 or later (the "GPL"), or
00026  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00027  * in which case the provisions of the GPL or the LGPL are applicable instead
00028  * of those above. If you wish to allow use of your version of this file only
00029  * under the terms of either the GPL or the LGPL, and not to allow others to
00030  * use your version of this file under the terms of the MPL, indicate your
00031  * decision by deleting the provisions above and replace them with the notice
00032  * and other provisions required by the GPL or the LGPL. If you do not delete
00033  * the provisions above, a recipient may use your version of this file under
00034  * the terms of any one of the MPL, the GPL or the LGPL.
00035  *
00036  * ***** END LICENSE BLOCK ***** */
00037 
00038 #import <AppKit/AppKit.h>          // for NSStringDrawing.h
00039 
00040 #import "NSString+Utils.h"
00041 
00042 #include "nsString.h"
00043 #include "nsPromiseFlatString.h"
00044 #include "nsCRT.h"
00045 
00046 
00047 @implementation NSString (ChimeraStringUtils)
00048 
00049 
00050 + (id)ellipsisString
00051 {
00052   static NSString* sEllipsisString = nil;
00053   if (!sEllipsisString)
00054   {
00055     unichar ellipsisChar = 0x2026;
00056     sEllipsisString = [[NSString alloc] initWithCharacters:&ellipsisChar length:1];
00057   }
00058   
00059   return sEllipsisString;
00060 }
00061 
00062 + (id)stringWithPRUnichars:(const PRUnichar*)inString
00063 {
00064   if (inString)
00065     return [self stringWithCharacters:inString length:nsCRT::strlen(inString)];
00066   else
00067     return [self string];
00068 }
00069 
00070 + (id)stringWith_nsAString:(const nsAString&)inString
00071 {
00072   nsPromiseFlatString flatString = PromiseFlatString(inString);
00073   return [self stringWithCharacters:flatString.get() length:flatString.Length()];
00074 }
00075 
00076 #define ASSIGN_STACK_BUFFER_CHARACTERS  256
00077 
00078 - (void)assignTo_nsAString:(nsAString&)ioString
00079 {
00080   PRUnichar     stackBuffer[ASSIGN_STACK_BUFFER_CHARACTERS];
00081   PRUnichar*    buffer = stackBuffer;
00082   
00083   // XXX maybe fix this to use SetLength(0), SetLength(len), and a writing iterator.
00084   unsigned int len = [self length];
00085   
00086   if (len + 1 > ASSIGN_STACK_BUFFER_CHARACTERS)
00087   {
00088     buffer = (PRUnichar *)malloc(sizeof(PRUnichar) * (len + 1));
00089     if (!buffer)
00090       return;
00091   }
00092 
00093   [self getCharacters: buffer];   // does not null terminate
00094   ioString.Assign(buffer, len);
00095   
00096   if (buffer != stackBuffer)
00097     free(buffer);
00098 }
00099 
00100 - (NSString *)stringByRemovingCharactersInSet:(NSCharacterSet*)characterSet
00101 {
00102   NSScanner*       cleanerScanner = [NSScanner scannerWithString:self];
00103   NSMutableString* cleanString    = [NSMutableString stringWithCapacity:[self length]];
00104   
00105   while (![cleanerScanner isAtEnd])
00106   {
00107     NSString* stringFragment;
00108     if ([cleanerScanner scanUpToCharactersFromSet:characterSet intoString:&stringFragment])
00109       [cleanString appendString:stringFragment];
00110 
00111     [cleanerScanner scanCharactersFromSet:characterSet intoString:nil];
00112   }
00113 
00114   return cleanString;
00115 }
00116 
00117 - (NSString *)stringByReplacingCharactersInSet:(NSCharacterSet*)characterSet withString:(NSString*)string
00118 {
00119   NSScanner*       cleanerScanner = [NSScanner scannerWithString:self];
00120   NSMutableString* cleanString    = [NSMutableString stringWithCapacity:[self length]];
00121   
00122   while (![cleanerScanner isAtEnd])
00123   {
00124     NSString* stringFragment;
00125     if ([cleanerScanner scanUpToCharactersFromSet:characterSet intoString:&stringFragment])
00126       [cleanString appendString:stringFragment];
00127 
00128     if ([cleanerScanner scanCharactersFromSet:characterSet intoString:nil])
00129       [cleanString appendString:string];
00130   }
00131 
00132   return cleanString;
00133 }
00134 
00135 - (NSString*)stringByTruncatingTo:(unsigned in)maxCharacters at:(ETruncationType)truncationType
00136 {
00137   if ([self length] > maxCharacters)
00138   {
00139     NSMutableString *mutableCopy = [self mutableCopy];
00140     [mutableCopy truncateTo:maxCharacters at:truncationType];
00141     return [mutableCopy autorelease];
00142   }
00143 
00144   return [[self copy] autorelease];
00145 }
00146 
00147 - (PRUnichar*)createNewUnicodeBuffer
00148 {
00149   PRUint32 length = [self length];
00150   PRUnichar* retStr = (PRUnichar*)nsMemory::Alloc((length + 1) * sizeof(PRUnichar));
00151   [self getCharacters:retStr];
00152   retStr[length] = PRUnichar(0);
00153   return retStr;
00154 }
00155 
00156 
00157 @end
00158 
00159 
00160 @implementation NSMutableString (ChimeraMutableStringUtils)
00161 
00162 - (void)truncateTo:(unsigned)maxCharacters at:(ETruncationType)truncationType
00163 {
00164   if ([self length] > maxCharacters)
00165   {
00166   NSRange replaceRange;
00167   replaceRange.length = [self length] - maxCharacters;
00168 
00169   switch (truncationType)
00170   {
00171     case kTruncateAtStart:
00172       replaceRange.location = 0;
00173       break;
00174 
00175     case kTruncateAtMiddle:
00176       replaceRange.location = maxCharacters / 2;
00177       break;
00178 
00179     case kTruncateAtEnd:
00180       replaceRange.location = maxCharacters;
00181       break;
00182 
00183     default:
00184 #if DEBUG
00185       NSLog(@"Unknown truncation type in stringByTruncatingTo::");
00186 #endif          
00187       replaceRange.location = maxCharacters;
00188       break;
00189   }
00190   
00191   [self replaceCharactersInRange:replaceRange withString:[NSString ellipsisString]];
00192   }
00193 }
00194 
00195 
00196 - (void)truncateToWidth:(float)maxWidth at:(ETruncationType)truncationType withAttributes:(NSDictionary *)attributes
00197 {
00198   // First check if we have to truncate at all.
00199   if ([self sizeWithAttributes:attributes].width > maxWidth)
00200   {
00201     // Essentially, we perform a binary search on the string length
00202     // which fits best into maxWidth.
00203 
00204     float width;
00205     int lo = 0;
00206     int hi = [self length];
00207     int mid;
00208 
00209     // Make a backup copy of the string so that we can restore it if we fail low.
00210     NSMutableString *backup = [self mutableCopy];
00211     
00212     while (hi >= lo)
00213     {
00214       mid = (hi + lo) / 2;
00215       
00216       // Cut to mid chars and calculate the resulting width
00217       [self truncateTo:mid at:truncationType];
00218       width = [self sizeWithAttributes:attributes].width;
00219       
00220       if (width > maxWidth) {
00221         // Fail high - string is still to wide. For the next cut, we can simply
00222         // work on the already cut string, so we don't restore using the backup. 
00223         hi = mid - 1;
00224       } else if (width == maxWidth) {
00225         // Perfect match, abort the search.
00226         break;
00227       } else {
00228         // Fail low - we cut off to much. Restore the string before cutting again.
00229         lo = mid + 1;
00230         [self setString:backup];
00231       }
00232     }
00233     // Perform the final cut (unless this was already a perfect match).
00234     if (width != maxWidth)
00235       [self truncateTo:hi at:truncationType];
00236     [backup release];
00237   }
00238 }
00239 
00240 @end