00001
00002
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
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
00231 self._enable_dynamic(False)
00232 elif not self._dynamic and dynamic:
00233
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
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
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
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
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
00373
00374 pass
00375
00376 def _remove_all_children(self):
00377
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
00387