API  1.0.0
CPSet+KVO.j
Go to the documentation of this file.
1 /*
2  * CPSet+KVO.j
3  * Foundation
4  *
5  * Created by Daniel Stolzenberg.
6  * Copyright 2010, University of Rostock
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  *
13  * This library 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 GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21  */
22 
23 
24 @implementation CPObject (CPSetKVO)
25 
26 - (id)mutableSetValueForKey:(id)aKey
27 {
28  return [[_CPKVCSet alloc] initWithKey:aKey forProxyObject:self];
29 }
30 
31 - (id)mutableSetValueForKeyPath:(id)aKeyPath
32 {
33  var dotIndex = aKeyPath.indexOf(".");
34 
35  if (dotIndex < 0)
36  return [self mutableSetValueForKey:aKeyPath];
37 
38  var firstPart = aKeyPath.substring(0, dotIndex),
39  lastPart = aKeyPath.substring(dotIndex + 1);
40 
41  return [[self valueForKeyPath:firstPart] mutableSetValueForKeyPath:lastPart];
42 }
43 
44 @end
45 
46 @implementation _CPKVCSet : CPMutableSet
47 {
48  id _proxyObject;
49  id _key;
50 
51 
52  SEL _accessSEL;
53  Function _access;
54 
55  SEL _setSEL;
56  Function _set;
57 
58  SEL _countSEL;
59  Function _count;
60 
61  SEL _enumeratorSEL;
62  Function _enumerator;
63 
64  SEL _memberSEL;
65  Function _member;
66 
67  SEL _addSEL;
68  Function _add;
69 
70  SEL _addManySEL;
71  Function _addMany;
72 
73  SEL _removeSEL;
74  Function _remove;
75 
76  SEL _removeManySEL;
77  Function _removeMany;
78 
79  SEL _intersectSEL;
80  Function _intersect;
81 }
82 
83 + (id)alloc
84 {
85  var set = [CPMutableSet set];
86 
87  set.isa = self;
88 
89  var ivars = class_copyIvarList(self),
90  count = ivars.length;
91 
92  while (count--)
93  set[ivar_getName(ivars[count])] = nil;
94 
95  return set;
96 }
97 
98 - (id)initWithKey:(id)aKey forProxyObject:(id)anObject
99 {
100  self = [super init];
101 
102  _key = aKey;
103  _proxyObject = anObject;
104 
105  var capitalizedKey = _key.charAt(0).toUpperCase() + _key.substring(1);
106 
107  _accessSEL = sel_getName(_key);
108  if ([_proxyObject respondsToSelector:_accessSEL])
109  _access = [_proxyObject methodForSelector:_accessSEL];
110 
111  _setSEL = sel_getName(@"set"+capitalizedKey+":");
112  if ([_proxyObject respondsToSelector:_setSEL])
113  _set = [_proxyObject methodForSelector:_setSEL];
114 
115  _countSEL = sel_getName(@"countOf"+capitalizedKey);
116  if ([_proxyObject respondsToSelector:_countSEL])
117  _count = [_proxyObject methodForSelector:_countSEL];
118 
119  _enumeratorSEL = sel_getName(@"enumeratorOf"+capitalizedKey);
120  if ([_proxyObject respondsToSelector:_enumeratorSEL])
121  _enumerator = [_proxyObject methodForSelector:_enumeratorSEL];
122 
123  _memberSEL = sel_getName(@"memberOf"+capitalizedKey+":");
124  if ([_proxyObject respondsToSelector:_memberSEL])
125  _member = [_proxyObject methodForSelector:_memberSEL];
126 
127  _addSEL = sel_getName(@"add"+capitalizedKey+"Object:");
128  if ([_proxyObject respondsToSelector:_addSEL])
129  _add = [_proxyObject methodForSelector:_addSEL];
130 
131  _addManySEL = sel_getName(@"add"+capitalizedKey+":");
132  if ([_proxyObject respondsToSelector:_addManySEL])
133  _addMany = [_proxyObject methodForSelector:_addManySEL];
134 
135  _removeSEL = sel_getName(@"remove"+capitalizedKey+"Object:");
136  if ([_proxyObject respondsToSelector:_removeSEL])
137  _remove = [_proxyObject methodForSelector:_removeSEL];
138 
139  _removeManySEL = sel_getName(@"remove"+capitalizedKey+":");
140  if ([_proxyObject respondsToSelector:_removeManySEL])
141  _removeMany = [_proxyObject methodForSelector:_removeManySEL];
142 
143  _intersectSEL = sel_getName(@"intersect"+capitalizedKey+":");
144  if ([_proxyObject respondsToSelector:_intersectSEL])
145  _intersect = [_proxyObject methodForSelector:_intersectSEL];
146 
147  return self;
148 }
149 
150 - (id)_representedObject
151 {
152  if (_access)
153  return _access(_proxyObject, _accessSEL);
154 
155  return [_proxyObject valueForKey:_key];
156 }
157 
158 - (void)_setRepresentedObject:(id)anObject
159 {
160  if (_set)
161  return _set(_proxyObject, _setSEL, anObject);
162 
163  [_proxyObject setValue:anObject forKey:_key];
164 }
165 
166 - (CPUInteger)count
167 {
168  if (_count)
169  return _count(_proxyObject, _countSEL);
170 
171  return [[self _representedObject] count];
172 }
173 
174 - (CPEnumerator)objectEnumerator
175 {
176  if (_enumerator)
177  return _enumerator(_proxyObject, _enumeratorSEL);
178 
179  return [[self _representedObject] objectEnumerator];
180 }
181 
182 - (id)member:(id)anObject
183 {
184  if (_member)
185  return _member(_proxyObject, _memberSEL, anObject);
186 
187  return [[self _representedObject] member:anObject];
188 }
189 
190 - (void)addObject:(id)anObject
191 {
192  if (_add)
193  _add(_proxyObject, _addSEL, anObject);
194  else if (_addMany)
195  {
196  var objectSet = [CPSet setWithObject: anObject];
197  _addMany(_proxyObject, _addManySEL, objectSet);
198  }
199  else
200  {
201  var target = [[self _representedObject] copy];
202  [target addObject:anObject];
203  [self _setRepresentedObject:target];
204  }
205 }
206 
207 - (void)addObjectsFromArray:(CPArray)objects
208 {
209  if (_addMany)
210  {
211  var objectSet = [CPSet setWithArray: objects];
212  _addMany(_proxyObject, _addManySEL, objectSet);
213  }
214  else if (_add)
215  {
216  var object,
217  objectEnumerator = [objects objectEnumerator];
218 
219  while ((object = [objectEnumerator nextObject]) !== nil)
220  _add(_proxyObject, _addSEL, object);
221  }
222  else
223  {
224  var target = [[self _representedObject] copy];
225  [target addObjectsFromArray:objects];
226  [self _setRepresentedObject:target];
227  }
228 }
229 
230 - (void)unionSet:(CPSet)aSet
231 {
232  if (_addMany)
233  _addMany(_proxyObject, _addManySEL, aSet);
234  else if (_add)
235  {
236  var object,
237  objectEnumerator = [aSet objectEnumerator];
238 
239  while ((object = [objectEnumerator nextObject]) !== nil)
240  _add(_proxyObject, _addSEL, object);
241  }
242  else
243  {
244  var target = [[self _representedObject] copy];
245  [target unionSet:aSet];
246  [self _setRepresentedObject:target];
247  }
248 }
249 
250 - (void)removeObject:(id)anObject
251 {
252  if (_remove)
253  _remove(_proxyObject, _removeSEL, anObject);
254  else if (_removeMany)
255  {
256  var objectSet = [CPSet setWithObject: anObject];
257  _removeMany(_proxyObject, _removeManySEL, objectSet);
258  }
259  else
260  {
261  var target = [[self _representedObject] copy];
262  [target removeObject:anObject];
263  [self _setRepresentedObject:target];
264  }
265 }
266 
267 - (void)minusSet:(CPSet)aSet
268 {
269  if (_removeMany)
270  _removeMany(_proxyObject, _removeManySEL, aSet);
271  else if (_remove)
272  {
273  var object,
274  objectEnumerator = [aSet objectEnumerator];
275 
276  while ((object = [objectEnumerator nextObject]) !== nil)
277  _remove(_proxyObject, _removeSEL, object);
278  }
279  else
280  {
281  var target = [[self _representedObject] copy];
282  [target minusSet:aSet];
283  [self _setRepresentedObject:target];
284  }
285 }
286 
287 - (void)removeObjectsInArray:(CPArray)objects
288 {
289  if (_removeMany)
290  {
291  var objectSet = [CPSet setWithArray:objects];
292  _removeMany(_proxyObject, _removeManySEL, objectSet);
293  }
294  else if (_remove)
295  {
296  var object,
297  objectEnumerator = [objects objectEnumerator];
298 
299  while ((object = [objectEnumerator nextObject]) !== nil)
300  _remove(_proxyObject, _removeSEL, object);
301  }
302  else
303  {
304  var target = [[self _representedObject] copy];
305  [target removeObjectsInArray:objects];
306  [self _setRepresentedObject:target];
307  }
308 }
309 
310 - (void)removeAllObjects
311 {
312  if (_removeMany)
313  {
314  var allObjectsSet = [[self _representedObject] copy];
315  _removeMany(_proxyObject, _removeManySEL, allObjectsSet);
316  }
317  else if (_remove)
318  {
319  var object,
320  objectEnumerator = [[[self _representedObject] copy] objectEnumerator];
321 
322  while ((object = [objectEnumerator nextObject]) !== nil)
323  _remove(_proxyObject, _removeSEL, object);
324  }
325  else
326  {
327  var target = [[self _representedObject] copy];
328  [target removeAllObjects];
329  [self _setRepresentedObject:target];
330  }
331 }
332 
333 - (void)intersectSet:(CPSet)aSet
334 {
335  if (_intersect)
336  _intersect(_proxyObject, _intersectSEL, aSet);
337  else
338  {
339  var target = [[self _representedObject] copy];
340  [target intersectSet:aSet];
341  [self _setRepresentedObject:target];
342  }
343 }
344 
345 - (void)setSet:(CPSet)set
346 {
347  [self _setRepresentedObject: set];
348 }
349 
350 - (CPArray)allObjects
351 {
352  return [[self _representedObject] allObjects];
353 }
354 
355 - (id)anyObject
356 {
357  return [[self _representedObject] anyObject];
358 }
359 
360 - (BOOL)containsObject:(id)anObject
361 {
362  return [[self _representedObject] containsObject: anObject];
363 }
364 
365 - (BOOL)intersectsSet:(CPSet)aSet
366 {
367  return [[self _representedObject] intersectsSet: aSet];
368 }
369 
370 - (BOOL)isEqualToSet:(CPSet)aSet
371 {
372  return [[self _representedObject] isEqualToSet: aSet];
373 }
374 
375 - (id)copy
376 {
377  return [[self _representedObject] copy];
378 }
379 
380 @end
381 
383 
384 - (id)valueForKeyPath:(CPString)aKeyPath
385 {
386  if (!aKeyPath)
387  [self valueForUndefinedKey:@"<empty path>"];
388 
389  if (aKeyPath.charAt(0) === "@")
390  {
391  var dotIndex = aKeyPath.indexOf("."),
392  operator,
393  parameter;
394 
395  if (dotIndex !== -1)
396  {
397  operator = aKeyPath.substring(1, dotIndex);
398  parameter = aKeyPath.substring(dotIndex + 1);
399  }
400  else
401  operator = aKeyPath.substring(1);
402 
403  return [_CPCollectionKVCOperator performOperation:operator withCollection:self propertyPath:parameter];
404  }
405  else
406  {
407  var valuesForKeySet = [CPSet set],
408  containedObject,
409  containedObjectValue,
410  containedObjectEnumerator = [self objectEnumerator];
411 
412  while ((containedObject = [containedObjectEnumerator nextObject]) !== nil)
413  {
414  containedObjectValue = [containedObject valueForKeyPath:aKeyPath];
415 
416  if (containedObjectValue === nil || containedObjectValue === undefined)
417  containedObjectValue = [CPNull null];
418 
419  [valuesForKeySet addObject:containedObjectValue];
420  }
421 
422  return valuesForKeySet;
423  }
424 }
425 
426 - (void)setValue:(id)aValue forKey:(CPString)aKey
427 {
428  var containedObject,
429  containedObjectEnumerator = [self objectEnumerator];
430 
431  while ((containedObject = [containedObjectEnumerator nextObject]) !== nil)
432  [containedObject setValue:aValue forKey:aKey];
433 }
434 
435 @end
An object representation of nil.
Definition: CPNull.h:2
An immutable string (collection of characters).
Definition: CPString.h:2
CPNull null()
Definition: CPNull.j:51
Defines an interface for enumerators.
Definition: CPEnumerator.h:2
id mutableSetValueForKey:(id aKey)
Definition: CPSet+KVO.j:26
id valueForKeyPath:(CPString aKeyPath)