blob: 4c9ef0947e6c94581031c649d1f91ad0d4a79e96 [file] [log] [blame]
#!/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)