Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(35)

Side by Side Diff: components/policy/tools/generate_policy_source.py

Issue 205923004: Add regex support in policy schema (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@json-schema-regex
Patch Set: Created 6 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « components/policy/policy_common.gypi ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
(...skipping 266 matching lines...) Expand 10 before | Expand all | Expand 10 after
277 'boolean': None, 277 'boolean': None,
278 'integer': None, 278 'integer': None,
279 'null': None, 279 'null': None,
280 'number': None, 280 'number': None,
281 'string': None, 281 'string': None,
282 } 282 }
283 self.stringlist_type = None 283 self.stringlist_type = None
284 self.ranges = {} 284 self.ranges = {}
285 285
286 def GetString(self, s): 286 def GetString(self, s):
287 return self.shared_strings[s] if s in self.shared_strings else '"%s"' % s 287 if s in self.shared_strings:
288 return self.shared_strings[s]
289 # Generate JSON escaped string, which is slightly different from desired
290 # C/C++ escaped string. Known differences includes unicode escaping format.
291 return json.dumps(s)
288 292
289 def AppendSchema(self, type, extra, comment=''): 293 def AppendSchema(self, type, extra, comment=''):
290 index = len(self.schema_nodes) 294 index = len(self.schema_nodes)
291 self.schema_nodes.append((type, extra, comment)) 295 self.schema_nodes.append((type, extra, comment))
292 return index 296 return index
293 297
294 def AppendRestriction(self, first, second): 298 def AppendRestriction(self, first, second):
295 r = (str(first), str(second)) 299 r = (str(first), str(second))
296 if not r in self.ranges: 300 if not r in self.ranges:
297 self.ranges[r] = len(self.restriction_nodes) 301 self.ranges[r] = len(self.restriction_nodes)
(...skipping 10 matching lines...) Expand all
308 312
309 def GetStringList(self): 313 def GetStringList(self):
310 if self.stringlist_type == None: 314 if self.stringlist_type == None:
311 self.stringlist_type = self.AppendSchema( 315 self.stringlist_type = self.AppendSchema(
312 'TYPE_LIST', 316 'TYPE_LIST',
313 self.GetSimpleType('string'), 317 self.GetSimpleType('string'),
314 'simple type: stringlist') 318 'simple type: stringlist')
315 return self.stringlist_type 319 return self.stringlist_type
316 320
317 def SchemaHaveRestriction(self, schema): 321 def SchemaHaveRestriction(self, schema):
318 return 'minimum' in schema or 'maximum' in schema or 'enum' in schema 322 return any(keyword in schema for keyword in
323 ['minimum', 'maximum', 'enum', 'pattern'])
319 324
320 def IsConsecutiveInterval(self, seq): 325 def IsConsecutiveInterval(self, seq):
321 sortedSeq = sorted(seq) 326 sortedSeq = sorted(seq)
322 return all(sortedSeq[i] + 1 == sortedSeq[i + 1] 327 return all(sortedSeq[i] + 1 == sortedSeq[i + 1]
323 for i in xrange(len(sortedSeq) - 1)) 328 for i in xrange(len(sortedSeq) - 1))
324 329
325 def GetEnumIntegerType(self, schema, name): 330 def GetEnumIntegerType(self, schema, name):
326 assert all(type(x) == int for x in schema['enum']) 331 assert all(type(x) == int for x in schema['enum'])
327 possible_values = schema['enum'] 332 possible_values = schema['enum']
328 if self.IsConsecutiveInterval(possible_values): 333 if self.IsConsecutiveInterval(possible_values):
(...skipping 19 matching lines...) Expand all
348 def GetEnumType(self, schema, name): 353 def GetEnumType(self, schema, name):
349 if len(schema['enum']) == 0: 354 if len(schema['enum']) == 0:
350 raise RuntimeError('Empty enumeration in %s' % name) 355 raise RuntimeError('Empty enumeration in %s' % name)
351 elif schema['type'] == 'integer': 356 elif schema['type'] == 'integer':
352 return self.GetEnumIntegerType(schema, name) 357 return self.GetEnumIntegerType(schema, name)
353 elif schema['type'] == 'string': 358 elif schema['type'] == 'string':
354 return self.GetEnumStringType(schema, name) 359 return self.GetEnumStringType(schema, name)
355 else: 360 else:
356 raise RuntimeError('Unknown enumeration type in %s' % name) 361 raise RuntimeError('Unknown enumeration type in %s' % name)
357 362
363 def GetPatternType(self, schema, name):
364 if schema['type'] != 'string':
365 raise RuntimeError('Unknown pattern type in %s' % name)
366 pattern = schema['pattern']
367 # Try to compile the pattern to validate it, note that the syntax used
368 # here might be slightly different from re2.
369 # TODO(binjin): Add a python wrapper of re2 and use it here.
370 re.compile(pattern)
371 index = len(self.string_enums);
372 self.string_enums.append(pattern);
373 return self.AppendSchema('TYPE_STRING',
374 self.AppendRestriction(index, index),
375 'string with pattern restriction: %s' % name);
376
358 def GetRangedType(self, schema, name): 377 def GetRangedType(self, schema, name):
359 if schema['type'] != 'integer': 378 if schema['type'] != 'integer':
360 raise RuntimeError('Unknown ranged type in %s' % name) 379 raise RuntimeError('Unknown ranged type in %s' % name)
361 min_value_set, max_value_set = False, False 380 min_value_set, max_value_set = False, False
362 if 'minimum' in schema: 381 if 'minimum' in schema:
363 min_value = int(schema['minimum']) 382 min_value = int(schema['minimum'])
364 min_value_set = True 383 min_value_set = True
365 if 'maximum' in schema: 384 if 'maximum' in schema:
366 max_value = int(schema['minimum']) 385 max_value = int(schema['minimum'])
367 max_value_set = True 386 max_value_set = True
(...skipping 10 matching lines...) Expand all
378 """Generates the structs for the given schema. 397 """Generates the structs for the given schema.
379 398
380 |schema|: a valid JSON schema in a dictionary. 399 |schema|: a valid JSON schema in a dictionary.
381 |name|: the name of the current node, for the generated comments.""" 400 |name|: the name of the current node, for the generated comments."""
382 if schema['type'] in self.simple_types: 401 if schema['type'] in self.simple_types:
383 if not self.SchemaHaveRestriction(schema): 402 if not self.SchemaHaveRestriction(schema):
384 # Simple types use shared nodes. 403 # Simple types use shared nodes.
385 return self.GetSimpleType(schema['type']) 404 return self.GetSimpleType(schema['type'])
386 elif 'enum' in schema: 405 elif 'enum' in schema:
387 return self.GetEnumType(schema, name) 406 return self.GetEnumType(schema, name)
407 elif 'pattern' in schema:
408 return self.GetPatternType(schema, name)
388 else: 409 else:
389 return self.GetRangedType(schema, name) 410 return self.GetRangedType(schema, name)
390 411
391 if schema['type'] == 'array': 412 if schema['type'] == 'array':
392 # Special case for lists of strings, which is a common policy type. 413 # Special case for lists of strings, which is a common policy type.
393 if schema['items']['type'] == 'string': 414 if schema['items']['type'] == 'string':
394 return self.GetStringList() 415 return self.GetStringList()
395 return self.AppendSchema( 416 return self.AppendSchema(
396 'TYPE_LIST', 417 'TYPE_LIST',
397 self.Generate(schema['items'], 'items of ' + name)) 418 self.Generate(schema['items'], 'items of ' + name))
398 elif schema['type'] == 'object': 419 elif schema['type'] == 'object':
399 # Reserve an index first, so that dictionaries come before their 420 # Reserve an index first, so that dictionaries come before their
400 # properties. This makes sure that the root node is the first in the 421 # properties. This makes sure that the root node is the first in the
401 # SchemaNodes array. 422 # SchemaNodes array.
402 index = self.AppendSchema('TYPE_DICTIONARY', -1) 423 index = self.AppendSchema('TYPE_DICTIONARY', -1)
403 424
404 if 'additionalProperties' in schema: 425 if 'additionalProperties' in schema:
405 additionalProperties = self.Generate( 426 additionalProperties = self.Generate(
406 schema['additionalProperties'], 427 schema['additionalProperties'],
407 'additionalProperties of ' + name) 428 'additionalProperties of ' + name)
408 else: 429 else:
409 additionalProperties = -1 430 additionalProperties = -1
410 431
411 # Properties must be sorted by name, for the binary search lookup. 432 # Properties must be sorted by name, for the binary search lookup.
412 # Note that |properties| must be evaluated immediately, so that all the 433 # Note that |properties| must be evaluated immediately, so that all the
413 # recursive calls to Generate() append the necessary child nodes; if 434 # recursive calls to Generate() append the necessary child nodes; if
414 # |properties| were a generator then this wouldn't work. 435 # |properties| were a generator then this wouldn't work.
415 sorted_properties = sorted(schema.get('properties', {}).items()) 436 sorted_properties = sorted(schema.get('properties', {}).items())
416 properties = [ (self.GetString(key), self.Generate(schema, key)) 437 properties = [ (self.GetString(key), self.Generate(subschema, key))
417 for key, schema in sorted_properties ] 438 for key, subschema in sorted_properties ]
439
440 pattern_properties = []
441 for pattern, subschema in schema.get('patternProperties', {}).items():
442 pattern_properties.append((self.GetString(pattern),
443 self.Generate(subschema, pattern)));
444
418 begin = len(self.property_nodes) 445 begin = len(self.property_nodes)
419 self.property_nodes += properties 446 self.property_nodes += properties
420 end = len(self.property_nodes) 447 end = len(self.property_nodes)
448 self.property_nodes += pattern_properties
449 pattern_end = len(self.property_nodes)
450
421 if index == 0: 451 if index == 0:
422 self.root_properties_begin = begin 452 self.root_properties_begin = begin
423 self.root_properties_end = end 453 self.root_properties_end = end
424 454
425 extra = len(self.properties_nodes) 455 extra = len(self.properties_nodes)
426 self.properties_nodes.append((begin, end, additionalProperties, name)) 456 self.properties_nodes.append((begin, end, pattern_end,
457 additionalProperties, name))
427 458
428 # Set the right data at |index| now. 459 # Set the right data at |index| now.
429 self.schema_nodes[index] = ('TYPE_DICTIONARY', extra, name) 460 self.schema_nodes[index] = ('TYPE_DICTIONARY', extra, name)
430 return index 461 return index
431 else: 462 else:
432 assert False 463 assert False
433 464
434 def Write(self, f): 465 def Write(self, f):
435 """Writes the generated structs to the given file. 466 """Writes the generated structs to the given file.
436 467
437 |f| an open file to write to.""" 468 |f| an open file to write to."""
438 f.write('const internal::SchemaNode kSchemas[] = {\n' 469 f.write('const internal::SchemaNode kSchemas[] = {\n'
439 '// Type Extra\n') 470 '// Type Extra\n')
440 for type, extra, comment in self.schema_nodes: 471 for type, extra, comment in self.schema_nodes:
441 type += ',' 472 type += ','
442 f.write(' { base::Value::%-18s %3d }, // %s\n' % (type, extra, comment)) 473 f.write(' { base::Value::%-18s %3d }, // %s\n' % (type, extra, comment))
443 f.write('};\n\n') 474 f.write('};\n\n')
444 475
445 if self.property_nodes: 476 if self.property_nodes:
446 f.write('const internal::PropertyNode kPropertyNodes[] = {\n' 477 f.write('const internal::PropertyNode kPropertyNodes[] = {\n'
447 '// Property #Schema\n') 478 '// Property #Schema\n')
448 for key, schema in self.property_nodes: 479 for key, schema in self.property_nodes:
449 key += ',' 480 key += ','
450 f.write(' { %-50s %6d },\n' % (key, schema)) 481 f.write(' { %-50s %6d },\n' % (key, schema))
451 f.write('};\n\n') 482 f.write('};\n\n')
452 483
453 if self.properties_nodes: 484 if self.properties_nodes:
454 f.write('const internal::PropertiesNode kProperties[] = {\n' 485 f.write('const internal::PropertiesNode kProperties[] = {\n'
455 '// Begin End Additional Properties\n') 486 '// Begin End PatternEnd Additional Properties\n')
456 for node in self.properties_nodes: 487 for node in self.properties_nodes:
457 f.write(' { %5d, %5d, %5d }, // %s\n' % node) 488 f.write(' { %5d, %5d, %10d, %5d }, // %s\n' % node)
458 f.write('};\n\n') 489 f.write('};\n\n')
459 490
460 if self.restriction_nodes: 491 if self.restriction_nodes:
461 f.write('const internal::RestrictionNode kRestrictionNodes[] = {\n') 492 f.write('const internal::RestrictionNode kRestrictionNodes[] = {\n')
462 f.write('// FIRST, SECOND\n') 493 f.write('// FIRST, SECOND\n')
463 for first, second in self.restriction_nodes: 494 for first, second in self.restriction_nodes:
464 f.write(' {{ %-8s %4s}},\n' % (first + ',', second)) 495 f.write(' {{ %-8s %4s}},\n' % (first + ',', second))
465 f.write('};\n\n') 496 f.write('};\n\n')
466 497
467 if self.int_enums: 498 if self.int_enums:
(...skipping 371 matching lines...) Expand 10 before | Expand all | Expand 10 after
839 def _WriteCloudPolicyDecoder(policies, os, f): 870 def _WriteCloudPolicyDecoder(policies, os, f):
840 f.write(CPP_HEAD) 871 f.write(CPP_HEAD)
841 for policy in policies: 872 for policy in policies:
842 if policy.is_supported and not policy.is_device_only: 873 if policy.is_supported and not policy.is_device_only:
843 _WritePolicyCode(f, policy) 874 _WritePolicyCode(f, policy)
844 f.write(CPP_FOOT) 875 f.write(CPP_FOOT)
845 876
846 877
847 if __name__ == '__main__': 878 if __name__ == '__main__':
848 sys.exit(main()) 879 sys.exit(main())
OLDNEW
« no previous file with comments | « components/policy/policy_common.gypi ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698