~ubuntu-branches/ubuntu/karmic/gnustep-base/karmic

« back to all changes in this revision

Viewing changes to Source/NSProtocolChecker.m

  • Committer: Bazaar Package Importer
  • Author(s): Eric Heintzmann
  • Date: 2005-04-17 00:14:38 UTC
  • mfrom: (1.2.1 upstream) (2.1.2 hoary)
  • Revision ID: james.westby@ubuntu.com-20050417001438-enf0y07c9tku85z1
Tags: 1.10.3-1
New upstream release.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
/** Implementation of NSProtocolChecker for GNUStep
2
2
   Copyright (C) 1995 Free Software Foundation, Inc.
3
3
 
4
 
   Written by:  Mike Kienenberger
 
4
   Original by:  Mike Kienenberger
5
5
   Date: Jun 1998
6
 
   
 
6
   Written: Richard Frith-Macdonald
 
7
   Date: April 2004
 
8
 
7
9
   This file is part of the GNUstep Base Library.
8
10
 
9
11
   This library is free software; you can redistribute it and/or
10
12
   modify it under the terms of the GNU Library General Public
11
13
   License as published by the Free Software Foundation; either
12
14
   version 2 of the License, or (at your option) any later version.
13
 
   
 
15
 
14
16
   This library is distributed in the hope that it will be useful,
15
17
   but WITHOUT ANY WARRANTY; without even the implied warranty of
16
18
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17
19
   Library General Public License for more details.
18
 
   
 
20
 
19
21
   You should have received a copy of the GNU Library General Public
20
22
   License along with this library; if not, write to the Free
21
23
   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA.
22
24
 
23
25
   <title>NSProtocolChecker class reference</title>
24
 
   $Date: 2001/12/18 16:54:15 $ $Revision: 1.10 $
25
 
   */ 
 
26
   $Date: 2004/08/20 17:53:16 $ $Revision: 1.19 $
 
27
   */
26
28
 
27
29
#include "config.h"
28
 
#include <base/preface.h>
29
 
#include <Foundation/NSProtocolChecker.h>
30
 
#include <Foundation/NSException.h>
31
 
#include <Foundation/NSInvocation.h>
32
 
#include <Foundation/NSMethodSignature.h>
33
 
 
 
30
#include "GNUstepBase/preface.h"
 
31
#include "Foundation/NSProtocolChecker.h"
 
32
#include "Foundation/NSException.h"
 
33
#include "Foundation/NSInvocation.h"
 
34
#include "Foundation/NSMethodSignature.h"
 
35
 
 
36
/**
 
37
 * The NSProtocolChecker and NSProxy classes provide message filtering and
 
38
 * forwarding capabilities. If you wish to ensure at runtime that a given
 
39
 * object will only be sent messages in a certain protocol, you create an
 
40
 * <code>NSProtocolChecker</code> instance with the protocol and the object as
 
41
 * arguments-
 
42
 
 
43
<example>
 
44
    id versatileObject = [[ClassWithManyMethods alloc] init];
 
45
    id narrowObject = [NSProtocolChecker protocolCheckerWithTarget: versatileObject
 
46
                                         protocol: @protocol(SomeSpecificProtocol)];
 
47
    return narrowObject;
 
48
</example>
 
49
 
 
50
 * This is often used in conjunction with distributed objects to expose only a
 
51
 * subset of an objects methods to remote processes
 
52
 */
34
53
@implementation NSProtocolChecker
35
54
 
36
 
/*
37
 
 * Allocates and initializes an NSProtocolChecker instance that will
38
 
 * forward any messages in the aProtocol protocol to anObject, its
39
 
 * target. Thus, the checker can be vended in lieu of anObject to
40
 
 * restrict the messages that can be sent to anObject. Returns the
41
 
 * new instance.
 
55
/**
 
56
 * Allocates and initializes an NSProtocolChecker instance by calling
 
57
 * -initWithTarget:protocol:<br />
 
58
 * Autoreleases and returns the new instance.
42
59
 */
43
 
 
44
60
+ (id) protocolCheckerWithTarget: (NSObject*)anObject
45
61
                        protocol: (Protocol*)aProtocol
46
62
{
48
64
                                                      protocol: aProtocol]);
49
65
}
50
66
 
51
 
/*
 
67
- (void) dealloc
 
68
{
 
69
  DESTROY(_myTarget);
 
70
  [super dealloc];
 
71
}
 
72
 
 
73
- (struct objc_method_description*) _methodDescription: (SEL)aSelector
 
74
{
 
75
  extern struct objc_method_description *GSDescriptionForInstanceMethod();
 
76
  extern struct objc_method_description *GSDescriptionForClassMethod();
 
77
 
 
78
  if (_myProtocol != nil && _myTarget != nil)
 
79
    {
 
80
      struct objc_method_description* mth;
 
81
 
 
82
      /* Older gcc versions may not initialise Protocol objects properly
 
83
       * so we have an evil hack which checks for a known bad value of
 
84
       * the class pointer, and uses an internal function
 
85
       * (implemented in NSObject.m) to examine the protocol contents
 
86
       * without sending any ObjectiveC message to it.
 
87
       */
 
88
      if (GSObjCIsInstance(_myTarget))
 
89
        {
 
90
          if ((int)GSObjCClass(_myProtocol) == 0x2)
 
91
            {
 
92
              mth = GSDescriptionForInstanceMethod(_myProtocol, aSelector);
 
93
            }
 
94
          else
 
95
            {
 
96
              mth = [_myProtocol descriptionForInstanceMethod: aSelector];
 
97
            }
 
98
        }
 
99
      else
 
100
        {
 
101
          if ((int)GSObjCClass(_myProtocol) == 0x2)
 
102
            {
 
103
              mth = GSDescriptionForClassMethod(_myProtocol, aSelector);
 
104
            }
 
105
          else
 
106
            {
 
107
              mth = [_myProtocol descriptionForClassMethod: aSelector];
 
108
            }
 
109
        }
 
110
      return mth;
 
111
    }
 
112
  return 0;
 
113
}
 
114
 
 
115
/**
52
116
 * Forwards any message to the delegate if the method is declared in
53
 
 * the checker's protocol; otherwise raises an NSInvalidArgumentException.
 
117
 * the checker's protocol; otherwise raises an
 
118
 * <code>NSInvalidArgumentException</code>.
54
119
 */
55
120
- (void) forwardInvocation: (NSInvocation*)anInvocation
56
121
{
57
 
  unsigned int  length;
58
 
  void          *buffer;
59
 
  
60
 
  if ((struct objc_method_description *)NULL
61
 
    != [self methodDescriptionForSelector: [anInvocation selector]])
62
 
    [[NSException exceptionWithName: NSInvalidArgumentException
63
 
                  reason: @"Method not declared in current protocol"
64
 
                  userInfo: nil] raise];
65
 
      
 
122
  const char    *type;
 
123
 
 
124
  if ([self _methodDescription: [anInvocation selector]] == 0)
 
125
    {
 
126
      if (GSObjCIsInstance(_myTarget))
 
127
        {
 
128
          [NSException raise: NSInvalidArgumentException
 
129
                      format: @"<%s -%@> not declared",
 
130
            [_myProtocol name], NSStringFromSelector([anInvocation selector])];
 
131
        }
 
132
      else
 
133
        {
 
134
          [NSException raise: NSInvalidArgumentException
 
135
                      format: @"<%s +%@> not declared",
 
136
            [_myProtocol name], NSStringFromSelector([anInvocation selector])];
 
137
        }
 
138
    }
66
139
  [anInvocation invokeWithTarget: _myTarget];
67
 
  
68
 
  length = [[anInvocation methodSignature] methodReturnLength];
69
 
  buffer = (void *)malloc(length);
70
 
  [anInvocation getReturnValue: buffer];
71
 
  
72
 
  if (0 == strcmp([[anInvocation methodSignature] methodReturnType],
73
 
                  [[anInvocation methodSignatureForSelector: 
74
 
                                  @selector(init: )] methodReturnType]) )
 
140
 
 
141
  /*
 
142
   * If the method returns 'self' (ie the target object) replace the
 
143
   * returned value with the protocol checker.
 
144
   */
 
145
  type = [[anInvocation methodSignature] methodReturnType];
 
146
  if (strcmp(type, @encode(id)) == 0)
75
147
    {
76
 
      if (((id)buffer) == _myTarget)
77
 
        {
78
 
          ((id)buffer) = self;
79
 
          [anInvocation setReturnValue: buffer];
80
 
        }
 
148
      id        buf;
 
149
 
 
150
      [anInvocation getReturnValue: &buf];
 
151
      if (buf == _myTarget)
 
152
        {
 
153
          buf = self;
 
154
          [anInvocation setReturnValue: &buf];
 
155
        }
81
156
    }
82
 
  
83
 
  return;
84
157
}
85
158
 
86
 
 
87
159
- (id) init
88
160
{
89
 
  _myProtocol = nil;
90
 
  _myTarget = nil;
91
 
  
 
161
  self = [self initWithTarget: nil protocol: nil];
92
162
  return self;
93
163
}
94
164
 
95
 
/*
 
165
/**
96
166
 * Initializes a newly allocated NSProtocolChecker instance that will
97
167
 * forward any messages in the aProtocol protocol to anObject, its
98
168
 * delegate. Thus, the checker can be vended in lieu of anObject to
99
 
 * restrict the messages that can be sent to anObject. If anObject is
100
 
 * allowed to be freed or dereferenced by clients, the free method
101
 
 * should be included in aProtocol. Returns the new instance.
 
169
 * restrict the messages that can be sent to anObject. If any method
 
170
 * in the protocol returns anObject, the checker will replace the returned
 
171
 * value with itself rather than the target object.<br />
 
172
 * Returns the new instance.
102
173
 */
103
174
- (id) initWithTarget: (NSObject*)anObject protocol: (Protocol*)aProtocol
104
175
{
105
 
  [super init];
106
 
  
107
176
  _myProtocol = aProtocol;
108
 
  
109
177
  ASSIGN(_myTarget, anObject);
110
 
  
111
178
  return self;
112
179
}
113
180
 
114
 
/*
115
 
 * Returns an Objective C description for a method in the checker's
116
 
 * protocol, or NULL if aSelector isn't declared as an instance method
117
 
 * in the protocol.
118
 
 */
119
 
- (struct objc_method_description*) methodDescriptionForSelector: (SEL)aSelector
 
181
- (NSMethodSignature*) methodSignatureForSelector: (SEL)aSelector
120
182
{
121
 
  return [_myProtocol descriptionForInstanceMethod: aSelector];
 
183
  const char            *types;
 
184
  struct objc_method    *mth;
 
185
  Class                 c;
 
186
 
 
187
  if (aSelector == 0)
 
188
    [NSException raise: NSInvalidArgumentException
 
189
                format: @"%@ null selector given", NSStringFromSelector(_cmd)];
 
190
 
 
191
  /*
 
192
   * Evil hack to prevent recursion - if we are asking a remote
 
193
   * object for a method signature, we can't ask it for the
 
194
   * signature of methodSignatureForSelector:, so we hack in
 
195
   * the signature required manually :-(
 
196
   */
 
197
  if (sel_eq(aSelector, _cmd))
 
198
    {
 
199
      static    NSMethodSignature       *sig = nil;
 
200
 
 
201
      if (sig == nil)
 
202
        {
 
203
          sig = [NSMethodSignature signatureWithObjCTypes: "@@::"];
 
204
          RETAIN(sig);
 
205
        }
 
206
      return sig;
 
207
    }
 
208
 
 
209
  if (_myProtocol != nil)
 
210
    {
 
211
      const char                        *types = 0;
 
212
      struct objc_method_description    *desc;
 
213
 
 
214
      desc = [self _methodDescription: aSelector];
 
215
      if (desc != 0)
 
216
        {
 
217
          types = desc->types;
 
218
        }
 
219
      if (types == 0)
 
220
        {
 
221
          return nil;
 
222
        }
 
223
      return [NSMethodSignature signatureWithObjCTypes: types];
 
224
    }
 
225
 
 
226
  c = GSObjCClass(self);
 
227
  mth = GSGetMethod(c, aSelector, YES, YES);
 
228
  if (mth == 0)
 
229
    {
 
230
      return nil; // Method not implemented
 
231
    }
 
232
  types = mth->method_types;
 
233
 
 
234
  /*
 
235
   * If there are protocols that this class conforms to,
 
236
   * the method may be listed in a protocol with more
 
237
   * detailed type information than in the class itself
 
238
   * and we must therefore use the information from the
 
239
   * protocol.
 
240
   * This is because protocols also carry information
 
241
   * used by the Distributed Objects system, which the
 
242
   * runtime does not maintain in classes.
 
243
   */
 
244
  if (c->protocols != 0)
 
245
    {
 
246
      struct objc_protocol_list *protocols = c->protocols;
 
247
      BOOL                      found = NO;
 
248
 
 
249
      while (found == NO && protocols != 0)
 
250
        {
 
251
          unsigned      i = 0;
 
252
 
 
253
          while (found == NO && i < protocols->count)
 
254
            {
 
255
              Protocol                          *p;
 
256
              struct objc_method_description    *pmth;
 
257
 
 
258
              p = protocols->list[i++];
 
259
              if (c == (Class)self)
 
260
                {
 
261
                  pmth = [p descriptionForClassMethod: aSelector];
 
262
                }
 
263
              else
 
264
                {
 
265
                  pmth = [p descriptionForInstanceMethod: aSelector];
 
266
                }
 
267
              if (pmth != 0)
 
268
                {
 
269
                  types = pmth->types;
 
270
                  found = YES;
 
271
                }
 
272
            }
 
273
          protocols = protocols->next;
 
274
        }
 
275
    }
 
276
 
 
277
  if (types == 0)
 
278
    {
 
279
      return nil;
 
280
    }
 
281
  return [NSMethodSignature signatureWithObjCTypes: types];
122
282
}
123
283
 
124
 
/*
 
284
/**
125
285
 * Returns the protocol object the checker uses to verify whether a
126
 
 * given message should be forwarded to its delegate, or the protocol
127
 
 * checker should raise an NSInvalidArgumentException.
 
286
 * given message should be forwarded to its delegate.
128
287
 */
129
288
- (Protocol*) protocol
130
289
{
131
 
  if (nil == _myProtocol)
132
 
    [[NSException exceptionWithName: NSInvalidArgumentException
133
 
                  reason: @"No protocol specified"
134
 
                  userInfo: nil] raise];
135
 
  
136
290
  return _myProtocol;
137
291
}
138
292
 
139
 
/*
 
293
/**
140
294
 * Returns the target of the NSProtocolChecker.
141
295
 */
142
296
- (NSObject*) target