aboutsummaryrefslogtreecommitdiff
path: root/Foundation/GTMNSEnumerator+Filter.m
blob: cc1cb41b947f730725879e711f16c3bf4a13d1f5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
//
//  GTMNSEnumerator+Filter.m
//
//  Copyright 2007-2009 Google Inc.
//
//  Licensed under the Apache License, Version 2.0 (the "License"); you may not
//  use this file except in compliance with the License.  You may obtain a copy
//  of the License at
//
//  http://www.apache.org/licenses/LICENSE-2.0
//
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
//  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
//  License for the specific language governing permissions and limitations under
//  the License.
//

#import "GTMNSEnumerator+Filter.h"
#import "GTMDebugSelectorValidation.h"
#import "GTMDefines.h"
#if GTM_IPHONE_SDK
#import <objc/message.h>
#import <objc/runtime.h>
#else
#import <objc/objc-runtime.h>
#endif

// a private subclass of NSEnumerator that does all the work.
// public interface just returns one of these.
// This top level class contains all the additional boilerplate. Specific
// behavior is in the subclasses.
@interface GTMEnumeratorPrivateBase : NSEnumerator {
 @protected
  NSEnumerator *base_;
  SEL operation_; // either a predicate or a transform depending on context.
  id target_;  // may be nil
  id object_;  // may be nil
}
@end

@interface GTMEnumeratorPrivateBase (SubclassesMustProvide)
- (BOOL)filterObject:(id)obj returning:(id *)resultp;
@end

@implementation GTMEnumeratorPrivateBase
- (id)initWithBase:(NSEnumerator *)base
               sel:(SEL)filter
            target:(id)optionalTarget
            object:(id)optionalOther {
  self = [super init];
  if (self) {

    // someone would have to subclass or directly create an object of this
    // class, and this class is private to this impl.
    _GTMDevAssert(base, @"can't initWithBase: a nil base enumerator");
    base_ = [base retain];
    operation_ = filter;
    target_ = [optionalTarget retain];
    object_ = [optionalOther retain];
  }
  return self;
}

// we don't provide an init because this base class is private to this
// impl, and no one would be able to create it (if they do, they get whatever
// they happens...).

- (void)dealloc {
  [base_ release];
  [target_ release];
  [object_ release];
  [super dealloc];
}

- (id)nextObject {
  for (id obj = [base_ nextObject]; obj; obj = [base_ nextObject]) {
    id result = nil;
    if ([self filterObject:obj returning:&result]) {
      return result;
    }
  }
  return nil;
}
@end

// a transformer, for each item in the enumerator, returns a f(item).
@interface GTMEnumeratorTransformer : GTMEnumeratorPrivateBase
@end
@implementation GTMEnumeratorTransformer
- (BOOL)filterObject:(id)obj returning:(id *)resultp {
  *resultp = [obj performSelector:operation_ withObject:object_];
  return nil != *resultp;
}
@end

// a transformer, for each item in the enumerator, returns a f(item).
// a target transformer swaps the target and the argument.
@interface GTMEnumeratorTargetTransformer : GTMEnumeratorPrivateBase
@end
@implementation GTMEnumeratorTargetTransformer
- (BOOL)filterObject:(id)obj returning:(id *)resultp {
  *resultp = [target_ performSelector:operation_
                           withObject:obj
                           withObject:object_];
  return nil != *resultp;
}
@end

// a filter, for each item in the enumerator, if(f(item)) { returns item. }
@interface GTMEnumeratorFilter : GTMEnumeratorPrivateBase
@end
@implementation GTMEnumeratorFilter
// We must take care here, since Intel leaves junk in high bytes of return
// register for predicates that return BOOL.
// For details see:
// http://developer.apple.com/legacy/mac/library/documentation/MacOSX/Conceptual/universal_binary/universal_binary_tips/universal_binary_tips.html#//apple_ref/doc/uid/TP40002217-CH239-280661
// and
// http://www.red-sweater.com/blog/320/abusing-objective-c-with-class#comment-83187
- (BOOL)filterObject:(id)obj returning:(id *)resultp {
  *resultp = obj;
  return ((BOOL (*)(id, SEL, id))objc_msgSend)(obj, operation_, object_);
}
@end

// a target filter, for each item in the enumerator, if(f(item)) { returns item. }
// a target transformer swaps the target and the argument.
@interface GTMEnumeratorTargetFilter : GTMEnumeratorPrivateBase
@end
@implementation GTMEnumeratorTargetFilter
// We must take care here, since Intel leaves junk in high bytes of return
// register for predicates that return BOOL.
// For details see:
// http://developer.apple.com/legacy/mac/library/documentation/MacOSX/Conceptual/universal_binary/universal_binary_tips/universal_binary_tips.html#//apple_ref/doc/uid/TP40002217-CH239-280661
// and
// http://www.red-sweater.com/blog/320/abusing-objective-c-with-class#comment-83187
- (BOOL)filterObject:(id)obj returning:(id *)resultp {
  *resultp = obj;
  return ((BOOL (*)(id, SEL, id, id))objc_msgSend)(target_, operation_, obj, object_);
}
@end

@implementation NSEnumerator (GTMEnumeratorFilterAdditions)

- (NSEnumerator *)gtm_filteredEnumeratorByMakingEachObjectPerformSelector:(SEL)selector
                                                               withObject:(id)argument {
  return [[[GTMEnumeratorFilter alloc] initWithBase:self
                                                sel:selector
                                             target:nil
                                             object:argument] autorelease];
}

- (NSEnumerator *)gtm_enumeratorByMakingEachObjectPerformSelector:(SEL)selector
                                                       withObject:(id)argument {
  return [[[GTMEnumeratorTransformer alloc] initWithBase:self
                                                     sel:selector
                                                  target:nil
                                                  object:argument] autorelease];
}


- (NSEnumerator *)gtm_filteredEnumeratorByTarget:(id)target
                           performOnEachSelector:(SEL)predicate {
  // make sure the object impls this selector taking an object as an arg.
  GTMAssertSelectorNilOrImplementedWithReturnTypeAndArguments(target, predicate,
                                                              @encode(BOOL),
                                                              @encode(id),
                                                              NULL);
  return [[[GTMEnumeratorTargetFilter alloc] initWithBase:self
                                                      sel:predicate
                                                   target:target
                                                   object:nil] autorelease];
}

- (NSEnumerator *)gtm_filteredEnumeratorByTarget:(id)target
                           performOnEachSelector:(SEL)predicate
                                      withObject:(id)object {
  // make sure the object impls this selector taking an object as an arg.
  GTMAssertSelectorNilOrImplementedWithReturnTypeAndArguments(target, predicate,
                                                              @encode(BOOL),
                                                              @encode(id),
                                                              @encode(id),
                                                              NULL);
  return [[[GTMEnumeratorTargetFilter alloc] initWithBase:self
                                                      sel:predicate
                                                   target:target
                                                   object:object] autorelease];
}

- (NSEnumerator *)gtm_enumeratorByTarget:(id)target
                   performOnEachSelector:(SEL)selector {
  // make sure the object impls this selector taking an object as an arg.
  GTMAssertSelectorNilOrImplementedWithReturnTypeAndArguments(target, selector,
                                                              @encode(id),
                                                              @encode(id),
                                                              NULL);
  return [[[GTMEnumeratorTargetTransformer alloc] initWithBase:self
                                                           sel:selector
                                                        target:target
                                                        object:nil]
          autorelease];
}

- (NSEnumerator *)gtm_enumeratorByTarget:(id)target
                   performOnEachSelector:(SEL)selector
                              withObject:(id)object {
  // make sure the object impls this selector taking an object as an arg.
  GTMAssertSelectorNilOrImplementedWithReturnTypeAndArguments(target, selector,
                                                              @encode(id),
                                                              @encode(id),
                                                              @encode(id),
                                                              NULL);
  return [[[GTMEnumeratorTargetTransformer alloc] initWithBase:self
                                                           sel:selector
                                                        target:target
                                                        object:object]
          autorelease];
}

@end