from peak.api import * from peak.model.features import FeatureClass from peak.binding.components import ComponentName # ----------- # New types # ----------- # XXX - This should probably be a user-defined type, but I don't know which, # if any, interfaces I should make it have.. so this is me being lazy. # I'm sure there are C API functions that will happily mutate this! class ImmutableDict(dict): def __init__(self, *args, **kwargs): dict.__init__(self, *args, **kwargs) self._cache = tuple(self.items()) def __hash__(self): return hash(self._cache) def __delitem__(self, item): raise TypeError, "object doesn't support item deletion" def __setitem__(self, item, value): raise TypeError, "object doesn't support item assignment" def pop(self, k, d=None): raise TypeError, "object doesn't support pop" def popitem(self): raise TypeError, "object doesn't support popitem" def update(self, other): raise TypeError, "object doesn't support update" # ---------- # Features # ---------- # XXX - I'm sure I should be extending some interface or something # for these two, because I'm adding meaning. class NewFeatureClass(FeatureClass): def keyTypeObject(self): """The actual type referred to by 'referencedKeyType' Since a feature's 'referencedKeyType' can be either a string or a type, the actual type object is cached in the 'keyTypeObject' attribute. If you need to get the key type of feature 'aFeature', just refer to 'aFeature.keyTypeObject'. This will of course fail if the 'referencedKeyType' attribute is invalid.""" rt = adapt(self.referencedKeyType,ComponentName,None) if rt is not None: return rt.findComponent(self) return self.referencedKeyType keyTypeObject = binding.Make(keyTypeObject) keyFromString = binding.Obtain('keyTypeObject/mdl_fromString') keyToString = binding.Obtain('keyTypeObject/mdl_toString', default=str) keyFromFields = binding.Obtain('keyTypeObject/mdl_fromFields') normalizeKey = binding.Obtain('keyTypeObject/mdl_normalizeKey', default=lambda x:x) class NewStructuralFeature(model.StructuralFeature): __metaclass__ = NewFeatureClass isMapping = False referencedKeyType = None newVerbs = model.StructuralFeature.newVerbs + Items( getForKey = 'get%(singularName.initCap)sForKey', setForKey = 'set%(singularName.initCap)sForKey', removeForKey = 'remove%(singularName.initCap)sForKey', getForKeyWithDefault = 'get%(singularName.initCap)sForKeyWithDefault', ) def get(f): if f.isDerived or not f.isMapping: return model.StructuralFeature.methodTemplates['get'](f) if f.isChangeable: def get(feature, element): try: return feature._doGet(element) except AttributeError: return {} else: def get(feature, element): try: return feature._doGet(element) except AttributeError: return ImmutableDict() return get get.isTemplate = True def set(f): if not (f.isChangeable or f.isMapping): set = model.StructuralFeature.methodTemplates['set'](f) else: def set(feature, element, val): feature.unset(element) add = featureLink.notifyLink for k, v in val.iteritems(): add(element, v, k) return set set.isTemplate = True def unset(f): if not (f.isChangeable or f.isMapping): unset = model.StructuralFeature.methodTemplates['unset'](f) if f.isOrdered: def unset(feature, element): d = feature.get(element) items = d.items() items.reverse() remove = feature._notifyUnlink for posn,item in items: remove(element, item, posn) feature._doDel(element) else: def unset(feature, element): d = feature.get(element) items = d.items() remove = feature._notifyUnlink for posn,item in items: remove(element, item, posn) feature._doDel(element) return unset unset.isTemplate = True def replace(f): if not f.isMapping: # not a template return model.StructuralFeature.methodTemplates['replace'] return None replace.isTemplate = True def add(f): if not f.isMapping: return model.StructuralFeature.methodTemplates['add'](f) return None add.isTemplate = True def remove(f): if not f.isMapping: return model.StructuralFeature.methodTemplates['remove'](f) return None remove.isTemplate = True def insertBefore(f): if not f.isMapping: # not a template return model.StructuralFeature.methodTemplates['insertBefore'] return None insertBefore.isTemplate = True def _setup(f): if f.isChangeable or not f.isMapping: # not a template! _setup = model.StructuralFeature.methodTemplates['_setup'] else: def _setup(feature, element, value): doLink = feature._onLink normalize = feature.normalize normalizeKey = feature.normalizeKey normalized = [ (normalizeKey(_k), normalize(_v)) for (_k, _v) in value.iteritems() ] value = ImmutableDict(normalized) for k,v in normalized: doLink(element, v, k) feature._doSet(element, value) return _setup _setup.verb = '_setup' _setup.isTemplate = True def _link(f): if not (f.isChangeable or f.isMapping): _link = model.StructuralFeature.methodTemplates['_link'](f) else: def _link(feature, element, item, posn=None): ub = feature.upperBound d = feature.get(element) if ub and len(d) >= ub: raise ValueError("Too many items") item = feature.normalize(item) posn = feature.normalizeKey(posn) feature._onLink(element, item, posn) feature._doSet(element, d) d[posn] = item return item return _link _link.verb = '_link' _link.isTemplate = True def _unlink(f): if not (f.isChangeable or f.isMapping): _unlink = model.StructuralFeature.methodTemplates['_unlink'](f) else: def _unlink(feature, element, item, posn=None): posn = feature.normalizeKey(posn) feature._onUnlink(element, item, posn) d = feature.get(element) feature._doSet(element, d) del d[posn] return _unlink _unlink.verb = '_unlink' _unlink.isTemplate = True def getForKey(feature, element, key): return feature._doGet(element)[feature.normalizeKey(key)] getForKey.installIf = lambda f,m: ( f.isMapping ) def getForKeyWithDefault(feature, element, key, default=NOT_FOUND): try: return feature._doGet(element).get(feature.normalizeKey(key), default) except AttributeError: return default getForKeyWithDefault.installIf = lambda f,m: ( f.isMapping ) def setForKey(feature, element, key, value): old = feature.getForKeyWithDefault(element, key, NOT_FOUND) if old is not NOT_FOUND: feature._notifyUnlink(element, old, key) feature._notifyLink(element, value, key) else: feature._notifyLink(element, value, key) setForKey.installIf = lambda f,m: ( f.isMapping and f.isChangeable ) def removeForKey(f): return f.methodTemplates['_notifyUnlink'](f) removeForKey.installIf = lambda f,m: ( f.isChangeable ) removeForKey.isTemplate = True # ---------------- # New Data Types # ---------------- class Mapping(NewStructuralFeature): isMapping = True class OrderedMapping(Mapping): isOrdered = True # ------------------- # Abstract Elements # ------------------- class NamedElement(model.Element): class name(model.Attribute): referencedType = model.String class shortName(model.Attribute): referencedType = model.String class abbreviation(model.Attribute): referencedType = model.String