Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / tools / cr / cr / visitor.py
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.
4
5 """.
6
7 """
8 import collections
9
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.
14 HIDDEN = object()
15
16
17 class VisitComplete(Exception):
18   """Indicates a vist traversal has finished early."""
19
20
21 class Visitor(object):
22   """The base class for anything that wants to "visit" all variables.
23
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.
26   """
27
28   def __init__(self):
29     self.stack = []
30
31   def VisitNode(self, node):
32     """Called for every node in the tree."""
33     if not node.enabled:
34       return self
35     try:
36       try:
37         self.stack.append(node)
38         self.StartNode()
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:
44           self.VisitNode(child)
45       finally:
46         self.EndNode()
47         self.stack.pop()
48     except VisitComplete:
49       if self.stack:
50         # propagate back up the stack
51         raise
52     return self
53
54   def Visit(self, key, value):
55     """Visit is called for every variable in each node."""
56
57   def StartNode(self):
58     """StartNode is called once for each node before traversal."""
59
60   def EndNode(self):
61     """Visit is called for every node after traversal."""
62
63   @property
64   def root_node(self):
65     """Returns the variable at the root of the current traversal."""
66     return self.stack[0]
67
68   @property
69   def current_node(self):
70     """Returns the node currently being scanned."""
71     return self.stack[-1]
72
73   def Resolve(self, key, value):
74     """Returns a fully substituted value.
75
76     This asks the root node to do the actual work.
77     Args:
78       key: The key being visited.
79       value: The unresolved value associated with the key.
80     Returns:
81       the fully resolved value.
82     """
83     return self.root_node.Resolve(self, key, value)
84
85   def Where(self):
86     """Returns the current traversal stack as a string."""
87     return '/'.join([entry.name for entry in self.stack])
88
89
90 class SearchVisitor(Visitor):
91   """A Visitor that finds a single matching key."""
92
93   def __init__(self, key):
94     super(SearchVisitor, self).__init__()
95     self.key = key
96     self.found = False
97     self.error = None
98
99   def KeysOf(self, store):
100     if self.key in store:
101       yield self.key
102
103   def Visit(self, key, value):
104     value, error = self.Resolve(key, value)
105     if value is not HIDDEN:
106       self.found = True
107       self.value = value
108       self.error = error
109       raise VisitComplete()
110
111
112 class WhereVisitor(SearchVisitor):
113   """A SearchVisitor that returns the path to the matching key."""
114
115   def Visit(self, key, value):
116     self.where = self.Where()
117     super(WhereVisitor, self).Visit(key, value)
118
119
120 class ExportVisitor(Visitor):
121   """A visitor that builds a fully resolved map of all variables."""
122
123   def __init__(self, store):
124     super(ExportVisitor, self).__init__()
125     self.store = store
126
127   def KeysOf(self, store):
128     if self.current_node.export is False:
129       # not exporting from this config
130       return
131     for key in store.keys():
132       if key in self.store:
133         # duplicate
134         continue
135       if (self.current_node.export is None) and key.startswith('_'):
136         # non exported name
137         continue
138       yield key
139
140   def Visit(self, key, value):
141     value, _ = self.Resolve(key, value)
142     if value is not HIDDEN:
143       self.store[key] = value
144
145
146 class Node(object):
147   """The base class for objects in a visitable node tree."""
148
149   def __init__(self, name='--', enabled=True, export=True):
150     self._name = name
151     self._children = collections.deque()
152     self._values = {}
153     self._viewers = []
154     self.trail = []
155     self._enabled = enabled
156     self._export = export
157     self._export_cache = None
158
159   @property
160   def name(self):
161     return self._name
162
163   @name.setter
164   def name(self, value):
165     self._name = value
166
167   @property
168   def enabled(self):
169     return self._enabled
170
171   @enabled.setter
172   def enabled(self, value):
173     if self._enabled == value:
174       return
175     self._enabled = value
176     self.NotifyChanged()
177
178   @property
179   def export(self):
180     return self._export
181
182   @property
183   def exported(self):
184     if self._export_cache is None:
185       self._export_cache = ExportVisitor({}).VisitNode(self).store
186     return self._export_cache
187
188   @property
189   def values(self):
190     return self._values
191
192   @property
193   def children(self):
194     return self._children
195
196   def RegisterViewer(self, viewer):
197     self._viewers.append(viewer)
198
199   def UnregisterViewer(self, viewer):
200     self._viewers.remove(viewer)
201
202   def OnChanged(self, child):
203     _ = child
204     self.NotifyChanged()
205
206   def NotifyChanged(self):
207     self._export_cache = None
208     for viewers in self._viewers:
209       viewers.OnChanged(self)
210
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)
215
216   def AddChild(self, child):
217     self._AddChild(child)
218     self.NotifyChanged()
219     return self
220
221   def AddChildren(self, *children):
222     for child in children:
223       self._AddChild(child)
224     self.NotifyChanged()
225     return self
226
227   def Find(self, key):
228     search = SearchVisitor(key).VisitNode(self)
229     if not search.found:
230       return None
231     return search.value
232
233   def WhereIs(self, key):
234     search = WhereVisitor(key).VisitNode(self)
235     if not search.found:
236       return None
237     return search.where
238
239   def Get(self, key, raise_errors=False):
240     search = SearchVisitor(key).VisitNode(self)
241     if not search.found:
242       self.Missing(key)
243     if search.error and raise_errors:
244       raise search.error  # bad type inference pylint: disable=raising-bad-type
245     return search.value
246
247   def Missing(self, key):
248     raise KeyError(key)
249
250   def Resolve(self, visitor, key, value):
251     _ = visitor, key
252     return value
253
254   def Wipe(self):
255     for child in self._children:
256       child.UnregisterViewer(self)
257     self._children = collections.deque()
258     self._values = {}
259     self.NotifyChanged()
260