/ NSAttributedString.m
NSAttributedString.m
  1  //
  2  //  NSAttributedString.m
  3  //  CoreFoundation
  4  //
  5  //  Copyright (c) 2014 Apportable. All rights reserved.
  6  //
  7  
  8  #import "NSAttributedStringInternal.h"
  9  #import "NSObjectInternal.h"
 10  #import <objc/runtime.h>
 11  
 12  // These are in Foundation
 13  /*
 14  @implementation NSAttributedString
 15  
 16  @end
 17  
 18  @implementation NSMutableAttributedString
 19  
 20  @end
 21  */
 22  
 23  @implementation __NSCFAttributedString
 24  
 25  + (BOOL)automaticallyNotifiesObserversForKey:(NSString *)key
 26  {
 27      return NO;
 28  }
 29  
 30  static BOOL _rangeCheckRange(__NSCFAttributedString *self, NSRange range)
 31  {
 32      if ((uint64_t)range.location + range.length > [self length])
 33      {
 34          [NSException raise:NSRangeException format:@"range (%d,%d) beyond NSAttributedString bounds (%d)", range.location, range.length, [self length]];
 35          return NO;
 36      }
 37      return YES;
 38  }
 39  
 40  #define RANGE_CHECK_RANGE(val) if (!_rangeCheckRange(self, range)) return val;
 41  
 42  static BOOL _rangeCheckIndex(__NSCFAttributedString *self, NSUInteger index)
 43  {
 44      if (index > [self length])
 45      {
 46          [NSException raise:NSRangeException format:@"index (%d) beyond NSAttributedString bounds (%d)", index, [self length]];
 47          return NO;
 48      }
 49      return YES;
 50  }
 51  
 52  #define RANGE_CHECK_INDEX() if (!_rangeCheckIndex(self, index)) return 0;
 53  
 54  - (void)removeAttribute:(NSString *)name range:(NSRange)range
 55  {
 56      RANGE_CHECK_RANGE();
 57      CFAttributedStringRemoveAttribute((CFMutableAttributedStringRef)self, CFRangeMake(range.location, range.length), (CFStringRef) name);
 58  }
 59  
 60  - (void)addAttribute:(NSString *)name value:(id)value range:(NSRange)range
 61  {
 62      NSDictionary *d = @{ name : value };
 63      [self addAttributes:d range:range];
 64  }
 65  
 66  - (void)addAttributes:(NSDictionary *)attrs range:(NSRange)range
 67  {
 68      RANGE_CHECK_RANGE();
 69      CFAttributedStringSetAttributes((CFMutableAttributedStringRef)self, CFRangeMake(range.location, range.length), (CFDictionaryRef)attrs, false);
 70  }
 71  
 72  - (void)setAttributedString:(NSAttributedString *)attrString
 73  {
 74      CFAttributedStringReplaceAttributedString((CFMutableAttributedStringRef)self, CFRangeMake(0, [self length]), (CFAttributedStringRef)attrString);
 75  }
 76  - (void)deleteCharactersInRange:(NSRange)range
 77  {
 78      RANGE_CHECK_RANGE();
 79      CFAttributedStringReplaceString((CFMutableAttributedStringRef)self, CFRangeMake(range.location, range.length), CFSTR("")); 
 80  }
 81  
 82  - (void)appendAttributedString:(NSAttributedString *)attrString
 83  {
 84      CFAttributedStringReplaceAttributedString((CFMutableAttributedStringRef)self, CFRangeMake([self length], 0), (CFAttributedStringRef)attrString);
 85  }
 86  
 87  - (void)insertAttributedString:(NSAttributedString *)attrString atIndex:(NSUInteger)loc
 88  {
 89      CFAttributedStringReplaceAttributedString((CFMutableAttributedStringRef)self, CFRangeMake(loc, 0), (CFAttributedStringRef)attrString);
 90  }
 91  - (void)replaceCharactersInRange:(NSRange)range withAttributedString:(NSAttributedString *)attrString
 92  {
 93      RANGE_CHECK_RANGE();
 94      CFAttributedStringReplaceAttributedString((CFMutableAttributedStringRef)self, CFRangeMake(range.location, range.length), (CFAttributedStringRef)attrString);
 95  }
 96  
 97  - (void)setAttributes:(NSDictionary *)attributes range:(NSRange)range
 98  {
 99      RANGE_CHECK_RANGE();
100      CFAttributedStringSetAttributes((CFMutableAttributedStringRef)self, CFRangeMake(range.location, range.length), (CFDictionaryRef)attributes, true);
101  }
102  
103  - (void)replaceCharactersInRange:(NSRange)range withString:(NSString *)string
104  {
105      RANGE_CHECK_RANGE();
106      CFAttributedStringReplaceString((CFMutableAttributedStringRef)self, CFRangeMake(range.location, range.length), (CFStringRef)string);
107  }
108  
109  - (Class)classForCoder
110  {
111      return [objc_getClass("NSAttributedString") self];
112  }
113  
114  - (id)mutableCopyWithZone:(NSZone *)zone
115  {
116      return (id)CFAttributedStringCreateMutableCopy(kCFAllocatorSystemDefault, 0, (CFAttributedStringRef)self);
117  }
118  
119  - (id)copyWithZone:(NSZone *)zone
120  {
121      return (id)CFAttributedStringCreateCopy(kCFAllocatorSystemDefault, (CFAttributedStringRef)self);
122  }
123  
124  - (NSAttributedString *)attributedSubstringFromRange:(NSRange)range
125  {
126      RANGE_CHECK_RANGE(nil);
127  
128      NSAttributedString *res = (NSAttributedString *) CFAttributedStringCreateWithSubstring(
129          kCFAllocatorSystemDefault, (CFAttributedStringRef) self,
130          CFRangeMake(range.location, range.length));
131  
132      if (range.length == 0 && res == nil)
133      {
134          // Core Foundation returns NULL/nil here, but Foundation returns an empty attributed string
135          res = [[objc_getClass("NSAttributedString") alloc] initWithString: @""];
136      }
137      return [res autorelease];
138  }
139  
140  - (NSDictionary *)attributesAtIndex:(NSUInteger)index longestEffectiveRange:(NSRangePointer)range inRange:(NSRange)rangeLimit
141  {
142      RANGE_CHECK_INDEX();
143      NSDictionary *retVal = (NSDictionary *)CFAttributedStringGetAttributesAndLongestEffectiveRange((CFAttributedStringRef)self, index, 
144          CFRangeMake(rangeLimit.location, rangeLimit.length), (CFRange *)range);
145      // CF doesn't zero out missing ranges
146      if (retVal && range) {
147          if ((signed)(range->length) <= 0)
148          {
149              *range = NSMakeRange(0,0);
150          }
151      }
152      return retVal;
153  }
154  
155  - (id)attribute:(NSString *)attrName atIndex:(NSUInteger)index longestEffectiveRange:(NSRangePointer)range inRange:(NSRange)rangeLimit
156  {
157      RANGE_CHECK_INDEX();
158      id retVal = (id)CFAttributedStringGetAttributeAndLongestEffectiveRange((CFAttributedStringRef)self, index, 
159          (CFStringRef)attrName, CFRangeMake(rangeLimit.location, rangeLimit.length), (CFRange *)range);
160      // CF doesn't zero out missing ranges
161      if (retVal && range) {
162          if ((signed)(range->length) <= 0)
163          {
164              *range = NSMakeRange(0,0);
165          }
166      }
167      return retVal;
168  }
169  
170   - (id)attribute:(NSString *)attrName atIndex:(NSUInteger)index effectiveRange:(NSRangePointer)rangePtr
171  {
172      RANGE_CHECK_INDEX();
173      return CFAttributedStringGetAttribute((CFAttributedStringRef)self, index, (CFStringRef)attrName, (CFRange *)rangePtr);
174  }
175  
176  - (NSUInteger)length
177  {
178      return CFAttributedStringGetLength((CFAttributedStringRef)self);
179  }
180  
181  - (NSDictionary *)attributesAtIndex:(NSUInteger)index effectiveRange:(NSRangePointer)aRange
182  {
183      RANGE_CHECK_INDEX();
184      return (NSDictionary *)CFAttributedStringGetAttributes((CFAttributedStringRef)self, index, (CFRange *)aRange);
185  }
186  
187  - (NSString *)string
188  {
189      return (NSString *)CFAttributedStringGetString((CFAttributedStringRef)self);
190  }
191  
192  - (BOOL)isEqual:(id)other
193  {
194      if (other == nil)
195      {
196          return NO;
197      }
198      return CFEqual((CFAttributedStringRef)self, (CFAttributedStringRef)other);
199  }
200  
201  - (BOOL)isEqualToAttributedString:(NSAttributedString *)other
202  {
203      if (other == nil)
204      {
205          return NO;
206      }
207      return CFEqual((CFAttributedStringRef)self, (CFAttributedStringRef)other);
208  }
209  
210  - (NSUInteger)retainCount
211  {
212      return CFGetRetainCount((CFTypeRef)self);
213  }
214  
215  - (BOOL)_isDeallocating
216  {
217      return _CFIsDeallocating((CFTypeRef)self);
218  }
219  
220  - (BOOL)_tryRetain
221  {
222      return _CFTryRetain((CFTypeRef)self) != NULL;
223  }
224  
225  - (oneway void)release
226  {
227      CFRelease(self);
228  }
229  
230  - (id)retain
231  {
232      return CFRetain(self);
233  }
234  
235  @end