CollectionUtils.m
author Jens Alfke <jens@mooseyard.com>
Sun May 10 18:57:43 2009 -0700 (2009-05-10)
changeset 29 8874aff14cc9
parent 11 e5976864dfe9
child 31 2068331949ee
permissions -rw-r--r--
* Added kv*Set utility functions for handling KV grunge when mutating an NSSet property.
* Change CFMakeCollectable to NSMakeCollectable.
     1 //
     2 //  CollectionUtils.m
     3 //  MYUtilities
     4 //
     5 //  Created by Jens Alfke on 1/5/08.
     6 //  Copyright 2008 Jens Alfke. All rights reserved.
     7 //
     8 
     9 #import "CollectionUtils.h"
    10 #import "Test.h"
    11 
    12 
    13 NSDictionary* _dictof(const struct _dictpair* pairs, size_t count)
    14 {
    15     CAssert(count<10000);
    16     id objects[count], keys[count];
    17     size_t n = 0;
    18     for( size_t i=0; i<count; i++,pairs++ ) {
    19         if( pairs->value ) {
    20             objects[n] = pairs->value;
    21             keys[n] = pairs->key;
    22             n++;
    23         }
    24     }
    25     return [NSDictionary dictionaryWithObjects: objects forKeys: keys count: n];
    26 }
    27 
    28 
    29 NSMutableDictionary* _mdictof(const struct _dictpair* pairs, size_t count)
    30 {
    31     CAssert(count<10000);
    32     id objects[count], keys[count];
    33     size_t n = 0;
    34     for( size_t i=0; i<count; i++,pairs++ ) {
    35         if( pairs->value ) {
    36             objects[n] = pairs->value;
    37             keys[n] = pairs->key;
    38             n++;
    39         }
    40     }
    41     return [NSMutableDictionary dictionaryWithObjects: objects forKeys: keys count: n];
    42 }
    43 
    44 
    45 NSArray* $apply( NSArray *src, SEL selector, id defaultValue )
    46 {
    47     NSMutableArray *dst = [NSMutableArray arrayWithCapacity: src.count];
    48     for( id obj in src ) {
    49         id result = [obj performSelector: selector] ?: defaultValue;
    50         [dst addObject: result];
    51     }
    52     return dst;
    53 }
    54 
    55 NSArray* $applyKeyPath( NSArray *src, NSString *keyPath, id defaultValue )
    56 {
    57     NSMutableArray *dst = [NSMutableArray arrayWithCapacity: src.count];
    58     for( id obj in src ) {
    59         id result = [obj valueForKeyPath: keyPath] ?: defaultValue;
    60         [dst addObject: result];
    61     }
    62     return dst;
    63 }
    64 
    65 
    66 BOOL $equal(id obj1, id obj2)      // Like -isEqual: but works even if either/both are nil
    67 {
    68     if( obj1 )
    69         return obj2 && [obj1 isEqual: obj2];
    70     else
    71         return obj2==nil;
    72 }
    73 
    74 
    75 NSValue* _box(const void *value, const char *encoding)
    76 {
    77     // file:///Developer/Documentation/DocSets/com.apple.ADC_Reference_Library.DeveloperTools.docset/Contents/Resources/Documents/documentation/DeveloperTools/gcc-4.0.1/gcc/Type-encoding.html
    78     char e = encoding[0];
    79     if( e=='r' )                // ignore 'const' modifier
    80         e = encoding[1];
    81     switch( e ) {
    82         case 'c':   return [NSNumber numberWithChar: *(char*)value];
    83         case 'C':   return [NSNumber numberWithUnsignedChar: *(char*)value];
    84         case 's':   return [NSNumber numberWithShort: *(short*)value];
    85         case 'S':   return [NSNumber numberWithUnsignedShort: *(unsigned short*)value];
    86         case 'i':   return [NSNumber numberWithInt: *(int*)value];
    87         case 'I':   return [NSNumber numberWithUnsignedInt: *(unsigned int*)value];
    88         case 'l':   return [NSNumber numberWithLong: *(long*)value];
    89         case 'L':   return [NSNumber numberWithUnsignedLong: *(unsigned long*)value];
    90         case 'q':   return [NSNumber numberWithLongLong: *(long long*)value];
    91         case 'Q':   return [NSNumber numberWithUnsignedLongLong: *(unsigned long long*)value];
    92         case 'f':   return [NSNumber numberWithFloat: *(float*)value];
    93         case 'd':   return [NSNumber numberWithDouble: *(double*)value];
    94         case '*':   return [NSString stringWithUTF8String: *(char**)value];
    95         case '@':   return *(id*)value;
    96         default:    return [NSValue value: value withObjCType: encoding];
    97     }
    98 }
    99 
   100 
   101 id _cast( Class requiredClass, id object )
   102 {
   103     if( object && ! [object isKindOfClass: requiredClass] )
   104         [NSException raise: NSInvalidArgumentException format: @"%@ required, but got %@ %p",
   105          requiredClass,[object class],object];
   106     return object;
   107 }
   108 
   109 id _castNotNil( Class requiredClass, id object )
   110 {
   111     if( ! [object isKindOfClass: requiredClass] )
   112         [NSException raise: NSInvalidArgumentException format: @"%@ required, but got %@ %p",
   113          requiredClass,[object class],object];
   114     return object;
   115 }
   116 
   117 id _castIf( Class requiredClass, id object )
   118 {
   119     if( object && ! [object isKindOfClass: requiredClass] )
   120         object = nil;
   121     return object;
   122 }
   123 
   124 NSArray* _castArrayOf(Class itemClass, NSArray *a)
   125 {
   126     id item;
   127     foreach( item, $cast(NSArray,a) )
   128         _cast(itemClass,item);
   129     return a;
   130 }
   131 
   132 
   133 void setObj( id *var, id value )
   134 {
   135     if( value != *var ) {
   136         [*var release];
   137         *var = [value retain];
   138     }
   139 }
   140 
   141 BOOL ifSetObj( id *var, id value )
   142 {
   143     if( value != *var && ![value isEqual: *var] ) {
   144         [*var release];
   145         *var = [value retain];
   146         return YES;
   147     } else {
   148         return NO;
   149     }
   150 }
   151 
   152 
   153 void setString( NSString **var, NSString *value )
   154 {
   155     if( value != *var ) {
   156         [*var release];
   157         *var = [value copy];
   158     }
   159 }
   160 
   161 
   162 BOOL ifSetString( NSString **var, NSString *value )
   163 {
   164     if( value != *var && ![value isEqualToString: *var] ) {
   165         [*var release];
   166         *var = [value copy];
   167         return YES;
   168     } else {
   169         return NO;
   170     }
   171 }
   172 
   173 
   174 NSString* $string( const char *utf8Str )
   175 {
   176     if( utf8Str )
   177         return [NSString stringWithCString: utf8Str encoding: NSUTF8StringEncoding];
   178     else
   179         return nil;
   180 }
   181 
   182 
   183 BOOL kvSetSet( id owner, NSString *property, NSMutableSet *set, NSSet *newSet ) {
   184     CAssert(set);
   185     if (!newSet)
   186         newSet = [NSSet set];
   187     if (![set isEqualToSet: newSet]) {
   188         [owner willChangeValueForKey: property
   189                      withSetMutation:NSKeyValueSetSetMutation 
   190                         usingObjects:newSet]; 
   191         [set setSet: newSet];
   192         [owner didChangeValueForKey: property 
   193                     withSetMutation:NSKeyValueSetSetMutation 
   194                        usingObjects:newSet]; 
   195         return YES;
   196     } else
   197         return NO;
   198 }
   199 
   200 
   201 BOOL kvAddToSet( id owner, NSString *property, NSMutableSet *set, id objToAdd ) {
   202     CAssert(set);
   203     if (![set containsObject: objToAdd]) {
   204         NSSet *changedObjects = [[NSSet alloc] initWithObjects: &objToAdd count: 1];
   205         [owner willChangeValueForKey: property
   206                      withSetMutation: NSKeyValueUnionSetMutation 
   207                         usingObjects: changedObjects]; 
   208         [set addObject: objToAdd];
   209         [owner didChangeValueForKey: property 
   210                     withSetMutation: NSKeyValueUnionSetMutation 
   211                        usingObjects: changedObjects]; 
   212         [changedObjects release];
   213         return YES;
   214     } else
   215         return NO;
   216 }
   217 
   218 
   219 BOOL kvRemoveFromSet( id owner, NSString *property, NSMutableSet *set, id objToRemove ) {
   220     if ([set containsObject: objToRemove]) {
   221         NSSet *changedObjects = [[NSSet alloc] initWithObjects: &objToRemove count: 1];
   222         [owner willChangeValueForKey: property
   223                      withSetMutation: NSKeyValueMinusSetMutation 
   224                         usingObjects: changedObjects]; 
   225         [set removeObject: objToRemove];
   226         [owner didChangeValueForKey: property 
   227                     withSetMutation: NSKeyValueMinusSetMutation 
   228                        usingObjects: changedObjects]; 
   229         [changedObjects release];
   230         return YES;
   231     } else
   232         return NO;
   233 }
   234 
   235 
   236 @implementation NSArray (MYUtils)
   237 
   238 - (BOOL) my_containsObjectIdenticalTo: (id)object
   239 {
   240     return [self indexOfObjectIdenticalTo: object] != NSNotFound;
   241 }
   242 
   243 @end
   244 
   245 
   246 
   247 
   248 @implementation NSSet (MYUtils)
   249 
   250 + (NSSet*) my_unionOfSet: (NSSet*)set1 andSet: (NSSet*)set2
   251 {
   252     if( set1 == set2 || set2.count==0 )
   253         return set1;
   254     else if( set1.count==0 )
   255         return set2;
   256     else {
   257         NSMutableSet *result = [set1 mutableCopy];
   258         [result unionSet: set2];
   259         return [result autorelease];
   260     }
   261 }
   262 
   263 + (NSSet*) my_intersectionOfSet: (NSSet*)set1 andSet: (NSSet*)set2
   264 {
   265     if( set1 == set2 || set1.count==0 )
   266         return set1;
   267     else if( set2.count==0 )
   268         return set2;
   269     else {
   270         NSMutableSet *result = [set1 mutableCopy];
   271         [result intersectSet: set2];
   272         return [result autorelease];
   273     }
   274 }
   275 
   276 + (NSSet*) my_differenceOfSet: (NSSet*)set1 andSet: (NSSet*)set2
   277 {
   278     if( set1.count==0 || set2.count==0 )
   279         return set1;
   280     else if( set1==set2 )
   281         return [NSSet set];
   282     else {
   283         NSMutableSet *result = [set1 mutableCopy];
   284         [result minusSet: set2];
   285         return [result autorelease];
   286     }
   287 }
   288 
   289 @end
   290 
   291 
   292 
   293 #import "Test.h"
   294 
   295 TestCase(CollectionUtils) {
   296     NSArray *a = $array(@"foo",@"bar",@"baz");
   297     //Log(@"a = %@",a);
   298     NSArray *aa = [NSArray arrayWithObjects: @"foo",@"bar",@"baz",nil];
   299     CAssertEqual(a,aa);
   300     
   301     const char *cstr = "a C string";
   302     id o = $object(cstr);
   303     //Log(@"o = %@",o);
   304     CAssertEqual(o,@"a C string");
   305     
   306     NSDictionary *d = $dict({@"int",    $object(1)},
   307                             {@"double", $object(-1.1)},
   308                             {@"char",   $object('x')},
   309                             {@"ulong",  $object(1234567UL)},
   310                             {@"longlong",$object(987654321LL)},
   311                             {@"cstr",   $object(cstr)});
   312     //Log(@"d = %@",d);
   313     NSDictionary *dd = [NSDictionary dictionaryWithObjectsAndKeys:
   314                         [NSNumber numberWithInt: 1],                    @"int",
   315                         [NSNumber numberWithDouble: -1.1],              @"double",
   316                         [NSNumber numberWithChar: 'x'],                 @"char",
   317                         [NSNumber numberWithUnsignedLong: 1234567UL],   @"ulong",
   318                         [NSNumber numberWithDouble: 987654321LL],       @"longlong",
   319                         @"a C string",                                  @"cstr",
   320                         nil];
   321     CAssertEqual(d,dd);
   322 }
   323 
   324 
   325 /*
   326  Copyright (c) 2008, Jens Alfke <jens@mooseyard.com>. All rights reserved.
   327  
   328  Redistribution and use in source and binary forms, with or without modification, are permitted
   329  provided that the following conditions are met:
   330  
   331  * Redistributions of source code must retain the above copyright notice, this list of conditions
   332  and the following disclaimer.
   333  * Redistributions in binary form must reproduce the above copyright notice, this list of conditions
   334  and the following disclaimer in the documentation and/or other materials provided with the
   335  distribution.
   336  
   337  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
   338  IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 
   339  FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI-
   340  BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
   341  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
   342   PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
   343  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 
   344  THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   345  */