]> git.wincent.com - WOTest.git/blob - NSObject+WOTest.m
Fix object-to-pointer comparisons on Leopard
[WOTest.git] / NSObject+WOTest.m
1 //
2 //  NSObject+WOTest.m
3 //  WOTest
4 //
5 //  Created by Wincent Colaiuta on 12 June 2005.
6 //
7 //  Copyright 2005-2007 Wincent Colaiuta.
8 //  This program is free software: you can redistribute it and/or modify
9 //  it under the terms of the GNU General Public License as published by
10 //  the Free Software Foundation, either version 3 of the License, or
11 //  (at your option) any later version.
12 //
13 //  This program is distributed in the hope that it will be useful,
14 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
15 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 //  GNU General Public License for more details.
17 //
18 //  You should have received a copy of the GNU General Public License
19 //  along with this program.  If not, see <http://www.gnu.org/licenses/>.
20 //
21
22 // category header
23 #import "NSObject+WOTest.h"
24
25 // system headers
26 #import <objc/objc-runtime.h>
27 #import <objc/Protocol.h>
28
29 // other headers
30 #import "NSScanner+WOTest.h"
31 #import "NSValue+WOTest.h"
32
33 @implementation NSObject (WOTest)
34
35 + (NSString *)WOTest_descriptionForObject:(id)anObject
36 {
37     if (!anObject) return @"(nil)";
38     
39     @try
40     {
41         // special case handling for NSValue objects
42         if ([self WOTest_object:anObject isKindOfClass:[NSValue class]])
43             return [(NSValue *)anObject WOTest_description];
44         
45         // fallback case: try "description" selector
46         if ([self WOTest_object:anObject respondsToSelector:@selector(description)])
47         {
48             NSString *returnType = [self WOTest_returnTypeForObject:anObject selector:@selector(description)];
49             
50             if ([self WOTest_isIdReturnType:returnType])
51             {
52                 NSString *description = [anObject description];
53                 if ([self WOTest_object:description isKindOfClass:[NSString class]])
54                     return description;
55             }
56             else if ([self WOTest_isCharacterStringReturnType:returnType] || 
57                      [self WOTest_isConstantCharacterStringReturnType:returnType])
58             {
59                 const char *charString = (const char *)[anObject description];
60                 return [NSString stringWithUTF8String:charString];
61             }
62         }
63     }
64     @catch (id e) 
65     {
66         // fall through
67     }
68     return NSStringFromClass(object_getClass(anObject)); // last resort
69 }
70
71 + (BOOL)WOTest_isRegisteredClass:(Class)aClass
72 {
73     if (!aClass) return NO;
74     
75     BOOL    isRegisteredClass   = NO;
76     int     numClasses          = 0;
77     int     newNumClasses       = objc_getClassList(NULL, 0);
78     Class   *classes            = NULL;
79     
80     // get list of all classes on the system
81     while (numClasses < newNumClasses)
82     {
83         numClasses          = newNumClasses;
84         size_t bufferSize   = sizeof(Class) * numClasses;
85         classes             = realloc(classes, bufferSize);
86         NSAssert1((classes != NULL), @"realloc() failed (size %d)", bufferSize);
87         newNumClasses       = objc_getClassList(classes, numClasses);
88     }
89     
90     if (classes)
91     {
92         for (int i = 0; i < newNumClasses; i++)
93         { 
94             if (aClass == classes[i])   // found in list!
95             {
96                 isRegisteredClass = YES;
97                 break;
98             }
99         }
100         free(classes);
101     }
102     return isRegisteredClass;
103 }
104
105 /*! Returns YES if aClass is the metaclass of a class that is registered with the runtime. */
106 + (BOOL)WOTest_isMetaClass:(Class)aClass
107 {
108     if (!aClass) return NO;
109     
110     BOOL    isMetaClass     = NO;
111     int     numClasses      = 0;
112     int     newNumClasses   = objc_getClassList(NULL, 0);
113     Class   *classes        = NULL;
114     
115     // get a list of all classes on the system
116     // get list of all classes on the system
117     while (numClasses < newNumClasses)
118     {
119         numClasses          = newNumClasses;
120         size_t bufferSize   = sizeof(Class) * numClasses;
121         classes             = realloc(classes, bufferSize);
122         NSAssert1((classes != NULL), @"realloc() failed (size %d)", bufferSize);
123         newNumClasses       = objc_getClassList(classes, numClasses);
124     }
125     
126     if (classes)
127     {
128         for (int i = 0; i < newNumClasses; i++)
129         {
130             if (class_isMetaClass(classes[i]))  // looking at a meta class
131             {
132                 if (aClass == classes[i])               // found in list!
133                 {
134                     isMetaClass = YES;
135                     break;
136                 }
137             }
138             else // not looking at a meta class, look up its meta class
139             {
140                 if (aClass == object_getClass(classes[i])) // found it!
141                 {
142                     isMetaClass = YES;
143                     break;
144                 }
145             }
146         }
147         free(classes);
148     }
149     return isMetaClass;    
150 }
151
152 + (BOOL)WOTest_object:(id)anObject isKindOfClass:(Class)aClass
153 {
154     if (!aClass)    return NO;
155     NSParameterAssert([self WOTest_isRegisteredClass:aClass] || [self WOTest_isMetaClass:aClass]);
156     if (!anObject)  return NO;
157     Class objectClass = object_getClass(anObject);
158     
159     if (objectClass == aClass)
160         return YES;
161     else
162     {
163                 // check superclass
164         Class superClass = class_getSuperclass(objectClass);
165                 if (superClass)
166                         return [self WOTest_instancesOfClass:superClass areKindOfClass:aClass];                
167     }
168     
169     return NO; // give up
170 }
171
172 + (BOOL)WOTest_instancesOfClass:(Class)aClass areKindOfClass:(Class)otherClass
173 {
174     if (!aClass)        return NO;
175     NSParameterAssert([self WOTest_isRegisteredClass:aClass] || [self WOTest_isMetaClass:aClass]);
176     if (!otherClass)    return NO;
177     NSParameterAssert([self WOTest_isRegisteredClass:otherClass] || [self WOTest_isMetaClass:otherClass]);
178     
179     if (aClass == otherClass)
180         return YES;
181         
182         Class superClass = class_getSuperclass(aClass);
183     if (superClass)
184         return [self WOTest_instancesOfClass:superClass areKindOfClass:otherClass];
185     
186     return NO; // give up
187 }
188
189 + (BOOL)WOTest_class:(Class)aClass respondsToSelector:(SEL)aSelector
190 {
191     if (!aClass) return NO;
192     NSParameterAssert([self WOTest_isRegisteredClass:aClass] || [self WOTest_isMetaClass:aClass]);
193     if (!aSelector) return NO;
194     return (class_getClassMethod(aClass, aSelector) ? YES : NO);
195 }
196
197 + (BOOL)WOTest_object:(id)anObject respondsToSelector:(SEL)aSelector
198 {
199     if (!anObject)  return NO;
200     if (!aSelector) return NO;
201     Class theClass = object_getClass(anObject);
202     if (class_isMetaClass(theClass))
203         return (class_getClassMethod(theClass, aSelector) ? YES : NO);
204     else
205         return (class_getInstanceMethod(theClass, aSelector) ? YES : NO);
206 }
207
208 + (BOOL)WOTest_instancesOfClass:(Class)aClass respondToSelector:(SEL)aSelector
209 {
210     if (!aClass) return NO;
211     NSParameterAssert([self WOTest_isRegisteredClass:aClass] || [self WOTest_isMetaClass:aClass]);
212     if (!aSelector) return NO;
213     return (class_getInstanceMethod(aClass, aSelector) ? YES : NO);
214 }
215
216 + (BOOL)WOTest_instancesOfClass:(Class)aClass conformToProtocol:(Protocol *)aProtocol
217 {
218     if (!aClass)    return NO;
219     NSParameterAssert([self WOTest_isRegisteredClass:aClass] || [self WOTest_isMetaClass:aClass]);
220     if (!aProtocol) return NO;
221         if (class_conformsToProtocol(aClass, aProtocol))
222                 return YES;
223         Class superClass = class_getSuperclass(aClass);
224         if (superClass)
225         return [self WOTest_instancesOfClass:superClass conformToProtocol:aProtocol];
226     
227     return NO;  // give up
228 }
229
230 + (NSString *)WOTest_returnTypeForClass:(Class)aClass selector:(SEL)aSelector
231 {
232     NSParameterAssert(aClass != NULL);
233     NSParameterAssert([self WOTest_isRegisteredClass:aClass] || [self WOTest_isMetaClass:aClass]);
234     NSParameterAssert(aSelector != NULL);
235     Method theMethod = class_getClassMethod(aClass, aSelector);
236     
237     if (theMethod == NULL) // class does not respond to this selector
238         return nil;
239
240     // get return type and list of arguments
241         char *returnType = method_copyReturnType(theMethod);
242         if (returnType)
243         {
244                 NSString *returnString = [[NSString alloc] initWithUTF8String:returnType];
245                 free(returnType);
246                 return returnString;
247         }
248         return nil;
249 }
250
251 + (NSString *)WOTest_returnTypeForObject:(id)anObject selector:(SEL)aSelector
252 {
253     NSParameterAssert(anObject != nil);
254     NSParameterAssert(aSelector != NULL);
255     Method theMethod = class_getInstanceMethod(object_getClass(anObject), aSelector);
256                 
257         if (theMethod == NULL) // object does not respond to this selector
258         return nil;
259         
260     // get return type and list of arguments
261         char *returnType = method_copyReturnType(theMethod);
262         if (returnType)
263         {
264                 NSString *typeString = [NSString stringWithUTF8String:returnType];
265                 free(returnType);
266                 return typeString;
267         }
268         return nil;    
269 }
270
271 + (BOOL)WOTest_isIdReturnType:(NSString *)returnType
272 {
273     return [NSValue WOTest_typeIsObject:returnType];
274 }
275
276 + (BOOL)WOTest_isCharacterStringReturnType:(NSString *)returnType
277 {
278     return [NSValue WOTest_typeIsCharacterString:returnType];
279 }
280
281 + (BOOL)WOTest_isConstantCharacterStringReturnType:(NSString *)returnType
282 {
283     return [NSValue WOTest_typeIsConstantCharacterString:returnType];
284 }
285
286 + (BOOL)WOTest_objectReturnsId:(id)anObject forSelector:(SEL)aSelector
287 {
288     NSParameterAssert(anObject != nil);
289     NSParameterAssert(aSelector != NULL);
290     return [self WOTest_isIdReturnType:[self WOTest_returnTypeForObject:anObject selector:aSelector]];
291 }
292
293 + (BOOL)WOTest_objectReturnsCharacterString:(id)anObject forSelector:(SEL)aSelector
294 {
295     NSParameterAssert(anObject != nil);
296     NSParameterAssert(aSelector != NULL);
297     return [self WOTest_isCharacterStringReturnType:[self WOTest_returnTypeForObject:anObject selector:aSelector]];
298 }
299
300 + (BOOL)WOTest_objectReturnsConstantCharacterString:(id)anObject forSelector:(SEL)aSelector
301 {
302     NSParameterAssert(anObject != nil);
303     NSParameterAssert(aSelector != NULL);
304     return [self WOTest_isConstantCharacterStringReturnType:[self WOTest_returnTypeForObject:anObject selector:aSelector]];    
305 }
306
307 @end