Package muntjac :: Package event :: Module listener_method
[hide private]
[frames] | no frames]

Source Code for Module muntjac.event.listener_method

  1  # Copyright (C) 2012 Vaadin Ltd.  
  2  # Copyright (C) 2012 Richard Lincoln 
  3  #  
  4  # Licensed under the Apache License, Version 2.0 (the "License");  
  5  # you may not use this file except in compliance with the License.  
  6  # You may obtain a copy of the License at  
  7  #  
  8  #     http://www.apache.org/licenses/LICENSE-2.0  
  9  #  
 10  # Unless required by applicable law or agreed to in writing, software  
 11  # distributed under the License is distributed on an "AS IS" BASIS,  
 12  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  
 13  # See the License for the specific language governing permissions and  
 14  # limitations under the License. 
 15   
 16  """Registered event listener.""" 
 17   
 18  import inspect 
 19  import logging 
 20   
 21  from muntjac.util import IEventListener 
 22  from muntjac.util import getSuperClass 
 23   
 24   
 25  logger = logging.getLogger(__name__) 
26 27 28 -class ListenerMethod(IEventListener):
29 """One registered event listener. This class contains the listener object 30 reference, listened event type, the trigger method to call when the event 31 fires, and the optional argument list to pass to the method and the index 32 of the argument to replace with the event object. 33 34 This Class provides several constructors that allow omission of the 35 optional arguments, and giving the listener method directly, or having the 36 constructor to reflect it using merely the name of the method. 37 38 It should be pointed out that the method L{receiveEvent} is the one that 39 filters out the events that do not match with the given event type and thus 40 do not result in calling of the trigger method. 41 42 @author: Vaadin Ltd. 43 @author: Richard Lincoln 44 @version: 1.1.2 45 """ 46
47 - def writeObject(self, out):
48 raise NotImplementedError 49 50 # Special serialization to handle method references 51 try: 52 out.defaultWriteObject() 53 name = self._method.__name__ 54 paramTypes = self._method.getParameterTypes() 55 out.writeObject(name) 56 out.writeObject(paramTypes) 57 except Exception, e: # FIXME: NotSerializableException 58 logger.warning(('Error in serialization of the application: Class ' 59 + self._target.__class__.__name__ 60 + ' must implement serialization.')) 61 raise e
62 63
64 - def readObject(self, in_):
65 raise NotImplementedError 66 67 # Special serialization to handle method references 68 in_.defaultReadObject() 69 try: 70 name = in_.readObject() 71 paramTypes = in_.readObject() 72 # We can not use getMethod directly as we want to support anonymous 73 # inner classes 74 self._method = self.findHighestMethod(self._target.__class__, 75 name, paramTypes) 76 except Exception: # FIXME: SecurityException 77 logger.critical('Internal deserialization error')
78 79 80 @classmethod
81 - def findHighestMethod(cls, klass, method, paramTypes):
82 ifaces = klass.getInterfaces() 83 for i in range(len(ifaces)): 84 ifaceMethod = cls.findHighestMethod(ifaces[i], method, paramTypes) 85 if ifaceMethod is not None: 86 return ifaceMethod 87 88 if getSuperClass(klass) is not None: 89 parentMethod = cls.findHighestMethod(getSuperClass(klass), method, paramTypes) 90 if parentMethod is not None: 91 return parentMethod 92 93 methods = klass.getMethods() 94 for i in range(len(methods)): 95 # we ignore parameter types for now - you need to add this 96 if methods[i].getName() == method: 97 return methods[i] 98 99 return None
100 101
102 - def __init__(self, eventType, target, method, arguments=None, 103 eventArgumentIndex=None):
104 """Constructs a new event listener from a trigger method, it's 105 arguments and the argument index specifying which one is replaced with 106 the event object when the trigger method is called. 107 108 This constructor gets the trigger method as a parameter so it does not 109 need to reflect to find it out. 110 111 @param eventType: 112 the event type that is listener listens to. All events of 113 this kind (or its subclasses) result in calling the trigger 114 method. 115 @param target: 116 the object instance that contains the trigger method 117 @param method: 118 the trigger method or the name of the trigger method. If 119 the object does not contain the method a C{ValueError} is 120 thrown. 121 @param arguments: 122 the arguments to be passed to the trigger method 123 @param eventArgumentIndex: 124 An index to the argument list. This index points out the 125 argument that is replaced with the event object before the 126 argument set is passed to the trigger method. If the 127 eventArgumentIndex is negative, the triggering event object 128 will not be passed to the trigger method, though it is still 129 called. 130 @raise ValueError: 131 if C{method} is not a member of C{target} 132 """ 133 #: Type of the event that should trigger this listener. Also the subclasses 134 # of this class are accepted to trigger the listener. 135 self._eventType = None 136 137 #: The object containing the trigger method. 138 self._target = None 139 140 #: The trigger method to call when an event passing the given criteria 141 # fires. 142 self._method = None 143 144 #: Optional argument set to pass to the trigger method. 145 self._arguments = None 146 147 #: Optional index to C{arguments} that point out which one 148 # should be replaced with the triggering event object and thus be 149 # passed to the trigger method. 150 self._eventArgumentIndex = None 151 152 153 # Checks that the object is of correct type 154 if arguments is None: 155 if isinstance(method, basestring): 156 methodName = method 157 158 # can't specify a callback function by name, must be method 159 method = getattr(target, methodName) 160 161 self._eventType = eventType 162 self._target = target 163 self._method = methodName 164 self._eventArgumentIndex = -1 165 166 params, _, _, _ = inspect.getargspec(method) 167 168 if len(params) == 1: # just "self" 169 self._arguments = [] 170 elif len(params) == 2: 171 self._arguments = [None] 172 self._eventArgumentIndex = 0 173 else: 174 raise ValueError 175 else: 176 if ((target is not None) and 177 not issubclass(target.__class__, method.im_class)): 178 raise ValueError, ('%s : %s' % (target.__class__, 179 method.im_class)) 180 181 self._eventType = eventType 182 self._target = target 183 184 if target is None: # function 185 self._method = method 186 else: 187 # can't pickle unbound method 188 self._method = method.im_func.func_name 189 190 self._eventArgumentIndex = -1 191 192 params, _, _, _ = inspect.getargspec(method) 193 nparam = len(params) 194 195 if self._target is not None: 196 # first argument to a method is "self" 197 nparam = nparam - 1 198 199 if nparam == 0: 200 self._arguments = [] 201 elif nparam == 1: 202 self._arguments = [None] 203 self._eventArgumentIndex = 0 204 else: 205 raise ValueError, 'listener takes too many arguments' 206 207 elif eventArgumentIndex is None: 208 # event not be be passed, just arguments 209 if isinstance(method, basestring): 210 methodName = method 211 method = getattr(target, methodName) 212 213 self._eventType = eventType 214 self._target = target 215 self._method = method 216 self._arguments = arguments 217 self._eventArgumentIndex = -1 218 else: 219 if ((target is not None) and 220 not issubclass(target.__class__, method.im_class)): 221 raise ValueError, ('%s : %s' % (target.__class__, 222 method.im_class)) 223 224 self._eventType = eventType 225 self._target = target 226 self._method = method 227 self._arguments = arguments 228 self._eventArgumentIndex = -1 229 else: 230 if isinstance(method, basestring): 231 methodName = method 232 method = getattr(target, methodName) 233 234 # Checks that the event argument is null 235 if (eventArgumentIndex >= 0 236 and arguments[eventArgumentIndex] is not None): 237 raise ValueError, 'event argument not None' 238 239 self._eventType = eventType 240 self._target = target 241 self._method = method 242 self._arguments = arguments 243 self._eventArgumentIndex = eventArgumentIndex 244 else: 245 if ((target is not None) and 246 not issubclass(target.__class__, method.im_class)): 247 raise ValueError, ('%s : %s' % (target.__class__, 248 method.im_class)) 249 250 # Checks that the event argument is null 251 if (eventArgumentIndex >= 0 252 and arguments[eventArgumentIndex] is not None): 253 raise ValueError, 'event argument not None' 254 255 self._eventType = eventType 256 self._target = target 257 self._method = method 258 self._arguments = arguments 259 self._eventArgumentIndex = eventArgumentIndex
260 261
262 - def receiveEvent(self, event):
263 """Receives one event from the C{EventRouter} and calls the trigger 264 method if it matches with the criteria defined for the listener. Only 265 the events of the same or subclass of the specified event class result 266 in the trigger method to be called. 267 268 @param event: 269 the fired event. Unless the trigger method's argument list 270 and the index to the to be replaced argument is specified, 271 this event will not be passed to the trigger method. 272 """ 273 # Only send events supported by the method 274 if issubclass(event.__class__, self._eventType): 275 # try: 276 if self._target is None: # function 277 m = self._method 278 else: 279 m_name = self._method 280 m = getattr(self._target, m_name) 281 282 if self._eventArgumentIndex >= 0: 283 if (self._eventArgumentIndex == 0 284 and len(self._arguments) == 1): 285 m(event) 286 else: 287 arg = list(self._arguments) 288 arg[self._eventArgumentIndex] = event 289 m(*arg) 290 else: 291 m(*self._arguments)
292 293 ## except Exception: # IllegalAccessException 294 ## raise RuntimeError, 'Internal error - please report' 295 # except AttributeError: # FIXME: InvocationTargetException 296 # raise MethodException, ('Invocation of method ' 297 # + self._method + ' failed.') 298 299
300 - def matches(self, eventType, target, method=None):
301 """Checks if the given object and event match with the ones stored in 302 this listener. 303 304 @param target: 305 the object to be matched against the object stored by this 306 listener. 307 @param eventType: 308 the type to be tested for equality against the type stored 309 by this listener. 310 @param method: 311 the method to be tested for equality against the method 312 stored by this listener. 313 @return: C{True} if C{target} is the same object as 314 the one stored in this object, C{eventType} equals 315 with the event type stored in this object and 316 C{method} equals with the method stored in this 317 object 318 """ 319 if method is None: 320 return self._target == target and eventType == self._eventType 321 else: 322 if target is None: # function 323 return (self._target == target and eventType == self._eventType 324 and method == self._method) 325 else: 326 return (self._target == target and eventType == self._eventType 327 and method.im_func.func_name == self._method)
328 329
330 - def __hash__(self):
331 hsh = 7 332 hsh = (31 * hsh) + self._eventArgumentIndex 333 hsh = (31 * hsh) + (0 if self._eventType is None else hash(self._eventType)) 334 hsh = (31 * hsh) + (0 if self._target is None else hash(self._target)) 335 hsh = (31 * hsh) + (0 if self._method is None else hash(self._method)) 336 return hsh
337 338
339 - def __eq__(self, obj):
340 341 # return false if obj is a subclass (do not use instanceof check) 342 if (obj is None) or (obj.__class__ != self.__class__): 343 return False 344 345 # obj is of same class, test it further 346 return (self._eventArgumentIndex == obj._eventArgumentIndex 347 and (self._eventType == obj._eventType) 348 and (self._target == obj._target) 349 and (self._method == obj._method) 350 and (self._arguments == obj._arguments))
351 352
353 - def isType(self, eventType):
354 """Compares the type of this ListenerMethod to the given type 355 356 @param eventType: 357 The type to compare with 358 @return: true if this type of this ListenerMethod matches the given 359 type, false otherwise 360 """ 361 return self._eventType == eventType
362 363
364 - def isOrExtendsType(self, eventType):
365 """Compares the type of this ListenerMethod to the given type 366 367 @param eventType: 368 The type to compare with 369 @return: true if this event type can be assigned to the given type, 370 false otherwise 371 """ 372 return issubclass(self._eventType, eventType)
373 374
375 - def getTarget(self):
376 """Returns the target object which contains the trigger method. 377 378 @return: The target object 379 """ 380 return self._target
381
382 383 -class MethodException(RuntimeError):
384 """Exception that wraps an exception thrown by an invoked method. When 385 C{ListenerMethod} invokes the target method, it may throw arbitrary 386 exception. The original exception is wrapped into MethodException instance 387 and rethrown by the C{ListenerMethod}. 388 389 @author: Vaadin Ltd. 390 @author: Richard Lincoln 391 @version: 1.1.2 392 """ 393
394 - def __init__(self, message, cause):
395 super(MethodException, self).__init__(message) 396 self._cause = cause
397 398
399 - def getCause(self):
400 """Retrieves the cause of this throwable or C{None} if the 401 cause does not exist or not known. 402 403 @return: the cause of this throwable or C{None} if the cause 404 is nonexistent or unknown. 405 """ 406 return self._cause
407 408
409 - def getMessage(self):
410 """Returns the error message string of this throwable object. 411 412 @return: the error message. 413 @see: Exception.message 414 """ 415 return self._message
416 417
418 - def __str__(self):
419 msg = super(MethodException, self).__str__() 420 421 if self._cause is not None: 422 msg += '\nCause: ' + str(self._cause) 423 424 return msg
425