|  | #!/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) |