Mercurial > parpg-source
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 |