from ModelObject import ModelObject
from Model import Model, ModelError
from Klass import Klass
from Attr import Attr
from MiscUtils import NoDefault
from MiscUtils.DataTable import DataTable
from MiscUtils.DictForArgs import *
from UserDict import UserDict
try:
True, False
except NameError:
True, False = 1, 0
class Klasses(ModelObject, UserDict):
"""A Klasses object can read a list of class specifications in a spreadsheet (.csv).
Note that Klasses inherits UserDict, allowing you to access class specifications by name.
"""
def __init__(self, model):
UserDict.__init__(self)
assert isinstance(model, Model)
self._model = model
self._klasses = []
self._filename = None
self._name = None
self._tableHeadings = None
self.initTypeMap()
def classNames(self):
return ['ModelObject', 'Klasses', 'Klass', 'Attr', 'BasicTypeAttr', 'ObjRefAttr', 'EnumAttr', 'DateTimeAttr']
def initTypeMap(self):
"""Initialize the type map.
Initializes self._typeNamesToAttrClassNames which maps MiddleKit type
names (like int and enum) to the name of the attribute class that would
implement them. Mapping to class names rather than actual classes is key,
because in __init__, a different set of attribute classes can be passed in.
"""
map = {}
names = 'bool int long float string enum date time list ObjRef decimal'
names = names.split()
for name in names:
map[name] = name.capitalize()+'Attr'
map['datetime'] = 'DateTimeAttr'
self._typeNamesToAttrClassNames = map
def assignClassIds(self, generator):
if self.setting('UseHashForClassIds', False):
from sets import Set
allIds = Set()
for klass in self._model._allKlassesInOrder:
klass.setId(allIds)
else:
id = 1
for klass in self._model._allKlassesInOrder:
klass.setId(id)
id += 1
def model(self):
return self._model
def filename(self):
return self._filename
def klassesInOrder(self):
"""Return a list of all the Klasses in the order they were declared.
Do not modify the list.
"""
return self._klasses
def read(self, filename):
self._filename = filename
table = DataTable(filename, usePickleCache=0)
self._tableHeadings = table.headings()
try:
line = 2
for row in table:
row = ExpandDictWithExtras(row, dictForArgs=PyDictForArgs)
for key in ['Class', 'Attribute']:
if not row.has_key(key):
print 'ERROR'
print 'Required key %s not found in row:' % key
print 'row:', row
print 'keys:', row.keys()
print row[key]
if row['Class']:
pyClass = self._model.coreClass('Klass')
klass = pyClass(self, row)
self.addKlass(klass)
else:
name = row['Attribute']
if name and name[0] != '#' and name[-1] != ':':
pyClassName = self.pyClassNameForAttrDict(row)
pyClass = self._model.coreClass(pyClassName)
klass.addAttr(pyClass(row))
line += 1
except ModelError, e:
e.setLine(line)
raise
def awakeFromRead(self, model):
"""Perform further initialization.
Expected to be invoked by the model.
"""
assert self._model is model
for klass in self._klasses:
supername = klass.supername()
if supername != 'MiddleObject':
klass.setSuperklass(self.model().klass(supername))
for klass in self._klasses:
klass.awakeFromRead(self)
def __getstate__(self):
"""For pickling purposes, the back reference to the model that owns self is removed."""
assert self._model
attrs = self.__dict__.copy()
del attrs['_model']
return attrs
def addKlass(self, klass):
"""Add a class definition.
Restrictions: Cannot add two classes with the same name.
"""
name = klass.name()
assert not self.has_key(name), 'Already have %s.' % name
self._klasses.append(klass)
self[klass.name()] = klass
klass.setKlasses(self)
def pyClassNameForAttrDict(self, dict):
"""Return class for attribute definition.
Given a raw attribute definition (in the form of a dictionary),
this method returns the name of the Python class that should be
instantiated for it. This method relies primarily on dict['Type'].
"""
typeName = dict['Type']
if not typeName:
if dict['Attribute']:
raise ModelError("no type specified for attribute '%s'" % dict['Attribute'])
else:
raise ModelError('type specifier missing')
if typeName.lower().startswith('list '):
typeName = 'list'
try:
return self._typeNamesToAttrClassNames[typeName]
except KeyError:
return 'ObjRefAttr'
def setting(self, name, default=NoDefault):
"""Return the value of a particular configuration setting taken from the model."""
return self._model.setting(name, default)
def dump(self):
"""Print each class."""
for klass in self._klasses:
print klass
def debugString(self):
return '<%s 0x%x model=%r>' % (self.__class__.__name__, id(self), getattr(self, '_model', '(none)'))