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 """ 7 import re 8 import sys 9 10 from idl_log import ErrOut, InfoOut, WarnOut 11 from 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 # 20 class 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 # 94 def 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 106 def 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 # 116 def 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 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 181 182 183 def 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 195 if __name__ == '__main__': 196 sys.exit(Main()) 197 198