comparison grease/component/field.py @ 5:bc88f7d5ca8b

Added base files for grease
author KarstenBock@gmx.net
date Tue, 12 Jul 2011 10:16:48 +0200
parents
children bf165b30254f
comparison
equal deleted inserted replaced
4:bf1dd9c24a7e 5:bc88f7d5ca8b
1 #############################################################################
2 #
3 # Copyright (c) 2010 by Casey Duncan and contributors
4 # All Rights Reserved.
5 #
6 # This software is subject to the provisions of the MIT License
7 # A copy of the license should accompany this distribution.
8 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
9 # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
10 # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
11 #
12 #############################################################################
13
14 __version__ = '$Id$'
15
16 import operator
17 from grease.geometry import Vec2d, Vec2dArray, Rect
18 from grease import color
19
20 # Allowed field types -> default values
21 types = {int:lambda: 0,
22 float:lambda: 0.0,
23 bool:lambda: False,
24 str:lambda:"",
25 object:lambda:None,
26 Vec2d:lambda: Vec2d(0,0),
27 Vec2dArray:lambda: Vec2dArray(),
28 color.RGBA: lambda: color.RGBA(0.0, 0.0, 0.0, 0.0),
29 Rect: lambda: Rect(0.0, 0.0, 0.0, 0.0)}
30
31 class Schema(dict):
32 """Field schema definition for custom components"""
33
34 def __init__(self, **fields):
35 for ftype in fields.values():
36 assert ftype in types, fname + " has an illegal field type"
37 self.update(fields)
38
39
40 class FieldAccessor(object):
41 """Facade for manipulating a field for a set of entities"""
42
43 __field = None
44 __entities = None
45 __attrs = None
46 __getter = None
47 __parent_getters = ()
48
49 def __init__(self, field, entities, attrs=()):
50 self.__field = field
51 self.__entities = entities
52 field_getter = operator.attrgetter(field.name)
53 self.__attrs = attrs
54 if attrs:
55 getters = [field_getter] + [operator.attrgetter(attr) for attr in attrs]
56 def get(entity):
57 value = entity
58 for getter in getters:
59 value = getter(value)
60 return value
61 self.__getter = get
62 self.__parent_getters = getters[:-1]
63 else:
64 self.__getter = field_getter
65
66 def __getattr__(self, name):
67 """Return a FieldAccessor for the child attribute"""
68 return self.__class__(self.__field, self.__entities, self.__attrs + (name,))
69
70 def __setattr__(self, name, value):
71 if value is self:
72 return # returned by mutators
73 if hasattr(self.__class__, name):
74 # Set local attr
75 self.__dict__[name] = value
76 elif not name.startswith('_'):
77 getattr(self, name).__set__(value)
78 else:
79 raise AttributeError("Cannot set field attribute: %s" % name)
80
81 @property
82 def __setter(self):
83 """Return the proper setter function for setting the field value"""
84 if not self.__attrs:
85 return setattr
86 else:
87 parent_getters = self.__parent_getters
88 def setter(data, name, value):
89 for getter in parent_getters:
90 data = getter(data)
91 setattr(data, name, value)
92 self.__setter = setter
93 return setter
94
95 def __set__(self, value):
96 """Set field values en masse"""
97 # Mass set field attr
98 setter = self.__setter
99 component = self.__field.component
100 if self.__attrs:
101 name = self.__attrs[-1]
102 else:
103 name = self.__field.name
104 if isinstance(value, FieldAccessor):
105 # Join set between two entity sets
106 if not self.__attrs:
107 cast = self.__field.cast
108 else:
109 cast = lambda x: x
110 for entity in self.__entities:
111 try:
112 setter(component[entity], name, cast(value[entity]))
113 except KeyError:
114 pass
115 else:
116 if not self.__attrs:
117 value = self.__field.cast(value)
118 for entity in self.__entities:
119 try:
120 setter(component[entity], name, value)
121 except KeyError:
122 pass
123
124 def __getitem__(self, entity):
125 """Return the field value for a single entity (used for joins)"""
126 if entity in self.__entities:
127 return self.__getter(self.__field.component[entity])
128 raise KeyError(entity)
129
130 def __contains__(self, entity):
131 return entity in self.__entities
132
133 def __repr__(self):
134 return '<%s %s @ %x>' % (
135 self.__class__.__name__,
136 '.'.join((self.__field.name,) + self.__attrs), id(self))
137
138 def __nonzero__(self):
139 return bool(self.__entities)
140
141 def __iter__(self):
142 """Return an iterator of all field values in the set"""
143 component = self.__field.component
144 getter = self.__getter
145 for entity in self.__entities:
146 try:
147 data = component[entity]
148 except KeyError:
149 continue
150 yield getter(data)
151
152 ## batch comparison operators ##
153
154 def __match(self, value, op):
155 component = self.__field.component
156 getter = self.__getter
157 matches = set()
158 add = matches.add
159 if isinstance(value, FieldAccessor):
160 # Join match between entity sets
161 for entity in self.__entities:
162 try:
163 data = component[entity]
164 other = value[entity]
165 except KeyError:
166 continue
167 if op(getter(data), other):
168 add(entity)
169 else:
170 for entity in self.__entities:
171 try:
172 data = component[entity]
173 except KeyError:
174 continue
175 if op(getter(data), value):
176 add(entity)
177 return matches
178
179 def __eq__(self, value):
180 """Return an entity set of all entities with a matching field value"""
181 return self.__match(value, operator.eq)
182
183 def __ne__(self, value):
184 """Return an entity set of all entities not matching field value"""
185 return self.__match(value, operator.ne)
186
187 def __gt__(self, value):
188 """Return an entity set of all entities with a greater field value"""
189 return self.__match(value, operator.gt)
190
191 def __ge__(self, value):
192 """Return an entity set of all entities with a greater or equal field value"""
193 return self.__match(value, operator.ge)
194
195 def __lt__(self, value):
196 """Return an entity set of all entities with a lesser field value"""
197 return self.__match(value, operator.lt)
198
199 def __le__(self, value):
200 """Return an entity set of all entities with a lesser or equal field value"""
201 return self.__match(value, operator.le)
202
203 def _contains(self, values):
204 """Return an entity set of all entities with a field value contained in values"""
205 return self.__match(values, operator.contains)
206
207 ## Batch in-place mutator methods
208
209 def __mutate(self, value, op):
210 component = self.__field.component
211 if self.__attrs:
212 name = self.__attrs[-1]
213 else:
214 name = self.__field.name
215 getter = self.__getter
216 setter = self.__setter
217 if isinstance(value, FieldAccessor):
218 # Join between entity sets
219 for entity in self.__entities:
220 try:
221 data = component[entity]
222 other = value[entity]
223 except KeyError:
224 continue
225 setter(data, name, op(getter(data), other))
226 else:
227 for entity in self.__entities:
228 try:
229 data = component[entity]
230 except KeyError:
231 continue
232 setter(data, name, op(getter(data), value))
233 return self
234
235 def __iadd__(self, value):
236 return self.__mutate(value, operator.iadd)
237
238 def __isub__(self, value):
239 return self.__mutate(value, operator.isub)
240
241 def __imul__(self, value):
242 return self.__mutate(value, operator.imul)
243
244 def __idiv__(self, value):
245 return self.__mutate(value, operator.idiv)
246
247 def __itruediv__(self, value):
248 return self.__mutate(value, operator.itruediv)
249
250 def __ifloordiv__(self, value):
251 return self.__mutate(value, operator.ifloordiv)
252
253 def __imod__(self, value):
254 return self.__mutate(value, operator.imod)
255
256 def __ipow__(self, value):
257 return self.__mutate(value, operator.ipow)
258
259 def __ilshift__(self, value):
260 return self.__mutate(value, operator.ilshift)
261
262 def __irshift__(self, value):
263 return self.__mutate(value, operator.irshift)
264
265 def __iand__(self, value):
266 return self.__mutate(value, operator.iand)
267
268 def __ior__(self, value):
269 return self.__mutate(value, operator.ior)
270
271 def __ixor__(self, value):
272 return self.__mutate(value, operator.ixor)
273
274
275 class Field(object):
276 """Component field metadata and accessor interface"""
277
278 def __init__(self, component, name, type, accessor_factory=FieldAccessor):
279 self.component = component
280 self.name = name
281 self.type = type
282 self.default = types.get(type)
283 self.accessor_factory = accessor_factory
284
285 def cast(self, value):
286 """Cast value to the appropriate type for thi field"""
287 if self.type is not object:
288 return self.type(value)
289 else:
290 return value
291
292 def accessor(self, entities=None):
293 """Return the field accessor for the entities in the component,
294 or all entities in the set specified that are also in the component
295 """
296 if entities is None or entities is self.component.entities:
297 entities = self.component.entities
298 else:
299 entities = entities & self.component.entities
300 return self.accessor_factory(self, entities)
301