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