Index: components/policy/tools/generate_policy_source.py |
diff --git a/components/policy/tools/generate_policy_source.py b/components/policy/tools/generate_policy_source.py |
index 9ccf881d17137a2ecd5b98f6e3ea77f21a44cda8..0be012abf272e7c6998a84d851e007416443d5ec 100755 |
--- a/components/policy/tools/generate_policy_source.py |
+++ b/components/policy/tools/generate_policy_source.py |
@@ -11,11 +11,13 @@ chromium_os_flag should be 1 if this is a Chromium OS build |
template is the path to a .json policy template file.''' |
from __future__ import with_statement |
+from functools import partial |
import json |
from optparse import OptionParser |
import re |
import sys |
import textwrap |
+import types |
CHROME_POLICY_KEY = 'SOFTWARE\\\\Policies\\\\Google\\\\Chrome' |
@@ -282,6 +284,7 @@ class SchemaNodesGenerator: |
} |
self.stringlist_type = None |
self.ranges = {} |
+ self.id_map = {} |
def GetString(self, s): |
if s in self.shared_strings: |
@@ -398,6 +401,12 @@ class SchemaNodesGenerator: |
|schema|: a valid JSON schema in a dictionary. |
|name|: the name of the current node, for the generated comments.""" |
+ if schema.has_key('$ref'): |
+ if schema.has_key('id'): |
+ raise RuntimeError("Schemas with a $ref can't have an id") |
+ if not isinstance(schema['$ref'], types.StringTypes): |
+ raise RuntimeError("$ref attribute must be a string") |
+ return schema['$ref'] |
if schema['type'] in self.simple_types: |
if not self.SchemaHaveRestriction(schema): |
# Simple types use shared nodes. |
@@ -413,9 +422,8 @@ class SchemaNodesGenerator: |
# Special case for lists of strings, which is a common policy type. |
if schema['items']['type'] == 'string': |
return self.GetStringList() |
- return self.AppendSchema( |
- 'TYPE_LIST', |
- self.Generate(schema['items'], 'items of ' + name)) |
+ return self.AppendSchema('TYPE_LIST', |
+ self.GenerateAndCollectID(schema['items'], 'items of ' + name)) |
elif schema['type'] == 'object': |
# Reserve an index first, so that dictionaries come before their |
# properties. This makes sure that the root node is the first in the |
@@ -423,7 +431,7 @@ class SchemaNodesGenerator: |
index = self.AppendSchema('TYPE_DICTIONARY', -1) |
if 'additionalProperties' in schema: |
- additionalProperties = self.Generate( |
+ additionalProperties = self.GenerateAndCollectID( |
schema['additionalProperties'], |
'additionalProperties of ' + name) |
else: |
@@ -434,13 +442,14 @@ class SchemaNodesGenerator: |
# recursive calls to Generate() append the necessary child nodes; if |
# |properties| were a generator then this wouldn't work. |
sorted_properties = sorted(schema.get('properties', {}).items()) |
- properties = [ (self.GetString(key), self.Generate(subschema, key)) |
- for key, subschema in sorted_properties ] |
+ properties = [ |
+ (self.GetString(key), self.GenerateAndCollectID(subschema, key)) |
+ for key, subschema in sorted_properties ] |
pattern_properties = [] |
for pattern, subschema in schema.get('patternProperties', {}).items(): |
pattern_properties.append((self.GetString(pattern), |
- self.Generate(subschema, pattern))); |
+ self.GenerateAndCollectID(subschema, pattern))); |
begin = len(self.property_nodes) |
self.property_nodes += properties |
@@ -462,6 +471,20 @@ class SchemaNodesGenerator: |
else: |
assert False |
+ def GenerateAndCollectID(self, schema, name): |
+ """A wrapper of Generate(), will take the return value, check and add 'id' |
+ attribute to self.id_map. The wrapper needs to be used for every call to |
+ Generate(). |
+ """ |
+ index = self.Generate(schema, name) |
+ if not schema.has_key('id'): |
+ return index |
+ id_str = schema['id'] |
+ if self.id_map.has_key(id_str): |
+ raise RuntimeError('Duplicated id: ' + id_str) |
+ self.id_map[id_str] = index |
+ return index |
+ |
def Write(self, f): |
"""Writes the generated structs to the given file. |
@@ -516,6 +539,29 @@ class SchemaNodesGenerator: |
f.write(' kStringEnumerations,\n' if self.string_enums else ' NULL,\n') |
f.write('};\n\n') |
+ def GetByID(self, id_str): |
+ if not isinstance(id_str, types.StringTypes): |
+ return id_str |
+ if not self.id_map.has_key(id_str): |
+ raise RuntimeError('Invalid $ref: ' + id_str) |
+ return self.id_map[id_str] |
+ |
+ def ResolveID(self, index, params): |
+ return params[:index] + (self.GetByID(params[index]),) + params[index+1:] |
+ |
+ def ResolveReferences(self): |
+ """Resolve reference mapping, required to be called after Generate() |
+ |
+ After calling Generate(), the type of indices used in schema structures |
+ might be either int or string. An int type suggests that it's a resolved |
+ index, but for string type it's unresolved. Resolving a reference is as |
+ simple as looking up for corresponding ID in self.id_map, and replace the |
+ old index with the mapped index. |
+ """ |
+ self.schema_nodes = map(partial(self.ResolveID, 1), self.schema_nodes) |
+ self.property_nodes = map(partial(self.ResolveID, 1), self.property_nodes) |
+ self.properties_nodes = map(partial(self.ResolveID, 3), |
+ self.properties_nodes) |
def _WritePolicyConstantSource(policies, os, f): |
f.write('#include "policy/policy_constants.h"\n' |
@@ -558,7 +604,8 @@ def _WritePolicyConstantSource(policies, os, f): |
f.write('};\n\n') |
schema_generator = SchemaNodesGenerator(shared_strings) |
- schema_generator.Generate(chrome_schema, 'root node') |
+ schema_generator.GenerateAndCollectID(chrome_schema, 'root node') |
+ schema_generator.ResolveReferences() |
schema_generator.Write(f) |
f.write('bool CompareKeys(const internal::PropertyNode& node,\n' |