18ffe1e44SBrad Bishop# Contributors Listed Below - COPYRIGHT 2016 28ffe1e44SBrad Bishop# [+] International Business Machines Corp. 38ffe1e44SBrad Bishop# 48ffe1e44SBrad Bishop# 58ffe1e44SBrad Bishop# Licensed under the Apache License, Version 2.0 (the "License"); 68ffe1e44SBrad Bishop# you may not use this file except in compliance with the License. 78ffe1e44SBrad Bishop# You may obtain a copy of the License at 88ffe1e44SBrad Bishop# 98ffe1e44SBrad Bishop# http://www.apache.org/licenses/LICENSE-2.0 108ffe1e44SBrad Bishop# 118ffe1e44SBrad Bishop# Unless required by applicable law or agreed to in writing, software 128ffe1e44SBrad Bishop# distributed under the License is distributed on an "AS IS" BASIS, 138ffe1e44SBrad Bishop# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148ffe1e44SBrad Bishop# implied. See the License for the specific language governing 158ffe1e44SBrad Bishop# permissions and limitations under the License. 168ffe1e44SBrad Bishop 178ffe1e44SBrad Bishop 188ffe1e44SBrad Bishopclass PathTreeItemIterator(object): 198ffe1e44SBrad Bishop def __init__(self, path_tree, subtree, depth): 208ffe1e44SBrad Bishop self.path_tree = path_tree 218ffe1e44SBrad Bishop self.path = [] 228ffe1e44SBrad Bishop self.itlist = [] 23*d641c086SBrad Bishop self.subtree = ['/'] + filter(bool, subtree.split('/')) 248ffe1e44SBrad Bishop self.depth = depth 258ffe1e44SBrad Bishop d = path_tree.root 268ffe1e44SBrad Bishop for k in self.subtree: 278ffe1e44SBrad Bishop try: 288ffe1e44SBrad Bishop d = d[k]['children'] 298ffe1e44SBrad Bishop except KeyError: 308ffe1e44SBrad Bishop raise KeyError(subtree) 31*d641c086SBrad Bishop self.it = d.iteritems() 328ffe1e44SBrad Bishop 338ffe1e44SBrad Bishop def __iter__(self): 348ffe1e44SBrad Bishop return self 358ffe1e44SBrad Bishop 368ffe1e44SBrad Bishop def __next__(self): 37*d641c086SBrad Bishop return super(PathTreeItemIterator, self).next() 388ffe1e44SBrad Bishop 39*d641c086SBrad Bishop def next(self): 408ffe1e44SBrad Bishop key, value = self._next() 418ffe1e44SBrad Bishop path = self.subtree[0] + '/'.join(self.subtree[1:] + self.path) 428ffe1e44SBrad Bishop return path, value.get('data') 438ffe1e44SBrad Bishop 448ffe1e44SBrad Bishop def _next(self): 458ffe1e44SBrad Bishop try: 468ffe1e44SBrad Bishop while True: 47*d641c086SBrad Bishop x = self.it.next() 488ffe1e44SBrad Bishop depth_exceeded = len(self.path) + 1 > self.depth 498ffe1e44SBrad Bishop if self.depth and depth_exceeded: 508ffe1e44SBrad Bishop continue 518ffe1e44SBrad Bishop self.itlist.append(self.it) 528ffe1e44SBrad Bishop self.path.append(x[0]) 53*d641c086SBrad Bishop self.it = x[1]['children'].iteritems() 548ffe1e44SBrad Bishop break 558ffe1e44SBrad Bishop 568ffe1e44SBrad Bishop except StopIteration: 578ffe1e44SBrad Bishop if not self.itlist: 588ffe1e44SBrad Bishop raise StopIteration 598ffe1e44SBrad Bishop 608ffe1e44SBrad Bishop self.it = self.itlist.pop() 618ffe1e44SBrad Bishop self.path.pop() 628ffe1e44SBrad Bishop x = self._next() 638ffe1e44SBrad Bishop 648ffe1e44SBrad Bishop return x 658ffe1e44SBrad Bishop 668ffe1e44SBrad Bishop 678ffe1e44SBrad Bishopclass PathTreeKeyIterator(PathTreeItemIterator): 688ffe1e44SBrad Bishop def __init__(self, path_tree, subtree, depth): 698ffe1e44SBrad Bishop super(PathTreeKeyIterator, self).__init__(path_tree, subtree, depth) 708ffe1e44SBrad Bishop 71*d641c086SBrad Bishop def next(self): 728ffe1e44SBrad Bishop return super(PathTreeKeyIterator, self).next()[0] 738ffe1e44SBrad Bishop 748ffe1e44SBrad Bishop 758ffe1e44SBrad Bishopclass PathTree: 768ffe1e44SBrad Bishop def __init__(self): 778ffe1e44SBrad Bishop self.root = {} 788ffe1e44SBrad Bishop 798ffe1e44SBrad Bishop def _try_delete_parent(self, elements): 808ffe1e44SBrad Bishop if len(elements) == 1: 818ffe1e44SBrad Bishop return False 828ffe1e44SBrad Bishop 838ffe1e44SBrad Bishop kids = 'children' 848ffe1e44SBrad Bishop elements.pop() 858ffe1e44SBrad Bishop d = self.root 868ffe1e44SBrad Bishop for k in elements[:-1]: 878ffe1e44SBrad Bishop d = d[k][kids] 888ffe1e44SBrad Bishop 898ffe1e44SBrad Bishop if 'data' not in d[elements[-1]] and not d[elements[-1]][kids]: 908ffe1e44SBrad Bishop del d[elements[-1]] 918ffe1e44SBrad Bishop self._try_delete_parent(elements) 928ffe1e44SBrad Bishop 938ffe1e44SBrad Bishop def _get_node(self, key): 948ffe1e44SBrad Bishop kids = 'children' 95*d641c086SBrad Bishop elements = ['/'] + filter(bool, key.split('/')) 968ffe1e44SBrad Bishop d = self.root 978ffe1e44SBrad Bishop for k in elements[:-1]: 988ffe1e44SBrad Bishop try: 998ffe1e44SBrad Bishop d = d[k][kids] 1008ffe1e44SBrad Bishop except KeyError: 1018ffe1e44SBrad Bishop raise KeyError(key) 1028ffe1e44SBrad Bishop 1038ffe1e44SBrad Bishop return d[elements[-1]] 1048ffe1e44SBrad Bishop 1058ffe1e44SBrad Bishop def __iter__(self): 1068ffe1e44SBrad Bishop return self 1078ffe1e44SBrad Bishop 1088ffe1e44SBrad Bishop def __missing__(self, key): 109*d641c086SBrad Bishop for x in self.iterkeys(): 1108ffe1e44SBrad Bishop if key == x: 1118ffe1e44SBrad Bishop return False 1128ffe1e44SBrad Bishop return True 1138ffe1e44SBrad Bishop 1148ffe1e44SBrad Bishop def __delitem__(self, key): 1158ffe1e44SBrad Bishop kids = 'children' 116*d641c086SBrad Bishop elements = ['/'] + filter(bool, key.split('/')) 1178ffe1e44SBrad Bishop d = self.root 1188ffe1e44SBrad Bishop for k in elements[:-1]: 1198ffe1e44SBrad Bishop try: 1208ffe1e44SBrad Bishop d = d[k][kids] 1218ffe1e44SBrad Bishop except KeyError: 1228ffe1e44SBrad Bishop raise KeyError(key) 1238ffe1e44SBrad Bishop 1248ffe1e44SBrad Bishop del d[elements[-1]] 1258ffe1e44SBrad Bishop self._try_delete_parent(elements) 1268ffe1e44SBrad Bishop 1278ffe1e44SBrad Bishop def __setitem__(self, key, value): 1288ffe1e44SBrad Bishop kids = 'children' 129*d641c086SBrad Bishop elements = ['/'] + filter(bool, key.split('/')) 1308ffe1e44SBrad Bishop d = self.root 1318ffe1e44SBrad Bishop for k in elements[:-1]: 1328ffe1e44SBrad Bishop d = d.setdefault(k, {kids: {}})[kids] 1338ffe1e44SBrad Bishop 1348ffe1e44SBrad Bishop children = d.setdefault(elements[-1], {kids: {}})[kids] 1358ffe1e44SBrad Bishop d[elements[-1]].update({kids: children, 'data': value}) 1368ffe1e44SBrad Bishop 1378ffe1e44SBrad Bishop def __getitem__(self, key): 1388ffe1e44SBrad Bishop return self._get_node(key).get('data') 1398ffe1e44SBrad Bishop 1408ffe1e44SBrad Bishop def setdefault(self, key, default): 1418ffe1e44SBrad Bishop if not self.get(key): 1428ffe1e44SBrad Bishop self.__setitem__(key, default) 1438ffe1e44SBrad Bishop 1448ffe1e44SBrad Bishop return self.__getitem__(key) 1458ffe1e44SBrad Bishop 1468ffe1e44SBrad Bishop def get(self, key, default=None): 1478ffe1e44SBrad Bishop try: 1488ffe1e44SBrad Bishop x = self.__getitem__(key) 1498ffe1e44SBrad Bishop except KeyError: 1508ffe1e44SBrad Bishop x = default 1518ffe1e44SBrad Bishop 1528ffe1e44SBrad Bishop return x 1538ffe1e44SBrad Bishop 1548ffe1e44SBrad Bishop def get_children(self, key): 155*d641c086SBrad Bishop return [x for x in self._get_node(key)['children'].iterkeys()] 1568ffe1e44SBrad Bishop 1578ffe1e44SBrad Bishop def demote(self, key): 1588ffe1e44SBrad Bishop n = self._get_node(key) 1598ffe1e44SBrad Bishop if 'data' in n: 1608ffe1e44SBrad Bishop del n['data'] 1618ffe1e44SBrad Bishop 1628ffe1e44SBrad Bishop def keys(self, subtree='/', depth=None): 1638ffe1e44SBrad Bishop return [x for x in self.iterkeys(subtree, depth)] 1648ffe1e44SBrad Bishop 1658ffe1e44SBrad Bishop def values(self, subtree='/', depth=None): 1668ffe1e44SBrad Bishop return [x[1] for x in self.iteritems(subtree, depth)] 1678ffe1e44SBrad Bishop 1688ffe1e44SBrad Bishop def items(self, subtree='/', depth=None): 1698ffe1e44SBrad Bishop return [x for x in self.iteritems(subtree, depth)] 1708ffe1e44SBrad Bishop 1718ffe1e44SBrad Bishop def dataitems(self, subtree='/', depth=None): 1728ffe1e44SBrad Bishop return [x for x in self.iteritems(subtree, depth) 1738ffe1e44SBrad Bishop if x[1] is not None] 1748ffe1e44SBrad Bishop 1758ffe1e44SBrad Bishop def iterkeys(self, subtree='/', depth=None): 1768ffe1e44SBrad Bishop if not self.root: 177*d641c086SBrad Bishop return {}.iterkeys() 1788ffe1e44SBrad Bishop return PathTreeKeyIterator(self, subtree, depth) 1798ffe1e44SBrad Bishop 1808ffe1e44SBrad Bishop def iteritems(self, subtree='/', depth=None): 1818ffe1e44SBrad Bishop if not self.root: 182*d641c086SBrad Bishop return {}.iteritems() 1838ffe1e44SBrad Bishop return PathTreeItemIterator(self, subtree, depth) 1844e601a0aSBrad Bishop 1854e601a0aSBrad Bishop def dumpd(self, subtree='/'): 1864e601a0aSBrad Bishop result = {} 1874e601a0aSBrad Bishop d = result 1884e601a0aSBrad Bishop 1894e601a0aSBrad Bishop for k, v in self.iteritems(subtree): 190*d641c086SBrad Bishop elements = ['/'] + filter(bool, k.split('/')) 1914e601a0aSBrad Bishop d = result 1924e601a0aSBrad Bishop for k in elements: 1934e601a0aSBrad Bishop d = d.setdefault(k, {}) 1944e601a0aSBrad Bishop if v is not None: 1954e601a0aSBrad Bishop d.update(v) 1964e601a0aSBrad Bishop 1974e601a0aSBrad Bishop return result 198