1# Copyright (c) 2012 The Chromium Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5import os
6import re
7import sys
8
9import idl_schema
10import json_schema
11from cpp_namespace_environment import CppNamespaceEnvironment
12from model import Model, UnixName
13
14def GenerateFilenames(full_namespace):
15  # Try to find the file defining the namespace. Eg. for
16  # nameSpace.sub_name_space.Type' the following heuristics looks for:
17  # 1. name_space_sub_name_space.json,
18  # 2. name_space_sub_name_space.idl,
19  # 3. sub_name_space.json,
20  # 4. sub_name_space.idl,
21  # 5. etc.
22  sub_namespaces = full_namespace.split('.')
23  filenames = [ ]
24  basename = None
25  for namespace in reversed(sub_namespaces):
26    if basename is not None:
27      basename = UnixName(namespace + '.' + basename)
28    else:
29      basename = UnixName(namespace)
30    for ext in ['json', 'idl']:
31      filenames.append('%s.%s' % (basename, ext))
32  return filenames
33
34class SchemaLoader(object):
35  '''Resolves a type name into the namespace the type belongs to.
36
37  Properties:
38  - |root| path to the root directory.
39  - |path| path to the directory with the API header files, relative to the
40    root.
41  - |include_rules| List containing tuples with (path, cpp_namespace_pattern)
42    used when searching for types.
43  - |cpp_namespace_pattern| Default namespace pattern
44  '''
45  def __init__(self,
46               root,
47               path,
48               include_rules,
49               cpp_namespace_pattern):
50    self._root = root
51    self._include_rules = [(path, cpp_namespace_pattern)]
52    self._include_rules.extend(include_rules)
53
54  def ResolveNamespace(self, full_namespace):
55    filenames = GenerateFilenames(full_namespace)
56    for path, cpp_namespace in self._include_rules:
57      for filename in reversed(filenames):
58        filepath = os.path.join(path, filename);
59        if os.path.exists(os.path.join(self._root, filepath)):
60          return Model().AddNamespace(
61              self.LoadSchema(filepath)[0],
62              filepath,
63              environment=CppNamespaceEnvironment(cpp_namespace))
64    return None
65
66  def ResolveType(self, full_name, default_namespace):
67    name_parts = full_name.rsplit('.', 1)
68    if len(name_parts) == 1:
69      if full_name not in default_namespace.types:
70        return None
71      return default_namespace
72    full_namespace, type_name = full_name.rsplit('.', 1)
73    namespace = self.ResolveNamespace(full_namespace)
74    if namespace and type_name in namespace.types:
75      return namespace
76    return None
77
78  def LoadSchema(self, schema):
79    '''Load a schema definition. The schema parameter must be a file name
80    with the full path relative to the root.'''
81    schema_filename, schema_extension = os.path.splitext(schema)
82
83    schema_path = os.path.join(self._root, schema)
84    if schema_extension == '.json':
85      api_defs = json_schema.Load(schema_path)
86    elif schema_extension == '.idl':
87      api_defs = idl_schema.Load(schema_path)
88    else:
89      sys.exit('Did not recognize file extension %s for schema %s' %
90               (schema_extension, schema))
91
92    return api_defs
93