File: NSProtocolChecker.m

package info (click to toggle)
gnustep-base 1.28.1%2Breally1.28.0-5
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 28,008 kB
  • sloc: objc: 223,137; ansic: 35,562; sh: 184; makefile: 128; cpp: 122; xml: 32
file content (186 lines) | stat: -rw-r--r-- 5,379 bytes parent folder | download | duplicates (2)
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
/** Implementation of NSProtocolChecker for GNUStep
   Copyright (C) 1995 Free Software Foundation, Inc.

   Original by:  Mike Kienenberger
   Date: Jun 1998
   Written: Richard Frith-Macdonald
   Date: April 2004

   This file is part of the GNUstep Base Library.

   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Lesser General Public
   License as published by the Free Software Foundation; either
   version 2 of the License, or (at your option) any later version.

   This library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Lesser General Public License for more details.

   You should have received a copy of the GNU Lesser General Public
   License along with this library; if not, write to the Free
   Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
   Boston, MA 02110 USA.

   <title>NSProtocolChecker class reference</title>
   $Date$ $Revision$
   */

#import "common.h"
#define	EXPOSE_NSProtocolChecker_IVARS	1
#import "Foundation/NSProtocolChecker.h"
#import "Foundation/NSException.h"
#import "Foundation/NSInvocation.h"
#import "Foundation/NSMethodSignature.h"
#include <objc/Protocol.h>

/**
 * The NSProtocolChecker and NSProxy classes provide message filtering and
 * forwarding capabilities. If you wish to ensure at runtime that a given
 * object will only be sent messages in a certain protocol, you create an
 * <code>NSProtocolChecker</code> instance with the protocol and the object as
 * arguments-

<example>
    id versatileObject = [[ClassWithManyMethods alloc] init];
    id narrowObject = [NSProtocolChecker protocolCheckerWithTarget: versatileObject
                                         protocol: @protocol(SomeSpecificProtocol)];
    return narrowObject;
</example>

 * This is often used in conjunction with distributed objects to expose only a
 * subset of an objects methods to remote processes
 */
@implementation NSProtocolChecker

/**
 * Allocates and initializes an NSProtocolChecker instance by calling
 * -initWithTarget:protocol:<br />
 * Autoreleases and returns the new instance.
 */
+ (id) protocolCheckerWithTarget: (NSObject*)anObject
			protocol: (Protocol*)aProtocol
{
  return AUTORELEASE([[NSProtocolChecker alloc] initWithTarget: anObject
						      protocol: aProtocol]);
}

- (void) dealloc
{
  DESTROY(_myTarget);
  [super dealloc];
}

- (const char *) _protocolTypeForSelector: (SEL)aSel
{
  struct objc_method_description desc;
  desc = GSProtocolGetMethodDescriptionRecursive(_myProtocol, aSel, YES, YES);
  if (desc.name == NULL && desc.types == NULL)
    {
      desc = GSProtocolGetMethodDescriptionRecursive(_myProtocol, aSel, YES, NO);
    }
  return desc.types;
}

/**
 * Forwards any message to the delegate if the method is declared in
 * the checker's protocol; otherwise raises an
 * <code>NSInvalidArgumentException</code>.
 */
- (void) forwardInvocation: (NSInvocation*)anInvocation
{
  const char	*type;

  if ([self _protocolTypeForSelector: [anInvocation selector]] == NULL)
    {
      if (GSObjCIsInstance(_myTarget))
	{
	  [NSException raise: NSInvalidArgumentException
		      format: @"<%s -%@> not declared",
	    protocol_getName(_myProtocol),
              NSStringFromSelector([anInvocation selector])];
	}
      else
	{
	  [NSException raise: NSInvalidArgumentException
		      format: @"<%s +%@> not declared",
	    protocol_getName(_myProtocol),
              NSStringFromSelector([anInvocation selector])];
	}
    }
  [anInvocation invokeWithTarget: _myTarget];

  /*
   * If the method returns 'self' (ie the target object) replace the
   * returned value with the protocol checker.
   */
  type = [[anInvocation methodSignature] methodReturnType];
  if (GSSelectorTypesMatch(type, @encode(id)))
    {
      id	buf;

      [anInvocation getReturnValue: &buf];
      if (buf == _myTarget)
	{
	  buf = self;
	  [anInvocation setReturnValue: &buf];
	}
    }
}

- (id) init
{
  self = [self initWithTarget: nil protocol: nil];
  return self;
}

/**
 * Initializes a newly allocated NSProtocolChecker instance that will
 * forward any messages in the aProtocol protocol to anObject, its
 * delegate. Thus, the checker can be vended in lieu of anObject to
 * restrict the messages that can be sent to anObject. If any method
 * in the protocol returns anObject, the checker will replace the returned
 * value with itself rather than the target object.<br />
 * Returns the new instance.
 */
- (id) initWithTarget: (NSObject*)anObject protocol: (Protocol*)aProtocol
{
  _myProtocol = aProtocol;
  ASSIGN(_myTarget, anObject);
  return self;
}

- (NSMethodSignature*) methodSignatureForSelector: (SEL)aSelector
{
  if (_myProtocol != nil)
    {
      const char *types = [self _protocolTypeForSelector: aSelector];
      if (types == NULL)
	{
	  return nil;
	}
      return [NSMethodSignature signatureWithObjCTypes: types];
    }

  return [super methodSignatureForSelector: aSelector];
}

/**
 * Returns the protocol object the checker uses to verify whether a
 * given message should be forwarded to its delegate.
 */
- (Protocol*) protocol
{
  return _myProtocol;
}

/**
 * Returns the target of the NSProtocolChecker.
 */
- (NSObject*) target
{
  return _myTarget;
}

@end