|
1 # |
|
2 # Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). |
|
3 # All rights reserved. |
|
4 # This component and the accompanying materials are made available |
|
5 # under the terms of "Eclipse Public License v1.0" |
|
6 # which accompanies this distribution, and is available |
|
7 # at the URL "http://www.eclipse.org/legal/epl-v10.html". |
|
8 # |
|
9 # Initial Contributors: |
|
10 # Nokia Corporation - initial contribution. |
|
11 # |
|
12 # Contributors: |
|
13 # |
|
14 # Description: |
|
15 # |
|
16 """ |
|
17 Cone public API. |
|
18 The core interface to the ConE functionality. |
|
19 """ |
|
20 |
|
21 import os |
|
22 import re |
|
23 import sys |
|
24 import logging |
|
25 import copy |
|
26 import sets |
|
27 |
|
28 import exceptions, utils, container, mapping |
|
29 |
|
30 class Base(container.ObjectContainer): |
|
31 """ |
|
32 The Base class is intended for capturing same kind of naming scheme. |
|
33 """ |
|
34 |
|
35 |
|
36 def __init__(self, ref="", **kwargs): |
|
37 if len(utils.dottedref.split_ref(ref)) > 1: |
|
38 raise exceptions.InvalidRef("Invalid reference for Base object %s!" % ref) |
|
39 self.ref = ref |
|
40 container.ObjectContainer.__init__(self, ref) |
|
41 for arg in kwargs.keys(): |
|
42 if kwargs.get(arg) != None: |
|
43 setattr(self, arg, kwargs.get(arg)) |
|
44 |
|
45 def __repr__(self): |
|
46 dict = self._dict() |
|
47 return "%s(%s)" % (self.__class__.__name__, dict) |
|
48 |
|
49 def _get_mapper(self,modelname): |
|
50 """ |
|
51 Return a instance of appropriate mapper for given model. |
|
52 """ |
|
53 return mapping.BaseMapper() |
|
54 |
|
55 def _compare(self, other, dict_keys=None): |
|
56 """ |
|
57 Compare the attributes of elements |
|
58 """ |
|
59 if isinstance(other, Base): |
|
60 keys = dict_keys or self._dict().keys() |
|
61 for key in keys: |
|
62 self_attr = None |
|
63 other_attr = None |
|
64 try: |
|
65 self_attr = getattr(self, key) |
|
66 other_attr = getattr(other, key) |
|
67 except AttributeError: |
|
68 # If the attribute is not found from either elements |
|
69 # ignore it entirely |
|
70 if self_attr == None and other_attr == None: |
|
71 continue |
|
72 if self_attr != other_attr: |
|
73 return False |
|
74 # If all given keys match report this as as similar element |
|
75 return True |
|
76 else: |
|
77 return False |
|
78 |
|
79 def _clone(self, **kwargs): |
|
80 """ |
|
81 A generic implementation for cloning the object. |
|
82 Copies all (public) members in dictionary. |
|
83 To clone objects recursively set the recursion level with recursion param. |
|
84 @param recursion: Boolean to define recursion on or off |
|
85 @param recursion_depth: positive integer to define recursion depth. default is -1 which will |
|
86 perform recursion to all objects. |
|
87 """ |
|
88 dict = self._dict() |
|
89 if kwargs.get('class_instance'): |
|
90 class_instance = kwargs.get('class_instance') |
|
91 del kwargs['class_instance'] |
|
92 else: |
|
93 class_instance = self.__class__ |
|
94 obj = class_instance(**dict) |
|
95 # Remove all children created at the construction phase |
|
96 # This is needed when the recursion adds children to the object so that there are not duplicates |
|
97 obj._order = [] |
|
98 obj._children = {} |
|
99 |
|
100 # handle the recursion argument |
|
101 recursion = kwargs.get('recursion', False) |
|
102 if recursion: |
|
103 recursion_depth = kwargs.get('recursion_depth', -1) |
|
104 if recursion_depth < 0 or recursion_depth > 0: |
|
105 # decrease the recursion |
|
106 kwargs['recursion_depth'] = recursion_depth - 1 |
|
107 for child in self._objects(): |
|
108 obj._add(child._clone(**kwargs), container.APPEND) |
|
109 return obj |
|
110 |
|
111 def _dict(self): |
|
112 """ |
|
113 Return the public variables in a dictionary |
|
114 """ |
|
115 dict = {} |
|
116 for key in self.__dict__.keys(): |
|
117 if key.startswith('_'): |
|
118 continue |
|
119 else: |
|
120 dict[key] = self.__dict__[key] |
|
121 return dict |
|
122 |
|
123 def _default_object(self, name): |
|
124 return Base(name) |
|
125 |
|
126 @property |
|
127 def fqr(self): |
|
128 """ |
|
129 Return a Fully Qualified Ref, which is the full name of the reference. |
|
130 Joins the namespace and ref to one string. |
|
131 @return: A string |
|
132 """ |
|
133 return utils.dottedref.join_refs([self.namespace, self.get_ref()]) |
|
134 |
|
135 @property |
|
136 def namespace(self): |
|
137 """ |
|
138 @return: The namespace of the object. |
|
139 """ |
|
140 containerpath = "" |
|
141 path = "" |
|
142 parentcontainer = self.find_parent(container=True) |
|
143 parent = self.find_parent(type=Base) |
|
144 paths = [] |
|
145 while parent and parent != parentcontainer: |
|
146 """ Skip the element if it is supposed to be hidden. Begins with _. """ |
|
147 if not parent.get_ref().startswith('_'): |
|
148 paths.append(parent.get_ref()) |
|
149 parent = parent._get_parent() |
|
150 if parentcontainer: |
|
151 paths.append(parentcontainer.namespace) |
|
152 paths.reverse() |
|
153 return utils.dottedref.join_refs(paths) |
|
154 |
|
155 def get_fullref(self): |
|
156 """ |
|
157 Return a full reference, reference including a |
|
158 possible index of the object in list. |
|
159 e.g. ref can be bar[1] or just the normal bar. |
|
160 |
|
161 @return: The full reference of the object. |
|
162 """ |
|
163 if self.parent and utils.is_list(self.parent._get(self.ref)): |
|
164 return "%s[%s]" % (self.ref, self.get_index()) |
|
165 else: |
|
166 return self.ref |
|
167 |
|
168 def get_fullfqr(self): |
|
169 """ |
|
170 Return a full reference, reference including a |
|
171 possible index of the object in list. |
|
172 ref and adds index. |
|
173 @return: A string |
|
174 """ |
|
175 return utils.dottedref.join_refs([self.get_fullnamespace(), self.get_fullref()]) |
|
176 |
|
177 def get_fullnamespace(self): |
|
178 """ |
|
179 @return: The full namespace of the object with possible indexes of the parent objects |
|
180 """ |
|
181 containerpath = "" |
|
182 path = "" |
|
183 parentcontainer = self.find_parent(container=True) |
|
184 parent = self.find_parent(type=Base) |
|
185 paths = [] |
|
186 while parent and parent != parentcontainer: |
|
187 paths.append(parent.get_fullref()) |
|
188 parent = parent.parent |
|
189 if parentcontainer: |
|
190 paths.append(parentcontainer.namespace) |
|
191 paths.reverse() |
|
192 return utils.dottedref.join_refs(paths) |
|
193 |
|
194 def get_storage(self): |
|
195 """ |
|
196 Get the root storage from the root object. |
|
197 """ |
|
198 if self._find_parent(): |
|
199 return self._find_parent().get_storage() |
|
200 else: |
|
201 raise exceptions.StorageException("Storage is not found from root!") |
|
202 |
|
203 def get_project(self): |
|
204 """ |
|
205 Get the root project from the root object. |
|
206 """ |
|
207 if isinstance(self, Project): |
|
208 return self |
|
209 elif self._find_parent(): |
|
210 return self._find_parent().get_project() |
|
211 else: |
|
212 raise exceptions.NotFound("Project not found!!") |
|
213 |
|
214 def get_default_view(self): |
|
215 """ |
|
216 Get the default view from the root object. |
|
217 """ |
|
218 try: |
|
219 return self._find_parent().get_default_view() |
|
220 except exceptions.NotFound: |
|
221 raise exceptions.NotFound("Default View is not found! No root configuration?") |
|
222 |
|
223 def get_root(self): |
|
224 """ |
|
225 Get the root object |
|
226 """ |
|
227 try: |
|
228 return self._find_parent().get_root() |
|
229 except exceptions.NotFound: |
|
230 return self |
|
231 |
|
232 def get_root_configuration(self): |
|
233 """ |
|
234 Get the root object |
|
235 """ |
|
236 if self.find_parent(type=Configuration): |
|
237 return self.find_parent(type=Configuration).get_root_configuration() |
|
238 elif isinstance(self, Configuration): |
|
239 return self |
|
240 else: |
|
241 return None |
|
242 |
|
243 def get_index(self): |
|
244 """ |
|
245 @return : the index of the data element for sequential data defined inside the same configuration. |
|
246 0 for normal data. |
|
247 """ |
|
248 # Get the list of items from parent which contains this element and ask my own index |
|
249 # Make sure that the returned element is a list with get_list |
|
250 selflist = utils.get_list(self._get_parent()._get(self.get_ref())) |
|
251 return selflist.index(self) |
|
252 |
|
253 def find_parent(self, **kwargs): |
|
254 """ |
|
255 find the closest parent object of given type. |
|
256 e.g. find_parent(type=Configuration) returns the closest parent |
|
257 Configuration parent instance |
|
258 @param type: class definitiob |
|
259 """ |
|
260 type = kwargs.get('type', None) |
|
261 container = kwargs.get('container', False) |
|
262 try: |
|
263 parent = self._find_parent() |
|
264 if type and isinstance(parent, type): |
|
265 return parent |
|
266 elif container and hasattr(parent, 'container'): |
|
267 return parent |
|
268 else: |
|
269 return parent.find_parent(**kwargs) |
|
270 except exceptions.NotFound: |
|
271 return None |
|
272 |
|
273 def add(self, child, policy=container.REPLACE): |
|
274 """ |
|
275 A generic add function to add child objects. The function is intended to act as |
|
276 proxy function that call the correct add function based on the child objects class. |
|
277 |
|
278 Example: obj.add(Feature("test")), actually obj.add_feature(Feature("test")) |
|
279 @param child: the child object to add |
|
280 @raise IncorrectClassError: if the given class cannot be added to this object. |
|
281 """ |
|
282 raise exceptions.NotSupportedException("Cannot add %s object to %s" % (child, self)) |
|
283 |
|
284 def get_elem(self, fqr): |
|
285 """ |
|
286 A generic get function to get child objects and members. The function uses getattr |
|
287 to traverse downwards the the object tree. The returned object is the final object or attribute |
|
288 if it is found. Raises AttributeError if the child is not found. |
|
289 |
|
290 Example: obj.get('test.bar'), returns child obj.test.bar |
|
291 @param fqr: the fully qualified ref to the object |
|
292 @raise AttributeError: if the given ref is not found. |
|
293 """ |
|
294 return None |
|
295 |
|
296 |
|
297 class Project(Base): |
|
298 """ |
|
299 A project is a container that can hold several Configuration objects. |
|
300 """ |
|
301 |
|
302 def __init__(self, storage, **kwargs): |
|
303 """ |
|
304 Project constructor |
|
305 """ |
|
306 Base.__init__(self, "") |
|
307 """ Try to set the model and tet the actual configuration class """ |
|
308 try: |
|
309 self._model = storage.persistentmodule.MODEL |
|
310 except AttributeError: |
|
311 self._model = None |
|
312 |
|
313 self.set_storage(storage) |
|
314 self.update() |
|
315 self.loaded = {} |
|
316 |
|
317 def __add_loaded__(self, ref, obj): |
|
318 """ |
|
319 Add the object to loaded |
|
320 """ |
|
321 self.loaded[ref] = {'counter': 0, 'obj': obj} |
|
322 |
|
323 def __get_loaded__(self, ref): |
|
324 """ |
|
325 Get a loaded object if it is existing and increase the reference counter |
|
326 @param ref: |
|
327 @return: The loaded object if it exists. None if it does not. |
|
328 """ |
|
329 if self.loaded.has_key(ref): |
|
330 return self.loaded[ref]['obj'] |
|
331 else: |
|
332 return None |
|
333 |
|
334 def __loaded__(self, ref): |
|
335 """ |
|
336 Get a loaded object if it is existing and increase the reference counter |
|
337 @param ref: |
|
338 @return: The loaded object if it exists. None if it does not. |
|
339 """ |
|
340 if self.loaded.has_key(ref): |
|
341 self.loaded[ref]['counter'] += 1 |
|
342 else: |
|
343 raise exceptions.NotFound("ref %s is not found from loaded!" % ref) |
|
344 |
|
345 def __unloaded__(self, ref): |
|
346 """ |
|
347 returns True when the reference count is zero and object can be released. |
|
348 """ |
|
349 if self.loaded.has_key(ref): |
|
350 self.loaded[ref]['counter'] -= 1 |
|
351 if self.loaded[ref]['counter'] == 0: |
|
352 del self.loaded[ref] |
|
353 return True |
|
354 else: |
|
355 return False |
|
356 else: |
|
357 return True |
|
358 |
|
359 def _supported_type(self, obj): |
|
360 if isinstance(obj, Configuration) \ |
|
361 or isinstance(obj, ConfigurationProxy): |
|
362 return True |
|
363 else: |
|
364 return False |
|
365 |
|
366 |
|
367 def update(self): |
|
368 """ |
|
369 update the root confml files as configurations |
|
370 """ |
|
371 root_confmls = self.get_storage().list_resources(".") |
|
372 root_confmls = utils.resourceref.filter_resources(root_confmls, "\.confml") |
|
373 for rootml in root_confmls: |
|
374 self._add(ConfigurationProxy(rootml)) |
|
375 |
|
376 def get_storage(self): |
|
377 """ |
|
378 Get the Storage instance of this Project. |
|
379 """ |
|
380 return self.storage |
|
381 |
|
382 def set_storage(self, storage): |
|
383 """ |
|
384 Set the Storage instance of this Project. |
|
385 """ |
|
386 if isinstance(storage, Storage): |
|
387 self.storage = storage |
|
388 else: |
|
389 raise exceptions.StorageException("The given storage is not a instance of Storage!") |
|
390 |
|
391 def list_configurations(self, filter_or_filters=None): |
|
392 """ |
|
393 List the direct child objects of the project (Root configurations) |
|
394 @param filter_or_filters: A regular expression or list of regular expressions |
|
395 used for filtering the configuration paths. If None, all configurations are |
|
396 returned. |
|
397 @return: a list for configuration file paths |
|
398 """ |
|
399 filters = None |
|
400 if isinstance(filter_or_filters, basestring): filters = [filter_or_filters] |
|
401 elif filter_or_filters is not None: filters = filter_or_filters |
|
402 |
|
403 configs = [obj.get_path() for obj in self._objects()] |
|
404 |
|
405 if filters is not None: |
|
406 result = [] |
|
407 for config in configs: |
|
408 for filter in filters: |
|
409 if re.match(filter, config) is not None: |
|
410 result.append(config) |
|
411 break |
|
412 return result |
|
413 else: |
|
414 return configs |
|
415 |
|
416 def list_all_configurations(self): |
|
417 """ |
|
418 List all configuration objects of the project (all configurations) |
|
419 @return: a list for configuration file paths |
|
420 """ |
|
421 return [obj.get_path() for obj in self._traverse(type=(Configuration, ConfigurationProxy))] |
|
422 |
|
423 def get_configuration(self, path): |
|
424 """ |
|
425 Get a configuration object from the given path |
|
426 @param path: path to configuration |
|
427 @return: a instance of Configuration. |
|
428 """ |
|
429 # Load the configuration object if it is not already loaded |
|
430 try: |
|
431 return self._get(utils.resourceref.to_objref(utils.resourceref.norm(path))) |
|
432 except exceptions.NotFound, e: |
|
433 if self.storage.is_resource(utils.resourceref.norm(path)): |
|
434 proxy = ConfigurationProxy(utils.resourceref.norm(path)) |
|
435 proxy._set_parent(self) |
|
436 return proxy |
|
437 else: |
|
438 raise e |
|
439 |
|
440 def is_configuration(self, path): |
|
441 """ |
|
442 Return true if the given path is a configuration object in this Project. |
|
443 @param path: path to configuration |
|
444 @return: Boolean return value. |
|
445 """ |
|
446 # Changed from list_all_configurations to list_configurations |
|
447 # (list_all_configurations causes a insane performance problem with _traverse) |
|
448 return path in self.list_configurations() |
|
449 |
|
450 def add_configuration(self, config): |
|
451 """ |
|
452 Add a Configuration object to this project |
|
453 """ |
|
454 if isinstance(config, Configuration): |
|
455 if self.is_configuration(config.get_path()): |
|
456 raise exceptions.AlreadyExists("%s" % config.get_path()) |
|
457 self._add(config) |
|
458 self.__add_loaded__(config.get_path(), config) |
|
459 self.__loaded__(config.get_path()) |
|
460 else: |
|
461 raise exceptions.IncorrectClassError("Only Configuration instance can be added to Project!") |
|
462 |
|
463 def create_configuration(self, path, namespace=""): |
|
464 """ |
|
465 Create a Configuration object to this project |
|
466 """ |
|
467 config = self.get_configuration_class()(utils.resourceref.norm(path), namespace=namespace) |
|
468 self.add_configuration(config) |
|
469 return config |
|
470 |
|
471 def remove_configuration(self, path): |
|
472 """ |
|
473 Remove a Configuration by its reference |
|
474 """ |
|
475 # remove configuration as an object and try to remove it from the storage |
|
476 self._remove(utils.resourceref.to_objref(path)) |
|
477 try: |
|
478 self.storage.delete_resource(path) |
|
479 except exceptions.NotSupportedException: |
|
480 pass |
|
481 return |
|
482 |
|
483 def import_configuration(self, configuration): |
|
484 """ |
|
485 Import a configuration object from another storage |
|
486 """ |
|
487 self.storage.import_resources(configuration.list_resources(), configuration.get_storage()) |
|
488 return |
|
489 |
|
490 def export_configuration(self, configuration, export_storage, empty_folders=False): |
|
491 """ |
|
492 Export a configuration object to another storage |
|
493 """ |
|
494 # First clone the configuration and then import the rest of the configuration resources |
|
495 if isinstance(configuration, ConfigurationProxy): |
|
496 configuration = configuration._get_obj() |
|
497 |
|
498 export_storage.unload(configuration.get_full_path(),configuration) |
|
499 for child in configuration._traverse(type=Configuration): |
|
500 export_storage.unload(child.get_full_path(),child) |
|
501 |
|
502 #If the configuration is not in the root of the project adding the path |
|
503 #to final exporting source path. |
|
504 #l = [] |
|
505 cpath = utils.resourceref.get_path(configuration.get_path()) |
|
506 resr = [utils.resourceref.join_refs([cpath,related]) \ |
|
507 for related in configuration.get_layer().list_all_related(empty_folders)] |
|
508 self.storage.export_resources(resr ,export_storage, empty_folders) |
|
509 return |
|
510 |
|
511 def get_configuration_class(self): |
|
512 """ |
|
513 return the default configuration class that is used with the model. |
|
514 """ |
|
515 return utils.get_class(self._model, Configuration) |
|
516 |
|
517 def save(self): |
|
518 """ |
|
519 Save the object to the permanent Storage object. Calls the save operation for |
|
520 all the children and also for the Storage. |
|
521 """ |
|
522 for child in self._objects(): |
|
523 if isinstance(child, (Configuration, ConfigurationProxy)): |
|
524 child.save() |
|
525 self.storage.save() |
|
526 |
|
527 def close(self): |
|
528 """ |
|
529 Close the Project. |
|
530 """ |
|
531 for child in self._objects(): |
|
532 if isinstance(child, (Configuration, ConfigurationProxy)): |
|
533 child.close() |
|
534 self.storage.close() |
|
535 |
|
536 def load(self, path): |
|
537 """ |
|
538 Load an object from a reference. The given reference is loaded once from storage |
|
539 and stored as a loaded object to the Project. Sequential loads to the same ref will |
|
540 return the same object. |
|
541 @param path: The reference where to load the object |
|
542 @raise StorageException: if the given object cannot be loaded as an |
|
543 object from this storage |
|
544 """ |
|
545 if not self.__get_loaded__(path): |
|
546 configuration = self.get_storage().load(path) |
|
547 if configuration.get_ref() == 'unknown': |
|
548 configuration.set_ref(utils.resourceref.to_dref(path)) |
|
549 self.__add_loaded__(path, configuration) |
|
550 """ increase the ref counter """ |
|
551 self.__loaded__(path) |
|
552 return self.__get_loaded__(path) |
|
553 |
|
554 def unload(self, path, object): |
|
555 """ |
|
556 Release the given ref, which decreases the reference counter of the given ref. |
|
557 @param path: The reference where to store the object |
|
558 @param object: The object instance to dump |
|
559 @raise StorageException: if the given object cannot be dumped to this storage |
|
560 """ |
|
561 if self.__unloaded__(path): |
|
562 self.get_storage().unload(path, object) |
|
563 |
|
564 def get_path(self): |
|
565 """ |
|
566 Return the path of the project, which is always root |
|
567 """ |
|
568 return "" |
|
569 |
|
570 |
|
571 class CompositeConfiguration(Base): |
|
572 """ |
|
573 A base class for composite Configuration objects. |
|
574 """ |
|
575 def __init__(self, ref="", **kwargs): |
|
576 # self.meta = {} |
|
577 # self.desc = "" |
|
578 super(CompositeConfiguration, self).__init__(ref, **kwargs) |
|
579 self.container = True |
|
580 |
|
581 def add_configuration(self, config): |
|
582 """ |
|
583 Add an existing Configuration to this configuration |
|
584 @param config: A Configuration instance: |
|
585 @return: None |
|
586 """ |
|
587 """ |
|
588 Merge the default view features from added config to this configs _default_view. |
|
589 """ |
|
590 self._add(config) |
|
591 |
|
592 def include_configuration(self, configref): |
|
593 """ |
|
594 Add an existing Configuration to this configuration by its resource reference |
|
595 @param config: A Configuration instance: |
|
596 @return: None |
|
597 """ |
|
598 # add the configuration load proxy to this configuration instead |
|
599 # adding the configuration directly |
|
600 self._add(ConfigurationProxy(configref)) |
|
601 |
|
602 def create_configuration(self, path): |
|
603 """ |
|
604 Create a new configuration by its name to the Configuration. |
|
605 1. Create new Configuration object |
|
606 2. Create new ConfigurationProxy |
|
607 3. Add proxy to this object |
|
608 4. Set proxy to point to the created Configuration object |
|
609 @param path: The reference of the configuration to create |
|
610 @return: The new configuration object. |
|
611 """ |
|
612 # normalise the path |
|
613 normpath = utils.resourceref.norm(path) |
|
614 cklass = self.get_configuration_class() |
|
615 conf = cklass(normpath, namespace=self.namespace) |
|
616 proxy = ConfigurationProxy(normpath) |
|
617 self.add_configuration(proxy) |
|
618 proxy._set_obj(conf) |
|
619 return conf |
|
620 |
|
621 def remove_configuration(self, path): |
|
622 """ |
|
623 Remove a Layer object from the Configuration by its reference. |
|
624 """ |
|
625 self._remove(utils.resourceref.to_objref(path)) |
|
626 |
|
627 def list_configurations(self): |
|
628 """ |
|
629 List all Layer objects in the Configuration |
|
630 @return: a copy array of layer references. |
|
631 """ |
|
632 return [config.get_path() for config in self._objects(type=(Configuration, ConfigurationProxy))] |
|
633 |
|
634 def list_all_configurations(self): |
|
635 """ |
|
636 List all Layer objects in the Configuration |
|
637 @return: a copy array of layer references. |
|
638 """ |
|
639 # TODO |
|
640 # huge performance problem |
|
641 return [config.get_path() for config in self._traverse(type=(Configuration, ConfigurationProxy))] |
|
642 |
|
643 def get_configuration(self, path): |
|
644 """ |
|
645 Get a Layer object by if path |
|
646 @return: a Layer object |
|
647 """ |
|
648 return self._get(utils.resourceref.to_objref(path)) |
|
649 |
|
650 def get_configuration_by_index(self, index): |
|
651 """ |
|
652 Get a Layer object by if indexing number |
|
653 @return: a Layer object |
|
654 """ |
|
655 configs = self._objects(type=(Configuration, ConfigurationProxy)) |
|
656 return configs[index] |
|
657 |
|
658 def get_last_configuration(self): |
|
659 """ |
|
660 Get the last Layer object from this configuration hierarchy. |
|
661 @return: a Layer object |
|
662 """ |
|
663 last_config = self |
|
664 try: |
|
665 last_config = last_config.get_configuration_by_index(-1) |
|
666 return last_config.get_last_configuration() |
|
667 except IndexError: |
|
668 return self |
|
669 |
|
670 def get_configuration_class(self): |
|
671 """ |
|
672 return the default configuration class retrieved from the project if it is found. |
|
673 Otherwise return cone.public.api.Configuration. |
|
674 """ |
|
675 try: |
|
676 return self.get_project().get_configuration_class() |
|
677 # catch the Parent/Project NotFound exception |
|
678 except exceptions.NotFound: |
|
679 return Configuration |
|
680 |
|
681 def add(self, child, policy=container.REPLACE): |
|
682 """ |
|
683 A generic add function to add child objects. The function is intended to act as |
|
684 proxy function that call the correct add function based on the child objects class. |
|
685 |
|
686 Example: obj.add(Feature("test")), actually obj.add_feature(Feature("test")) |
|
687 @param child: the child object to add |
|
688 @raise IncorrectClassError: if the given class cannot be added to this object. |
|
689 """ |
|
690 if isinstance(child, Configuration): |
|
691 self.add_configuration(child) |
|
692 elif isinstance(child, ConfigurationProxy): |
|
693 self.add_configuration(child) |
|
694 elif isinstance(child, Base): |
|
695 self._add(child) |
|
696 else: |
|
697 raise exceptions.IncorrectClassError("Cannot add %s to %s" % (child, self)) |
|
698 |
|
699 def layered_content(self, layers=None): |
|
700 """ |
|
701 fetch content from first to last and override content |
|
702 if it is found from a later layer |
|
703 Create an array of the layers based on the layer indexes. |
|
704 """ |
|
705 configuration_array = [] |
|
706 if layers == None: |
|
707 configuration_array = self.list_configurations() |
|
708 else: |
|
709 all = self.list_configurations() |
|
710 for i in layers: |
|
711 configuration_array.append(all[i]) |
|
712 |
|
713 content = container.DataContainer() |
|
714 for configuration_path in configuration_array: |
|
715 content_folder = self.get_configuration(configuration_path).get_layer().content_folder() |
|
716 content_path = content_folder.get_current_path() |
|
717 for content_file in content_folder.list_resources("", True): |
|
718 source_file = utils.resourceref.join_refs([content_path, content_file]) |
|
719 content.add_value(content_file, source_file) |
|
720 |
|
721 return content |
|
722 |
|
723 |
|
724 class Configuration(CompositeConfiguration): |
|
725 """ |
|
726 A Configuration is a container that can hold several Layer objects. |
|
727 """ |
|
728 |
|
729 def __init__(self, ref="", **kwargs): |
|
730 self.path = kwargs.get('path') or ref |
|
731 self.namespace = kwargs.get('namespace', '') |
|
732 self.name = utils.resourceref.to_objref(self.path) |
|
733 super(Configuration, self).__init__(utils.resourceref.to_objref(self.path)) |
|
734 self.container = True |
|
735 |
|
736 def _default_object(self, name): |
|
737 return Feature(name) |
|
738 |
|
739 def _supported_type(self, obj): |
|
740 if isinstance(obj, Configuration) \ |
|
741 or isinstance(obj, Feature) \ |
|
742 or isinstance(obj, Data) \ |
|
743 or isinstance(obj, ConfigurationProxy) \ |
|
744 or isinstance(obj, View) \ |
|
745 or isinstance(obj, Base): |
|
746 return True |
|
747 else: |
|
748 return False |
|
749 |
|
750 def _dict(self): |
|
751 """ |
|
752 Return the public variables in a dictionary |
|
753 """ |
|
754 dict = {} |
|
755 for key in self.__dict__.keys(): |
|
756 if key.startswith('_'): |
|
757 continue |
|
758 else: |
|
759 dict[key] = self.__dict__[key] |
|
760 dict['namespace'] = self.namespace |
|
761 return dict |
|
762 |
|
763 def get_name(self): |
|
764 """ |
|
765 Return the name of the configuration |
|
766 """ |
|
767 return self.name |
|
768 |
|
769 def set_name(self, name): |
|
770 """ |
|
771 Set the name |
|
772 """ |
|
773 self.name = name |
|
774 |
|
775 def get_path(self): |
|
776 """ |
|
777 Return the path of the configuration resource |
|
778 """ |
|
779 return self.path |
|
780 |
|
781 def set_path(self, path): |
|
782 """ |
|
783 Set the path of the configuration resource, and update the name and ref to correspond |
|
784 """ |
|
785 self.path = path |
|
786 #self.name = utils.resourceref.to_objref(self.path) |
|
787 self.set_ref(utils.resourceref.to_objref(self.path)) |
|
788 |
|
789 #@property |
|
790 def get_full_path(self): |
|
791 """ |
|
792 Return the path of the configuration resource |
|
793 """ |
|
794 try: |
|
795 parentconfig = self._find_parent(type=Configuration) |
|
796 parent_path = utils.resourceref.get_path(parentconfig.get_path()) |
|
797 except exceptions.NotFound: |
|
798 parent_path = "" |
|
799 |
|
800 return utils.resourceref.join_refs([parent_path, self.path]) |
|
801 |
|
802 def get_layer(self): |
|
803 """ |
|
804 Get the layer object where this Configuration is located. |
|
805 """ |
|
806 if not hasattr(self, "layer"): |
|
807 layerpath = utils.resourceref.get_path(self.get_path()) |
|
808 # hardcoded removal of confml folder from the layer path it is there |
|
809 layerpath = utils.resourceref.remove_end(layerpath, '/confml') |
|
810 self.layer = Layer(self.get_storage(), layerpath) |
|
811 """ Add the sublayers to this layer if they are different from this configuration """ |
|
812 for configpath in self.list_configurations(): |
|
813 sublayer_path = utils.resourceref.get_path(self.get_configuration(configpath).get_full_path()) |
|
814 sublayer_path = utils.resourceref.remove_end(sublayer_path, '/confml') |
|
815 if sublayer_path != utils.resourceref.get_path(self.get_path()): |
|
816 self.layer.add_layer(self.get_configuration(configpath).get_layer()) |
|
817 return self.layer |
|
818 |
|
819 def set_namespace(self, namespace): |
|
820 """ |
|
821 @param namespace: The new namespace of the object |
|
822 """ |
|
823 self._namespace = namespace |
|
824 #self.root.set_namespace(namespace) |
|
825 |
|
826 def get_namespace(self): |
|
827 """ |
|
828 @return: The reference of the object. |
|
829 """ |
|
830 return self._namespace |
|
831 |
|
832 def del_namespace(self): |
|
833 """ |
|
834 @return: The reference of the object. |
|
835 """ |
|
836 self._namespace = None |
|
837 namespace = property(get_namespace, set_namespace, del_namespace) |
|
838 |
|
839 def list_resources(self, empty_folders=False): |
|
840 """ |
|
841 List all resources used in this configuration |
|
842 """ |
|
843 """ |
|
844 1. First ensure that all configuration resource files are added |
|
845 2. Then add all layer resources |
|
846 3. Make the list distinct |
|
847 """ |
|
848 |
|
849 |
|
850 resources = [self.get_full_path()] |
|
851 for config in self._traverse(type=Configuration): |
|
852 resources.append(config.get_full_path()) |
|
853 layer = self.get_layer() |
|
854 for resref in layer.list_all_resources(empty_folders): |
|
855 resources.append(utils.resourceref.join_refs([layer.get_current_path(), resref])) |
|
856 |
|
857 return utils.distinct_array(resources) |
|
858 |
|
859 def get_resource(self, ref, mode="r"): |
|
860 """ |
|
861 Get the given resource as a Resource object. The resource is searched relative to the |
|
862 Configuration path, e.g. Configuration('test/foo/root.confml') => searches from 'test/foo'. |
|
863 @param ref: the reference path to the requested resource |
|
864 @return: a instance of Resource. |
|
865 """ |
|
866 mypath = utils.resourceref.get_path(self.path) |
|
867 myref = utils.resourceref.join_refs([mypath, ref]) |
|
868 return self.get_storage().open_resource(myref, mode) |
|
869 |
|
870 def get_all_resources(self): |
|
871 """ |
|
872 Get all resources in resource list of Resource objects |
|
873 """ |
|
874 resources = [] |
|
875 res_list = self.list_resources() |
|
876 for res in res_list: |
|
877 resources.append(self.get_storage().open_resource(res)) |
|
878 return resources |
|
879 |
|
880 def get_root_resource(self): |
|
881 """ |
|
882 Get the configuration reference resource. |
|
883 """ |
|
884 return self.get_storage().open_resource(self.get_path()) |
|
885 |
|
886 def get_feature(self, ref): |
|
887 """ |
|
888 Get a feature object by its reference. |
|
889 @param ref: The reference to the feature object. |
|
890 @return: A Feature object |
|
891 """ |
|
892 return self._get(ref) |
|
893 |
|
894 def add_feature(self, feature, namespace=""): |
|
895 """ |
|
896 Add a feature object to the configuration. |
|
897 @param feature: The Feature object to add. |
|
898 @param namespace: The sub namespace for the feature. |
|
899 e.g. to add fea2 under fea1 add_feature(fea2, 'fea1') |
|
900 @return: None |
|
901 """ |
|
902 self._add_to_path(namespace, feature) |
|
903 |
|
904 def remove_feature(self, ref): |
|
905 """ |
|
906 remove feature by its reference |
|
907 @param ref: |
|
908 """ |
|
909 self._remove(ref) |
|
910 |
|
911 def list_features(self): |
|
912 """ |
|
913 List immediate features found under the this configuration (the top nodes). |
|
914 The features are also available via the _default_view of the configuration. |
|
915 @return: a list of feature references. |
|
916 """ |
|
917 return [fea.get_ref() for fea in self._objects(type=Feature)] |
|
918 |
|
919 def list_all_features(self): |
|
920 """ |
|
921 List all features found under the this configuration. The features are also |
|
922 available via the _default_view of the configuration. |
|
923 @return: a list of feature references. |
|
924 """ |
|
925 return [fea.fqr for fea in self._traverse(type=Feature)] |
|
926 |
|
927 def add_data(self, data, policy=container.REPLACE): |
|
928 """ |
|
929 Add a data object to this configuration object. |
|
930 @param data: The Data object to add. |
|
931 @return: None |
|
932 """ |
|
933 if not self._has(data.attr): |
|
934 self._add(DataContainer(data.attr, container=True)) |
|
935 (namespace, name) = utils.dottedref.psplit_ref(data.get_fearef()) |
|
936 self._get(data.attr)._add_to_path(namespace, data, policy) |
|
937 |
|
938 def get_data(self, ref): |
|
939 """ |
|
940 Get a data object by its reference. |
|
941 @param ref: The reference to the data object. |
|
942 @return: A Data object |
|
943 """ |
|
944 return self.data._get(ref) |
|
945 |
|
946 def remove_data(self, ref): |
|
947 """ |
|
948 remove feature by its reference |
|
949 @param ref: |
|
950 """ |
|
951 self.data._remove(ref) |
|
952 |
|
953 def list_datas(self): |
|
954 """ |
|
955 List all datas found under the this configuration. |
|
956 @return: a list of Data references. |
|
957 """ |
|
958 if self._has('data'): |
|
959 return [dataelem.fqr for dataelem in self.data._objects(type=Data)] |
|
960 else: |
|
961 return [] |
|
962 |
|
963 def get_datas(self): |
|
964 """ |
|
965 List immediate datas found under the this configuration (the top nodes). |
|
966 @return: a list of Data references. |
|
967 """ |
|
968 if self._has('data'): |
|
969 return [dataelem for dataelem in self.data._objects(type=Data)] |
|
970 else: |
|
971 return [] |
|
972 |
|
973 def list_all_datas(self): |
|
974 """ |
|
975 List all Data elements found under the this configuration (or subconfigurations). |
|
976 @return: a list of Data references. |
|
977 """ |
|
978 return [dataelem.fqr for dataelem in self._traverse(type=Data)] |
|
979 |
|
980 def get_all_datas(self): |
|
981 """ |
|
982 List all Data elements found under the this configuration (or subconfigurations). |
|
983 @return: a list of Data references. |
|
984 """ |
|
985 return [dataelem for dataelem in self._traverse(type=Data)] |
|
986 |
|
987 def list_leaf_datas(self): |
|
988 """ |
|
989 List all leaf Data elements (i.e. actually modified settings) found under this configuration (or subconfigurations). |
|
990 @return: A list of Data references. |
|
991 """ |
|
992 return [dataelem.fqr for dataelem in self._find_leaves(type=Data)] |
|
993 |
|
994 def get_leaf_datas(self): |
|
995 """ |
|
996 Get all leaf Data elements (i.e. actually modified settings) found under this configuration (or subconfigurations). |
|
997 @return: A list of Data objects. |
|
998 """ |
|
999 return [dataelem for dataelem in self._find_leaves(type=Data)] |
|
1000 |
|
1001 def get_view(self, ref): |
|
1002 """ |
|
1003 Get a view object by its reference. |
|
1004 @param ref: The reference to the view object. |
|
1005 @return: A View object |
|
1006 """ |
|
1007 # Populate the view object before returning it |
|
1008 view = self._get(ref) |
|
1009 view.populate() |
|
1010 return view |
|
1011 |
|
1012 def add_view(self, viewname): |
|
1013 """ |
|
1014 Add a view object to the configuration. |
|
1015 @param viewname: The name of the view to add. |
|
1016 @return: None |
|
1017 """ |
|
1018 return self._add(View(viewname)) |
|
1019 |
|
1020 def remove_view(self, ref): |
|
1021 """ |
|
1022 Remove a view object from the configuration. |
|
1023 @param ref: The reference to the View. |
|
1024 @return: None |
|
1025 @raise NotFound: when view is not found. |
|
1026 """ |
|
1027 return self._remove(ref) |
|
1028 |
|
1029 def list_views(self): |
|
1030 """ |
|
1031 List all views found under the this configuration. |
|
1032 @return: a list of view references. |
|
1033 """ |
|
1034 return [view._path(self) for view in self._traverse(type=View)] |
|
1035 |
|
1036 def save(self): |
|
1037 """ |
|
1038 Save the object to the permanent Storage object. Calls the save operation of |
|
1039 all the children. |
|
1040 """ |
|
1041 for child in self._objects(): |
|
1042 if isinstance(child, (Configuration,ConfigurationProxy)): |
|
1043 child.save() |
|
1044 self.get_project().unload(self.get_full_path(), self) |
|
1045 |
|
1046 def close(self): |
|
1047 """ |
|
1048 Close the configuration |
|
1049 """ |
|
1050 for child in self._objects(): |
|
1051 if isinstance(child, (Configuration, ConfigurationProxy)): |
|
1052 child.close() |
|
1053 # if self.get_full_path() != "": |
|
1054 # self.get_project().unload(self.get_full_path(), self) |
|
1055 |
|
1056 def add(self, child, policy=container.REPLACE): |
|
1057 """ |
|
1058 A generic add function to add child objects. The function is intended to act as |
|
1059 proxy function that call the correct add function based on the child objects class. |
|
1060 |
|
1061 Example: obj.add(Feature("test")), actually obj.add_feature(Feature("test")) |
|
1062 @param child: the child object to add |
|
1063 @raise IncorrectClassError: if the given class cannot be added to this object. |
|
1064 """ |
|
1065 if isinstance(child, Feature): |
|
1066 self.add_feature(child) |
|
1067 elif isinstance(child, View): |
|
1068 self._add(child) |
|
1069 elif isinstance(child, (Data)): |
|
1070 self.add_data(child) |
|
1071 else: |
|
1072 super(Configuration, self).add(child) |
|
1073 |
|
1074 def get_default_view(self): |
|
1075 """ |
|
1076 Get the default view from this configuration hierarchy. |
|
1077 This returns always the view from the Root configuration point of view. |
|
1078 """ |
|
1079 try: |
|
1080 parent = self._find_parent_or_default() |
|
1081 if parent and isinstance(parent, Configuration): |
|
1082 return parent.get_default_view() |
|
1083 else: |
|
1084 if not hasattr(self, '_default_view'): |
|
1085 self._create_default_view() |
|
1086 return self._default_view |
|
1087 except exceptions.NotFound, e: |
|
1088 raise e |
|
1089 # raise exceptions.NotFound("Default View is not found! No root configuration?") |
|
1090 |
|
1091 def recreate_default_view(self): |
|
1092 try: |
|
1093 parent = self._find_parent_or_default() |
|
1094 if parent and isinstance(parent, Configuration): |
|
1095 parent.recreate_default_view() |
|
1096 else: |
|
1097 self._create_default_view() |
|
1098 except exceptions.NotFound, e: |
|
1099 raise e |
|
1100 # raise exceptions.NotFound("Default View is not found! No root configuration?") |
|
1101 |
|
1102 def _create_default_view(self): |
|
1103 # Rebuild the default view for this Configuration |
|
1104 self._default_view = View("_default_view", data=True) |
|
1105 self._default_view._parent= self |
|
1106 # First add all features of the configuration to the view. |
|
1107 # Then add all data elements under the features |
|
1108 for child in self._traverse(type=Feature): |
|
1109 self._default_view.add_feature(child, child.namespace) |
|
1110 for child in self._traverse(type=Data): |
|
1111 #parent_config = child._find_parent_or_default(type=Configuration) |
|
1112 #print "Adding data %s: fqr: %s from file %s." % (child.get_value(), child.fqr, parent_config.get_path()) |
|
1113 try: |
|
1114 fea = self._default_view.get_feature(child.fqr) |
|
1115 fea.add_data(child) |
|
1116 except exceptions.NotFound, e: |
|
1117 data_parent_config = child._find_parent_or_default(type=Configuration) |
|
1118 logging.getLogger('cone').info("Warning: Feature '%s' for data in %s not found." % (child.fqr, data_parent_config.get_path())) |
|
1119 |
|
1120 class ConfigurationProxy(container.LoadProxy): |
|
1121 """ |
|
1122 Configuration loading proxy. Loads the configuration from the given reference, when needed. |
|
1123 """ |
|
1124 def __init__(self, path, **kwargs): |
|
1125 """ |
|
1126 The ConfigurationProxy that represents a configuration that is included in another configuration. |
|
1127 @param ref: the reference to the storage resource |
|
1128 The ConfigurationProxy trust to get the store_interface from the parent object with get_storage() function. |
|
1129 |
|
1130 """ |
|
1131 container.LoadProxy.__init__(self, path) |
|
1132 self.set('_name', utils.resourceref.to_objref(path)) |
|
1133 |
|
1134 def _clone(self, **kwargs): |
|
1135 """ |
|
1136 A ConfigurationProxy specific implementation for cloning. |
|
1137 Copies all (public) members in dictionary. |
|
1138 To clone call the actual object that is proxied as well if the reqursion is on. |
|
1139 @param recursion: Boolean to define recursion on or off |
|
1140 @param recursion_depth: positive integer to define recursion depth. default is -1 which will |
|
1141 perform recursion to all objects. |
|
1142 """ |
|
1143 dict = self._dict() |
|
1144 obj = self.__class__(**dict) |
|
1145 # handle the recursion argument |
|
1146 recursion = kwargs.get('recursion', False) |
|
1147 if recursion: |
|
1148 recursion_depth = kwargs.get('recursion_depth', -1) |
|
1149 if recursion_depth < 0 or recursion_depth > 0: |
|
1150 # decrease the recursion |
|
1151 kwargs['recursion_depth'] = recursion_depth - 1 |
|
1152 newobj = self._get_obj()._clone(**kwargs) |
|
1153 obj._set_obj(newobj) |
|
1154 return obj |
|
1155 |
|
1156 def _dict(self): |
|
1157 """ |
|
1158 Return the public variables in a dictionary |
|
1159 """ |
|
1160 dict = {} |
|
1161 for key in self.__dict__.keys(): |
|
1162 if key.startswith('_'): |
|
1163 continue |
|
1164 else: |
|
1165 dict[key] = self.__dict__[key] |
|
1166 return dict |
|
1167 |
|
1168 def _get_mapper(self,modelname): |
|
1169 """ |
|
1170 Return a instance of appropriate mapper for given model. |
|
1171 """ |
|
1172 return mapping.BaseMapper() |
|
1173 |
|
1174 class Group(Base): |
|
1175 """ |
|
1176 A Group class. Group is used in View to group up other Group/Feature objects. |
|
1177 """ |
|
1178 def __init__(self, ref="", **kwargs): |
|
1179 super(Group, self).__init__(ref, **kwargs) |
|
1180 self.name = ref |
|
1181 self.support_data = kwargs.get("data", False) |
|
1182 |
|
1183 def _supported_type(self, obj): |
|
1184 if isinstance(obj, (Group, \ |
|
1185 Base, \ |
|
1186 _FeatureProxy, \ |
|
1187 FeatureLink)): |
|
1188 return True |
|
1189 else: |
|
1190 return False |
|
1191 |
|
1192 def _default_object(self, name): |
|
1193 return Group(name) |
|
1194 |
|
1195 def get_name(self): |
|
1196 """ |
|
1197 Return the name of the configuration |
|
1198 """ |
|
1199 return self.name |
|
1200 |
|
1201 def set_name(self, name): |
|
1202 """ |
|
1203 Set the name |
|
1204 """ |
|
1205 self.name |
|
1206 |
|
1207 def add(self, child, policy=container.REPLACE): |
|
1208 """ |
|
1209 A generic add function to add child objects. The function is intended to act as |
|
1210 proxy function that call the correct add function based on the child objects class. |
|
1211 |
|
1212 Example: obj.add(Feature("test")), actually obj.add_feature(Feature("test")) |
|
1213 @param child: the child object to add |
|
1214 @raise IncorrectClassError: if the given class cannot be added to this object. |
|
1215 """ |
|
1216 if isinstance(child, (Group, \ |
|
1217 Base, \ |
|
1218 _FeatureProxy, \ |
|
1219 FeatureLink)): |
|
1220 self._add(child) |
|
1221 else: |
|
1222 raise exceptions.IncorrectClassError("Cannot add %s to %s" % (child, self)) |
|
1223 |
|
1224 def get_name(self): |
|
1225 """ |
|
1226 Return the name of the configuration |
|
1227 """ |
|
1228 return self.name |
|
1229 |
|
1230 def set_name(self, name): |
|
1231 """ |
|
1232 Set the name |
|
1233 """ |
|
1234 self.name = name |
|
1235 |
|
1236 def add_feature(self, feature, path=""): |
|
1237 """ |
|
1238 Add feature to this Group. |
|
1239 """ |
|
1240 if not isinstance(feature, Feature): |
|
1241 raise exceptions.IncorrectClassError("add_feature requires instance of Feature!! Given %s" % feature) |
|
1242 if not self.support_data: |
|
1243 self._add_to_path(path, _FeatureProxy(feature._name, feature)) |
|
1244 else: |
|
1245 self._add_to_path(path, _FeatureDataProxy(feature._name, feature)) |
|
1246 |
|
1247 def remove_feature(self, ref): |
|
1248 """ |
|
1249 remove a given feature from this view by reference. |
|
1250 @param ref: |
|
1251 """ |
|
1252 self._remove(ref) |
|
1253 |
|
1254 def get_feature(self, ref): |
|
1255 """ |
|
1256 @param path: The path (ref) to the given feature |
|
1257 """ |
|
1258 try: |
|
1259 return self._get(ref) |
|
1260 except exceptions.NotFound: |
|
1261 raise exceptions.NotFound("Feature '%s' not found." % ref) |
|
1262 |
|
1263 def get_features(self, ref, **kwargs): |
|
1264 """ |
|
1265 Get a list of features that match the ref. |
|
1266 Example1: get_features('foo.bar') would be the same as get_feature('foo.bar'), but this returns |
|
1267 always a list [<Feature>]. |
|
1268 Example2: get_features('foo.*') would try to retrieve a list of all foo children. |
|
1269 Example3: get_features('foo.*', type='') would try to retrieve a list of all foo children, |
|
1270 that have a defined type. |
|
1271 @param path: The path (ref) to the given feature or xpath like expression |
|
1272 @return: A list of features. |
|
1273 """ |
|
1274 (startref, last) = utils.dottedref.psplit_ref(ref) |
|
1275 startelem = self._get(startref) |
|
1276 if last == '**': |
|
1277 return [fea for fea in startelem._traverse(**kwargs)] |
|
1278 elif last == '*': |
|
1279 return [fea for fea in startelem._objects(**kwargs)] |
|
1280 else: |
|
1281 return [self._get(ref)] |
|
1282 |
|
1283 def list_features(self): |
|
1284 """ |
|
1285 Return a array of all Feature children references under this object. |
|
1286 """ |
|
1287 return [fea.get_ref() for fea in self._objects(type=(_FeatureProxy))] |
|
1288 |
|
1289 def list_all_features(self): |
|
1290 """ |
|
1291 Return a array of all Feature children references under this object. |
|
1292 """ |
|
1293 return [fea.fqr for fea in self._traverse(type=(_FeatureProxy))] |
|
1294 |
|
1295 def add_group(self, groupname): |
|
1296 """ |
|
1297 """ |
|
1298 self._add(Group(groupname)) |
|
1299 |
|
1300 def remove_group(self, ref): |
|
1301 """ |
|
1302 remove a given feature from this view by reference. |
|
1303 @param ref: |
|
1304 """ |
|
1305 self._remove(ref) |
|
1306 |
|
1307 def get_group(self, ref): |
|
1308 """ |
|
1309 @param path: The path (ref) to the given feature |
|
1310 """ |
|
1311 return self._get(ref) |
|
1312 |
|
1313 def list_groups(self): |
|
1314 """ |
|
1315 """ |
|
1316 return [group.get_name() for group in self._objects(type=Group)] |
|
1317 |
|
1318 def populate(self): |
|
1319 """ |
|
1320 Populate or fetch the link to the actual feature for this featureproxy. |
|
1321 This method fetches the feature to the _obj member variable and populates also |
|
1322 subfeatures. |
|
1323 """ |
|
1324 for child in self._traverse(type=FeatureLink): |
|
1325 child.populate() |
|
1326 |
|
1327 |
|
1328 |
|
1329 class View(Group): |
|
1330 """ |
|
1331 A View class. View is intended to create new or different hierarchies of existing features. A View can contain Group and/or Feature objects. |
|
1332 """ |
|
1333 def __init__(self, ref="", **kwargs): |
|
1334 super(View, self).__init__(self.to_ref(ref), **kwargs) |
|
1335 self.name = ref |
|
1336 self.container = True |
|
1337 |
|
1338 @classmethod |
|
1339 def to_ref(cls, ref): |
|
1340 """ |
|
1341 return a view reference converted from name |
|
1342 """ |
|
1343 return ref.replace('.', '').replace('/', '') |
|
1344 |
|
1345 |
|
1346 class Feature(Base): |
|
1347 """ |
|
1348 A Feature class. Feature is the base for all Configurable items in a Configuration. |
|
1349 """ |
|
1350 PROPERTIES = ['value'] |
|
1351 def __init__(self, ref="", **kwargs): |
|
1352 super(Feature, self).__init__(ref, **kwargs) |
|
1353 self.name = kwargs.get('name', ref) |
|
1354 self.type = kwargs.get('type', None) |
|
1355 self._dataproxy = None |
|
1356 |
|
1357 def __copy__(self): |
|
1358 dict = {} |
|
1359 for key in self.__dict__.keys(): |
|
1360 if key.startswith('_') or key == 'ref': |
|
1361 continue |
|
1362 else: |
|
1363 dict[key] = self.__dict__[key] |
|
1364 fea = self.__class__(self.ref, **dict) |
|
1365 return fea |
|
1366 |
|
1367 |
|
1368 def _supported_type(self, obj): |
|
1369 # For now support added for desc element via support for Base |
|
1370 if isinstance(obj, (Feature, Option, Base)): |
|
1371 return True |
|
1372 else: |
|
1373 return False |
|
1374 |
|
1375 def add(self, child, policy=container.REPLACE): |
|
1376 """ |
|
1377 A generic add function to add child objects. The function is intended to act as |
|
1378 proxy function that call the correct add function based on the child objects class. |
|
1379 |
|
1380 Example: obj.add(Feature("test")), actually obj.add_feature(Feature("test")) |
|
1381 @param child: the child object to add |
|
1382 @raise IncorrectClassError: if the given class cannot be added to this object. |
|
1383 """ |
|
1384 if isinstance(child, Feature): |
|
1385 self.add_feature(child) |
|
1386 elif isinstance(child, Option): |
|
1387 self._add(child, policy) |
|
1388 elif isinstance(child, Base): |
|
1389 self._add(child, policy) |
|
1390 else: |
|
1391 raise exceptions.IncorrectClassError("Cannot add %s to %s" % (child, self)) |
|
1392 |
|
1393 def get_name(self): |
|
1394 """ |
|
1395 Return the name of the configuration |
|
1396 """ |
|
1397 return self.name |
|
1398 |
|
1399 def set_name(self, name): |
|
1400 """ |
|
1401 Set the name |
|
1402 """ |
|
1403 self.name = name |
|
1404 |
|
1405 def get_type(self): |
|
1406 return self.type |
|
1407 |
|
1408 def set_type(self, type): |
|
1409 self.type = type |
|
1410 |
|
1411 def add_feature(self, feature, path=""): |
|
1412 """ |
|
1413 @param feature: The Feature object to add |
|
1414 """ |
|
1415 configuration = self.find_parent(type=Configuration) |
|
1416 if configuration: |
|
1417 feapath = utils.dottedref.join_refs([self._path(configuration), path]) |
|
1418 configuration.add_feature(feature, feapath) |
|
1419 else: |
|
1420 self._add_to_path(path, feature) |
|
1421 |
|
1422 def get_feature(self, path): |
|
1423 """ |
|
1424 @param path: The path (ref) to the given feature |
|
1425 """ |
|
1426 return self._get(path) |
|
1427 |
|
1428 def remove_feature(self, ref): |
|
1429 """ |
|
1430 remove a given feature from this view by reference. |
|
1431 @param ref: |
|
1432 """ |
|
1433 configuration = self.find_parent(type=Configuration) |
|
1434 if configuration: |
|
1435 fullfqr = utils.dottedref.join_refs([self._path(configuration), ref]) |
|
1436 configuration.remove_feature(fullfqr) |
|
1437 else: |
|
1438 self._remove(ref) |
|
1439 |
|
1440 def list_features(self): |
|
1441 """ |
|
1442 Return a array of all Feature children references under this object. |
|
1443 """ |
|
1444 return [fea.get_ref() for fea in self._objects(type=Feature)] |
|
1445 |
|
1446 def list_all_features(self): |
|
1447 """ |
|
1448 Return a array of all Feature children references under this object. |
|
1449 """ |
|
1450 return [fea._path(self) for fea in self._traverse(type=Feature)] |
|
1451 |
|
1452 def add_option(self, option): |
|
1453 """ |
|
1454 @param option: option object |
|
1455 """ |
|
1456 if not isinstance(option, Option): |
|
1457 raise TypeError("%r is not an instance of Option!" % option) |
|
1458 self._add(option) |
|
1459 |
|
1460 def create_option(self, name, value): |
|
1461 """ |
|
1462 @param name: option name |
|
1463 @param value: option value |
|
1464 """ |
|
1465 self._add(Option(name, value)) |
|
1466 |
|
1467 def get_option(self, ref): |
|
1468 """ |
|
1469 @param name: The option reference of the option (as returned by list_options()) |
|
1470 """ |
|
1471 real_ref = 'opt_' + ref |
|
1472 obj = self._get(real_ref) |
|
1473 if not isinstance(obj, Option): |
|
1474 raise TypeError('Object %r is not an instance of Option (%r instead)' % (real_ref, type(obj))) |
|
1475 return obj |
|
1476 |
|
1477 def remove_option(self, ref): |
|
1478 """ |
|
1479 remove a given option from this feature by option reference. |
|
1480 """ |
|
1481 real_ref = 'opt_' + ref |
|
1482 obj = self._get(real_ref) |
|
1483 if not isinstance(obj, Option): |
|
1484 raise TypeError('Trying to remove option with ref %r, but object with ref %r is not an instance of Option (%s instead)' % (ref, real_ref, type(obj))) |
|
1485 self._remove(real_ref) |
|
1486 |
|
1487 def list_options(self): |
|
1488 """ |
|
1489 Return a array of all Option children references under this object. |
|
1490 """ |
|
1491 # Return option refs without the leading 'opt_' |
|
1492 return [opt.ref[4:] for opt in self._objects(type=Option)] |
|
1493 |
|
1494 def get_value(self, attr=None): |
|
1495 """ |
|
1496 Get the current value of the feature |
|
1497 @param attr: The attribute name of the data. E.g. attr='data', attr='rfs' |
|
1498 """ |
|
1499 # Do not allow getting of setting of sequence values directly with Feature object |
|
1500 if not self.is_sequence(): |
|
1501 return self.get_value_cast(self.dataproxy._get_value(attr), attr) |
|
1502 else: |
|
1503 """ get the feature specific data from sequence => a column of data table """ |
|
1504 coldata = [] |
|
1505 feasequence = self.get_sequence_parent() |
|
1506 feapath = self._path(feasequence) |
|
1507 for row in feasequence.data: |
|
1508 feadata = row.get_feature(feapath) |
|
1509 coldata.append(feadata.value) |
|
1510 return coldata |
|
1511 |
|
1512 def set_value(self, value, attr=None): |
|
1513 """ |
|
1514 Set the current value for this feature. Set the value on the topmost layer. |
|
1515 @param value: the value to set |
|
1516 """ |
|
1517 # Do not allow setting of setting of sequence values directly with Feature object |
|
1518 if not self.is_sequence(): |
|
1519 value = self.set_value_cast(value, attr) |
|
1520 self.dataproxy._set_value(value, attr) |
|
1521 |
|
1522 def del_value(self, attr=None): |
|
1523 """ |
|
1524 Delete the topmost value for this feature. |
|
1525 """ |
|
1526 if not self.is_sequence(): |
|
1527 self.dataproxy._del_value(attr) |
|
1528 |
|
1529 def get_value_cast(self, value, attr=None): |
|
1530 """ |
|
1531 A function to perform the value type casting in get operation |
|
1532 @param value: the value to cast |
|
1533 @param attr: the attribute which is fetched from model (normally in confml either None='data' or 'rfs') |
|
1534 """ |
|
1535 return value |
|
1536 |
|
1537 def set_value_cast(self, value, attr=None): |
|
1538 """ |
|
1539 A function to perform the value type casting in the set operation |
|
1540 @param value: the value to cast |
|
1541 @param attr: the attribute which is fetched from model (normally in confml either None='data' or 'rfs') |
|
1542 """ |
|
1543 return value |
|
1544 |
|
1545 def get_original_value(self, attr=None): |
|
1546 """ |
|
1547 Get the current value of the feature |
|
1548 @param attr: The attribute name of the data. E.g. attr='data', attr='rfs' |
|
1549 """ |
|
1550 # Do not allow getting of setting of sequence values directly with Feature object |
|
1551 if not self.is_sequence(): |
|
1552 return self.dataproxy._get_value(attr) |
|
1553 else: |
|
1554 """ get the feature specific data from sequence => a column of data table """ |
|
1555 coldata = [] |
|
1556 feasequence = self.get_sequence_parent() |
|
1557 feapath = self._path(feasequence.data) |
|
1558 for row in feasequence.data: |
|
1559 feadata = row.get_feature(feapath) |
|
1560 coldata.append(feadata.value) |
|
1561 return coldata |
|
1562 |
|
1563 def add_data(self, data): |
|
1564 """ |
|
1565 Add a data value. |
|
1566 @param data: A Data object |
|
1567 """ |
|
1568 try: |
|
1569 return self.dataproxy._add_data(data) |
|
1570 except AttributeError: |
|
1571 self.dataproxy = self.get_default_view().get_feature(self.get_fullfqr()) |
|
1572 return self.dataproxy._add_data(data) |
|
1573 |
|
1574 def get_data(self, attr=None): |
|
1575 """ |
|
1576 Helper function to get the topmost data value from the default view. |
|
1577 @param attr: The attribute name of the data. E.g. attr='data', attr='rfs' |
|
1578 """ |
|
1579 try: |
|
1580 return self.dataproxy._get_data(attr) |
|
1581 except AttributeError: |
|
1582 self.dataproxy = self.get_default_view().get_feature(self.get_fullfqr()) |
|
1583 return self.dataproxy._get_data(attr) |
|
1584 |
|
1585 def get_datas(self): |
|
1586 """ |
|
1587 Helper function to get the data values from the default view. |
|
1588 """ |
|
1589 try: |
|
1590 return self.dataproxy._get_datas() |
|
1591 except AttributeError: |
|
1592 self.dataproxy = self.get_default_view().get_feature(self.get_fullfqr()) |
|
1593 return self.dataproxy._get_datas() |
|
1594 |
|
1595 def get_valueset(self): |
|
1596 """ |
|
1597 Get the ValueSet object for this feature, that has the list of available values. |
|
1598 """ |
|
1599 if self.get_type() == 'boolean': |
|
1600 return ValueSet([True, False]) |
|
1601 elif self.get_type() == 'int': |
|
1602 return ValueRange(0, sys.maxint) |
|
1603 elif self.get_type() == 'string': |
|
1604 return ValueRe('.*') |
|
1605 elif self.get_type() in ('selection', 'multiSelection'): |
|
1606 values = [] |
|
1607 for opt in self._objects(type=Option): |
|
1608 v = opt.get_value() |
|
1609 if v is not None: values.append(v) |
|
1610 return ValueSet(values) |
|
1611 |
|
1612 def is_sequence(self): |
|
1613 """ Return true if the feature is a sequence or part of a sequence """ |
|
1614 try: |
|
1615 return self._parent.is_sequence() |
|
1616 except AttributeError: |
|
1617 return False |
|
1618 |
|
1619 def get_sequence_parent(self): |
|
1620 """ Try to get a FeatureSequence object for this Feature if it is found """ |
|
1621 try: |
|
1622 return self._parent.get_sequence_parent() |
|
1623 except AttributeError: |
|
1624 return None |
|
1625 |
|
1626 def getdataproxy(self): |
|
1627 if self._dataproxy == None: |
|
1628 self.dataproxy = self.get_default_view().get_feature(self.get_fullfqr()) |
|
1629 return self._dataproxy |
|
1630 def setdataproxy(self, value): self._dataproxy = value |
|
1631 def deldataproxy(self): self._dataproxy = None |
|
1632 dataproxy = property(getdataproxy, setdataproxy, deldataproxy) |
|
1633 value = property(get_value, set_value, del_value) |
|
1634 |
|
1635 class FeatureSequence(Feature): |
|
1636 POLICY_REPLACE = 0 |
|
1637 POLICY_APPEND = 1 |
|
1638 POLICY_PREPEND = 2 |
|
1639 """ |
|
1640 A Feature class. Feature is the base for all Configurable items in a Configuration. |
|
1641 """ |
|
1642 dataelem_name = '?datarows' |
|
1643 template_name = '?template' |
|
1644 def __init__(self, ref="", **kwargs): |
|
1645 super(FeatureSequence, self).__init__(ref) |
|
1646 self.name = kwargs.get('name', ref) |
|
1647 self.type = 'sequence' |
|
1648 self.mapKey = kwargs.get('mapKey') |
|
1649 self.mapValue = kwargs.get('mapValue') |
|
1650 self._templatedata = None |
|
1651 |
|
1652 def _get_policy(self, data): |
|
1653 """ |
|
1654 parse the policy from a policy string and return a constant |
|
1655 @return: POLICY_* constant |
|
1656 """ |
|
1657 try: |
|
1658 containerdata = utils.get_list(data._get_parent()._get(data.get_ref())) |
|
1659 firstdata = containerdata[0] |
|
1660 except AttributeError: |
|
1661 firstdata = data |
|
1662 |
|
1663 if firstdata.policy == 'append': |
|
1664 return self.POLICY_APPEND |
|
1665 elif firstdata.policy == 'prefix': |
|
1666 return self.POLICY_PREPEND |
|
1667 elif firstdata == data: |
|
1668 # otherwise the policy is either replace or undefined |
|
1669 # (firstdata.policy == 'replace' or firstdata.policy == ''): |
|
1670 return self.POLICY_REPLACE |
|
1671 else: |
|
1672 return self.POLICY_APPEND |
|
1673 |
|
1674 def _set_template_data(self, data=None): |
|
1675 """ |
|
1676 Set the template of the feature sequence |
|
1677 """ |
|
1678 # If template data is not existing, create it |
|
1679 if data != None: |
|
1680 self._templatedata = data |
|
1681 for feaname in self.list_features(): |
|
1682 if self._templatedata._has(feaname): |
|
1683 self.get_feature(feaname)._templatedata = self._templatedata._get(feaname) |
|
1684 else: |
|
1685 subdata = Data(ref=feaname) |
|
1686 self.get_feature(feaname)._templatedata = subdata |
|
1687 self._templatedata._add(subdata) |
|
1688 |
|
1689 def _add_datarow(self, dataobj=None, policy=POLICY_APPEND): |
|
1690 """ |
|
1691 Add a feature data row for a new data in this sequence |
|
1692 """ |
|
1693 if dataobj == None: |
|
1694 dataobj = Data(fqr=self.fqr) |
|
1695 elif dataobj.attr != 'data': |
|
1696 # Add data rows only for data objects (not e.g. RFS) |
|
1697 return |
|
1698 fea = FeatureSequenceSub(self.dataelem_name) |
|
1699 rowproxy = _FeatureDataProxy(fea._name, fea) |
|
1700 """ the imaginary features share the parent relation of the proxy objects """ |
|
1701 self.dataproxy._add(rowproxy, policy) |
|
1702 fea._parent = rowproxy._parent |
|
1703 rowproxy._add_data(dataobj) |
|
1704 """ update the FeatureSequenceSub index from the index number of dataproxy """ |
|
1705 fea._index = utils.get_list(self.dataproxy._get(self.dataelem_name)).index(rowproxy) |
|
1706 # Create a the subfeatures / columns for the parent feature and |
|
1707 # add a data element under each feature. |
|
1708 for feaname in self.list_all_features(): |
|
1709 (pathto_fea, fearef) = utils.dottedref.psplit_ref(feaname) |
|
1710 rowproxy.add_feature(FeatureSequenceSub(fearef), pathto_fea) |
|
1711 subproxy = rowproxy.get_feature(feaname) |
|
1712 subproxy._obj._parent = subproxy._parent |
|
1713 if not dataobj._has(feaname): |
|
1714 dataobj._add_to_path(pathto_fea, Data(ref=fearef)) |
|
1715 subproxy._add_data(dataobj._get(feaname)) |
|
1716 |
|
1717 def add(self, child, policy=container.REPLACE): |
|
1718 """ |
|
1719 A generic add function to add child objects. The function is intended to act as |
|
1720 proxy function that call the correct add function based on the child objects class. |
|
1721 |
|
1722 Example: obj.add(Feature("test")), actually obj.add_feature(Feature("test")) |
|
1723 @param child: the child object to add |
|
1724 @raise IncorrectClassError: if the given class cannot be added to this object. |
|
1725 """ |
|
1726 if isinstance(child, Feature): |
|
1727 self.add_feature(child) |
|
1728 elif isinstance(child, Option): |
|
1729 self._add(child) |
|
1730 elif isinstance(child, Base): |
|
1731 self._add(child) |
|
1732 else: |
|
1733 raise exceptions.IncorrectClassError("Cannot add %s to %s" % (child, self)) |
|
1734 |
|
1735 def add_sequence(self, data=None, policy=POLICY_APPEND): |
|
1736 """ |
|
1737 Add a feature data row for a new data in this sequence |
|
1738 """ |
|
1739 self._add_datarow(None, policy) |
|
1740 # set the initial data if it is given |
|
1741 rowproxy = utils.get_list(self.dataproxy._get(self.dataelem_name))[-1] |
|
1742 if data != None: |
|
1743 for index in range(len(data)): |
|
1744 rowproxy[index].set_value(data[index]) |
|
1745 # add the new data sequence/row to the last configuration layer |
|
1746 dataobj = rowproxy._get_data() |
|
1747 last_config = self.get_root_configuration().get_last_configuration() |
|
1748 last_config.add_data(dataobj, container.APPEND) |
|
1749 return dataobj |
|
1750 |
|
1751 def set_template(self, data=None): |
|
1752 """ |
|
1753 Set the template of the feature sequence |
|
1754 """ |
|
1755 # If template data is not existing, create it |
|
1756 if self._templatedata == None: |
|
1757 self._set_template_data(Data(ref=self.ref, template=True)) |
|
1758 # Add the template data to parent config |
|
1759 pconfig = self.find_parent(type=Configuration) |
|
1760 pconfig.add_data(self._templatedata) |
|
1761 |
|
1762 if data != None: |
|
1763 templdatas = self._templatedata._objects() |
|
1764 for index in range(len(data)): |
|
1765 templdatas[index].set_value(data[index]) |
|
1766 |
|
1767 def get_template(self): |
|
1768 """ |
|
1769 Add a feature data row for a new data in this sequence |
|
1770 """ |
|
1771 #self._set_template(None) |
|
1772 # set the initial data if it is given |
|
1773 if self._templatedata: |
|
1774 return [data.get_value() for data in self._templatedata._objects()] |
|
1775 else: |
|
1776 return None |
|
1777 |
|
1778 def get_data(self): |
|
1779 """ |
|
1780 Helper function to get the topmost data value from the default view. |
|
1781 """ |
|
1782 if self.dataproxy._has(self.dataelem_name): |
|
1783 return utils.get_list(self.dataproxy._get(self.dataelem_name)) |
|
1784 else: |
|
1785 return [] |
|
1786 |
|
1787 def add_data(self, data): |
|
1788 """ |
|
1789 Add a data value. |
|
1790 @param data: A Data object |
|
1791 """ |
|
1792 # Skip template data adding |
|
1793 if data.template: |
|
1794 self._set_template_data(data) |
|
1795 else: |
|
1796 # Get the data index |
|
1797 self._add_datarow(data, self._get_policy(data)) |
|
1798 return |
|
1799 |
|
1800 def get_map_key(self): |
|
1801 """ |
|
1802 Returns the setting that corresponds to mapKey attribute of this sequence feature. |
|
1803 """ |
|
1804 if self.mapKey != None: |
|
1805 mapkey = self.get_feature(self.mapKey) |
|
1806 return mapkey |
|
1807 else: |
|
1808 return None |
|
1809 |
|
1810 def get_map_key_value(self,key): |
|
1811 """ |
|
1812 Returns the setting that corresponds to mapKey attribute of this sequence feature. |
|
1813 """ |
|
1814 value = None |
|
1815 if self.mapKey != None and self.mapValue != None: |
|
1816 data = self.get_data() |
|
1817 for item in data: |
|
1818 kv = item.get_feature(self.mapKey).get_value() |
|
1819 if kv == key: |
|
1820 value = item.get_feature(self.mapValue).get_value() |
|
1821 return value |
|
1822 |
|
1823 def get_map_value(self): |
|
1824 """ |
|
1825 Returns the setting that corresponds to mapValue attribute of this sequence feature. |
|
1826 """ |
|
1827 if self.mapValue != None: |
|
1828 mapvalue = self.get_feature(self.mapValue) |
|
1829 return mapvalue |
|
1830 else: |
|
1831 return None |
|
1832 |
|
1833 def get_value(self, attr=None): |
|
1834 """ |
|
1835 Helper function to get the topmost data value from the default view. |
|
1836 """ |
|
1837 datatable = self.get_data() |
|
1838 rettable = [] |
|
1839 for row in datatable: |
|
1840 rowvalues = row.value |
|
1841 rettable.append(rowvalues) |
|
1842 return rettable |
|
1843 |
|
1844 def set_value(self, value, attr=None): |
|
1845 """ |
|
1846 Set the current value for this feature. Set the value on the topmost layer. |
|
1847 @param value: the value to set. The value must be a two dimensional array (e.g. matrix) |
|
1848 """ |
|
1849 # sets the first data element to replace policy |
|
1850 try: |
|
1851 self.add_sequence(value.pop(0), self.POLICY_REPLACE) |
|
1852 # ignore the index error of an empty list |
|
1853 except IndexError: |
|
1854 pass |
|
1855 for row in value: |
|
1856 self.add_sequence(row) |
|
1857 |
|
1858 def is_sequence(self): |
|
1859 """ Return always true from a sequence object """ |
|
1860 return True |
|
1861 |
|
1862 def get_sequence_parent(self): |
|
1863 """ Return this object as a sequence parent """ |
|
1864 return self |
|
1865 |
|
1866 value = property(get_value, set_value) |
|
1867 data = property(get_data) |
|
1868 |
|
1869 class FeatureSequenceCell(Feature): |
|
1870 """ |
|
1871 A Feature class. Feature is the base for all Configurable items in a Configuration. |
|
1872 """ |
|
1873 def __init__(self, ref="", **kwargs): |
|
1874 super(Feature, self).__init__(ref) |
|
1875 self.name = kwargs.get('name', ref) |
|
1876 self.type = 'seqcell' |
|
1877 |
|
1878 def get_value(self, attr=None): |
|
1879 """ |
|
1880 Get the current value of the feature |
|
1881 @param attr: The attribute name of the data. E.g. attr='data', attr='rfs' |
|
1882 """ |
|
1883 return self.dataproxy._get_value(attr) |
|
1884 |
|
1885 def set_value(self, value): |
|
1886 """ |
|
1887 Set the current value for this feature. Set the value on the topmost layer. |
|
1888 @param value: the value to set |
|
1889 """ |
|
1890 # The sequence cell only updates the latest value in the proxy |
|
1891 self.dataproxy.get_data().set_value(value) |
|
1892 |
|
1893 value = property(get_value, set_value) |
|
1894 |
|
1895 class FeatureSequenceSub(Feature): |
|
1896 """ |
|
1897 A Feature class. Feature is the base for all Configurable items in a Configuration. |
|
1898 """ |
|
1899 def __init__(self, ref="", **kwargs): |
|
1900 super(Feature, self).__init__(ref) |
|
1901 self.name = kwargs.get('name', ref) |
|
1902 self.type = 'subseq' |
|
1903 self._index = 0 |
|
1904 |
|
1905 def get_index(self): |
|
1906 """ |
|
1907 @return : the index of the data element for sequential data defined inside the same configuration. |
|
1908 0 for normal data. |
|
1909 """ |
|
1910 return self._index |
|
1911 |
|
1912 def set_value(self, value, attr=None): |
|
1913 """ |
|
1914 Set the current value for this sequence row. |
|
1915 @param value: the value row to set |
|
1916 """ |
|
1917 if utils.is_list(value): |
|
1918 for subindex in range(0, len(value)): |
|
1919 self.dataproxy[subindex].get_data().set_value(value[subindex]) |
|
1920 else: |
|
1921 self.dataproxy.get_data().set_value(value) |
|
1922 |
|
1923 def get_value(self, attr=None): |
|
1924 """ |
|
1925 Set the current value for this feature. Set the value on the topmost layer. |
|
1926 @param value: the value to set |
|
1927 """ |
|
1928 # dataproxy = self.get_default_view().get_feature(self.get_fullfqr()) |
|
1929 # The sequence cell only updates the latest value in the proxy |
|
1930 childdatas = self.dataproxy._objects() |
|
1931 if len(childdatas) > 0: |
|
1932 return [subdata.value for subdata in childdatas] |
|
1933 else: |
|
1934 return self.dataproxy._get_value(attr=attr) |
|
1935 |
|
1936 value = property(get_value, set_value) |
|
1937 |
|
1938 |
|
1939 class FeatureLink(Base): |
|
1940 """ |
|
1941 A _FeatureProxy class. _FeatureProxy is the object that is added to View as a |
|
1942 link to the actual Feature object. |
|
1943 """ |
|
1944 def __init__(self, link="", **kwargs): |
|
1945 # Store the fully qualified reference to this object |
|
1946 self.link = link |
|
1947 ref = link.replace('.', '_') |
|
1948 super(FeatureLink, self).__init__(ref) |
|
1949 self._obj = None |
|
1950 self._populated = False |
|
1951 |
|
1952 @property |
|
1953 def fqr(self): |
|
1954 return self.link |
|
1955 |
|
1956 def populate(self): |
|
1957 """ |
|
1958 Populate or fetch the link to the actual feature for this featureproxy. |
|
1959 This method fetches the feature to the _obj member variable and populates also |
|
1960 subfeatures. |
|
1961 """ |
|
1962 try: |
|
1963 if not self._populated: |
|
1964 feas = self.get_default_view().get_features(self.link) |
|
1965 # add the found features to the parent |
|
1966 for fea in feas: |
|
1967 self._get_parent().add_feature(fea._obj) |
|
1968 except exceptions.NotFound, e: |
|
1969 parent_view = self._find_parent_or_default(type=View) |
|
1970 view_name = parent_view.get_name() |
|
1971 logging.getLogger('cone').info("Warning: Feature '%s' in view '%s' not found." % (self.link, view_name)) |
|
1972 |
|
1973 |
|
1974 class _FeatureProxy(container.ObjectProxyContainer, Base): |
|
1975 """ |
|
1976 A _FeatureProxy class. _FeatureProxy is the object that is added to View as a |
|
1977 link to the actual Feature object. |
|
1978 """ |
|
1979 def __init__(self, ref="", obj=None, **kwargs): |
|
1980 super(_FeatureProxy, self).__init__(obj, ref) |
|
1981 Base.__init__(self, ref) |
|
1982 self.support_data = False |
|
1983 |
|
1984 def __getattr__(self, name): |
|
1985 """ |
|
1986 First check if the requested attr is a children then |
|
1987 direct all not found attribute calls to the sub object getattr |
|
1988 """ |
|
1989 try: |
|
1990 return self.__dict__['_children'][name] |
|
1991 except KeyError: |
|
1992 return getattr(self._obj, name) |
|
1993 |
|
1994 def __getitem__(self, index): |
|
1995 return self._objects()[index] |
|
1996 |
|
1997 def __setitem__(self, index, value): |
|
1998 raise exceptions.NotSupported() |
|
1999 |
|
2000 def __delitem__(self, index): |
|
2001 item = self.__getitem__(index) |
|
2002 return self._remove(item.get_ref()) |
|
2003 |
|
2004 def __len__(self): |
|
2005 return len(self._order) |
|
2006 |
|
2007 def _supported_type(self, obj): |
|
2008 if isinstance(obj, _FeatureProxy): |
|
2009 return True |
|
2010 else: |
|
2011 return False |
|
2012 |
|
2013 def _default_object(self, name): |
|
2014 return Group(name) |
|
2015 |
|
2016 def _set_parent(self, newparent): |
|
2017 """ |
|
2018 @param newparent: The new parent object |
|
2019 @return: None |
|
2020 """ |
|
2021 self._parent = newparent |
|
2022 |
|
2023 def add_feature(self, feature, path=""): |
|
2024 """ |
|
2025 """ |
|
2026 if not isinstance(feature, Feature): |
|
2027 raise exceptions.IncorrectClassError("add_feature requires instance of Feature!! Given %s" % feature) |
|
2028 if not self.support_data: |
|
2029 self._add_to_path(path, _FeatureProxy(feature._name, feature)) |
|
2030 else: |
|
2031 self._add_to_path(path, _FeatureDataProxy(feature._name, feature)) |
|
2032 |
|
2033 def remove_feature(self, ref): |
|
2034 """ |
|
2035 remove a given feature from this view by reference. |
|
2036 @param ref: |
|
2037 """ |
|
2038 self._remove(ref) |
|
2039 |
|
2040 def get_feature(self, path): |
|
2041 """ |
|
2042 @param path: The path (ref) to the given feature |
|
2043 """ |
|
2044 return self._get(path) |
|
2045 |
|
2046 def list_features(self): |
|
2047 """ |
|
2048 Return a array of all Feature children references under this object. |
|
2049 """ |
|
2050 return self._list() |
|
2051 |
|
2052 def list_all_features(self): |
|
2053 """ |
|
2054 Return a array of all Feature children references under this object. |
|
2055 """ |
|
2056 return [fea._path(self) for fea in self._traverse(type=_FeatureProxy)] |
|
2057 |
|
2058 def populate(self): |
|
2059 """ |
|
2060 Dummy implementation of populate |
|
2061 """ |
|
2062 pass |
|
2063 |
|
2064 |
|
2065 class _FeatureDataProxy(_FeatureProxy): |
|
2066 """ |
|
2067 A Feature class. Feature is the base for all Configurable items in a Configuration. |
|
2068 """ |
|
2069 DEFAULT_KEY = 'data' |
|
2070 def __init__(self, ref="", obj=None, **kwargs): |
|
2071 # Initialize _obj to None, because __getattr__(), __setattr__() |
|
2072 # and __delattr__() access it. |
|
2073 # Note that we cannot use self._obj = None here, since that would |
|
2074 # invoke __setattr__(), causing a kind of a chicken-and-egg problem |
|
2075 object.__setattr__(self, '_obj', None) |
|
2076 |
|
2077 super(_FeatureDataProxy, self).__init__(ref, obj) |
|
2078 self.support_data = True |
|
2079 """ Create the data container of all types of data. Add the key for the default key. """ |
|
2080 |
|
2081 self.defaultkey = _FeatureDataProxy.DEFAULT_KEY |
|
2082 self.datas = {self.defaultkey : []} |
|
2083 |
|
2084 def __getattr__(self, name): |
|
2085 """ |
|
2086 """ |
|
2087 if object.__getattribute__(self, '_obj') is not None: |
|
2088 self._obj.dataproxy = self |
|
2089 |
|
2090 if name in Feature.PROPERTIES: |
|
2091 return getattr(self._obj, name) |
|
2092 else: |
|
2093 return super(_FeatureDataProxy, self).__getattr__(name) |
|
2094 |
|
2095 def __setattr__(self, name, value): |
|
2096 """ |
|
2097 """ |
|
2098 if object.__getattribute__(self, '_obj') is not None: |
|
2099 self._obj.dataproxy = self |
|
2100 |
|
2101 if name in Feature.PROPERTIES: |
|
2102 return setattr(self._obj, name, value) |
|
2103 else: |
|
2104 super(_FeatureDataProxy, self).__setattr__(name, value) |
|
2105 |
|
2106 def __delattr__(self, name): |
|
2107 """ |
|
2108 """ |
|
2109 if name in Feature.PROPERTIES: |
|
2110 return delattr(self._obj, name) |
|
2111 else: |
|
2112 return super(_FeatureDataProxy, self).__delattr__(name) |
|
2113 |
|
2114 def _add_data(self, data): |
|
2115 """ |
|
2116 Add a data value. |
|
2117 @param data: A Data object |
|
2118 """ |
|
2119 try: |
|
2120 self.datas[data.attr].append(data) |
|
2121 except KeyError: |
|
2122 """ Create a list object for missing attribute """ |
|
2123 self.datas[data.attr] = [] |
|
2124 self.datas[data.attr].append(data) |
|
2125 |
|
2126 def _get_data(self, attr=None): |
|
2127 """ |
|
2128 Get the data value. in sequence setting cases returns an array of data. |
|
2129 """ |
|
2130 dataattr = attr or self.defaultkey |
|
2131 try: |
|
2132 if len(self.datas[dataattr]) > 0: |
|
2133 return self.datas[dataattr][-1] |
|
2134 else: |
|
2135 return None |
|
2136 except KeyError: |
|
2137 """ return None for missing attribute """ |
|
2138 return None |
|
2139 |
|
2140 def _get_datas(self, attr=None): |
|
2141 """ |
|
2142 Get the entire data array. |
|
2143 """ |
|
2144 dataattr = attr or self.defaultkey |
|
2145 return self.datas[dataattr] |
|
2146 |
|
2147 def _get_value(self, attr=None): |
|
2148 """ |
|
2149 Get the topmost data value. |
|
2150 """ |
|
2151 if self._get_data(attr): |
|
2152 return self._get_data(attr).get_value() |
|
2153 else: |
|
2154 return None |
|
2155 |
|
2156 def _set_value(self, datavalue, attr=None): |
|
2157 """ |
|
2158 Set the value for the feature the last configuration in the current hierarchy |
|
2159 @param value: The value for the feature. |
|
2160 @return: The created Data object. |
|
2161 """ |
|
2162 # Make sure that data value exists only once the the last configuration layer |
|
2163 # So if last_data exists on last layer, update the value of that data element. |
|
2164 # otherwise create a new data elem to the topmost layer |
|
2165 dataobj = self._get_data(attr) |
|
2166 last_config = self.get_root_configuration().get_last_configuration() |
|
2167 if dataobj and dataobj.find_parent(type=Configuration) == last_config: |
|
2168 dataobj.set_value(datavalue) |
|
2169 else: |
|
2170 dataobj = Data(fqr=self.fqr, value=datavalue, attr=attr) |
|
2171 last_config.add_data(dataobj) |
|
2172 self._add_data(dataobj) |
|
2173 return dataobj |
|
2174 |
|
2175 def _del_value(self, attr=None): |
|
2176 """ |
|
2177 Remove the |
|
2178 """ |
|
2179 data = self._get_data(attr) |
|
2180 if data: |
|
2181 dataattr = attr or self.defaultkey |
|
2182 parentconfig = data.find_parent(type=Configuration) |
|
2183 if parentconfig: |
|
2184 parentconfig.remove_data(data.get_fullfqr()) |
|
2185 del self.datas[dataattr][-1] |
|
2186 |
|
2187 def _get_values(self, attr=None): |
|
2188 """ |
|
2189 Get the topmost data value. |
|
2190 """ |
|
2191 dataattr = attr or self.defaultkey |
|
2192 return [dataelem.get_value() for dataelem in self.datas[dataattr]] |
|
2193 |
|
2194 |
|
2195 class DataBase(Base): |
|
2196 def __init__(self, ref="", **kwargs): |
|
2197 super(DataBase, self).__init__(ref, **kwargs) |
|
2198 |
|
2199 def _supported_type(self, obj): |
|
2200 if isinstance(obj, (DataContainer, DataBase)): |
|
2201 return True |
|
2202 else: |
|
2203 return False |
|
2204 |
|
2205 def _default_object(self, name): |
|
2206 return Data(ref=name) |
|
2207 |
|
2208 def count(self): |
|
2209 return len(self._objects()) |
|
2210 |
|
2211 def add(self, child, policy=container.REPLACE): |
|
2212 """ |
|
2213 A generic add function to add child objects. The function is intended to act as |
|
2214 proxy function that call the correct add function based on the child objects class. |
|
2215 |
|
2216 Example: obj.add(Feature("test")), actually obj.add_feature(Feature("test")) |
|
2217 @param child: the child object to add |
|
2218 @raise IncorrectClassError: if the given class cannot be added to this object. |
|
2219 """ |
|
2220 if isinstance(child, (Data)): |
|
2221 self._add(child, container.APPEND) |
|
2222 else: |
|
2223 raise exceptions.IncorrectClassError("Cannot add %s object to %s" % (child, self)) |
|
2224 |
|
2225 |
|
2226 class DataContainer(DataBase): |
|
2227 def __init__(self, ref="", **kwargs): |
|
2228 super(DataContainer, self).__init__(ref, **kwargs) |
|
2229 |
|
2230 |
|
2231 class Data(DataBase): |
|
2232 """ |
|
2233 The data element can contain any data setting for a feature. The data element can be |
|
2234 a value definition for any type of data. It basically just links some data to a feature. |
|
2235 The default Data attribute is 'data', but it can be any string. For example current use case |
|
2236 is 'rfs'. |
|
2237 """ |
|
2238 def __init__(self, **kwargs): |
|
2239 """ |
|
2240 @param ref: the reference to the feature. E.g. foo |
|
2241 @param fqr: the full reference to the feature. E.g. 'foo.bar' |
|
2242 @param value: the value of the data |
|
2243 @param attr: the attribute which the Data object defines. e.g. default is 'data'. But could be |
|
2244 for example 'rfs' |
|
2245 """ |
|
2246 name = kwargs.get('ref', '') |
|
2247 self.fearef = kwargs.get('fqr', None) |
|
2248 if self.fearef: |
|
2249 (namespace, name) = utils.dottedref.psplit_ref(self.fearef) |
|
2250 super(Data, self).__init__(name) |
|
2251 self.value = kwargs.get('value', None) |
|
2252 self.attr = kwargs.get('attr') or 'data' |
|
2253 self.policy = kwargs.get('policy', '') |
|
2254 self.template = kwargs.get('template', False) |
|
2255 self.map = kwargs.get('map') |
|
2256 |
|
2257 def get_fearef(self): |
|
2258 if self.fearef: |
|
2259 return self.fearef |
|
2260 else: |
|
2261 return self.fqr |
|
2262 |
|
2263 def get_value(self): |
|
2264 if self.map != None: |
|
2265 ref = utils.resourceref.to_dref(self.get_map_ref()) |
|
2266 key = self.get_map_key_value() |
|
2267 dview = self.get_root_configuration().get_default_view() |
|
2268 fea = dview.get_feature(ref) |
|
2269 return fea.get_map_key_value(key) |
|
2270 else: |
|
2271 return self.value |
|
2272 |
|
2273 def get_map(self): |
|
2274 return self.map |
|
2275 |
|
2276 def set_map(self, map): |
|
2277 self.map = map |
|
2278 if self.value: |
|
2279 #Either value or mapping can be defined. Not both. |
|
2280 self.value = None |
|
2281 |
|
2282 def get_map_ref(self): |
|
2283 if self.map != None: |
|
2284 return utils.DataMapRef.get_feature_ref(self.map) |
|
2285 else: |
|
2286 return None |
|
2287 |
|
2288 def get_map_key_value(self): |
|
2289 if self.map != None: |
|
2290 return utils.DataMapRef.get_key_value(self.map) |
|
2291 else: |
|
2292 return None |
|
2293 |
|
2294 def set_value(self, value): |
|
2295 self.value = value |
|
2296 if self.map: |
|
2297 #Either value or mapping can be defined. Not both. |
|
2298 self.map = None |
|
2299 |
|
2300 def get_policy(self): return self._policy |
|
2301 def set_policy(self, value): self._policy = value |
|
2302 def del_policy(self): self._policy = None |
|
2303 policy = property(get_policy, set_policy, del_policy) |
|
2304 |
|
2305 |
|
2306 class ValueSet(sets.Set): |
|
2307 """ |
|
2308 A value set object to indicate a set of possible values for a feature. |
|
2309 e.g. A boolean feature ValueSet([True, False]) |
|
2310 """ |
|
2311 def __init__(self, initial_set=None): |
|
2312 super(ValueSet, self).__init__(initial_set or []) |
|
2313 |
|
2314 |
|
2315 class ValueRange(object): |
|
2316 """ |
|
2317 """ |
|
2318 def __init__(self, fromvalue, tovalue, step=1): |
|
2319 self.fromvalue = fromvalue |
|
2320 self.tovalue = tovalue |
|
2321 self.step = step |
|
2322 |
|
2323 def __contains__(self, value): |
|
2324 return self.fromvalue <= value and value <= self.tovalue and (value-self.fromvalue) % self.step == 0 |
|
2325 |
|
2326 |
|
2327 class ValueRe(object): |
|
2328 """ |
|
2329 """ |
|
2330 def __init__(self, regexp): |
|
2331 self.regexp = re.compile(regexp) |
|
2332 |
|
2333 def __contains__(self, value): |
|
2334 if isinstance(value, str): |
|
2335 return self.regexp.match(value) |
|
2336 else: |
|
2337 return False |
|
2338 |
|
2339 |
|
2340 class Option(Base): |
|
2341 """ |
|
2342 Confml option class. |
|
2343 """ |
|
2344 def __init__(self, name, value, **kwargs): |
|
2345 super(Option, self).__init__(Option.to_optref(value, kwargs.get('map', None))) |
|
2346 self.name = name |
|
2347 self.value = value |
|
2348 self.map = kwargs.get('map', None) |
|
2349 self.relevant = kwargs.get('relevant', None) |
|
2350 |
|
2351 @classmethod |
|
2352 def to_optref(cls, value, map): |
|
2353 """ |
|
2354 @return: An option reference converted from value or map, depending |
|
2355 on which one is not None. |
|
2356 """ |
|
2357 if value is not None: |
|
2358 return "opt_value_%s" % value.replace('.', '').replace('/', '').replace(' ', '') |
|
2359 elif map is not None: |
|
2360 return "opt_map_%s" % map.replace('.', '').replace('/', '').replace(' ', '') |
|
2361 else: |
|
2362 raise ValueError("Both value and map are None!") |
|
2363 |
|
2364 def get_name(self): |
|
2365 return self.name |
|
2366 |
|
2367 def get_value(self): |
|
2368 return self.value |
|
2369 |
|
2370 def __cmp__(self, other): |
|
2371 try: |
|
2372 ref = getattr(other, 'ref') |
|
2373 except AttributeError: |
|
2374 ref = other |
|
2375 if self.ref < ref: |
|
2376 return -1 |
|
2377 elif self.ref == ref: |
|
2378 return 0 |
|
2379 else: |
|
2380 return 1 |
|
2381 |
|
2382 |
|
2383 class Storage(object): |
|
2384 """ |
|
2385 A general base class for all storage type classes |
|
2386 """ |
|
2387 """ File open modes """ |
|
2388 MODE_UNKNOWN= -1 |
|
2389 MODE_READ = 1 |
|
2390 MODE_WRITE = 2 |
|
2391 MODE_APPEND = 3 |
|
2392 MODE_DELETE = 4 |
|
2393 |
|
2394 def __init__(self, path): |
|
2395 """ |
|
2396 @param path: the reference to the root of the storage. |
|
2397 """ |
|
2398 self.rootpath = path |
|
2399 self.curpath = "" |
|
2400 self.container = True |
|
2401 self.__opened_res__ = {} |
|
2402 |
|
2403 def __opened__(self, res): |
|
2404 """ |
|
2405 Internal function to add a newly opened Resource object to the list of open resources. |
|
2406 @param res: The resource object |
|
2407 """ |
|
2408 if self.__opened_res__.has_key(res.path): |
|
2409 self.__opened_res__[res.path].append(res) |
|
2410 else: |
|
2411 self.__opened_res__[res.path] = [res] |
|
2412 |
|
2413 def __closed__(self, res): |
|
2414 """ |
|
2415 Internal function to remove a Resource object from the list of open resources. |
|
2416 @param res: The resource object to remove |
|
2417 @raise StorageException if the given resource object is not found: |
|
2418 """ |
|
2419 try: |
|
2420 self.__opened_res__[res.path].remove(res) |
|
2421 if len(self.__opened_res__[res.path]) == 0: |
|
2422 del self.__opened_res__[res.path] |
|
2423 except KeyError, e: |
|
2424 raise exceptions.StorageException("No such %s open resource! %s" % (res, e)) |
|
2425 |
|
2426 def __has_open__(self, ref): |
|
2427 """ |
|
2428 Internal function to find out if any Resource objects are open from given ref. |
|
2429 @param ref: The resource ref |
|
2430 @return: True if resources found. Otherwise False. |
|
2431 """ |
|
2432 return self.__opened_res__.has_key(ref) |
|
2433 |
|
2434 def __get_open__(self, path): |
|
2435 """ |
|
2436 Internal function to get all resource opened on a certain ref . |
|
2437 @param ref: The resource ref |
|
2438 @return: A list of open resources. Empty list if nothing is found |
|
2439 """ |
|
2440 if self.__has_open__(path): |
|
2441 # return a copy of currently open resources |
|
2442 return self.__opened_res__[path][:] |
|
2443 else: |
|
2444 return [] |
|
2445 |
|
2446 def __has_resource__(self, res): |
|
2447 """ |
|
2448 Internal function to find out if the given Resource objects is open in this storage. |
|
2449 @param ref: The resource object |
|
2450 @return: True if resources found. Otherwise False. |
|
2451 """ |
|
2452 try: |
|
2453 res = self.__opened_res__[res.path].index(res) |
|
2454 return True |
|
2455 except KeyError, e: |
|
2456 return False |
|
2457 |
|
2458 @classmethod |
|
2459 def open(cls,path, mode="r", **kwargs): |
|
2460 """ |
|
2461 Class method for opening an instance of Storage |
|
2462 @param path: path to storage, which will determine what type of storage is initiated. |
|
2463 """ |
|
2464 # import all storage instances |
|
2465 from cone.storage import storages |
|
2466 for storagename in storages: |
|
2467 storagemodule = 'cone.storage.'+storagename |
|
2468 module = __import__(storagemodule) |
|
2469 for storage_class in utils.all_subclasses(Storage): |
|
2470 if storage_class.supported_storage(path): |
|
2471 if hasattr(storage_class, '__open__'): |
|
2472 return storage_class.__open__(path, mode, **kwargs) |
|
2473 else: |
|
2474 return storage_class(path, mode, **kwargs) |
|
2475 |
|
2476 obj = Storage(path) |
|
2477 return obj |
|
2478 |
|
2479 @classmethod |
|
2480 def supported_storage(cls, path): |
|
2481 """ |
|
2482 Class method for determing if the given clas supports a storage by given path. |
|
2483 E.g. foo.zip, foo.cpd, foo/bar, http://foo.com/ |
|
2484 @param path: |
|
2485 @return: Boolean value. True if the storage of the path is supported. False if not. |
|
2486 """ |
|
2487 return False |
|
2488 |
|
2489 def set_path(self, path): |
|
2490 """ |
|
2491 """ |
|
2492 self.rootpath = path |
|
2493 |
|
2494 def get_path(self): |
|
2495 """ |
|
2496 """ |
|
2497 return self.rootpath |
|
2498 |
|
2499 def set_current_path(self, path): |
|
2500 """ |
|
2501 @param path: the current path under the Storage. |
|
2502 """ |
|
2503 self.curpath = utils.resourceref.remove_end_slash(utils.resourceref.remove_begin_slash(path)) |
|
2504 |
|
2505 def get_current_path(self): |
|
2506 """ |
|
2507 get the current path under the Storage. |
|
2508 """ |
|
2509 return self.curpath |
|
2510 |
|
2511 def close(self): |
|
2512 """ |
|
2513 Close the repository, which will save and close all open resources. |
|
2514 """ |
|
2515 for openref in self.__opened_res__.keys(): |
|
2516 for res in self.__get_open__(openref): |
|
2517 self.close_resource(res) |
|
2518 |
|
2519 def save(self): |
|
2520 """ |
|
2521 Flush changes from all resources to the repository. |
|
2522 """ |
|
2523 for openref in self.__opened_res__.keys(): |
|
2524 for res in self.__get_open__(openref): |
|
2525 self.save_resource(res) |
|
2526 |
|
2527 def open_resource(self, path, mode="r"): |
|
2528 """ |
|
2529 Open the given resource and return a File object. |
|
2530 @param path : reference to the resource |
|
2531 @param mode : the mode in which to open. Can be one of r = read, w = write, a = append. |
|
2532 raises a NotResource exception if the ref item is not a resource. |
|
2533 """ |
|
2534 raise exceptions.NotSupportedException() |
|
2535 |
|
2536 def delete_resource(self, path): |
|
2537 """ |
|
2538 Delete the given resource from storage |
|
2539 @param path: reference to the resource |
|
2540 raises a NotSupportedException exception if delete operation is not supported by the storage |
|
2541 """ |
|
2542 raise exceptions.NotSupportedException() |
|
2543 |
|
2544 def close_resource(self, path): |
|
2545 """ |
|
2546 Close a given resource instance. Normally this is called by the Resource object |
|
2547 in its own close. |
|
2548 @param path the reference to the resource to close. |
|
2549 """ |
|
2550 raise exceptions.NotSupportedException() |
|
2551 |
|
2552 def is_resource(self, path): |
|
2553 """ |
|
2554 Return true if the ref is a resource |
|
2555 @param ref : reference to path where resources are searched |
|
2556 """ |
|
2557 raise exceptions.NotSupportedException() |
|
2558 |
|
2559 def list_resources(self, path, recurse=False): |
|
2560 """ |
|
2561 find the resources under certain ref/path |
|
2562 @param ref : reference to path where resources are searched |
|
2563 @param recurse : defines whether to return resources directly under the path or does the listing recurse to subfolders. |
|
2564 Default value is False. Set to True to enable recursion. |
|
2565 """ |
|
2566 return [] |
|
2567 |
|
2568 def import_resources(self, paths, storage): |
|
2569 """ |
|
2570 import resources from a list of resources to this storage |
|
2571 @param paths : a list of Resourse objects. |
|
2572 @param storage : the external storage from which files are imported. |
|
2573 """ |
|
2574 raise exceptions.NotSupportedException() |
|
2575 |
|
2576 def export_resources(self, paths, storage): |
|
2577 """ |
|
2578 export resources from this storage based on a list of reference to this storage |
|
2579 @param path : a list of resource paths in this storage (references). |
|
2580 @param storage : the external storage where to export. |
|
2581 """ |
|
2582 raise exceptions.NotSupportedException() |
|
2583 |
|
2584 def close_resource(self, path): |
|
2585 """ |
|
2586 Close a given resource instance. Normally this is called by the Resource object |
|
2587 in its own close. |
|
2588 @param ref the reference to the resource to close. |
|
2589 """ |
|
2590 raise exceptions.NotSupportedException() |
|
2591 |
|
2592 def save_resource(self, path): |
|
2593 """ |
|
2594 Flush the changes of a given resource instance. Normally this is called by the Resource object |
|
2595 in its own save. |
|
2596 @param ref the reference to the resource to close. |
|
2597 """ |
|
2598 raise exceptions.NotSupportedException() |
|
2599 |
|
2600 def create_folder(self, path): |
|
2601 """ |
|
2602 Create a folder entry to a path |
|
2603 @param path : path to the folder |
|
2604 """ |
|
2605 raise exceptions.NotSupportedException() |
|
2606 |
|
2607 def delete_folder(self, path): |
|
2608 """ |
|
2609 Delete a folder entry from a path. The path must be empty. |
|
2610 @param path : path to the folder |
|
2611 """ |
|
2612 raise exceptions.NotSupportedException() |
|
2613 |
|
2614 def is_folder(self, path): |
|
2615 """ |
|
2616 Check if the given path is an existing folder in the storage |
|
2617 @param path : path to the folder |
|
2618 """ |
|
2619 raise exceptions.NotSupportedException() |
|
2620 |
|
2621 def get_mode(self, mode_str): |
|
2622 if mode_str.find("w") != -1: |
|
2623 return self.MODE_WRITE |
|
2624 elif mode_str.find("r") != -1: |
|
2625 return self.MODE_READ |
|
2626 elif mode_str.find("a") != -1: |
|
2627 return self.MODE_APPEND |
|
2628 elif mode_str.find("d") != -1: |
|
2629 return self.MODE_DELETE |
|
2630 else: |
|
2631 return self.MODE_UNKNOWN |
|
2632 |
|
2633 def unload(self, path, object): |
|
2634 """ |
|
2635 Dump a given object to the storage |
|
2636 @param object: The object to dump to the storage, which is expected to be an instance |
|
2637 of Base class. |
|
2638 @param path: The reference where to store the object |
|
2639 @param object: The object instance to dump |
|
2640 @raise StorageException: if the given object cannot be dumped to this storage |
|
2641 """ |
|
2642 raise exceptions.NotSupportedException() |
|
2643 |
|
2644 def load(self, path): |
|
2645 """ |
|
2646 Load an object from a reference. |
|
2647 @param path: The reference where to load the object |
|
2648 @raise StorageException: if the given object cannot be loaded as an object from this storage |
|
2649 """ |
|
2650 raise exceptions.NotSupportedException() |
|
2651 |
|
2652 path = property(get_path, set_path) |
|
2653 |
|
2654 class Resource(object): |
|
2655 STATE_OPEN = 0 |
|
2656 STATE_CLOSE = 1 |
|
2657 def __init__(self, storage, path, mode=Storage.MODE_READ): |
|
2658 self.storage = storage |
|
2659 self.path = path |
|
2660 self.mode = mode |
|
2661 self.state = Resource.STATE_OPEN |
|
2662 self.content_info = None |
|
2663 |
|
2664 def get_path(self): |
|
2665 return self.path |
|
2666 |
|
2667 def close(self): |
|
2668 """ |
|
2669 Close the resource. |
|
2670 Note1: the resource object cannot be accessed anymore after it has been closed. |
|
2671 Note2: the changes are not automatically saved. The save operation must be explicitly called, |
|
2672 to save data. |
|
2673 """ |
|
2674 self.storage.close_resource(self.path) |
|
2675 self.state = Resource.STATE_OPEN |
|
2676 |
|
2677 def read(self, bytes=0): |
|
2678 """ |
|
2679 Read data. |
|
2680 """ |
|
2681 raise exceptions.NotSupportedException() |
|
2682 |
|
2683 def write(self, string): |
|
2684 """ |
|
2685 Write data. |
|
2686 """ |
|
2687 raise exceptions.NotSupportedException() |
|
2688 |
|
2689 def truncate(self, size=0): |
|
2690 """ |
|
2691 Trunkate this resource data to the given size. |
|
2692 @param size: The size to trunkate. Default value is zero, which make the resource empty. |
|
2693 """ |
|
2694 raise NotSupportedException() |
|
2695 |
|
2696 def save(self, size=0): |
|
2697 """ |
|
2698 Save all changes to data to storage. |
|
2699 """ |
|
2700 raise NotSupportedException() |
|
2701 |
|
2702 def get_mode(self): |
|
2703 return self.storage.get_mode(self.mode) |
|
2704 |
|
2705 def get_size(self): |
|
2706 """ |
|
2707 Return the size of this resource in bytes. |
|
2708 |
|
2709 Note that this does not work in write mode. |
|
2710 @return: The resource size in bytes: |
|
2711 @raise exceptions.StorageException: The resource was opened in write mode. |
|
2712 """ |
|
2713 raise exceptions.NotSupportedException() |
|
2714 |
|
2715 def get_content_info(self): |
|
2716 """ |
|
2717 Return the ContentInfo class that contains content information about |
|
2718 resource. |
|
2719 """ |
|
2720 raise exceptions.NotSupportedException() |
|
2721 |
|
2722 class ContentInfo(object): |
|
2723 """ |
|
2724 A ContentInfo object is used to describe content of Resource. |
|
2725 """ |
|
2726 logger = logging.getLogger('cone.contentinfo') |
|
2727 |
|
2728 |
|
2729 def __init__(self, mimetype, mimesubtype): |
|
2730 #: MIME Media type (http://www.iana.org/assignments/media-types/) |
|
2731 #: as a string. E.g. 'image' or 'application' |
|
2732 self.mimetype = mimetype |
|
2733 #: MIME Media subtype as a string. E.g. 'svg+xml' or 'bmp'. |
|
2734 self.mimesubtype = mimesubtype |
|
2735 |
|
2736 @property |
|
2737 def content_type(self): |
|
2738 """ |
|
2739 Returns MIME Media type (http://www.iana.org/assignments/media-types/) |
|
2740 and subtype as a string. E.g. 'image/bmp'. |
|
2741 """ |
|
2742 return self.mimetype + '/' + self.mimesubtype |
|
2743 |
|
2744 class ImageContentInfo(ContentInfo): |
|
2745 |
|
2746 """ |
|
2747 A ImageContentInfo object is used to describe content of image Resources. |
|
2748 """ |
|
2749 def __init__(self): |
|
2750 ContentInfo.__init__(self, 'image', '') |
|
2751 |
|
2752 class BmpImageContentInfo(ImageContentInfo): |
|
2753 """ |
|
2754 A BmpImageContentInfo object is used to describe content of bmp image |
|
2755 Resources. |
|
2756 """ |
|
2757 |
|
2758 _BMP_BITS_PER_PIXEL_OFFSET_ = int('0x1C', 16) |
|
2759 |
|
2760 def __init__(self, resource, data): |
|
2761 ContentInfo.__init__(self, 'image', 'bmp') |
|
2762 |
|
2763 #: Color depth as bits per pixel. |
|
2764 self.color_depth = None |
|
2765 if (resource != None): |
|
2766 try: |
|
2767 self.color_depth = ord(data[self._BMP_BITS_PER_PIXEL_OFFSET_]) |
|
2768 except Exception, e: |
|
2769 self.logger.warning("Invalid BMP-file: %s" % resource.get_path()) |
|
2770 |
|
2771 class Folder(object): |
|
2772 """ |
|
2773 A Folder object is a subfolder of a Storage, offering access to part of the Storages resources. |
|
2774 """ |
|
2775 def __init__(self, storage, path): |
|
2776 """ |
|
2777 Create a layer folder to the storage if it does not exist. |
|
2778 """ |
|
2779 #if not storage.is_folder(path): |
|
2780 # storage.create_folder(path) |
|
2781 self.storage = copy.copy(storage) |
|
2782 self.storage.set_current_path(path) |
|
2783 |
|
2784 def __getattr__(self, name): |
|
2785 return getattr(self.storage, name) |
|
2786 |
|
2787 class CompositeLayer(object): |
|
2788 """ |
|
2789 A base class for composite Configuration objects. |
|
2790 """ |
|
2791 def __init__(self, path="", **kwargs): |
|
2792 self.layers = kwargs.get('layers', []) |
|
2793 self.path = path |
|
2794 |
|
2795 def add_layer(self, layer): |
|
2796 self.layers.append(layer) |
|
2797 |
|
2798 def remove_layer(self, path): |
|
2799 if self.get_layer(path): |
|
2800 self.layers.remove(self.get_layer(path)) |
|
2801 else: |
|
2802 raise exceptions.NotFound('Layer with given path %s not found!' % path) |
|
2803 |
|
2804 def get_layer(self, path): |
|
2805 for layer in self.layers: |
|
2806 if layer.get_current_path() == path: |
|
2807 return layer |
|
2808 return None |
|
2809 |
|
2810 def list_layers(self): |
|
2811 return [layer.get_current_path() for layer in self.layers] |
|
2812 |
|
2813 def list_confml(self): |
|
2814 """ |
|
2815 @return: array of confml file references. |
|
2816 """ |
|
2817 lres = [] |
|
2818 for layerpath in self.list_layers(): |
|
2819 for respath in self.get_layer(layerpath).list_confml(): |
|
2820 lres.append(utils.resourceref.join_refs([layerpath, respath])) |
|
2821 return lres |
|
2822 |
|
2823 def list_implml(self): |
|
2824 """ |
|
2825 @return: array of implml file references. |
|
2826 """ |
|
2827 lres = [] |
|
2828 for layerpath in self.list_layers(): |
|
2829 for respath in self.get_layer(layerpath).list_implml(): |
|
2830 lres.append(utils.resourceref.join_refs([layerpath, respath])) |
|
2831 return lres |
|
2832 |
|
2833 def list_content(self): |
|
2834 """ |
|
2835 @return: array of content file references. |
|
2836 """ |
|
2837 lres = [] |
|
2838 for layerpath in self.list_layers(): |
|
2839 for respath in self.get_layer(layerpath).list_content(): |
|
2840 lres.append(utils.resourceref.join_refs([layerpath, respath])) |
|
2841 return lres |
|
2842 |
|
2843 def list_doc(self): |
|
2844 """ |
|
2845 @return: array of document file references. |
|
2846 """ |
|
2847 lres = [] |
|
2848 for layerpath in self.list_layers(): |
|
2849 for respath in self.get_layer(layerpath).list_doc(): |
|
2850 lres.append(utils.resourceref.join_refs([layerpath, respath])) |
|
2851 return lres |
|
2852 |
|
2853 def list_all_resources(self, empty_folders=False): |
|
2854 """ |
|
2855 Returns a list of all layer related resource paths with full path in the storage. |
|
2856 """ |
|
2857 lres = [] |
|
2858 for layerpath in self.list_layers(): |
|
2859 sublayer = self.get_layer(layerpath) |
|
2860 for respath in sublayer.list_all_resources(empty_folders): |
|
2861 lres.append(utils.resourceref.join_refs([layerpath, respath])) |
|
2862 |
|
2863 return lres |
|
2864 |
|
2865 class Layer(CompositeLayer): |
|
2866 """ |
|
2867 A Layer object is a subfolder of a Storage, offering access to part of the Storages resources. |
|
2868 """ |
|
2869 def __init__(self, storage, path, **kwargs): |
|
2870 """ |
|
2871 Create a layer folder to the storage if it does not exist. |
|
2872 @param storage: a reference to the Storage object |
|
2873 @param path: path for the layer |
|
2874 @param confml_path: optional parameter for confml files path (give in confml_path="something") |
|
2875 @param imlpml_path: optional parameter for implml files path (give in implml_path="something") |
|
2876 @param content_path: optional parameter for content files path (give in content_path="something") |
|
2877 @param doc_path: optional parameter for doc files path (give in doc_path="something") |
|
2878 """ |
|
2879 super(Layer, self).__init__(path, **kwargs) |
|
2880 #if not storage.is_folder(path): |
|
2881 # storage.create_folder(path) |
|
2882 self.storage = copy.copy(storage) |
|
2883 self.storage.set_current_path(path) |
|
2884 self.predefined = {'confml_path' : 'confml', |
|
2885 'implml_path' : 'implml', |
|
2886 'content_path' : 'content', |
|
2887 'doc_path' : 'doc'} |
|
2888 # list through all "hardcoded" paths and check whether the |
|
2889 # hardcoded or given path exists under this Layer. |
|
2890 # if it does then create a folder instance to that path |
|
2891 for (pretag, prevalue) in self.predefined.items(): |
|
2892 self.predefined[pretag] = kwargs.get(pretag, prevalue) |
|
2893 |
|
2894 def __getattr__(self, name): |
|
2895 return getattr(self.storage, name) |
|
2896 |
|
2897 def list_confml(self): |
|
2898 """ |
|
2899 @return: array of confml file references. |
|
2900 """ |
|
2901 res = self.storage.list_resources(self.predefined['confml_path'], True) |
|
2902 res += super(Layer, self).list_confml() |
|
2903 return res |
|
2904 |
|
2905 def list_implml(self): |
|
2906 """ |
|
2907 @return: array of implml file references. |
|
2908 """ |
|
2909 res = self.storage.list_resources(self.predefined['implml_path'], True) |
|
2910 res += super(Layer, self).list_implml() |
|
2911 return res |
|
2912 |
|
2913 def list_content(self): |
|
2914 """ |
|
2915 @return: array of content file references. |
|
2916 """ |
|
2917 res = self.storage.list_resources(self.predefined['content_path'], True) |
|
2918 res += super(Layer, self).list_content() |
|
2919 return res |
|
2920 |
|
2921 def list_doc(self): |
|
2922 """ |
|
2923 @return: array of document file references. |
|
2924 """ |
|
2925 res = self.storage.list_resources(self.predefined['doc_path'], True) |
|
2926 res += super(Layer, self).list_doc() |
|
2927 return res |
|
2928 |
|
2929 def confml_folder(self): |
|
2930 cpath = self.storage.get_current_path() |
|
2931 spath = self.predefined['confml_path'] |
|
2932 return Folder(self.storage, utils.resourceref.join_refs([cpath, spath])) |
|
2933 |
|
2934 def implml_folder(self): |
|
2935 cpath = self.storage.get_current_path() |
|
2936 spath = self.predefined['implml_path'] |
|
2937 return Folder(self.storage, utils.resourceref.join_refs([cpath, spath])) |
|
2938 |
|
2939 def content_folder(self): |
|
2940 cpath = self.storage.get_current_path() |
|
2941 spath = self.predefined['content_path'] |
|
2942 return Folder(self.storage, utils.resourceref.join_refs([cpath, spath])) |
|
2943 |
|
2944 def doc_folder(self): |
|
2945 cpath = self.storage.get_current_path() |
|
2946 spath = self.predefined['doc_path'] |
|
2947 return Folder(self.storage, utils.resourceref.join_refs([cpath, spath])) |
|
2948 |
|
2949 def list_all_resources(self, empty_folders=False): |
|
2950 """ |
|
2951 Returns a list of all layer related resource paths with full path in the storage. |
|
2952 """ |
|
2953 lres = [] |
|
2954 mypath = self.get_current_path() |
|
2955 |
|
2956 for folderpath in sorted(self.predefined.values()): |
|
2957 lres += self.storage.list_resources(folderpath, recurse=True, empty_folders=empty_folders) |
|
2958 |
|
2959 lres += super(Layer, self).list_all_resources(empty_folders) |
|
2960 |
|
2961 return lres |
|
2962 |
|
2963 def list_all_related(self, empty_folders=False): |
|
2964 """ |
|
2965 Returns a list of all (non confml) layer related resource paths with full path in the storage. |
|
2966 """ |
|
2967 lres = [] |
|
2968 predef = self.predefined.copy() |
|
2969 del predef['confml_path'] |
|
2970 mypath = self.get_current_path() |
|
2971 for folderpath in sorted(predef.values()): |
|
2972 lres += self.storage.list_resources(folderpath, recurse=True, empty_folders=empty_folders) |
|
2973 lres += super(Layer, self).list_all_resources(empty_folders=empty_folders) |
|
2974 |
|
2975 return lres |
|
2976 |
|
2977 |
|
2978 class Rule(object): |
|
2979 """ |
|
2980 Base class for Rules in the system. |
|
2981 """ |
|
2982 def __init__(self): |
|
2983 raise exceptions.NotSupportedException() |
|
2984 |
|
2985 |
|
2986 class FactoryBase(object): |
|
2987 pass |
|
2988 |
|
2989 class Factory(object): |
|
2990 def __getattr__(self, name): |
|
2991 """ |
|
2992 The Factory getattr find all subclasses for the Factory and searches for given attr |
|
2993 in those. |
|
2994 """ |
|
2995 for sub_factory in utils.all_subclasses(FactoryBase): |
|
2996 try: |
|
2997 return getattr(sub_factory(), name) |
|
2998 except AttributeError: |
|
2999 continue |
|
3000 raise AttributeError("type object %s has no attribute '%s'" % (self.__class__, name)) |
|
3001 |
|
3002 def get_mapper(modelname): |
|
3003 """ |
|
3004 Return a instance of appropriate mapper for given model. |
|
3005 """ |
|
3006 mapmodule = __import__('cone.public.mapping') |
|
3007 return mapmodule.public.mapping.BaseMapper() |
|
3008 |
|
3009 |
|
3010 ################################################################## |
|
3011 class NullHandler(logging.Handler): |
|
3012 """ |
|
3013 Default handler that does not do anything. |
|
3014 """ |
|
3015 def emit(self, record): |
|
3016 pass |
|
3017 |
|
3018 #Initialization of default logger that contains NullHandler. |
|
3019 logger = logging.getLogger('cone') |
|
3020 logger.addHandler(NullHandler()) |