27
|
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
|