1/*
2 * Copyright (c) 2006-2008, The RubyCocoa Project.
3 * Copyright (c) 2001-2006, FUJIMOTO Hisakuni.
4 * All Rights Reserved.
5 *
6 * RubyCocoa is free software, covered under either the Ruby's license or the
7 * LGPL. See the COPYRIGHT file for more information.
8 */
9
10#import "cls_objcid.h"
11
12#import "osx_ruby.h"
13#import "ocdata_conv.h"
14#import <Foundation/Foundation.h>
15#import <string.h>
16#import <stdlib.h>
17#import "RBObject.h"
18#import "internal_macros.h"
19#import "BridgeSupport.h"
20#import "mdl_osxobjc.h"
21
22static VALUE _kObjcID = Qnil;
23
24static void
25_objcid_data_free(struct _objcid_data* dp)
26{
27  id pool = [[NSAutoreleasePool alloc] init];
28  if (dp != NULL) {
29    if (dp->ocid != nil) {
30      remove_from_oc2rb_cache(dp->ocid);
31      if (dp->retained && dp->can_be_released) {
32        DLOG("CLSOBJ", "releasing %p", dp->ocid);
33        [dp->ocid release];
34      }
35    }
36    free(dp);
37  }
38  [pool release];
39}
40
41static struct _objcid_data*
42_objcid_data_new()
43{
44  struct _objcid_data* dp;
45  dp = malloc(sizeof(struct _objcid_data));
46  dp->ocid = nil;
47  dp->retained = NO;
48  dp->can_be_released = NO;
49  return dp;
50}
51
52static VALUE
53objcid_s_new(int argc, VALUE* argv, VALUE klass)
54{
55  VALUE obj;
56  obj = Data_Wrap_Struct(klass, NULL, _objcid_data_free, _objcid_data_new());
57  rb_obj_call_init(obj, argc, argv);
58  return obj;
59}
60
61VALUE
62objcid_new_with_ocid(VALUE klass, id ocid)
63{
64  VALUE obj;
65
66  obj = Data_Wrap_Struct(klass, 0, _objcid_data_free, _objcid_data_new());
67
68  // The retention of the ObjC instance is delayed in ocm_send, to not
69  // violate the "init-must-follow-alloc" initialization pattern.
70  // Retaining here could message in the middle.
71  if (ocid != nil) {
72    OBJCID_DATA_PTR(obj)->ocid = ocid;
73    OBJCID_DATA_PTR(obj)->retained = NO;
74  }
75
76  rb_obj_call_init(obj, 0, NULL);
77  return obj;
78}
79
80static VALUE
81wrapper_objcid_s_new_with_ocid(VALUE klass, VALUE rbocid)
82{
83  return objcid_new_with_ocid(klass, NUM2OCID(rbocid));
84}
85
86static VALUE
87objcid_release(VALUE rcv)
88{
89  if (OBJCID_DATA_PTR(rcv)->can_be_released) {
90    [OBJCID_ID(rcv) release];
91    OBJCID_DATA_PTR(rcv)->can_be_released = NO;
92  }
93  return rcv;
94}
95
96static VALUE
97objcid_initialize(int argc, VALUE* argv, VALUE rcv)
98{
99  return rcv;
100}
101
102static VALUE
103objcid_ocid(VALUE rcv)
104{
105  return OCID2NUM(OBJCID_ID(rcv));
106}
107
108static VALUE
109objcid_inspect(VALUE rcv)
110{
111  char              s[512];
112  id                ocid;
113  struct bsConst *  bs_const;
114  const char *      class_desc;
115  id                pool;
116
117  ocid = OBJCID_ID(rcv);
118  bs_const = find_magic_cookie_const_by_value(ocid);
119  if (bs_const != NULL) {
120    pool = nil;
121    class_desc = bs_const->class_name;
122  }
123  else {
124    pool = [[NSAutoreleasePool alloc] init];
125    class_desc = [[[ocid class] description] UTF8String];
126  }
127
128  snprintf(s, sizeof(s), "#<%s:0x%lx class='%s' id=%p>",
129    rb_class2name(CLASS_OF(rcv)),
130    NUM2ULONG(rb_obj_id(rcv)),
131    class_desc, ocid);
132
133  if (pool != nil)
134    [pool release];
135
136  return rb_str_new2(s);
137}
138
139/** class methods **/
140
141VALUE
142objid_s_class ()
143{
144  return _kObjcID;
145}
146
147/*******/
148
149VALUE
150init_cls_ObjcID(VALUE outer)
151{
152  _kObjcID = rb_define_class_under(outer, "ObjcID", rb_cObject);
153
154  rb_define_singleton_method(_kObjcID, "new", objcid_s_new, -1);
155  rb_define_singleton_method(_kObjcID, "new_with_ocid", wrapper_objcid_s_new_with_ocid, 1);
156
157  rb_define_method(_kObjcID, "initialize", objcid_initialize, -1);
158  rb_define_method(_kObjcID, "__ocid__", objcid_ocid, 0);
159  rb_define_method(_kObjcID, "__inspect__", objcid_inspect, 0);
160  rb_define_method(_kObjcID, "release", objcid_release, 0);
161  rb_define_method(_kObjcID, "inspect", objcid_inspect, 0);
162
163  return _kObjcID;
164}
165