node.py
Go to the documentation of this file.
00001 # -*- Python -*-
00002 # -*- coding: utf-8 -*-
00003 
00004 '''rtctree
00005 
00006 Copyright (C) 2009-2014
00007     Geoffrey Biggs
00008     RT-Synthesis Research Group
00009     Intelligent Systems Research Institute,
00010     National Institute of Advanced Industrial Science and Technology (AIST),
00011     Japan
00012     All rights reserved.
00013 Licensed under the Eclipse Public License -v 1.0 (EPL)
00014 http://www.opensource.org/licenses/eclipse-1.0.txt
00015 
00016 Object representing a generic node in the tree.
00017 
00018 '''
00019 
00020 
00021 import threading
00022 
00023 from rtctree.exceptions import NotRelatedError, NoSuchEventError
00024 
00025 
00026 ##############################################################################
00027 ## Base node object
00028 
00029 class TreeNode(object):
00030     '''Base class for a node in the tree.
00031 
00032     Do not create this class directly. Create objects using a suitable child
00033     class of this class.
00034 
00035     '''
00036     def __init__(self, name=None, parent=None, children=None, filter=[],
00037             dynamic=False, *args, **kwargs):
00038         '''Constructor.
00039 
00040         @param name Name of this node (i.e. its entry in the path).
00041         @param parent The parent node of this node, if any.
00042         @param children If the list of children is already known, put it here.
00043         @param filter A list of paths to filter by.
00044         @param dynamic Enable dynamic features such as observers on this node
00045                        and any children it creates.
00046 
00047         '''
00048         super(TreeNode, self).__init__(*args, **kwargs)
00049         self._mutex = threading.RLock()
00050         self._name = name
00051         self._parent = parent
00052         if children:
00053             self._children = children
00054         else:
00055             self._children = {}
00056         self._cbs = {}
00057         self._dynamic = dynamic
00058         if dynamic:
00059             self._enable_dynamic(dynamic)
00060 
00061     def __str__(self):
00062         '''Get this node as a string.'''
00063         with self._mutex:
00064             indent = ''.rjust(self.depth)
00065             result = '{0}{1}, {2}\n'.format(indent, self._name, self._children)
00066             for child in self._children:
00067                 result += str(self._children[child])
00068         return result
00069 
00070     def add_callback(self, event, cb, args=None):
00071         '''Add a callback to this node.
00072 
00073         Callbacks are called when the specified event occurs. The available
00074         events depends on the specific node type. Args should be a value to
00075         pass to the callback when it is called. The callback should be of the
00076         format:
00077 
00078         def callback(node, value, cb_args):
00079 
00080         where node will be the node that called the function, value is the
00081         relevant information for the event, and cb_args are the arguments you
00082         registered with the callback.
00083 
00084         '''
00085         if event not in self._cbs:
00086             raise exceptions.NoSuchEventError
00087         self._cbs[event] = [(cb, args)]
00088 
00089     def get_node(self, path):
00090         '''Get a child node of this node, or this node, based on a path.
00091 
00092         @param path A list of path elements pointing to a node in the tree.
00093                     For example, ['/', 'localhost', 'dir.host']. The first
00094                     element in this path should be this node's name.
00095         @return The node pointed to by @ref path, or None if the path does not
00096                 point to a node in the tree below this node.
00097 
00098         '''
00099         with self._mutex:
00100             if path[0] == self._name:
00101                 if len(path) == 1:
00102                     return self
00103                 elif path[1] in self._children:
00104                     return self._children[path[1]].get_node(path[1:])
00105                 else:
00106                     return None
00107             else:
00108                 return None
00109 
00110     def has_path(self, path):
00111         '''Check if a path exists below this node.
00112 
00113         @param path A list of path elements pointing to a node in the tree.
00114                     For example, ['/', 'localhost', 'dir.host']. The first
00115                     element in this path should be this node's name.
00116         @return True if the path points to a node in the tree below this node,
00117                 or this node itself (for paths one element long). False
00118                 otherwise.
00119 
00120         '''
00121         with self._mutex:
00122             if path[0] == self._name:
00123                 if len(path) == 1:
00124                     return True
00125                 elif path[1] in self._children:
00126                     return self._children[path[1]].has_path(path[1:])
00127                 else:
00128                     return False
00129             else:
00130                 return False
00131 
00132     def is_child(self, other_node):
00133         '''Is @ref other_node a child of this node?'''
00134         with self._mutex:
00135             return other_node in self._children
00136 
00137     def is_parent(self, other_node):
00138         '''Is @ref other_node the parent of this note?'''
00139         return other_node == self.parent
00140 
00141     def iterate(self, func, args=None, filter=[]):
00142         '''Call a function on this node, and recursively all its children.
00143 
00144         This is a depth-first iteration.
00145 
00146         @param func The function to call. Its declaration must be
00147                     'def blag(node, args)', where 'node' is the current node
00148                     in the iteration and args is the value of @ref args.
00149         @param args Extra arguments to pass to the function at each iteration.
00150                     Pass multiple arguments in as a tuple.
00151         @param filter A list of filters to apply before calling func for each
00152                       node in the iteration. If the filter is not True,
00153                       @ref func will not be called for that node. Each filter
00154                       entry should be a string, representing one of the is_*
00155                       properties (is_component, etc), or a function object.
00156         @return The results of the calls to @ref func in a list.
00157 
00158         '''
00159         with self._mutex:
00160             result = []
00161             if filter:
00162                 filters_passed = True
00163                 for f in filter:
00164                     if type(f) == str:
00165                         if not eval('self.' + f):
00166                             filters_passed = False
00167                             break
00168                     else:
00169                         if not f(self):
00170                             filters_passed = False
00171                             break
00172                 if filters_passed:
00173                     result = [func(self, args)]
00174             else:
00175                 result = [func(self, args)]
00176             for child in self._children:
00177                 result += self._children[child].iterate(func, args, filter)
00178         return result
00179 
00180     def rem_callback(self, event, cb):
00181         '''Remove a callback from this node.
00182 
00183         The callback is removed from the specified event.
00184 
00185         @param cb The callback function to remove.
00186 
00187         '''
00188         if event not in self._cbs:
00189             raise exceptions.NoSuchEventError(self.name, event)
00190         c = [(x[0], x[1]) for x in self._cbs[event]]
00191         if not c:
00192             raise exceptions.NoCBError(self.name, event, cb)
00193         self._cbs[event].remove(c[0])
00194 
00195     @property
00196     def children(self):
00197         '''The child nodes of this node (if any).'''
00198         with self._mutex:
00199             return self._children.values()
00200 
00201     @property
00202     def children_names(self):
00203         '''A list of the names of the child nodes of this node (if any).'''
00204         with self._mutex:
00205             return self._children.keys()
00206 
00207     @property
00208     def depth(self):
00209         '''The depth of this node in the tree.
00210 
00211         The root node is depth 0.
00212 
00213         '''
00214         with self._mutex:
00215             if self._parent:
00216                 return len(self.full_path) - 1
00217             else:
00218                 return 0
00219 
00220     @property
00221     def dynamic(self):
00222         '''Get and change the dynamic setting of this node.'''
00223         with self._mutex:
00224             return self._dynamic
00225 
00226     @dynamic.setter
00227     def dynamic(self, dynamic):
00228         with self._mutex:
00229             if self._dynamic and not dynamic:
00230                 # Disable dynamism
00231                 self._enable_dynamic(False)
00232             elif not self._dynamic and dynamic:
00233                 # Enable dynamism
00234                 self._enable_dynamic(True)
00235 
00236     @property
00237     def full_path(self):
00238         '''The full path of this node.'''
00239         with self._mutex:
00240             if self._parent:
00241                 return self._parent.full_path + [self._name]
00242             else:
00243                 return [self._name]
00244 
00245     @property
00246     def full_path_str(self):
00247         '''The full path of this node as a string.'''
00248         with self._mutex:
00249             if self._parent:
00250                 if self._parent._name == '/':
00251                     return self._parent.full_path_str + self._name
00252                 else:
00253                     return self._parent.full_path_str + '/' + self._name
00254             else:
00255                 return self._name
00256 
00257     @property
00258     def is_component(self):
00259         '''Is this node a component?'''
00260         return False
00261 
00262     @property
00263     def is_directory(self):
00264         '''Is this node a directory?'''
00265         with self._mutex:
00266             if self._name == '/':
00267                 return True
00268             return False
00269 
00270     @property
00271     def is_manager(self):
00272         '''Is this node a manager?'''
00273         return False
00274 
00275     @property
00276     def is_nameserver(self):
00277         '''Is this node a name server (specialisation of directory nodes)?'''
00278         return False
00279 
00280     @property
00281     def is_unknown(self):
00282         '''Is this node unknown?'''
00283         return False
00284 
00285     @property
00286     def is_zombie(self):
00287         '''Is this node a zombie?'''
00288         return False
00289 
00290     @property
00291     def name(self):
00292         '''The name of this node.'''
00293         with self._mutex:
00294             return self._name
00295 
00296     @property
00297     def nameserver(self):
00298         '''The name server of the node (i.e. its top-most parent below /).'''
00299         with self._mutex:
00300             if not self._parent:
00301                 # The root node does not have a name server
00302                 return None
00303             elif self._parent.name == '/':
00304                 return self
00305             else:
00306                 return self._parent.nameserver
00307 
00308     @property
00309     def orb(self):
00310         '''The ORB used to access this object.
00311 
00312         This property's value will be None if no object above this object is a
00313         name server.
00314 
00315         '''
00316         with self._mutex:
00317             if self._parent.name == '/':
00318                 return None
00319             return self._parent.orb
00320 
00321     @property
00322     def parent(self):
00323         '''This node's parent, or None if no parent.'''
00324         with self._mutex:
00325             return self._parent
00326 
00327     def remove_child(self, child):
00328         # Remove a child from this node.
00329         with self._mutex:
00330             if child.name not in self._children:
00331                 raise NotRelatedError(self.name, child.name)
00332             del self._children[child.name]
00333 
00334     @parent.setter
00335     def parent(self, new_parent):
00336         with self._mutex:
00337             if self._parent:
00338                 # Make sure to unlink the tree as well
00339                 self._parent.remove_child(self)
00340             self._parent = new_parent
00341 
00342     @property
00343     def parent_name(self):
00344         '''The name of this node's parent or an empty string if no parent.'''
00345         with self._mutex:
00346             if self._parent:
00347                 return self._parent.name
00348             else:
00349                 return ''
00350 
00351     @property
00352     def root(self):
00353         '''The root node of the tree this node is in.'''
00354         with self._mutex:
00355             if self._parent:
00356                 return self._parent.root
00357             else:
00358                 return self
00359 
00360     def _add_child(self, new_child):
00361         # Add a child to this node.
00362         with self._mutex:
00363             self._children[new_child._name] = new_child
00364 
00365     def _call_cb(self, event, value):
00366         if event not in self._cbs:
00367             raise exceptions.NoSuchEventError(self.name, event)
00368         for (cb, args) in self._cbs[event]:
00369             cb(self, value, args)
00370 
00371     def _enable_dynamic(self, enable=True):
00372         # Enable or disable dynamic features.
00373         # By default, do nothing.
00374         pass
00375 
00376     def _remove_all_children(self):
00377         # Remove all children from this node.
00378         self._children = {}
00379 
00380     def _set_events(self, events):
00381         self._cbs = {}
00382         for e in events:
00383             self._cbs[e] = []
00384 
00385 
00386 # vim: tw=79
00387 


rtctree
Author(s): Geoffrey Biggs
autogenerated on Wed Aug 26 2015 16:13:08