| OLD | NEW |
| 1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
| 2 # Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 3 # Use of this source code is governed by a BSD-style license that can be |
| 4 # found in the LICENSE file. | 4 # found in the LICENSE file. |
| 5 | 5 |
| 6 """ Hierarchical property system for IDL AST """ | 6 """ Hierarchical property system for IDL AST """ |
| 7 import re | 7 import re |
| 8 import sys | 8 import sys |
| 9 | 9 |
| 10 from idl_log import ErrOut, InfoOut, WarnOut | 10 from idl_log import ErrOut, InfoOut, WarnOut |
| 11 from idl_option import GetOption, Option, ParseOptions | |
| 12 | 11 |
| 13 # | 12 # |
| 14 # IDLPropertyNode | 13 # IDLPropertyNode |
| 15 # | 14 # |
| 16 # A property node is a hierarchically aware system for mapping | 15 # A property node is a hierarchically aware system for mapping |
| 17 # keys to values, such that a local dictionary is search first, | 16 # keys to values, such that a local dictionary is search first, |
| 18 # followed by parent dictionaries in order. | 17 # followed by parent dictionaries in order. |
| 19 # | 18 # |
| 20 class IDLPropertyNode(object): | 19 class IDLPropertyNode(object): |
| 21 def __init__(self): | 20 def __init__(self): |
| 22 self.parents = [] | 21 self.parents = [] |
| 23 self.property_map = {} | 22 self.property_map = {} |
| 24 | 23 |
| 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): | 24 def AddParent(self, parent): |
| 31 assert parent | 25 assert parent |
| 32 self.parents.append(parent) | 26 self.parents.append(parent) |
| 33 | 27 |
| 34 def SetProperty(self, name, val): | 28 def SetProperty(self, name, val): |
| 35 self.property_map[name] = val | 29 self.property_map[name] = val |
| 36 | 30 |
| 37 def _GetProperty_(self, name): | 31 def GetProperty(self, name): |
| 38 # Check locally for the property, and return it if found. | 32 # Check locally for the property, and return it if found. |
| 39 prop = self.property_map.get(name, None) | 33 prop = self.property_map.get(name, None) |
| 40 if prop is not None: return prop | 34 if prop is not None: |
| 35 return prop |
| 41 # If not, seach parents in order | 36 # If not, seach parents in order |
| 42 for parent in self.parents: | 37 for parent in self.parents: |
| 43 prop = parent.GetProperty(name) | 38 prop = parent.GetProperty(name) |
| 44 if prop is not None: return prop | 39 if prop is not None: |
| 40 return prop |
| 45 # Otherwise, it can not be found. | 41 # Otherwise, it can not be found. |
| 46 return None | 42 return None |
| 47 | 43 |
| 48 def GetProperty(self, name, default=None): | 44 def GetPropertyLocal(self, name): |
| 49 prop = self._GetProperty_(name) | 45 # Search for the property, but only locally. |
| 50 if prop is None: | 46 return self.property_map.get(name, 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 | 47 |
| 65 def GetPropertyList(self): | 48 def GetPropertyList(self): |
| 66 return self.property_map.keys() | 49 return self.property_map.keys() |
| 67 | 50 |
| 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 # | 51 # |
| 88 # Testing functions | 52 # Testing functions |
| 89 # | 53 # |
| 90 | 54 |
| 91 # Build a property node, setting the properties including a name, and | 55 # Build a property node, setting the properties including a name, and |
| 92 # associate the children with this new node. | 56 # associate the children with this new node. |
| 93 # | 57 # |
| 94 def BuildNode(name, props, children=[], parents=[]): | 58 def BuildNode(name, props, children=None, parents=None): |
| 95 node = IDLPropertyNode() | 59 node = IDLPropertyNode() |
| 96 node.SetProperty('NAME', name) | 60 node.SetProperty('NAME', name) |
| 97 for prop in props: | 61 for prop in props: |
| 98 toks = prop.split('=') | 62 toks = prop.split('=') |
| 99 node.SetProperty(toks[0], toks[1]) | 63 node.SetProperty(toks[0], toks[1]) |
| 100 for child in children: | 64 if children: |
| 101 child.AddParent(node) | 65 for child in children: |
| 102 for parent in parents: | 66 child.AddParent(node) |
| 103 node.AddParent(parent) | 67 if parents: |
| 68 for parent in parents: |
| 69 node.AddParent(parent) |
| 104 return node | 70 return node |
| 105 | 71 |
| 106 def ExpectProp(node, name, val): | 72 def ExpectProp(node, name, val): |
| 107 found = node.GetProperty(name) | 73 found = node.GetProperty(name) |
| 108 if found != val: | 74 if found != val: |
| 109 ErrOut.Log('Got property %s expecting %s' % (found, val)) | 75 ErrOut.Log('Got property %s expecting %s' % (found, val)) |
| 110 return 1 | 76 return 1 |
| 111 return 0 | 77 return 0 |
| 112 | 78 |
| 113 # | 79 # |
| 114 # Verify property inheritance | 80 # Verify property inheritance |
| 115 # | 81 # |
| 116 def PropertyTest(): | 82 def PropertyTest(): |
| 117 errors = 0 | 83 errors = 0 |
| 118 left = BuildNode('Left', ['Left=Left']) | 84 left = BuildNode('Left', ['Left=Left']) |
| 119 right = BuildNode('Right', ['Right=Right']) | 85 right = BuildNode('Right', ['Right=Right']) |
| 120 top = BuildNode('Top', ['Left=Top', 'Right=Top'], [left, right]) | 86 top = BuildNode('Top', ['Left=Top', 'Right=Top'], [left, right]) |
| 121 | 87 |
| 122 errors += ExpectProp(top, 'Left', 'Top') | 88 errors += ExpectProp(top, 'Left', 'Top') |
| 123 errors += ExpectProp(top, 'Right', 'Top') | 89 errors += ExpectProp(top, 'Right', 'Top') |
| 124 | 90 |
| 125 errors += ExpectProp(left, 'Left', 'Left') | 91 errors += ExpectProp(left, 'Left', 'Left') |
| 126 errors += ExpectProp(left, 'Right', 'Top') | 92 errors += ExpectProp(left, 'Right', 'Top') |
| 127 | 93 |
| 128 errors += ExpectProp(right, 'Left', 'Top') | 94 errors += ExpectProp(right, 'Left', 'Top') |
| 129 errors += ExpectProp(right, 'Right', 'Right') | 95 errors += ExpectProp(right, 'Right', 'Right') |
| 130 | 96 |
| 131 if not errors: InfoOut.Log('Passed PropertyTest') | 97 if not errors: |
| 132 return errors | 98 InfoOut.Log('Passed PropertyTest') |
| 133 | |
| 134 | |
| 135 def 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 # | |
| 145 def 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 | |
| 164 def 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 | 99 return errors |
| 181 | 100 |
| 182 | 101 |
| 183 def Main(): | 102 def Main(): |
| 184 errors = 0 | 103 errors = 0 |
| 185 errors += PropertyTest() | 104 errors += PropertyTest() |
| 186 errors += ReplaceTest() | |
| 187 errors += MultiParentTest() | |
| 188 | 105 |
| 189 if errors: | 106 if errors: |
| 190 ErrOut.Log('IDLNode failed with %d errors.' % errors) | 107 ErrOut.Log('IDLNode failed with %d errors.' % errors) |
| 191 return -1 | 108 return -1 |
| 192 return 0 | 109 return 0 |
| 193 | 110 |
| 194 | 111 |
| 195 if __name__ == '__main__': | 112 if __name__ == '__main__': |
| 196 sys.exit(Main()) | 113 sys.exit(Main()) |
| 197 | 114 |
| OLD | NEW |