1#include "pyobjc.h"
2
3
4static PyObject* mapTypes = NULL;
5
6
7@implementation OC_PythonSet
8
9+ (instancetype)depythonifyObject:(PyObject*)object
10{
11	Py_ssize_t i, len;
12	
13	if (mapTypes == NULL) return NULL;
14
15	len = PyList_GET_SIZE(mapTypes);
16
17	for (i = 0; i < len; i++) {
18		PyObject* tp = PyList_GET_ITEM(mapTypes, i);
19		int r = PyObject_IsInstance(object, tp);
20		if (r == -1) {
21			return NULL;
22		}
23
24		if (!r) continue;
25
26		/* Instance of this type should be pythonifyed as a sequence */
27		return [OC_PythonSet setWithPythonObject:object];
28	}
29
30	return NULL;
31}
32
33+ (id)depythonifyTable
34{
35	NSObject* result; 
36
37	PyObjC_BEGIN_WITH_GIL
38
39		if (mapTypes == NULL) {
40			mapTypes = PyList_New(0);
41			if (mapTypes == NULL) {
42				PyObjC_GIL_FORWARD_EXC();
43			}
44		}
45		result = PyObjC_PythonToId(mapTypes);
46		if (result == NULL) {
47			PyObjC_GIL_FORWARD_EXC();
48		}
49
50	PyObjC_END_WITH_GIL
51
52	return result;
53}
54
55+ (instancetype)setWithPythonObject:(PyObject*)v
56{
57	OC_PythonSet* res;
58
59	res = [[OC_PythonSet alloc] initWithPythonObject:v];
60	[res autorelease];
61	return res;
62}
63
64- (id)initWithPythonObject:(PyObject*)v
65{
66	self = [super init];
67	if (unlikely(self == nil)) return nil;
68
69	Py_INCREF(v);
70	Py_XDECREF(value);
71	value = v;
72	return self;
73}
74
75-(PyObject*)__pyobjc_PythonObject__
76{
77	Py_INCREF(value);
78	return value;
79}
80
81-(PyObject*)__pyobjc_PythonTransient__:(int*)cookie
82{
83	*cookie = 0;
84	Py_INCREF(value);
85	return value;
86}
87
88-(BOOL)supportsWeakPointers { return YES; }
89
90-(oneway void)release
91{
92	/* See comment in OC_PythonUnicode */
93	PyObjC_BEGIN_WITH_GIL
94		[super release];
95	PyObjC_END_WITH_GIL
96}
97
98-(void)dealloc
99{
100	PyObjC_BEGIN_WITH_GIL
101		PyObjC_UnregisterObjCProxy(value, self);
102		Py_XDECREF(value);
103
104	PyObjC_END_WITH_GIL
105
106	[super dealloc];
107}
108
109
110/* NSCoding support */
111
112-(Class)classForCoder
113{
114	return [OC_PythonSet class];
115}
116
117
118- (void)encodeWithCoder:(NSCoder*)coder
119{
120	PyObjC_encodeWithCoder(value, coder);
121}
122
123
124/* 
125 * Helper method for initWithCoder, needed to deal with
126 * recursive objects (e.g. o.value = o)
127 */
128-(void)pyobjcSetValue:(NSObject*)other
129{
130	PyObjC_BEGIN_WITH_GIL
131		PyObject* v = PyObjC_IdToPython(other);
132		Py_XDECREF(value);
133		value = v;
134	PyObjC_END_WITH_GIL
135}
136
137- (id)initWithCoder:(NSCoder*)coder
138{
139	if (PyObjC_Decoder != NULL) {
140		PyObjC_BEGIN_WITH_GIL
141			PyObject* cdr = PyObjC_IdToPython(coder);
142			if (cdr == NULL) {
143				PyObjC_GIL_FORWARD_EXC();
144			}
145
146			PyObject* setValue;
147			PyObject* selfAsPython = PyObjCObject_New(self, 0, YES);
148			setValue = PyObject_GetAttrString(selfAsPython, "pyobjcSetValue_");
149
150			PyObject* v = PyObject_CallFunction(PyObjC_Decoder, "OO", cdr, setValue);
151			Py_DECREF(cdr);
152			Py_DECREF(setValue);
153			Py_DECREF(selfAsPython);
154
155			if (v == NULL) {
156				PyObjC_GIL_FORWARD_EXC();
157			}
158
159			Py_XDECREF(value);
160			value = v;
161
162			NSObject* proxy = PyObjC_FindObjCProxy(value);
163			if (proxy == NULL) {
164				PyObjC_RegisterObjCProxy(value, self);
165			} else {
166				[self release];
167				[proxy retain];
168				self = (OC_PythonSet*)proxy;
169			}
170
171
172		PyObjC_END_WITH_GIL
173
174		return self;
175
176	} else {
177		[NSException raise:NSInvalidArgumentException
178				format:@"decoding Python objects is not supported"];
179		return nil;
180
181	}
182}
183
184
185/*
186 * Implementation of the NSMutableSet interface
187 */
188-(id)copyWithZone:(NSZone*)zone
189{
190	(void)zone;
191	if (PyObjC_CopyFunc != NULL) {
192		PyObjC_BEGIN_WITH_GIL
193			PyObject* tmp = PyObject_CallFunction(PyObjC_CopyFunc, "O", value);
194			if (tmp == NULL) {
195				PyObjC_GIL_FORWARD_EXC();
196			}
197
198			NSObject* result = PyObjC_PythonToId(tmp);
199			Py_DECREF(tmp);
200			if (PyErr_Occurred()) {
201				PyObjC_GIL_FORWARD_EXC();
202			}
203
204			[result retain];
205			PyObjC_GIL_RETURN(result);
206
207		PyObjC_END_WITH_GIL
208
209	} else {
210		[NSException raise:NSInvalidArgumentException 
211		            format:@"cannot copy python set"];
212		return nil;
213	}
214}
215
216-(id)mutableCopyWithZone:(NSZone*)zone
217{
218	(void)zone;
219	PyObjC_BEGIN_WITH_GIL
220
221		PyObject* tmp = PySet_New(value);
222		if (tmp == NULL) {
223			PyObjC_GIL_FORWARD_EXC();
224		}
225
226		NSObject* result = PyObjC_PythonToId(tmp);
227		Py_DECREF(tmp);
228		if (PyErr_Occurred()) {
229			PyObjC_GIL_FORWARD_EXC();
230		}
231		
232		[result retain];
233		PyObjC_GIL_RETURN(result);
234
235	PyObjC_END_WITH_GIL
236}
237
238-(NSArray*)allObjects
239{
240	PyObjC_BEGIN_WITH_GIL
241		PyObject* tmp = PySequence_List(value);
242		if (tmp == NULL) {
243			PyObjC_GIL_FORWARD_EXC();
244		}
245
246		NSArray* result = (NSArray*)PyObjC_PythonToId(tmp);
247		Py_DECREF(tmp);
248		if (PyErr_Occurred()) {
249			PyObjC_GIL_FORWARD_EXC();
250		}
251
252		PyObjC_GIL_RETURN(result);
253
254	PyObjC_END_WITH_GIL
255}
256
257-(NSObject*)anyObject
258{
259	PyObjC_BEGIN_WITH_GIL
260		if (PySet_Size(value) == 0) {
261			PyObjC_GIL_RETURN(nil);
262		}
263
264		PyObject* tmp = PyObject_GetIter(value);
265		if (tmp == NULL) {
266			PyObjC_GIL_FORWARD_EXC();
267		}
268
269		PyObject* v = PyIter_Next(tmp);
270		Py_DECREF(tmp);
271		if (v == NULL) {
272			PyObjC_GIL_FORWARD_EXC();
273		}
274		
275		NSObject* result = PyObjC_PythonToId(v);
276		Py_DECREF(v);
277		if (PyErr_Occurred()) {
278			PyObjC_GIL_FORWARD_EXC();
279		}
280		
281		PyObjC_GIL_RETURN(result);
282
283	PyObjC_END_WITH_GIL
284}
285
286-(BOOL)containsObject:(id)anObject
287{
288	PyObjC_BEGIN_WITH_GIL
289		PyObject* tmp = PyObjC_IdToPython(anObject);
290		if (tmp == NULL) {
291			PyObjC_GIL_FORWARD_EXC();
292		}
293
294		int r = PySequence_Contains(value, tmp);
295		Py_DECREF(tmp);
296		if (r == -1) {
297			PyObjC_GIL_FORWARD_EXC();
298		}
299
300
301		if (r) {
302			PyObjC_GIL_RETURN(YES);
303		} else {
304			PyObjC_GIL_RETURN(NO);
305		}
306	PyObjC_END_WITH_GIL
307}
308
309-(NSUInteger)count
310{
311	PyObjC_BEGIN_WITH_GIL
312		Py_ssize_t result = PySequence_Size(value);
313		if (result == -1) {
314			PyObjC_GIL_FORWARD_EXC();
315		}
316
317		PyObjC_GIL_RETURN((NSUInteger)result);
318	PyObjC_END_WITH_GIL
319}
320
321-(NSEnumerator*)objectEnumerator
322{
323	PyObjC_BEGIN_WITH_GIL
324		PyObject* tmp = PyObject_GetIter(value);
325		if (tmp == NULL) {
326			PyObjC_GIL_FORWARD_EXC();
327		}
328
329		NSEnumerator* result = [OC_PythonEnumerator enumeratorWithPythonObject:tmp];
330		Py_DECREF(tmp);
331
332		PyObjC_GIL_RETURN(result);
333
334	PyObjC_END_WITH_GIL
335}
336
337/* It seems impossible to create an efficient implementation of this method,
338 * iteration is basicly the only way to fetch the requested object
339 *
340 * XXX: this means we should implement more of NS(Mutable)Set interface,
341 * that's a lot more efficient than iterating over and over again.
342 *
343 */
344-(id)member:(id)anObject
345{
346	PyObjC_BEGIN_WITH_GIL
347		PyObject* tmpMember = PyObjC_IdToPython(anObject);
348		if (tmpMember == NULL) {
349			PyObjC_GIL_FORWARD_EXC();
350		}
351
352		int r = PySequence_Contains(value, tmpMember);
353		if (r == -1) {
354			Py_DECREF(tmpMember);
355			PyObjC_GIL_FORWARD_EXC();
356		}
357
358		if (!r) {
359			Py_DECREF(tmpMember);
360			PyObjC_GIL_RETURN(nil);
361		}
362
363
364		/* This sucks, we have to iterate over the contents of the
365		 * set to find the object we need...
366		 */
367		PyObject* tmp = PyObject_GetIter(value);
368		if (tmp == NULL) {
369			PyObjC_GIL_FORWARD_EXC();
370		}
371
372		PyObject* v;
373		
374		while ((v = PyIter_Next(tmp)) != NULL) {
375			r = PyObject_RichCompareBool(v, tmpMember, Py_EQ);
376			if (r == -1) {
377				Py_DECREF(tmp);
378				Py_DECREF(tmpMember);
379				PyObjC_GIL_FORWARD_EXC();
380			}
381
382			if (r) {
383				/* Found the object */
384				Py_DECREF(tmp);
385				Py_DECREF(tmpMember);
386
387				NSObject* result = PyObjC_PythonToId(v);
388				if (PyErr_Occurred()) {
389					PyObjC_GIL_FORWARD_EXC();
390				}
391				PyObjC_GIL_RETURN(result);
392			}
393		}
394
395		Py_DECREF(tmp);
396		Py_DECREF(tmpMember);
397		PyObjC_GIL_RETURN(nil);
398
399
400	PyObjC_END_WITH_GIL
401}
402
403-(void)removeAllObjects
404{
405	PyObjC_BEGIN_WITH_GIL
406		if (PyFrozenSet_CheckExact(value)) {
407			PyErr_SetString(PyExc_TypeError,
408				"Cannot mutate a frozenstring");
409			PyObjC_GIL_FORWARD_EXC();
410		}
411
412		if (PyAnySet_Check(value)) {
413			int r = PySet_Clear(value);\
414			if (r == -1) {
415				PyObjC_GIL_FORWARD_EXC();
416			}
417		} else {
418			/* Assume an object that conforms to 
419			 * the set interface 
420			 */
421			 PyObject* r;
422			 
423			 r = PyObject_CallMethod(value, "clear", NULL);
424			 if (r == NULL) {
425				 PyObjC_GIL_FORWARD_EXC();
426			 }
427			 Py_DECREF(r);
428		}
429	PyObjC_END_WITH_GIL
430}
431
432-(void)removeObject:(id)anObject
433{
434	PyObjC_BEGIN_WITH_GIL
435		PyObject* tmp = PyObjC_IdToPython(anObject);
436		if (tmp == NULL) {
437			PyObjC_GIL_FORWARD_EXC();
438		} 
439
440		if (PyFrozenSet_CheckExact(value)) {
441			PyErr_SetString(PyExc_TypeError,
442				"Cannot mutate a frozenstring");
443			PyObjC_GIL_FORWARD_EXC();
444		}
445
446		if (PyAnySet_Check(value)) {
447			int r = PySet_Discard(value, tmp);
448			Py_DECREF(tmp);
449			if (r == -1) {
450				PyObjC_GIL_FORWARD_EXC();
451			}
452
453		} else {
454			 PyObject* r;
455			 
456			 r = PyObject_CallMethod(value, "discard", "O", tmp);
457			 Py_DECREF(tmp);
458			 if (r == NULL) {
459				 PyObjC_GIL_FORWARD_EXC();
460			 }
461			 Py_DECREF(r);
462		}
463
464	PyObjC_END_WITH_GIL
465}
466
467-(void)addObject:(id)anObject
468{
469	PyObjC_BEGIN_WITH_GIL
470		PyObject* tmp = PyObjC_IdToPython(anObject);
471		if (tmp == NULL) {
472			PyObjC_GIL_FORWARD_EXC();
473		} 
474
475		if (PyFrozenSet_CheckExact(value)) {
476			PyErr_SetString(PyExc_TypeError,
477				"Cannot mutate a frozenstring");
478			PyObjC_GIL_FORWARD_EXC();
479		}
480
481		if (PyAnySet_Check(value)) {
482			int r = PySet_Add(value, tmp);
483			Py_DECREF(tmp);
484			if (r == -1) {
485				PyObjC_GIL_FORWARD_EXC();
486			}
487
488		} else {
489			 PyObject* r;
490			 
491			 r = PyObject_CallMethod(value, "add", "O", tmp);
492			 Py_DECREF(tmp);
493			 if (r == NULL) {
494				 PyObjC_GIL_FORWARD_EXC();
495			 }
496			 Py_DECREF(r);
497		}
498		
499	PyObjC_END_WITH_GIL
500}
501
502@end
503