idl_propertynode.py revision 5821806d5e7f356e8fa4b058a389a808ea183019
1#!/usr/bin/env python
2# Copyright (c) 2012 The Chromium Authors. All rights reserved.
3# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
5
6""" Hierarchical property system for IDL AST """
7import re
8import sys
9
10from idl_log import ErrOut, InfoOut, WarnOut
11from idl_option import GetOption, Option, ParseOptions
12
13#
14# IDLPropertyNode
15#
16# A property node is a hierarchically aware system for mapping
17# keys to values, such that a local dictionary is search first,
18# followed by parent dictionaries in order.
19#
20class IDLPropertyNode(object):
21  def __init__(self):
22    self.parents = []
23    self.property_map = {}
24
25  def Error(self, msg):
26    name = self.GetProperty('NAME', 'Unknown')
27    parents = [parent.GetProperty('NAME', '???') for parent in self.parents]
28    ErrOut.Log('%s [%s] : %s' % (name, ' '.join(parents), msg))
29
30  def AddParent(self, parent):
31    assert parent
32    self.parents.append(parent)
33
34  def SetProperty(self, name, val):
35    self.property_map[name] = val
36
37  def _GetProperty_(self, name):
38    # Check locally for the property, and return it if found.
39    prop = self.property_map.get(name, None)
40    if prop is not None: return prop
41    # If not, seach parents in order
42    for parent in self.parents:
43      prop = parent.GetProperty(name)
44      if prop is not None: return prop
45    # Otherwise, it can not be found.
46    return None
47
48  def GetProperty(self, name, default=None):
49    prop = self._GetProperty_(name)
50    if prop is None:
51      return default
52    else:
53      return prop
54
55  def GetPropertyLocal(self, name, default=None):
56    # Search for the property, but only locally, returning the
57    # default if not found.
58    prop = self.property_map.get(name, default)
59    return prop
60
61  # Regular expression to parse property keys in a string such that a string
62  #  "My string $NAME$" will find the key "NAME".
63  regex_var = re.compile('(?P<src>[^\\$]+)|(?P<key>\\$\\w+\\$)')
64
65  def GetPropertyList(self):
66    return self.property_map.keys()
67
68  # Recursively expands text keys in the form of $KEY$ with the value
69  # of the property of the same name.  Since this is done recursively
70  # one property can be defined in terms of another.
71  def Replace(self, text):
72    itr = IDLPropertyNode.regex_var.finditer(text)
73    out = ''
74    for m in itr:
75      (start, stop) = m.span()
76      if m.lastgroup == 'src':
77        out += text[start:stop]
78      if m.lastgroup == 'key':
79        key = text[start+1:stop-1]
80        val = self.GetProperty(key, None)
81        if not val:
82          self.Error('No property "%s"' % key)
83        out += self.Replace(str(val))
84    return out
85
86
87#
88# Testing functions
89#
90
91# Build a property node, setting the properties including a name, and
92# associate the children with this new node.
93#
94def BuildNode(name, props, children=[], parents=[]):
95  node = IDLPropertyNode()
96  node.SetProperty('NAME', name)
97  for prop in props:
98    toks = prop.split('=')
99    node.SetProperty(toks[0], toks[1])
100  for child in children:
101    child.AddParent(node)
102  for parent in parents:
103    node.AddParent(parent)
104  return node
105
106def ExpectProp(node, name, val):
107  found = node.GetProperty(name)
108  if found != val:
109    ErrOut.Log('Got property %s expecting %s' % (found, val))
110    return 1
111  return 0
112
113#
114# Verify property inheritance
115#
116def PropertyTest():
117  errors = 0
118  left = BuildNode('Left', ['Left=Left'])
119  right = BuildNode('Right', ['Right=Right'])
120  top = BuildNode('Top', ['Left=Top', 'Right=Top'], [left, right])
121
122  errors += ExpectProp(top, 'Left', 'Top')
123  errors += ExpectProp(top, 'Right', 'Top')
124
125  errors += ExpectProp(left, 'Left', 'Left')
126  errors += ExpectProp(left, 'Right', 'Top')
127
128  errors += ExpectProp(right, 'Left', 'Top')
129  errors += ExpectProp(right, 'Right', 'Right')
130
131  if not errors: InfoOut.Log('Passed PropertyTest')
132  return errors
133
134
135def ExpectText(node, text, val):
136  found = node.Replace(text)
137  if found != val:
138    ErrOut.Log('Got replacement %s expecting %s' % (found, val))
139    return 1
140  return 0
141
142#
143# Verify text replacement
144#
145def ReplaceTest():
146  errors = 0
147  left = BuildNode('Left', ['Left=Left'])
148  right = BuildNode('Right', ['Right=Right'])
149  top = BuildNode('Top', ['Left=Top', 'Right=Top'], [left, right])
150
151  errors += ExpectText(top, '$Left$', 'Top')
152  errors += ExpectText(top, '$Right$', 'Top')
153
154  errors += ExpectText(left, '$Left$', 'Left')
155  errors += ExpectText(left, '$Right$', 'Top')
156
157  errors += ExpectText(right, '$Left$', 'Top')
158  errors += ExpectText(right, '$Right$', 'Right')
159
160  if not errors: InfoOut.Log('Passed ReplaceTest')
161  return errors
162
163
164def MultiParentTest():
165  errors = 0
166
167  parent1 = BuildNode('parent1', ['PARENT1=parent1', 'TOPMOST=$TOP$'])
168  parent2 = BuildNode('parent2', ['PARENT1=parent2', 'PARENT2=parent2'])
169  child = BuildNode('child', ['CHILD=child'], parents=[parent1, parent2])
170  BuildNode('top', ['TOP=top'], children=[parent1])
171
172  errors += ExpectText(child, '$CHILD$', 'child')
173  errors += ExpectText(child, '$PARENT1$', 'parent1')
174  errors += ExpectText(child, '$PARENT2$', 'parent2')
175
176  # Verify recursive resolution
177  errors += ExpectText(child, '$TOPMOST$', 'top')
178
179  if not errors: InfoOut.Log('Passed MultiParentTest')
180  return errors
181
182
183def Main():
184  errors = 0
185  errors += PropertyTest()
186  errors += ReplaceTest()
187  errors += MultiParentTest()
188
189  if errors:
190    ErrOut.Log('IDLNode failed with %d errors.' % errors)
191    return  -1
192  return 0
193
194
195if __name__ == '__main__':
196  sys.exit(Main())
197
198