| #!/usr/bin/python | |
| # Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file | |
| # for details. All rights reserved. Use of this source code is governed by a | |
| # BSD-style license that can be found in the LICENSE file. | |
| """Module to manage IDL files.""" | |
| import copy | |
| import pickle | |
| import logging | |
| import os | |
| import os.path | |
| import shutil | |
| import idlnode | |
| import idlparser | |
| import idlrenderer | |
| _logger = logging.getLogger('database') | |
| class Database(object): | |
| """The Database class manages a collection of IDL files stored | |
| inside a directory. | |
| Each IDL is describing a single interface. The IDL files are written in the | |
| FremontCut syntax, which is derived from the Web IDL syntax and includes | |
| annotations. | |
| Database operations include adding, updating and removing IDL files. | |
| """ | |
| def __init__(self, root_dir): | |
| """Initializes a Database over a given directory. | |
| Args: | |
| root_dir -- a directory. If directory does not exist, it will | |
| be created. | |
| """ | |
| self._root_dir = root_dir | |
| if not os.path.exists(root_dir): | |
| _logger.debug('creating root directory %s' % root_dir) | |
| os.makedirs(root_dir) | |
| self._all_interfaces = {} | |
| self._interfaces_to_delete = [] | |
| self._idlparser = idlparser.IDLParser(idlparser.FREMONTCUT_SYNTAX) | |
| def Clone(self): | |
| new_database = Database(self._root_dir) | |
| new_database._all_interfaces = copy.deepcopy(self._all_interfaces) | |
| new_database._interfaces_to_delete = copy.deepcopy( | |
| self._interfaces_to_delete) | |
| return new_database | |
| def Delete(self): | |
| """Deletes the database by deleting its directory""" | |
| if os.path.exists(self._root_dir): | |
| shutil.rmtree(self._root_dir) | |
| # reset in-memory constructs | |
| self._all_interfaces = {} | |
| def _ScanForInterfaces(self): | |
| """Iteratores over the database files and lists all interface names. | |
| Return: | |
| A list of interface names. | |
| """ | |
| res = [] | |
| def Visitor(_, dirname, names): | |
| for name in names: | |
| if os.path.isfile(os.path.join(dirname, name)): | |
| root, ext = os.path.splitext(name) | |
| if ext == '.idl': | |
| res.append(root) | |
| os.path.walk(self._root_dir, Visitor, None) | |
| return res | |
| def _FilePath(self, interface_name): | |
| """Calculates the file path that a given interface should | |
| be saved to. | |
| Args: | |
| interface_name -- the name of the interface. | |
| """ | |
| return os.path.join(self._root_dir, '%s.idl' % interface_name) | |
| def _LoadInterfaceFile(self, interface_name): | |
| """Loads an interface from the database. | |
| Returns: | |
| An IDLInterface instance or None if the interface is not found. | |
| Args: | |
| interface_name -- the name of the interface. | |
| """ | |
| file_name = self._FilePath(interface_name) | |
| _logger.info('loading %s' % file_name) | |
| if not os.path.exists(file_name): | |
| return None | |
| f = open(file_name, 'r') | |
| content = f.read() | |
| f.close() | |
| # Parse file: | |
| idl_file = idlnode.IDLFile(self._idlparser.parse(content), file_name) | |
| if not idl_file.interfaces: | |
| raise RuntimeError('No interface found in %s' % file_name) | |
| elif len(idl_file.interfaces) > 1: | |
| raise RuntimeError('Expected one interface in %s' % file_name) | |
| interface = idl_file.interfaces[0] | |
| self._all_interfaces[interface_name] = interface | |
| return interface | |
| def Load(self): | |
| """Loads all interfaces into memory. | |
| """ | |
| # FIXME: Speed this up by multi-threading. | |
| for (interface_name) in self._ScanForInterfaces(): | |
| self._LoadInterfaceFile(interface_name) | |
| self.Cache() | |
| def Cache(self): | |
| """Serialize the database using pickle for faster startup in the future | |
| """ | |
| output_file = open(os.path.join(self._root_dir, 'cache.pickle'), 'wb') | |
| pickle.dump(self._all_interfaces, output_file) | |
| pickle.dump(self._interfaces_to_delete, output_file) | |
| def LoadFromCache(self): | |
| """Deserialize the database using pickle for fast startup | |
| """ | |
| input_file_name = os.path.join(self._root_dir, 'cache.pickle') | |
| if not os.path.isfile(input_file_name): | |
| self.Load() | |
| return | |
| input_file = open(input_file_name, 'rb') | |
| self._all_interfaces = pickle.load(input_file) | |
| self._interfaces_to_delete = pickle.load(input_file) | |
| input_file.close() | |
| def Save(self): | |
| """Saves all in-memory interfaces into files.""" | |
| for interface in self._all_interfaces.values(): | |
| self._SaveInterfaceFile(interface) | |
| for interface_name in self._interfaces_to_delete: | |
| self._DeleteInterfaceFile(interface_name) | |
| def _SaveInterfaceFile(self, interface): | |
| """Saves an interface into the database. | |
| Args: | |
| interface -- an IDLInterface instance. | |
| """ | |
| interface_name = interface.id | |
| # Actual saving | |
| file_path = self._FilePath(interface_name) | |
| _logger.debug('writing %s' % file_path) | |
| dir_name = os.path.dirname(file_path) | |
| if not os.path.exists(dir_name): | |
| _logger.debug('creating directory %s' % dir_name) | |
| os.mkdir(dir_name) | |
| # Render the IDLInterface object into text. | |
| text = idlrenderer.render(interface) | |
| f = open(file_path, 'w') | |
| f.write(text) | |
| f.close() | |
| def HasInterface(self, interface_name): | |
| """Returns True if the interface is in memory""" | |
| return interface_name in self._all_interfaces | |
| def GetInterface(self, interface_name): | |
| """Returns an IDLInterface corresponding to the interface_name | |
| from memory. | |
| Args: | |
| interface_name -- the name of the interface. | |
| """ | |
| if interface_name not in self._all_interfaces: | |
| raise RuntimeError('Interface %s is not loaded' % interface_name) | |
| return self._all_interfaces[interface_name] | |
| def AddInterface(self, interface): | |
| """Returns an IDLInterface corresponding to the interface_name | |
| from memory. | |
| Args: | |
| interface -- the name of the interface. | |
| """ | |
| interface_name = interface.id | |
| if interface_name in self._all_interfaces: | |
| raise RuntimeError('Interface %s already exists' % interface_name) | |
| self._all_interfaces[interface_name] = interface | |
| def GetInterfaces(self): | |
| """Returns a list of all loaded interfaces.""" | |
| res = [] | |
| for _, interface in sorted(self._all_interfaces.items()): | |
| res.append(interface) | |
| return res | |
| def DeleteInterface(self, interface_name): | |
| """Deletes an interface from the database. File is deleted when | |
| Save() is called. | |
| Args: | |
| interface_name -- the name of the interface. | |
| """ | |
| if interface_name not in self._all_interfaces: | |
| raise RuntimeError('Interface %s not found' % interface_name) | |
| self._interfaces_to_delete.append(interface_name) | |
| del self._all_interfaces[interface_name] | |
| def _DeleteInterfaceFile(self, interface_name): | |
| """Actual file deletion""" | |
| file_path = self._FilePath(interface_name) | |
| if os.path.exists(file_path): | |
| _logger.debug('deleting %s' % file_path) | |
| os.remove(file_path) |