""" provides node functionality for the eventable odml types Document, Section, Property and Value additionally implements change notifications up to the corresponding section """ import odml.property import odml.tools.event as event def identity_index(obj, val): """ same as obj.index(val) but will only return a position if the object val is found in the list i.e. >>> identity_index(["1"], "1") will raise an ValueError because the two strings are different objects with the same content >>> a = "1" >>> identity_index([a], a) 0 The difference to obj.index comes apparent here as well: >>> a="1", b="1" >>> identity_index([a,b], b) 1 >>> [a,b].index(b) 0 """ for i, v in enumerate(obj): if v is val: return i import pdb; pdb.set_trace() raise ValueError("%s does not contain the item %s" % (repr(obj), repr(val))) class RootNode(object): @property def children(self): return self._sections def from_path(self, path): child = self.children[path[0]] if len(path) == 1: return child return child.from_path(path[1:]) def to_path(self, parent=None): return () def path_to(self, child): """return the path from this node to its direct child *child*""" return (identity_index(self._sections, child),) class ParentedNode(RootNode): def to_path(self, parent=None): if self.parent is parent: return self.parent.path_to(self) return self.parent.to_path(parent) + self.parent.path_to(self) def successor(self): return self.parent.children[self.position + 1] def next(self): """ returns the next section following in this section's parent's list of sections returns None if there is no further element available i.e.: parent sec-a (<- self) sec-b will return sec-b """ try: return self.successor() except IndexError: return None @property def position(self): return self.parent.path_to(self)[-1] class SectionNode(ParentedNode): """ SectionNodes are special as they wrap two types of sub-nodes: * SubSections (path = (0, idx)) * Properties (path = (1, idx)) """ def from_path(self, path): assert len(path) > 1 if path[0] == 0: # sections return super(SectionNode, self).from_path(path[1:]) # else: properties child = self._props[path[1]] if len(path) == 2: return child return child.from_path(path[2:]) def path_to(self, child): if isinstance(child, odml.property.Property): return (1, identity_index(self._props, child)) return (0, identity_index(self._sections, child)) class PropertyNode(ParentedNode): @property def children(self): return self.values def successor(self): return self.parent._props[self.position + 1] def path_to(self, child): return (identity_index(self.values, child),) class ValueNode(ParentedNode): def path_from(self, path): raise TypeError("Value objects have no children") def path_to(self, child): raise TypeError("Value objects have no children") #TODO? provide this externally? name = "nodes" provides = event.provides + ["nodes"] class Document(event.Document, RootNode): pass class Value(event.Value, ValueNode): pass class Property(event.Property, PropertyNode): pass class Section(event.Section, SectionNode): pass import sys, odml odml.addImplementation(sys.modules[__name__])