X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=pyatspi%2Faccessible.py;h=c9323a80133a35bce194e69781c0934e2903b2ec;hb=f01ffde94695198fe9e68df3b07eea59d563c495;hp=aef89e14a2acd7512a3c0c9a060e900096bc5276;hpb=62b0c2bb65b782ec4d5cdc69aa8090286c08ace7;p=platform%2Fcore%2Fuifw%2Fat-spi2-atk.git diff --git a/pyatspi/accessible.py b/pyatspi/accessible.py index aef89e1..c9323a8 100644 --- a/pyatspi/accessible.py +++ b/pyatspi/accessible.py @@ -1,576 +1,270 @@ -''' -Creates functions at import time that are mixed into the -Accessibility.Accessible base class to make it more Pythonic. - -Based on public domain code originally posted at -U{http://wwwx.cs.unc.edu/~parente/cgi-bin/RuntimeClassMixins}. - -@var _ACCESSIBLE_CACHE: Pairs hash values for accessible objects to - L{_PropertyCache} bags. We do not store actual accessibles in the dictionary - because that would +1 their ref counts and cause __del__ to never be called - which is the method we rely on to properly invalidate cache entries. -@type _ACCESSIBLE_CACHE: dictionary -@var _CACHE_LEVEL: Current level of caching enabled. Checked dynamically by - L{_AccessibleMixin} -@type _CACHE_LEVEL: integer - -@author: Peter Parente -@organization: IBM Corporation -@copyright: Copyright (c) 2005, 2007 IBM Corporation -@license: LGPL - -This library is free software; you can redistribute it and/or -modify it under the terms of the GNU Library 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 -Library General Public License for more details. - -You should have received a copy of the GNU Library General Public -License along with this library; if not, write to the -Free Software Foundation, Inc., 59 Temple Place - Suite 330, -Boston, MA 02111-1307, USA. - -Portions of this code originally licensed and copyright (c) 2005, 2007 -IBM Corporation under the BSD license, available at -U{http://www.opensource.org/licenses/bsd-license.php} -''' -import new -import types -import ORBit -import Accessibility -import constants -import utils -import registry - -_ACCESSIBLE_CACHE = {} -_CACHE_LEVEL = None - -class _PropertyCache(object): - '''Fixed-size bag class for holding cached values.''' - __slots__ = ('name', 'description', 'rolename') - -def getCacheLevel(): - ''' - Gets the current level of caching. - - @return: None indicating no caching is in effect. - L{constants.CACHE_INTERFACES} indicating all interface query results are - cached. L{constants.CACHE_PROPERTIES} indicating all basic accessible - properties are cached. - @rtype: integer - ''' - return _CACHE_LEVEL - -def setCacheLevel(val): - ''' - Sets the desired level of caching for all accessible objects created after - this function is invoked. Immediately clears the current accessible cache. - - @param val: None indicating no caching is in effect. - L{constants.CACHE_INTERFACES} indicating all interface query results are - cached. L{constants.CACHE_PROPERTIES} indicating all basic accessible - properties are cached plus all interfaces. - @type val: integer - ''' - global _CACHE_LEVEL - if _CACHE_LEVEL != val: - # empty our accessible cache - _ACCESSIBLE_CACHE.clear() - # need to register/unregister for listeners depending on caching level - if val == constants.CACHE_PROPERTIES: - r = registry.Registry() - r.registerEventListener(_updateCache, *constants.CACHE_EVENTS) - else: - r = registry.Registry() - r.deregisterEventListener(_updateCache, *constants.CACHE_EVENTS) - _CACHE_LEVEL = val - -def clearCache(): - '''Forces a clear of the entire cache.''' - _ACCESSIBLE_CACHE.clear() - -def printCache(template='%s'): - ''' - Prints the contents of the cache. - - @param template: Format string to use when printing - @type template: string - ''' - print template % _ACCESSIBLE_CACHE - -def _updateCache(event): - ''' - Invalidates an entry in the cache when the hash value of a source of an event - matches an entry in the cache. - - @param event: One of the L{constants.CACHE_EVENTS} event types - @type event: L{event.Event} - ''' - try: - del _ACCESSIBLE_CACHE[hash(event.source)] - except KeyError: - return - -def _makeQuery(iid): - ''' - Builds a function querying to a specific interface and returns it. - - @param iid: Interface identifier to use when querying - @type iid: string - @return: Function querying to the given interface - @rtype: function - ''' - def _inner(self): - ''' - Queries an object for another interface. - - @return: An object with the desired interface - @rtype: object - @raise NotImplementedError: When the desired interface is not supported - ''' - try: - i = self._icache[iid] - except KeyError: - # interface not cached - caching = True - except TypeError: - # determine if we're caching - caching = _CACHE_LEVEL is not None - if caching: - # initialize the cache - self._icache = {} - else: - # check if our cached result was an interface, or an indicator that the - # interface is not supported - if i is None: - raise NotImplementedError - else: - return i - - try: - # do the query remotely - i = self.queryInterface(iid) - except Exception, e: - raise LookupError(e) - if i is None: - # cache that the interface is not supported - if caching: - self._icache[iid] = None - raise NotImplementedError - - # not needed according to ORBit2 spec, but makes Java queries work - # more reliably according to Orca experience - i = i._narrow(i.__class__) - if caching: - # cache the narrow'ed result, but only if we're caching for this object - self._icache[iid] = i - return i - - return _inner - -def _makeExceptionHandler(func): - ''' - Builds a function calling the one it wraps in try/except statements catching - CORBA exceptions. - - @return: Function calling the method being wrapped - @rtype: function - ''' - def _inner(self, *args, **kwargs): - try: - # try calling the original func - return func(self, *args, **kwargs) - except ORBit.CORBA.NO_IMPLEMENT, e: - # raise Python exception - raise NotImplementedError(e) - except ORBit.CORBA.Exception, e: - # raise Python exception - raise LookupError(e) - return _inner - -def _mixInterfaces(cls, interfaces): - ''' - Add methods for querying to interfaces other than the base accessible to - the given class. - - @param cls: Class to mix interface methods into - @type cls: class - @param interfaces: Classes representing AT-SPI interfaces - @type interfaces: list of class - ''' - # create functions in this module for all interfaces listed in constants - for interface in interfaces: - # build name of converter from the name of the interface - name = 'query%s' % utils.getInterfaceName(interface) - # build a function that queries to the given interface - func = _makeQuery(utils.getInterfaceIID(interface)) - # build a new method that is a clone of the original function - method = new.function(func.func_code, func.func_globals, name, - func.func_defaults, func.func_closure) - # add the method to the given class - setattr(cls, name, method) - -def _mixExceptions(cls): - ''' - Wraps all methods and properties in a class with handlers for CORBA - exceptions. - - @param cls: Class to mix interface methods into - @type cls: class - ''' - # get a method type as a reference from a known method - method_type = Accessibility.Accessible.getRole.__class__ - # loop over all names in the new class - for name in cls.__dict__.keys(): - obj = cls.__dict__[name] - # check if we're on a protected or private method - if name.startswith('_'): - continue - # check if we're on a method - elif isinstance(obj, method_type): - # wrap the function in an exception handler - method = _makeExceptionHandler(obj) - # add the wrapped function to the class - setattr(cls, name, method) - # check if we're on a property - elif isinstance(obj, property): - # wrap the getters and setters - if obj.fget: - func = getattr(cls, obj.fget.__name__) - getter = _makeExceptionHandler(func) - else: - getter = None - if obj.fset: - func = getattr(cls, obj.fset.__name__) - setter = _makeExceptionHandler(func) - else: - setter = None - setattr(cls, name, property(getter, setter)) - -def _mixClass(cls, new_cls, ignore=[]): - ''' - Adds the methods in new_cls to cls. After mixing, all instances of cls will - have the new methods. If there is a method name clash, the method already in - cls will be prefixed with '_mix_' before the new method of the same name is - mixed in. - - @note: _ is not the prefix because if you wind up with __ in front of a - variable, it becomes private and mangled when an instance is created. - Difficult to invoke from the mixin class. - - @param cls: Existing class to mix features into - @type cls: class - @param new_cls: Class containing features to add - @type new_cls: class - @param ignore: Ignore these methods from the mixin - @type ignore: iterable - ''' - # loop over all names in the new class - for name, func in new_cls.__dict__.items(): - if name in ignore: - continue - if isinstance(func, types.FunctionType): - # build a new function that is a clone of the one from new_cls - method = new.function(func.func_code, func.func_globals, name, - func.func_defaults, func.func_closure) - try: - # check if a method of the same name already exists in the target - old_method = getattr(cls, name) - except AttributeError: - pass - else: - # rename the old method so we can still call it if need be - setattr(cls, '_mix_'+name, old_method) - # add the clone to cls - setattr(cls, name, method) - elif isinstance(func, staticmethod): - try: - # check if a method of the same name already exists in the target - old_method = getattr(cls, name) - except AttributeError: - pass - else: - # rename the old method so we can still call it if need be - setattr(cls, '_mix_'+name, old_method) - setattr(cls, name, func) - elif isinstance(func, property): - try: - # check if a method of the same name already exists in the target - old_prop = getattr(cls, name) - except AttributeError: - pass - else: - # IMPORTANT: We save the old property before overwriting it, even - # though we never end up calling the old prop from our mixin class. - # If we don't save the old one, we seem to introduce a Python ref count - # problem where the property get/set methods disappear before we can - # use them at a later time. This is a minor waste of memory because - # a property is a class object and we only overwrite a few of them. - setattr(cls, '_mix_'+name, old_prop) - setattr(cls, name, func) - -class _AccessibleMixin(object): - ''' - Defines methods to be added to the Accessibility.Accessible class. The - features defined here will be added to the Accessible class at run time so - that all instances of Accessible have them (i.e. there is no need to - explicitly wrap an Accessible in this class or derive a new class from it.) - - @cvar SLOTTED_CLASSES: Mapping from raw Accessibility class to a new class - having the slots defined by L{SLOTS} - @type SLOTTED_CLASSES: dictionary - @cvar SLOTS: All slots to create - @type SLOTS: tuple - ''' - SLOTTED_CLASSES = {} - SLOTS = ('_icache',) - - def __new__(cls): - ''' - Creates a new class mimicking the one requested, but with an extra _cache - attribute set in the __slots__ tuple. This field can be set to a dictionary - or other object to allow caching to occur. - - Note that we can't simply mix __slots__ into this class because __slots__ - has an effect only at class creation time. - - @param cls: Accessibility object class - @type cls: class - @return: Instance of the new class - @rtype: object - ''' - try: - # check if we've already created a new version of the class - new_cls = _AccessibleMixin.SLOTTED_CLASSES[cls] - except KeyError: - # create the new class if not - new_cls = type(cls.__name__, (cls,), - {'__module__' : cls.__module__, - '__slots__' : _AccessibleMixin.SLOTS}) - _AccessibleMixin.SLOTTED_CLASSES[cls] = new_cls - obj = cls._mix___new__(new_cls) - # don't create the interface cache until we need it - obj._icache = None - return obj - - def __del__(self): - ''' - Decrements the reference count on the accessible object when there are no - Python references to this object. This provides automatic reference - counting for AT-SPI objects. Also removes this object from the cache if - we're caching properties. - ''' - try: - del _ACCESSIBLE_CACHE[hash(self)] - except KeyError: - pass - try: - self.unref() - except Exception: - pass - - def __iter__(self): - ''' - Iterator that yields one accessible child per iteration. If an exception is - encountered, None is yielded instead. - - @return: A child accessible - @rtype: Accessibility.Accessible - ''' - for i in xrange(self.childCount): - try: - yield self.getChildAtIndex(i) - except LookupError: - yield None - - def __str__(self): - ''' - Gets a human readable representation of the accessible. - - @return: Role and name information for the accessible - @rtype: string - ''' - try: - return '[%s | %s]' % (self.getRoleName(), self.name) - except Exception: - return '[DEAD]' - - def __nonzero__(self): - ''' - @return: True, always - @rtype: boolean - ''' - return True - - def __getitem__(self, index): - ''' - Thin wrapper around getChildAtIndex. - - @param index: Index of desired child - @type index: integer - @return: Accessible child - @rtype: Accessibility.Accessible - ''' - n = self.childCount - if index >= n: - raise IndexError - elif index < -n: - raise IndexError: - elif index < 0: - index += n - return self.getChildAtIndex(index) - - def __len__(self): - ''' - Thin wrapper around childCount. - - @return: Number of child accessibles - @rtype: integer - ''' - return self.childCount - - def _get_name(self): - ''' - Gets the name of the accessible from the cache if it is available, - otherwise, fetches it remotely. - - @return: Name of the accessible - @rtype: string - ''' - if _CACHE_LEVEL != constants.CACHE_PROPERTIES: - return self._get_name() - - cache = _ACCESSIBLE_CACHE - h = hash(self) - try: - return cache[h].name - except KeyError: - # no cached info for this object yet - name = self._get_name() - pc = _PropertyCache() - pc.name = name - cache[h] = pc - return name - except AttributeError: - # no cached name for this object yet - name = self._get_name() - cache[h].name = name - return name - - name = property(_get_name, Accessibility.Accessible._set_name) - - def getRoleName(self): - ''' - Gets the unlocalized role name of the accessible from the cache if it is - available, otherwise, fetches it remotely. - - @return: Role name of the accessible - @rtype: string - ''' - if _CACHE_LEVEL != constants.CACHE_PROPERTIES: - return self._mix_getRoleName() - - cache = _ACCESSIBLE_CACHE - h = hash(self) - try: - return cache[h].rolename - except KeyError, e: - # no cached info for this object yet - rolename = self._mix_getRoleName() - pc = _PropertyCache() - pc.rolename = rolename - cache[h] = pc - return rolename - except AttributeError, e: - # no cached name for this object yet - rolename = self._mix_getRoleName() - cache[h].rolename = rolename - return rolename - - def _get_description(self): - ''' - Gets the description of the accessible from the cache if it is available, - otherwise, fetches it remotely. - - @return: Description of the accessible - @rtype: string - ''' - if _CACHE_LEVEL != constants.CACHE_PROPERTIES: - return self._get_description() - - cache = _ACCESSIBLE_CACHE - h = hash(self) - try: - return cache[h].description - except KeyError: - # no cached info for this object yet - description = self._get_description() - pc = _PropertyCache() - pc.description = description - cache[h] = pc - return description - except AttributeError: - # no cached name for this object yet - description = self._get_description() - cache[h].description = description - return description - - description = property(_get_description, - Accessibility.Accessible._set_description) - - def getIndexInParent(self): - ''' - Gets the index of this accessible in its parent. Uses the implementation of - this method provided by the Accessibility.Accessible object, but checks the - bound of the value to ensure it is not outside the range of childCount - reported by this accessible's parent. - - @return: Index of this accessible in its parent - @rtype: integer - ''' - i = self._mix_getIndexInParent() - try: - # correct for out-of-bounds index reporting - return min(self.parent.childCount-1, i) - except AttributeError: - # return sentinel if there is no parent - return -1 - - def getApplication(self): - ''' - Gets the most-parent accessible (the application) of this accessible. Tries - using the getApplication method introduced in AT-SPI 1.7.0 first before - resorting to traversing parent links. - - @warning: Cycles involving more than the previously traversed accessible - are not detected by this code. - @return: Application object - @rtype: Accessibility.Application - ''' - try: - return self._mix_getApplication() - except AttributeError: - pass - curr = self - try: - while curr.parent is not None and (not curr.parent == curr): - curr = curr.parent - return curr - except Exception: - pass - # return None if the application isn't reachable for any reason - return None - -# 1. mix the exception handlers into all queryable interfaces -map(_mixExceptions, constants.ALL_INTERFACES) -# 2. mix the exception handlers into other Accessibility objects -map(_mixExceptions, [Accessibility.StateSet]) -# 3. mix the new functions -_mixClass(Accessibility.Accessible, _AccessibleMixin, - ['_get_name', '_get_description']) -# 4. mix queryInterface convenience methods -_mixInterfaces(Accessibility.Accessible, constants.ALL_INTERFACES) +#Copyright (C) 2008 Codethink Ltd + +#This library is free software; you can redistribute it and/or +#modify it under the terms of the GNU Lesser General Public +#License version 2 as published by the Free Software Foundation. + +#This program 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 General Public License for more details. +#You should have received a copy of the GNU Lesser General Public License +#along with this program; if not, write to the Free Software +#Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +from interfaces import ATSPI_ACCESSIBLE, ATSPI_APPLICATION +from base import BaseProxy, Enum +from factory import accessible_factory +from state import StateSet, _marshal_state_set +from relation import _marshal_relation_set +from role import Role + +__all__ = [ + "LOCALE_TYPE", + "LOCALE_TYPE_COLLATE", + "LOCALE_TYPE_CTYPE", + "LOCALE_TYPE_MESSAGES", + "LOCALE_TYPE_MONETARY", + "LOCALE_TYPE_NUMERIC", + "LOCALE_TYPE_TIME", + "BoundingBox", + "Accessible", + ] + +#------------------------------------------------------------------------------ + +class LOCALE_TYPE(Enum): + _enum_lookup = { + 0:'LOCALE_TYPE_MESSAGES', + 1:'LOCALE_TYPE_COLLATE', + 2:'LOCALE_TYPE_CTYPE', + 3:'LOCALE_TYPE_MONETARY', + 4:'LOCALE_TYPE_NUMERIC', + 5:'LOCALE_TYPE_TIME', + } + +LOCALE_TYPE_COLLATE = LOCALE_TYPE(1) +LOCALE_TYPE_CTYPE = LOCALE_TYPE(2) +LOCALE_TYPE_MESSAGES = LOCALE_TYPE(0) +LOCALE_TYPE_MONETARY = LOCALE_TYPE(3) +LOCALE_TYPE_NUMERIC = LOCALE_TYPE(4) +LOCALE_TYPE_TIME = LOCALE_TYPE(5) + +#------------------------------------------------------------------------------ + +class BoundingBox(list): + def __new__(cls, x, y, width, height): + return list.__new__(cls, (x, y, width, height)) + def __init__(self, x, y, width, height): + list.__init__(self, (x, y, width, height)) + + def __str__(self): + return ("(%d, %d, %d, %d)" % (self.x, self.y, self.width, self.height)) + + def _get_x(self): + return self[0] + def _set_x(self, val): + self[0] = val + x = property(fget=_get_x, fset=_set_x) + def _get_y(self): + return self[1] + def _set_y(self, val): + self[1] = val + y = property(fget=_get_y, fset=_set_y) + def _get_width(self): + return self[2] + def _set_width(self, val): + self[2] = val + width = property(fget=_get_width, fset=_set_width) + def _get_height(self): + return self[3] + def _set_height(self, val): + self[3] = val + height = property(fget=_get_height, fset=_set_height) + +#------------------------------------------------------------------------------ + +class Accessible(BaseProxy): + """ + The base interface which is implemented by all accessible objects. + All objects support interfaces for querying their contained + 'children' and position in the accessible-object hierarchy, + whether or not they actually have children. + """ + + def __nonzero__(self): + return True + + def __len__(self): + return self.getChildCount() + + def __getitem__(self, index): + return self.getChildAtIndex(index) + + def getApplication(self): + """ + Get the containing Application for this object. + @return the Application instance to which this object belongs. + """ + return self._cache.create_application(self._app_name) + + def getAttributes(self): + """ + Get a list of properties applied to this object as a whole, as + an AttributeSet consisting of name-value pairs. As such these + attributes may be considered weakly-typed properties or annotations, + as distinct from the strongly-typed interface instance data declared + using the IDL "attribute" keyword. + Not all objects have explicit "name-value pair" AttributeSet + properties. + Attribute names and values may have any UTF-8 string value, however + where possible, in order to facilitate consistent use and exposure + of "attribute" properties by applications and AT clients, attribute + names and values should chosen from a publicly-specified namespace + where appropriate. + Where possible, the names and values in the name-value pairs + should be chosen from well-established attribute namespaces using + standard semantics. For example, attributes of Accessible objects + corresponding to XHTML content elements should correspond to + attribute names and values specified in the w3c XHTML specification, + at http://www.w3.org/TR/xhtml2, where such values are not already + exposed via a more strongly-typed aspect of the AT-SPI API. Metadata + names and values should be chosen from the 'Dublin Core' Metadata + namespace using Dublin Core semantics: http://dublincore.org/dcregistry/ + Similarly, relevant structural metadata should be exposed using + attribute names and values chosen from the CSS2 and WICD specification: + http://www.w3.org/TR/1998/REC-CSS2-19980512 WICD (http://www.w3.org/TR/2005/WD-WICD-20051121/). + + @return : An AttributeSet encapsulating any "attribute values" + currently defined for the object. An attribute set is a list of strings + with each string comprising an name-value pair format 'name:value'. + """ + func = self.get_dbus_method("getAttributes", dbus_interface=ATSPI_ACCESSIBLE) + return func() + + def getChildAtIndex(self, index): + """ + Get the accessible child of this object at index. + @param : index + an in parameter indicating which child is requested (zero-indexed). + @return : the 'nth' Accessible child of this object. + """ + path = self.cached_data.children[index] + return self._cache.create_accessible(self._app_name, path, ATSPI_ACCESSIBLE) + + def getIndexInParent(self): + """ + Get the index of this object in its parent's child list. + @return : a long integer indicating this object's index in the + parent's list. + """ + for i in range(0, self.parent.childCount): + child = self.parent.getChildAtIndex(i) + if self.isEqual(child): + return i + raise AccessibleObjectNoLongerExists("Child not found within parent") + + def getLocalizedRoleName(self): + """ + Get a string indicating the type of UI role played by this object, + translated to the current locale. + @return : a UTF-8 string indicating the type of UI role played + by this object. + """ + func = self.get_dbus_method("getLocalizedRoleName", dbus_interface=ATSPI_ACCESSIBLE) + return func() + + def getRelationSet(self): + """ + Get a set defining this object's relationship to other accessible + objects. + @return : a RelationSet defining this object's relationships. + """ + func = self.get_dbus_method("getRelationSet", dbus_interface=ATSPI_ACCESSIBLE) + relation_set = func() + return _marshal_relation_set(self._cache, self._app_name, relation_set) + + def getRole(self): + """ + Get the Role indicating the type of UI role played by this object. + @return : a Role indicating the type of UI role played by this + object. + """ + return Role(self.cached_data.role) + + def getRoleName(self): + """ + Get a string indicating the type of UI role played by this object. + @return : a UTF-8 string indicating the type of UI role played + by this object. + """ + func = self.get_dbus_method("getRoleName", dbus_interface=ATSPI_ACCESSIBLE) + return func() + + def getState(self): + """ + Get the current state of the object as a StateSet. + @return : a StateSet encapsulating the currently true states + of the object. + """ + func = self.get_dbus_method("getState", dbus_interface=ATSPI_ACCESSIBLE) + bitfield = func() + return _marshal_state_set(bitfield) + + def isEqual(self, accessible): + """ + Determine whether an Accessible refers to the same object as + another. This method should be used rather than brute-force comparison + of object references (i.e. "by-value" comparison), as two object + references may have different apparent values yet refer to the + same object. + @param : obj + an Accessible object reference to compare to + @return : a boolean indicating whether the two object references + point to the same object. + """ + return (self._app_name == accessible._app_name) and \ + (self._acc_path == accessible._acc_path) + + + def get_childCount(self): + return len(self.cached_data.children) + _childCountDoc = \ + """ + childCount: the number of children contained by this object. + """ + childCount = property(fget=get_childCount, doc=_childCountDoc) + + getChildCount = get_childCount + + def get_description(self): + return self.cached_data.description + _descriptionDoc = \ + """ + a string describing the object in more detail than name. + """ + description = property(fget=get_description, doc=_descriptionDoc) + + def get_name(self): + return self.cached_data.name + _nameDoc = \ + """ + a (short) string representing the object's name. + """ + name = property(fget=get_name, doc=_nameDoc) + + def get_parent(self): + return self._cache.create_accessible(self._app_name, + self.cached_data.parent, + ATSPI_ACCESSIBLE) + + _parentDoc = \ + """ + an Accessible object which is this object's containing object. + """ + parent = property(fget=get_parent, doc=_parentDoc) + +# Register the accessible class with the factory. +accessible_factory.register_accessible_class(ATSPI_ACCESSIBLE, Accessible) + +#END----------------------------------------------------------------------------