| 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 '''python %prog [options] platform chromium_os_flag template | 6 '''python %prog [options] platform chromium_os_flag template |
| 7 | 7 |
| 8 platform specifies which platform source is being generated for | 8 platform specifies which platform source is being generated for |
| 9 and can be one of (win, mac, linux) | 9 and can be one of (win, mac, linux) |
| 10 chromium_os_flag should be 1 if this is a Chromium OS build | 10 chromium_os_flag should be 1 if this is a Chromium OS build |
| 11 template is the path to a .json policy template file.''' | 11 template is the path to a .json policy template file.''' |
| 12 | 12 |
| 13 from __future__ import with_statement | 13 from __future__ import with_statement |
| 14 from functools import partial |
| 14 import json | 15 import json |
| 15 from optparse import OptionParser | 16 from optparse import OptionParser |
| 16 import re | 17 import re |
| 17 import sys | 18 import sys |
| 18 import textwrap | 19 import textwrap |
| 20 import types |
| 19 | 21 |
| 20 | 22 |
| 21 CHROME_POLICY_KEY = 'SOFTWARE\\\\Policies\\\\Google\\\\Chrome' | 23 CHROME_POLICY_KEY = 'SOFTWARE\\\\Policies\\\\Google\\\\Chrome' |
| 22 CHROMIUM_POLICY_KEY = 'SOFTWARE\\\\Policies\\\\Chromium' | 24 CHROMIUM_POLICY_KEY = 'SOFTWARE\\\\Policies\\\\Chromium' |
| 23 | 25 |
| 24 | 26 |
| 25 class PolicyDetails: | 27 class PolicyDetails: |
| 26 """Parses a policy template and caches all its details.""" | 28 """Parses a policy template and caches all its details.""" |
| 27 | 29 |
| 28 # Maps policy types to a tuple with 3 other types: | 30 # Maps policy types to a tuple with 3 other types: |
| (...skipping 246 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 275 self.string_enums = [] | 277 self.string_enums = [] |
| 276 self.simple_types = { | 278 self.simple_types = { |
| 277 'boolean': None, | 279 'boolean': None, |
| 278 'integer': None, | 280 'integer': None, |
| 279 'null': None, | 281 'null': None, |
| 280 'number': None, | 282 'number': None, |
| 281 'string': None, | 283 'string': None, |
| 282 } | 284 } |
| 283 self.stringlist_type = None | 285 self.stringlist_type = None |
| 284 self.ranges = {} | 286 self.ranges = {} |
| 287 self.id_map = {} |
| 285 | 288 |
| 286 def GetString(self, s): | 289 def GetString(self, s): |
| 287 if s in self.shared_strings: | 290 if s in self.shared_strings: |
| 288 return self.shared_strings[s] | 291 return self.shared_strings[s] |
| 289 # Generate JSON escaped string, which is slightly different from desired | 292 # Generate JSON escaped string, which is slightly different from desired |
| 290 # C/C++ escaped string. Known differences includes unicode escaping format. | 293 # C/C++ escaped string. Known differences includes unicode escaping format. |
| 291 return json.dumps(s) | 294 return json.dumps(s) |
| 292 | 295 |
| 293 def AppendSchema(self, type, extra, comment=''): | 296 def AppendSchema(self, type, extra, comment=''): |
| 294 index = len(self.schema_nodes) | 297 index = len(self.schema_nodes) |
| (...skipping 96 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 391 str(min_value) if min_value_set else 'INT_MIN') | 394 str(min_value) if min_value_set else 'INT_MIN') |
| 392 return self.AppendSchema('TYPE_INTEGER', | 395 return self.AppendSchema('TYPE_INTEGER', |
| 393 index, | 396 index, |
| 394 'integer with ranged restriction: %s' % name) | 397 'integer with ranged restriction: %s' % name) |
| 395 | 398 |
| 396 def Generate(self, schema, name): | 399 def Generate(self, schema, name): |
| 397 """Generates the structs for the given schema. | 400 """Generates the structs for the given schema. |
| 398 | 401 |
| 399 |schema|: a valid JSON schema in a dictionary. | 402 |schema|: a valid JSON schema in a dictionary. |
| 400 |name|: the name of the current node, for the generated comments.""" | 403 |name|: the name of the current node, for the generated comments.""" |
| 404 if schema.has_key('$ref'): |
| 405 if schema.has_key('id'): |
| 406 raise RuntimeError("Schemas with a $ref can't have an id") |
| 407 if not isinstance(schema['$ref'], types.StringTypes): |
| 408 raise RuntimeError("$ref attribute must be a string") |
| 409 return schema['$ref'] |
| 401 if schema['type'] in self.simple_types: | 410 if schema['type'] in self.simple_types: |
| 402 if not self.SchemaHaveRestriction(schema): | 411 if not self.SchemaHaveRestriction(schema): |
| 403 # Simple types use shared nodes. | 412 # Simple types use shared nodes. |
| 404 return self.GetSimpleType(schema['type']) | 413 return self.GetSimpleType(schema['type']) |
| 405 elif 'enum' in schema: | 414 elif 'enum' in schema: |
| 406 return self.GetEnumType(schema, name) | 415 return self.GetEnumType(schema, name) |
| 407 elif 'pattern' in schema: | 416 elif 'pattern' in schema: |
| 408 return self.GetPatternType(schema, name) | 417 return self.GetPatternType(schema, name) |
| 409 else: | 418 else: |
| 410 return self.GetRangedType(schema, name) | 419 return self.GetRangedType(schema, name) |
| 411 | 420 |
| 412 if schema['type'] == 'array': | 421 if schema['type'] == 'array': |
| 413 # Special case for lists of strings, which is a common policy type. | 422 # Special case for lists of strings, which is a common policy type. |
| 414 if schema['items']['type'] == 'string': | 423 if schema['items']['type'] == 'string': |
| 415 return self.GetStringList() | 424 return self.GetStringList() |
| 416 return self.AppendSchema( | 425 return self.AppendSchema('TYPE_LIST', |
| 417 'TYPE_LIST', | 426 self.GenerateAndCollectID(schema['items'], 'items of ' + name)) |
| 418 self.Generate(schema['items'], 'items of ' + name)) | |
| 419 elif schema['type'] == 'object': | 427 elif schema['type'] == 'object': |
| 420 # Reserve an index first, so that dictionaries come before their | 428 # Reserve an index first, so that dictionaries come before their |
| 421 # properties. This makes sure that the root node is the first in the | 429 # properties. This makes sure that the root node is the first in the |
| 422 # SchemaNodes array. | 430 # SchemaNodes array. |
| 423 index = self.AppendSchema('TYPE_DICTIONARY', -1) | 431 index = self.AppendSchema('TYPE_DICTIONARY', -1) |
| 424 | 432 |
| 425 if 'additionalProperties' in schema: | 433 if 'additionalProperties' in schema: |
| 426 additionalProperties = self.Generate( | 434 additionalProperties = self.GenerateAndCollectID( |
| 427 schema['additionalProperties'], | 435 schema['additionalProperties'], |
| 428 'additionalProperties of ' + name) | 436 'additionalProperties of ' + name) |
| 429 else: | 437 else: |
| 430 additionalProperties = -1 | 438 additionalProperties = -1 |
| 431 | 439 |
| 432 # Properties must be sorted by name, for the binary search lookup. | 440 # Properties must be sorted by name, for the binary search lookup. |
| 433 # Note that |properties| must be evaluated immediately, so that all the | 441 # Note that |properties| must be evaluated immediately, so that all the |
| 434 # recursive calls to Generate() append the necessary child nodes; if | 442 # recursive calls to Generate() append the necessary child nodes; if |
| 435 # |properties| were a generator then this wouldn't work. | 443 # |properties| were a generator then this wouldn't work. |
| 436 sorted_properties = sorted(schema.get('properties', {}).items()) | 444 sorted_properties = sorted(schema.get('properties', {}).items()) |
| 437 properties = [ (self.GetString(key), self.Generate(subschema, key)) | 445 properties = [ |
| 438 for key, subschema in sorted_properties ] | 446 (self.GetString(key), self.GenerateAndCollectID(subschema, key)) |
| 447 for key, subschema in sorted_properties ] |
| 439 | 448 |
| 440 pattern_properties = [] | 449 pattern_properties = [] |
| 441 for pattern, subschema in schema.get('patternProperties', {}).items(): | 450 for pattern, subschema in schema.get('patternProperties', {}).items(): |
| 442 pattern_properties.append((self.GetString(pattern), | 451 pattern_properties.append((self.GetString(pattern), |
| 443 self.Generate(subschema, pattern))); | 452 self.GenerateAndCollectID(subschema, pattern))); |
| 444 | 453 |
| 445 begin = len(self.property_nodes) | 454 begin = len(self.property_nodes) |
| 446 self.property_nodes += properties | 455 self.property_nodes += properties |
| 447 end = len(self.property_nodes) | 456 end = len(self.property_nodes) |
| 448 self.property_nodes += pattern_properties | 457 self.property_nodes += pattern_properties |
| 449 pattern_end = len(self.property_nodes) | 458 pattern_end = len(self.property_nodes) |
| 450 | 459 |
| 451 if index == 0: | 460 if index == 0: |
| 452 self.root_properties_begin = begin | 461 self.root_properties_begin = begin |
| 453 self.root_properties_end = end | 462 self.root_properties_end = end |
| 454 | 463 |
| 455 extra = len(self.properties_nodes) | 464 extra = len(self.properties_nodes) |
| 456 self.properties_nodes.append((begin, end, pattern_end, | 465 self.properties_nodes.append((begin, end, pattern_end, |
| 457 additionalProperties, name)) | 466 additionalProperties, name)) |
| 458 | 467 |
| 459 # Set the right data at |index| now. | 468 # Set the right data at |index| now. |
| 460 self.schema_nodes[index] = ('TYPE_DICTIONARY', extra, name) | 469 self.schema_nodes[index] = ('TYPE_DICTIONARY', extra, name) |
| 461 return index | 470 return index |
| 462 else: | 471 else: |
| 463 assert False | 472 assert False |
| 464 | 473 |
| 474 def GenerateAndCollectID(self, schema, name): |
| 475 """A wrapper of Generate(), will take the return value, check and add 'id' |
| 476 attribute to self.id_map. The wrapper needs to be used for every call to |
| 477 Generate(). |
| 478 """ |
| 479 index = self.Generate(schema, name) |
| 480 if not schema.has_key('id'): |
| 481 return index |
| 482 id_str = schema['id'] |
| 483 if self.id_map.has_key(id_str): |
| 484 raise RuntimeError('Duplicated id: ' + id_str) |
| 485 self.id_map[id_str] = index |
| 486 return index |
| 487 |
| 465 def Write(self, f): | 488 def Write(self, f): |
| 466 """Writes the generated structs to the given file. | 489 """Writes the generated structs to the given file. |
| 467 | 490 |
| 468 |f| an open file to write to.""" | 491 |f| an open file to write to.""" |
| 469 f.write('const internal::SchemaNode kSchemas[] = {\n' | 492 f.write('const internal::SchemaNode kSchemas[] = {\n' |
| 470 '// Type Extra\n') | 493 '// Type Extra\n') |
| 471 for type, extra, comment in self.schema_nodes: | 494 for type, extra, comment in self.schema_nodes: |
| 472 type += ',' | 495 type += ',' |
| 473 f.write(' { base::Value::%-18s %3d }, // %s\n' % (type, extra, comment)) | 496 f.write(' { base::Value::%-18s %3d }, // %s\n' % (type, extra, comment)) |
| 474 f.write('};\n\n') | 497 f.write('};\n\n') |
| (...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 509 | 532 |
| 510 f.write('const internal::SchemaData kChromeSchemaData = {\n' | 533 f.write('const internal::SchemaData kChromeSchemaData = {\n' |
| 511 ' kSchemas,\n') | 534 ' kSchemas,\n') |
| 512 f.write(' kPropertyNodes,\n' if self.property_nodes else ' NULL,\n') | 535 f.write(' kPropertyNodes,\n' if self.property_nodes else ' NULL,\n') |
| 513 f.write(' kProperties,\n' if self.properties_nodes else ' NULL,\n') | 536 f.write(' kProperties,\n' if self.properties_nodes else ' NULL,\n') |
| 514 f.write(' kRestrictionNodes,\n' if self.restriction_nodes else ' NULL,\n') | 537 f.write(' kRestrictionNodes,\n' if self.restriction_nodes else ' NULL,\n') |
| 515 f.write(' kIntegerEnumerations,\n' if self.int_enums else ' NULL,\n') | 538 f.write(' kIntegerEnumerations,\n' if self.int_enums else ' NULL,\n') |
| 516 f.write(' kStringEnumerations,\n' if self.string_enums else ' NULL,\n') | 539 f.write(' kStringEnumerations,\n' if self.string_enums else ' NULL,\n') |
| 517 f.write('};\n\n') | 540 f.write('};\n\n') |
| 518 | 541 |
| 542 def GetByID(self, id_str): |
| 543 if not isinstance(id_str, types.StringTypes): |
| 544 return id_str |
| 545 if not self.id_map.has_key(id_str): |
| 546 raise RuntimeError('Invalid $ref: ' + id_str) |
| 547 return self.id_map[id_str] |
| 548 |
| 549 def ResolveID(self, index, params): |
| 550 return params[:index] + (self.GetByID(params[index]),) + params[index+1:] |
| 551 |
| 552 def ResolveReferences(self): |
| 553 """Resolve reference mapping, required to be called after Generate() |
| 554 |
| 555 After calling Generate(), the type of indices used in schema structures |
| 556 might be either int or string. An int type suggests that it's a resolved |
| 557 index, but for string type it's unresolved. Resolving a reference is as |
| 558 simple as looking up for corresponding ID in self.id_map, and replace the |
| 559 old index with the mapped index. |
| 560 """ |
| 561 self.schema_nodes = map(partial(self.ResolveID, 1), self.schema_nodes) |
| 562 self.property_nodes = map(partial(self.ResolveID, 1), self.property_nodes) |
| 563 self.properties_nodes = map(partial(self.ResolveID, 3), |
| 564 self.properties_nodes) |
| 519 | 565 |
| 520 def _WritePolicyConstantSource(policies, os, f): | 566 def _WritePolicyConstantSource(policies, os, f): |
| 521 f.write('#include "policy/policy_constants.h"\n' | 567 f.write('#include "policy/policy_constants.h"\n' |
| 522 '\n' | 568 '\n' |
| 523 '#include <algorithm>\n' | 569 '#include <algorithm>\n' |
| 524 '#include <climits>\n' | 570 '#include <climits>\n' |
| 525 '\n' | 571 '\n' |
| 526 '#include "base/logging.h"\n' | 572 '#include "base/logging.h"\n' |
| 527 '#include "components/policy/core/common/schema_internal.h"\n' | 573 '#include "components/policy/core/common/schema_internal.h"\n' |
| 528 '\n' | 574 '\n' |
| (...skipping 22 matching lines...) Expand all Loading... |
| 551 for policy in policies: | 597 for policy in policies: |
| 552 if policy.is_supported: | 598 if policy.is_supported: |
| 553 f.write(' { %-14s %-16s %3s, %24s },\n' % ( | 599 f.write(' { %-14s %-16s %3s, %24s },\n' % ( |
| 554 'true,' if policy.is_deprecated else 'false,', | 600 'true,' if policy.is_deprecated else 'false,', |
| 555 'true,' if policy.is_device_only else 'false,', | 601 'true,' if policy.is_device_only else 'false,', |
| 556 policy.id, | 602 policy.id, |
| 557 policy.max_size)) | 603 policy.max_size)) |
| 558 f.write('};\n\n') | 604 f.write('};\n\n') |
| 559 | 605 |
| 560 schema_generator = SchemaNodesGenerator(shared_strings) | 606 schema_generator = SchemaNodesGenerator(shared_strings) |
| 561 schema_generator.Generate(chrome_schema, 'root node') | 607 schema_generator.GenerateAndCollectID(chrome_schema, 'root node') |
| 608 schema_generator.ResolveReferences() |
| 562 schema_generator.Write(f) | 609 schema_generator.Write(f) |
| 563 | 610 |
| 564 f.write('bool CompareKeys(const internal::PropertyNode& node,\n' | 611 f.write('bool CompareKeys(const internal::PropertyNode& node,\n' |
| 565 ' const std::string& key) {\n' | 612 ' const std::string& key) {\n' |
| 566 ' return node.key < key;\n' | 613 ' return node.key < key;\n' |
| 567 '}\n\n') | 614 '}\n\n') |
| 568 | 615 |
| 569 f.write('} // namespace\n\n') | 616 f.write('} // namespace\n\n') |
| 570 | 617 |
| 571 if os == 'win': | 618 if os == 'win': |
| (...skipping 298 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 870 def _WriteCloudPolicyDecoder(policies, os, f): | 917 def _WriteCloudPolicyDecoder(policies, os, f): |
| 871 f.write(CPP_HEAD) | 918 f.write(CPP_HEAD) |
| 872 for policy in policies: | 919 for policy in policies: |
| 873 if policy.is_supported and not policy.is_device_only: | 920 if policy.is_supported and not policy.is_device_only: |
| 874 _WritePolicyCode(f, policy) | 921 _WritePolicyCode(f, policy) |
| 875 f.write(CPP_FOOT) | 922 f.write(CPP_FOOT) |
| 876 | 923 |
| 877 | 924 |
| 878 if __name__ == '__main__': | 925 if __name__ == '__main__': |
| 879 sys.exit(main()) | 926 sys.exit(main()) |
| OLD | NEW |