Mercurial > parpg-source
view grease/component/field.py @ 13:bf165b30254f
Added dict and list as possible component field types
author | KarstenBock@gmx.net |
---|---|
date | Sun, 31 Jul 2011 21:53:38 +0200 |
parents | bc88f7d5ca8b |
children |
line wrap: on
line source
############################################################################# # # Copyright (c) 2010 by Casey Duncan and contributors # All Rights Reserved. # # This software is subject to the provisions of the MIT License # A copy of the license should accompany this distribution. # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. # ############################################################################# __version__ = '$Id$' import operator from grease.geometry import Vec2d, Vec2dArray, Rect from grease import color # Allowed field types -> default values types = {int:lambda: 0, float:lambda: 0.0, bool:lambda: False, str:lambda:"", object:lambda:None, Vec2d:lambda: Vec2d(0,0), Vec2dArray:lambda: Vec2dArray(), color.RGBA: lambda: color.RGBA(0.0, 0.0, 0.0, 0.0), Rect: lambda: Rect(0.0, 0.0, 0.0, 0.0), list: lambda: list(), dict: lambda: dict(), } class Schema(dict): """Field schema definition for custom components""" def __init__(self, **fields): for ftype in fields.values(): assert ftype in types, fname + " has an illegal field type" self.update(fields) class FieldAccessor(object): """Facade for manipulating a field for a set of entities""" __field = None __entities = None __attrs = None __getter = None __parent_getters = () def __init__(self, field, entities, attrs=()): self.__field = field self.__entities = entities field_getter = operator.attrgetter(field.name) self.__attrs = attrs if attrs: getters = [field_getter] + [operator.attrgetter(attr) for attr in attrs] def get(entity): value = entity for getter in getters: value = getter(value) return value self.__getter = get self.__parent_getters = getters[:-1] else: self.__getter = field_getter def __getattr__(self, name): """Return a FieldAccessor for the child attribute""" return self.__class__(self.__field, self.__entities, self.__attrs + (name,)) def __setattr__(self, name, value): if value is self: return # returned by mutators if hasattr(self.__class__, name): # Set local attr self.__dict__[name] = value elif not name.startswith('_'): getattr(self, name).__set__(value) else: raise AttributeError("Cannot set field attribute: %s" % name) @property def __setter(self): """Return the proper setter function for setting the field value""" if not self.__attrs: return setattr else: parent_getters = self.__parent_getters def setter(data, name, value): for getter in parent_getters: data = getter(data) setattr(data, name, value) self.__setter = setter return setter def __set__(self, value): """Set field values en masse""" # Mass set field attr setter = self.__setter component = self.__field.component if self.__attrs: name = self.__attrs[-1] else: name = self.__field.name if isinstance(value, FieldAccessor): # Join set between two entity sets if not self.__attrs: cast = self.__field.cast else: cast = lambda x: x for entity in self.__entities: try: setter(component[entity], name, cast(value[entity])) except KeyError: pass else: if not self.__attrs: value = self.__field.cast(value) for entity in self.__entities: try: setter(component[entity], name, value) except KeyError: pass def __getitem__(self, entity): """Return the field value for a single entity (used for joins)""" if entity in self.__entities: return self.__getter(self.__field.component[entity]) raise KeyError(entity) def __contains__(self, entity): return entity in self.__entities def __repr__(self): return '<%s %s @ %x>' % ( self.__class__.__name__, '.'.join((self.__field.name,) + self.__attrs), id(self)) def __nonzero__(self): return bool(self.__entities) def __iter__(self): """Return an iterator of all field values in the set""" component = self.__field.component getter = self.__getter for entity in self.__entities: try: data = component[entity] except KeyError: continue yield getter(data) ## batch comparison operators ## def __match(self, value, op): component = self.__field.component getter = self.__getter matches = set() add = matches.add if isinstance(value, FieldAccessor): # Join match between entity sets for entity in self.__entities: try: data = component[entity] other = value[entity] except KeyError: continue if op(getter(data), other): add(entity) else: for entity in self.__entities: try: data = component[entity] except KeyError: continue if op(getter(data), value): add(entity) return matches def __eq__(self, value): """Return an entity set of all entities with a matching field value""" return self.__match(value, operator.eq) def __ne__(self, value): """Return an entity set of all entities not matching field value""" return self.__match(value, operator.ne) def __gt__(self, value): """Return an entity set of all entities with a greater field value""" return self.__match(value, operator.gt) def __ge__(self, value): """Return an entity set of all entities with a greater or equal field value""" return self.__match(value, operator.ge) def __lt__(self, value): """Return an entity set of all entities with a lesser field value""" return self.__match(value, operator.lt) def __le__(self, value): """Return an entity set of all entities with a lesser or equal field value""" return self.__match(value, operator.le) def _contains(self, values): """Return an entity set of all entities with a field value contained in values""" return self.__match(values, operator.contains) ## Batch in-place mutator methods def __mutate(self, value, op): component = self.__field.component if self.__attrs: name = self.__attrs[-1] else: name = self.__field.name getter = self.__getter setter = self.__setter if isinstance(value, FieldAccessor): # Join between entity sets for entity in self.__entities: try: data = component[entity] other = value[entity] except KeyError: continue setter(data, name, op(getter(data), other)) else: for entity in self.__entities: try: data = component[entity] except KeyError: continue setter(data, name, op(getter(data), value)) return self def __iadd__(self, value): return self.__mutate(value, operator.iadd) def __isub__(self, value): return self.__mutate(value, operator.isub) def __imul__(self, value): return self.__mutate(value, operator.imul) def __idiv__(self, value): return self.__mutate(value, operator.idiv) def __itruediv__(self, value): return self.__mutate(value, operator.itruediv) def __ifloordiv__(self, value): return self.__mutate(value, operator.ifloordiv) def __imod__(self, value): return self.__mutate(value, operator.imod) def __ipow__(self, value): return self.__mutate(value, operator.ipow) def __ilshift__(self, value): return self.__mutate(value, operator.ilshift) def __irshift__(self, value): return self.__mutate(value, operator.irshift) def __iand__(self, value): return self.__mutate(value, operator.iand) def __ior__(self, value): return self.__mutate(value, operator.ior) def __ixor__(self, value): return self.__mutate(value, operator.ixor) class Field(object): """Component field metadata and accessor interface""" def __init__(self, component, name, type, accessor_factory=FieldAccessor): self.component = component self.name = name self.type = type self.default = types.get(type) self.accessor_factory = accessor_factory def cast(self, value): """Cast value to the appropriate type for thi field""" if self.type is not object: return self.type(value) else: return value def accessor(self, entities=None): """Return the field accessor for the entities in the component, or all entities in the set specified that are also in the component """ if entities is None or entities is self.component.entities: entities = self.component.entities else: entities = entities & self.component.entities return self.accessor_factory(self, entities)