Split property system into it's own file.

Split the property system into it's own file to
make testing and updating easier.

TEST= python idl_propertynode.py
BUG= http://code.google.com/p/chromium/issues/detail?id=74634

R= [email protected]
Review URL: http://codereview.chromium.org/7356008

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@92546 0039d316-1c4b-4281-b951-d872f2087c98
diff --git a/ppapi/generators/idl_propertynode.py b/ppapi/generators/idl_propertynode.py
new file mode 100644
index 0000000..caf0974
--- /dev/null
+++ b/ppapi/generators/idl_propertynode.py
@@ -0,0 +1,196 @@
+#!/usr/bin/python
+#
+# Copyright (c) 2011 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+""" Hierarchical property system for IDL AST """
+import re
+import sys
+
+from idl_log import ErrOut, InfoOut, WarnOut
+from idl_option import GetOption, Option, ParseOptions
+
+#
+# IDLPropertyNode
+#
+# A property node is a hierarchically aware system for mapping
+# keys to values, such that a local dictionary is search first,
+# followed by parent dictionaries in order.
+#
+class IDLPropertyNode(object):
+  def __init__(self):
+    self.parents = []
+    self.property_map = {}
+
+  def Error(self, msg):
+    name = self.GetProperty('NAME', 'Unknown')
+    parents = [parent.GetProperty('NAME', '???') for parent in self.parents]
+    ErrOut.Log('%s [%s] : %s' % (name, ' '.join(parents), msg))
+
+  def AddParent(self, parent):
+    assert parent
+    self.parents.append(parent)
+
+  def SetProperty(self, name, val):
+    self.property_map[name] = val
+
+  def _GetProperty_(self, name):
+    # Check locally for the property, and return it if found.
+    prop = self.property_map.get(name, None)
+    if prop is not None: return prop
+    # If not, seach parents in order
+    for parent in self.parents:
+      prop = parent.GetProperty(name)
+      if prop is not None: return prop
+    # Otherwise, it can not be found.
+    return None
+
+  def GetProperty(self, name, default=None):
+    prop = self._GetProperty_(name)
+    if prop is None:
+      return default
+    else:
+      return prop
+
+  def GetPropertyLocal(self, name, default=None):
+    # Search for the property, but only locally, returning the
+    # default if not found.
+    prop = self.property_map.get(name, default)
+    return prop
+
+  # Regular expression to parse property keys in a string such that a string
+  #  "My string $NAME$" will find the key "NAME".
+  regex_var = re.compile('(?P<src>[^\\$]+)|(?P<key>\\$\\w+\\$)')
+
+  def GetPropertyList(self):
+    return self.property_map.keys()
+
+  # Recursively expands text keys in the form of $KEY$ with the value
+  # of the property of the same name.  Since this is done recursively
+  # one property can be defined in terms of another.
+  def Replace(self, text):
+    itr = IDLPropertyNode.regex_var.finditer(text)
+    out = ''
+    for m in itr:
+      (start, stop) = m.span()
+      if m.lastgroup == 'src':
+        out += text[start:stop]
+      if m.lastgroup == 'key':
+        key = text[start+1:stop-1]
+        val = self.GetProperty(key, None)
+        if not val:
+          self.Error('No property "%s"' % key)
+        out += self.Replace(str(val))
+    return out
+
+
+#
+# Testing functions
+#
+
+# Build a property node, setting the properties including a name, and
+# associate the children with this new node.
+#
+def BuildNode(name, props, children=[], parents=[]):
+  node = IDLPropertyNode()
+  node.SetProperty('NAME', name)
+  for prop in props:
+    toks = prop.split('=')
+    node.SetProperty(toks[0], toks[1])
+  for child in children:
+    child.AddParent(node)
+  for parent in parents:
+    node.AddParent(parent)
+  return node
+
+def ExpectProp(node, name, val):
+  found = node.GetProperty(name)
+  if found != val:
+    ErrOut.Log('Got property %s expecting %s' % (found, val))
+    return 1
+  return 0
+
+#
+# Verify property inheritance
+#
+def PropertyTest():
+  errors = 0
+  left = BuildNode('Left', ['Left=Left'])
+  right = BuildNode('Right', ['Right=Right'])
+  top = BuildNode('Top', ['Left=Top', 'Right=Top'], [left, right])
+
+  errors += ExpectProp(top, 'Left', 'Top')
+  errors += ExpectProp(top, 'Right', 'Top')
+
+  errors += ExpectProp(left, 'Left', 'Left')
+  errors += ExpectProp(left, 'Right', 'Top')
+
+  errors += ExpectProp(right, 'Left', 'Top')
+  errors += ExpectProp(right, 'Right', 'Right')
+
+  if not errors: InfoOut.Log('Passed PropertyTest')
+  return errors
+
+
+def ExpectText(node, text, val):
+  found = node.Replace(text)
+  if found != val:
+    ErrOut.Log('Got replacement %s expecting %s' % (found, val))
+    return 1
+  return 0
+
+#
+# Verify text replacement
+#
+def ReplaceTest():
+  errors = 0
+  left = BuildNode('Left', ['Left=Left'])
+  right = BuildNode('Right', ['Right=Right'])
+  top = BuildNode('Top', ['Left=Top', 'Right=Top'], [left, right])
+
+  errors += ExpectText(top, '$Left$', 'Top')
+  errors += ExpectText(top, '$Right$', 'Top')
+
+  errors += ExpectText(left, '$Left$', 'Left')
+  errors += ExpectText(left, '$Right$', 'Top')
+
+  errors += ExpectText(right, '$Left$', 'Top')
+  errors += ExpectText(right, '$Right$', 'Right')
+
+  if not errors: InfoOut.Log('Passed ReplaceTest')
+  return errors
+
+
+def MultiParentTest():
+  errors = 0
+
+  parent1 = BuildNode('parent1', ['PARENT1=parent1', 'TOPMOST=$TOP$'])
+  parent2 = BuildNode('parent2', ['PARENT1=parent2', 'PARENT2=parent2'])
+  child = BuildNode('child', ['CHILD=child'], parents=[parent1, parent2])
+  BuildNode('top', ['TOP=top'], children=[parent1])
+
+  errors += ExpectText(child, '$CHILD$', 'child')
+  errors += ExpectText(child, '$PARENT1$', 'parent1')
+  errors += ExpectText(child, '$PARENT2$', 'parent2')
+
+  # Verify recursive resolution
+  errors += ExpectText(child, '$TOPMOST$', 'top')
+
+  if not errors: InfoOut.Log('Passed MultiParentTest')
+  return errors
+
+def Main():
+  errors = 0
+  errors += PropertyTest()
+  errors += ReplaceTest()
+  errors += MultiParentTest()
+
+  if errors:
+    ErrOut.Log('IDLNode failed with %d errors.' % errors)
+    return  -1
+  return 0
+
+if __name__ == '__main__':
+  sys.exit(Main())
+