1 # Copyright 2013 The Chromium Authors. All rights reserved.
2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file.
10 # HIDDEN is a marker used to suppress a value, making it as if it were not set
11 # in that object. This causes the search to continue through the tree.
12 # This is most useful as a return value of dynamic values that want to find
13 # the value they are shadowing.
17 class VisitComplete(Exception):
18 """Indicates a vist traversal has finished early."""
21 class Visitor(object):
22 """The base class for anything that wants to "visit" all variables.
24 The two main uses of visitor are search and export. They differ in that export
25 is trying to find all variables, whereas search is just looking for one.
31 def VisitNode(self, node):
32 """Called for every node in the tree."""
37 self.stack.append(node)
39 # Visit all the values first
40 for key in self.KeysOf(node.values):
41 self.Visit(key, node.values[key])
42 # And now recurse into all the children
43 for child in node.children:
50 # propagate back up the stack
54 def Visit(self, key, value):
55 """Visit is called for every variable in each node."""
58 """StartNode is called once for each node before traversal."""
61 """Visit is called for every node after traversal."""
65 """Returns the variable at the root of the current traversal."""
69 def current_node(self):
70 """Returns the node currently being scanned."""
73 def Resolve(self, key, value):
74 """Returns a fully substituted value.
76 This asks the root node to do the actual work.
78 key: The key being visited.
79 value: The unresolved value associated with the key.
81 the fully resolved value.
83 return self.root_node.Resolve(self, key, value)
86 """Returns the current traversal stack as a string."""
87 return '/'.join([entry.name for entry in self.stack])
90 class SearchVisitor(Visitor):
91 """A Visitor that finds a single matching key."""
93 def __init__(self, key):
94 super(SearchVisitor, self).__init__()
99 def KeysOf(self, store):
100 if self.key in store:
103 def Visit(self, key, value):
104 value, error = self.Resolve(key, value)
105 if value is not HIDDEN:
109 raise VisitComplete()
112 class WhereVisitor(SearchVisitor):
113 """A SearchVisitor that returns the path to the matching key."""
115 def Visit(self, key, value):
116 self.where = self.Where()
117 super(WhereVisitor, self).Visit(key, value)
120 class ExportVisitor(Visitor):
121 """A visitor that builds a fully resolved map of all variables."""
123 def __init__(self, store):
124 super(ExportVisitor, self).__init__()
127 def KeysOf(self, store):
128 if self.current_node.export is False:
129 # not exporting from this config
131 for key in store.keys():
132 if key in self.store:
135 if (self.current_node.export is None) and key.startswith('_'):
140 def Visit(self, key, value):
141 value, _ = self.Resolve(key, value)
142 if value is not HIDDEN:
143 self.store[key] = value
147 """The base class for objects in a visitable node tree."""
149 def __init__(self, name='--', enabled=True, export=True):
151 self._children = collections.deque()
155 self._enabled = enabled
156 self._export = export
157 self._export_cache = None
164 def name(self, value):
172 def enabled(self, value):
173 if self._enabled == value:
175 self._enabled = value
184 if self._export_cache is None:
185 self._export_cache = ExportVisitor({}).VisitNode(self).store
186 return self._export_cache
194 return self._children
196 def RegisterViewer(self, viewer):
197 self._viewers.append(viewer)
199 def UnregisterViewer(self, viewer):
200 self._viewers.remove(viewer)
202 def OnChanged(self, child):
206 def NotifyChanged(self):
207 self._export_cache = None
208 for viewers in self._viewers:
209 viewers.OnChanged(self)
211 def _AddChild(self, child):
212 if child and child != self and child not in self._children:
213 self._children.appendleft(child)
214 child.RegisterViewer(self)
216 def AddChild(self, child):
217 self._AddChild(child)
221 def AddChildren(self, *children):
222 for child in children:
223 self._AddChild(child)
228 search = SearchVisitor(key).VisitNode(self)
233 def WhereIs(self, key):
234 search = WhereVisitor(key).VisitNode(self)
239 def Get(self, key, raise_errors=False):
240 search = SearchVisitor(key).VisitNode(self)
243 if search.error and raise_errors:
244 raise search.error # bad type inference pylint: disable=raising-bad-type
247 def Missing(self, key):
250 def Resolve(self, visitor, key, value):
255 for child in self._children:
256 child.UnregisterViewer(self)
257 self._children = collections.deque()