comparison bGrease/component/field.py @ 41:ff3e395abf91

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 grease/component/field.py@bf165b30254f
children e856b604b650
comparison
equal deleted inserted replaced
40:2e3ab06a2f47 41:ff3e395abf91
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)