diff -r 000000000000 -r 2e8eeb919028 configurationengine/source/cone/public/api.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/configurationengine/source/cone/public/api.py Thu Mar 11 17:04:37 2010 +0200 @@ -0,0 +1,3020 @@ +# +# Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +# All rights reserved. +# This component and the accompanying materials are made available +# under the terms of "Eclipse Public License v1.0" +# which accompanies this distribution, and is available +# at the URL "http://www.eclipse.org/legal/epl-v10.html". +# +# Initial Contributors: +# Nokia Corporation - initial contribution. +# +# Contributors: +# +# Description: +# +""" +Cone public API. +The core interface to the ConE functionality. +""" + +import os +import re +import sys +import logging +import copy +import sets + +import exceptions, utils, container, mapping + +class Base(container.ObjectContainer): + """ + The Base class is intended for capturing same kind of naming scheme. + """ + + + def __init__(self, ref="", **kwargs): + if len(utils.dottedref.split_ref(ref)) > 1: + raise exceptions.InvalidRef("Invalid reference for Base object %s!" % ref) + self.ref = ref + container.ObjectContainer.__init__(self, ref) + for arg in kwargs.keys(): + if kwargs.get(arg) != None: + setattr(self, arg, kwargs.get(arg)) + + def __repr__(self): + dict = self._dict() + return "%s(%s)" % (self.__class__.__name__, dict) + + def _get_mapper(self,modelname): + """ + Return a instance of appropriate mapper for given model. + """ + return mapping.BaseMapper() + + def _compare(self, other, dict_keys=None): + """ + Compare the attributes of elements + """ + if isinstance(other, Base): + keys = dict_keys or self._dict().keys() + for key in keys: + self_attr = None + other_attr = None + try: + self_attr = getattr(self, key) + other_attr = getattr(other, key) + except AttributeError: + # If the attribute is not found from either elements + # ignore it entirely + if self_attr == None and other_attr == None: + continue + if self_attr != other_attr: + return False + # If all given keys match report this as as similar element + return True + else: + return False + + def _clone(self, **kwargs): + """ + A generic implementation for cloning the object. + Copies all (public) members in dictionary. + To clone objects recursively set the recursion level with recursion param. + @param recursion: Boolean to define recursion on or off + @param recursion_depth: positive integer to define recursion depth. default is -1 which will + perform recursion to all objects. + """ + dict = self._dict() + if kwargs.get('class_instance'): + class_instance = kwargs.get('class_instance') + del kwargs['class_instance'] + else: + class_instance = self.__class__ + obj = class_instance(**dict) + # Remove all children created at the construction phase + # This is needed when the recursion adds children to the object so that there are not duplicates + obj._order = [] + obj._children = {} + + # handle the recursion argument + recursion = kwargs.get('recursion', False) + if recursion: + recursion_depth = kwargs.get('recursion_depth', -1) + if recursion_depth < 0 or recursion_depth > 0: + # decrease the recursion + kwargs['recursion_depth'] = recursion_depth - 1 + for child in self._objects(): + obj._add(child._clone(**kwargs), container.APPEND) + return obj + + def _dict(self): + """ + Return the public variables in a dictionary + """ + dict = {} + for key in self.__dict__.keys(): + if key.startswith('_'): + continue + else: + dict[key] = self.__dict__[key] + return dict + + def _default_object(self, name): + return Base(name) + + @property + def fqr(self): + """ + Return a Fully Qualified Ref, which is the full name of the reference. + Joins the namespace and ref to one string. + @return: A string + """ + return utils.dottedref.join_refs([self.namespace, self.get_ref()]) + + @property + def namespace(self): + """ + @return: The namespace of the object. + """ + containerpath = "" + path = "" + parentcontainer = self.find_parent(container=True) + parent = self.find_parent(type=Base) + paths = [] + while parent and parent != parentcontainer: + """ Skip the element if it is supposed to be hidden. Begins with _. """ + if not parent.get_ref().startswith('_'): + paths.append(parent.get_ref()) + parent = parent._get_parent() + if parentcontainer: + paths.append(parentcontainer.namespace) + paths.reverse() + return utils.dottedref.join_refs(paths) + + def get_fullref(self): + """ + Return a full reference, reference including a + possible index of the object in list. + e.g. ref can be bar[1] or just the normal bar. + + @return: The full reference of the object. + """ + if self.parent and utils.is_list(self.parent._get(self.ref)): + return "%s[%s]" % (self.ref, self.get_index()) + else: + return self.ref + + def get_fullfqr(self): + """ + Return a full reference, reference including a + possible index of the object in list. + ref and adds index. + @return: A string + """ + return utils.dottedref.join_refs([self.get_fullnamespace(), self.get_fullref()]) + + def get_fullnamespace(self): + """ + @return: The full namespace of the object with possible indexes of the parent objects + """ + containerpath = "" + path = "" + parentcontainer = self.find_parent(container=True) + parent = self.find_parent(type=Base) + paths = [] + while parent and parent != parentcontainer: + paths.append(parent.get_fullref()) + parent = parent.parent + if parentcontainer: + paths.append(parentcontainer.namespace) + paths.reverse() + return utils.dottedref.join_refs(paths) + + def get_storage(self): + """ + Get the root storage from the root object. + """ + if self._find_parent(): + return self._find_parent().get_storage() + else: + raise exceptions.StorageException("Storage is not found from root!") + + def get_project(self): + """ + Get the root project from the root object. + """ + if isinstance(self, Project): + return self + elif self._find_parent(): + return self._find_parent().get_project() + else: + raise exceptions.NotFound("Project not found!!") + + def get_default_view(self): + """ + Get the default view from the root object. + """ + try: + return self._find_parent().get_default_view() + except exceptions.NotFound: + raise exceptions.NotFound("Default View is not found! No root configuration?") + + def get_root(self): + """ + Get the root object + """ + try: + return self._find_parent().get_root() + except exceptions.NotFound: + return self + + def get_root_configuration(self): + """ + Get the root object + """ + if self.find_parent(type=Configuration): + return self.find_parent(type=Configuration).get_root_configuration() + elif isinstance(self, Configuration): + return self + else: + return None + + def get_index(self): + """ + @return : the index of the data element for sequential data defined inside the same configuration. + 0 for normal data. + """ + # Get the list of items from parent which contains this element and ask my own index + # Make sure that the returned element is a list with get_list + selflist = utils.get_list(self._get_parent()._get(self.get_ref())) + return selflist.index(self) + + def find_parent(self, **kwargs): + """ + find the closest parent object of given type. + e.g. find_parent(type=Configuration) returns the closest parent + Configuration parent instance + @param type: class definitiob + """ + type = kwargs.get('type', None) + container = kwargs.get('container', False) + try: + parent = self._find_parent() + if type and isinstance(parent, type): + return parent + elif container and hasattr(parent, 'container'): + return parent + else: + return parent.find_parent(**kwargs) + except exceptions.NotFound: + return None + + def add(self, child, policy=container.REPLACE): + """ + A generic add function to add child objects. The function is intended to act as + proxy function that call the correct add function based on the child objects class. + + Example: obj.add(Feature("test")), actually obj.add_feature(Feature("test")) + @param child: the child object to add + @raise IncorrectClassError: if the given class cannot be added to this object. + """ + raise exceptions.NotSupportedException("Cannot add %s object to %s" % (child, self)) + + def get_elem(self, fqr): + """ + A generic get function to get child objects and members. The function uses getattr + to traverse downwards the the object tree. The returned object is the final object or attribute + if it is found. Raises AttributeError if the child is not found. + + Example: obj.get('test.bar'), returns child obj.test.bar + @param fqr: the fully qualified ref to the object + @raise AttributeError: if the given ref is not found. + """ + return None + + +class Project(Base): + """ + A project is a container that can hold several Configuration objects. + """ + + def __init__(self, storage, **kwargs): + """ + Project constructor + """ + Base.__init__(self, "") + """ Try to set the model and tet the actual configuration class """ + try: + self._model = storage.persistentmodule.MODEL + except AttributeError: + self._model = None + + self.set_storage(storage) + self.update() + self.loaded = {} + + def __add_loaded__(self, ref, obj): + """ + Add the object to loaded + """ + self.loaded[ref] = {'counter': 0, 'obj': obj} + + def __get_loaded__(self, ref): + """ + Get a loaded object if it is existing and increase the reference counter + @param ref: + @return: The loaded object if it exists. None if it does not. + """ + if self.loaded.has_key(ref): + return self.loaded[ref]['obj'] + else: + return None + + def __loaded__(self, ref): + """ + Get a loaded object if it is existing and increase the reference counter + @param ref: + @return: The loaded object if it exists. None if it does not. + """ + if self.loaded.has_key(ref): + self.loaded[ref]['counter'] += 1 + else: + raise exceptions.NotFound("ref %s is not found from loaded!" % ref) + + def __unloaded__(self, ref): + """ + returns True when the reference count is zero and object can be released. + """ + if self.loaded.has_key(ref): + self.loaded[ref]['counter'] -= 1 + if self.loaded[ref]['counter'] == 0: + del self.loaded[ref] + return True + else: + return False + else: + return True + + def _supported_type(self, obj): + if isinstance(obj, Configuration) \ + or isinstance(obj, ConfigurationProxy): + return True + else: + return False + + + def update(self): + """ + update the root confml files as configurations + """ + root_confmls = self.get_storage().list_resources(".") + root_confmls = utils.resourceref.filter_resources(root_confmls, "\.confml") + for rootml in root_confmls: + self._add(ConfigurationProxy(rootml)) + + def get_storage(self): + """ + Get the Storage instance of this Project. + """ + return self.storage + + def set_storage(self, storage): + """ + Set the Storage instance of this Project. + """ + if isinstance(storage, Storage): + self.storage = storage + else: + raise exceptions.StorageException("The given storage is not a instance of Storage!") + + def list_configurations(self, filter_or_filters=None): + """ + List the direct child objects of the project (Root configurations) + @param filter_or_filters: A regular expression or list of regular expressions + used for filtering the configuration paths. If None, all configurations are + returned. + @return: a list for configuration file paths + """ + filters = None + if isinstance(filter_or_filters, basestring): filters = [filter_or_filters] + elif filter_or_filters is not None: filters = filter_or_filters + + configs = [obj.get_path() for obj in self._objects()] + + if filters is not None: + result = [] + for config in configs: + for filter in filters: + if re.match(filter, config) is not None: + result.append(config) + break + return result + else: + return configs + + def list_all_configurations(self): + """ + List all configuration objects of the project (all configurations) + @return: a list for configuration file paths + """ + return [obj.get_path() for obj in self._traverse(type=(Configuration, ConfigurationProxy))] + + def get_configuration(self, path): + """ + Get a configuration object from the given path + @param path: path to configuration + @return: a instance of Configuration. + """ + # Load the configuration object if it is not already loaded + try: + return self._get(utils.resourceref.to_objref(utils.resourceref.norm(path))) + except exceptions.NotFound, e: + if self.storage.is_resource(utils.resourceref.norm(path)): + proxy = ConfigurationProxy(utils.resourceref.norm(path)) + proxy._set_parent(self) + return proxy + else: + raise e + + def is_configuration(self, path): + """ + Return true if the given path is a configuration object in this Project. + @param path: path to configuration + @return: Boolean return value. + """ + # Changed from list_all_configurations to list_configurations + # (list_all_configurations causes a insane performance problem with _traverse) + return path in self.list_configurations() + + def add_configuration(self, config): + """ + Add a Configuration object to this project + """ + if isinstance(config, Configuration): + if self.is_configuration(config.get_path()): + raise exceptions.AlreadyExists("%s" % config.get_path()) + self._add(config) + self.__add_loaded__(config.get_path(), config) + self.__loaded__(config.get_path()) + else: + raise exceptions.IncorrectClassError("Only Configuration instance can be added to Project!") + + def create_configuration(self, path, namespace=""): + """ + Create a Configuration object to this project + """ + config = self.get_configuration_class()(utils.resourceref.norm(path), namespace=namespace) + self.add_configuration(config) + return config + + def remove_configuration(self, path): + """ + Remove a Configuration by its reference + """ + # remove configuration as an object and try to remove it from the storage + self._remove(utils.resourceref.to_objref(path)) + try: + self.storage.delete_resource(path) + except exceptions.NotSupportedException: + pass + return + + def import_configuration(self, configuration): + """ + Import a configuration object from another storage + """ + self.storage.import_resources(configuration.list_resources(), configuration.get_storage()) + return + + def export_configuration(self, configuration, export_storage, empty_folders=False): + """ + Export a configuration object to another storage + """ + # First clone the configuration and then import the rest of the configuration resources + if isinstance(configuration, ConfigurationProxy): + configuration = configuration._get_obj() + + export_storage.unload(configuration.get_full_path(),configuration) + for child in configuration._traverse(type=Configuration): + export_storage.unload(child.get_full_path(),child) + + #If the configuration is not in the root of the project adding the path + #to final exporting source path. + #l = [] + cpath = utils.resourceref.get_path(configuration.get_path()) + resr = [utils.resourceref.join_refs([cpath,related]) \ + for related in configuration.get_layer().list_all_related(empty_folders)] + self.storage.export_resources(resr ,export_storage, empty_folders) + return + + def get_configuration_class(self): + """ + return the default configuration class that is used with the model. + """ + return utils.get_class(self._model, Configuration) + + def save(self): + """ + Save the object to the permanent Storage object. Calls the save operation for + all the children and also for the Storage. + """ + for child in self._objects(): + if isinstance(child, (Configuration, ConfigurationProxy)): + child.save() + self.storage.save() + + def close(self): + """ + Close the Project. + """ + for child in self._objects(): + if isinstance(child, (Configuration, ConfigurationProxy)): + child.close() + self.storage.close() + + def load(self, path): + """ + Load an object from a reference. The given reference is loaded once from storage + and stored as a loaded object to the Project. Sequential loads to the same ref will + return the same object. + @param path: The reference where to load the object + @raise StorageException: if the given object cannot be loaded as an + object from this storage + """ + if not self.__get_loaded__(path): + configuration = self.get_storage().load(path) + if configuration.get_ref() == 'unknown': + configuration.set_ref(utils.resourceref.to_dref(path)) + self.__add_loaded__(path, configuration) + """ increase the ref counter """ + self.__loaded__(path) + return self.__get_loaded__(path) + + def unload(self, path, object): + """ + Release the given ref, which decreases the reference counter of the given ref. + @param path: The reference where to store the object + @param object: The object instance to dump + @raise StorageException: if the given object cannot be dumped to this storage + """ + if self.__unloaded__(path): + self.get_storage().unload(path, object) + + def get_path(self): + """ + Return the path of the project, which is always root + """ + return "" + + +class CompositeConfiguration(Base): + """ + A base class for composite Configuration objects. + """ + def __init__(self, ref="", **kwargs): +# self.meta = {} +# self.desc = "" + super(CompositeConfiguration, self).__init__(ref, **kwargs) + self.container = True + + def add_configuration(self, config): + """ + Add an existing Configuration to this configuration + @param config: A Configuration instance: + @return: None + """ + """ + Merge the default view features from added config to this configs _default_view. + """ + self._add(config) + + def include_configuration(self, configref): + """ + Add an existing Configuration to this configuration by its resource reference + @param config: A Configuration instance: + @return: None + """ + # add the configuration load proxy to this configuration instead + # adding the configuration directly + self._add(ConfigurationProxy(configref)) + + def create_configuration(self, path): + """ + Create a new configuration by its name to the Configuration. + 1. Create new Configuration object + 2. Create new ConfigurationProxy + 3. Add proxy to this object + 4. Set proxy to point to the created Configuration object + @param path: The reference of the configuration to create + @return: The new configuration object. + """ + # normalise the path + normpath = utils.resourceref.norm(path) + cklass = self.get_configuration_class() + conf = cklass(normpath, namespace=self.namespace) + proxy = ConfigurationProxy(normpath) + self.add_configuration(proxy) + proxy._set_obj(conf) + return conf + + def remove_configuration(self, path): + """ + Remove a Layer object from the Configuration by its reference. + """ + self._remove(utils.resourceref.to_objref(path)) + + def list_configurations(self): + """ + List all Layer objects in the Configuration + @return: a copy array of layer references. + """ + return [config.get_path() for config in self._objects(type=(Configuration, ConfigurationProxy))] + + def list_all_configurations(self): + """ + List all Layer objects in the Configuration + @return: a copy array of layer references. + """ + # TODO + # huge performance problem + return [config.get_path() for config in self._traverse(type=(Configuration, ConfigurationProxy))] + + def get_configuration(self, path): + """ + Get a Layer object by if path + @return: a Layer object + """ + return self._get(utils.resourceref.to_objref(path)) + + def get_configuration_by_index(self, index): + """ + Get a Layer object by if indexing number + @return: a Layer object + """ + configs = self._objects(type=(Configuration, ConfigurationProxy)) + return configs[index] + + def get_last_configuration(self): + """ + Get the last Layer object from this configuration hierarchy. + @return: a Layer object + """ + last_config = self + try: + last_config = last_config.get_configuration_by_index(-1) + return last_config.get_last_configuration() + except IndexError: + return self + + def get_configuration_class(self): + """ + return the default configuration class retrieved from the project if it is found. + Otherwise return cone.public.api.Configuration. + """ + try: + return self.get_project().get_configuration_class() + # catch the Parent/Project NotFound exception + except exceptions.NotFound: + return Configuration + + def add(self, child, policy=container.REPLACE): + """ + A generic add function to add child objects. The function is intended to act as + proxy function that call the correct add function based on the child objects class. + + Example: obj.add(Feature("test")), actually obj.add_feature(Feature("test")) + @param child: the child object to add + @raise IncorrectClassError: if the given class cannot be added to this object. + """ + if isinstance(child, Configuration): + self.add_configuration(child) + elif isinstance(child, ConfigurationProxy): + self.add_configuration(child) + elif isinstance(child, Base): + self._add(child) + else: + raise exceptions.IncorrectClassError("Cannot add %s to %s" % (child, self)) + + def layered_content(self, layers=None): + """ + fetch content from first to last and override content + if it is found from a later layer + Create an array of the layers based on the layer indexes. + """ + configuration_array = [] + if layers == None: + configuration_array = self.list_configurations() + else: + all = self.list_configurations() + for i in layers: + configuration_array.append(all[i]) + + content = container.DataContainer() + for configuration_path in configuration_array: + content_folder = self.get_configuration(configuration_path).get_layer().content_folder() + content_path = content_folder.get_current_path() + for content_file in content_folder.list_resources("", True): + source_file = utils.resourceref.join_refs([content_path, content_file]) + content.add_value(content_file, source_file) + + return content + + +class Configuration(CompositeConfiguration): + """ + A Configuration is a container that can hold several Layer objects. + """ + + def __init__(self, ref="", **kwargs): + self.path = kwargs.get('path') or ref + self.namespace = kwargs.get('namespace', '') + self.name = utils.resourceref.to_objref(self.path) + super(Configuration, self).__init__(utils.resourceref.to_objref(self.path)) + self.container = True + + def _default_object(self, name): + return Feature(name) + + def _supported_type(self, obj): + if isinstance(obj, Configuration) \ + or isinstance(obj, Feature) \ + or isinstance(obj, Data) \ + or isinstance(obj, ConfigurationProxy) \ + or isinstance(obj, View) \ + or isinstance(obj, Base): + return True + else: + return False + + def _dict(self): + """ + Return the public variables in a dictionary + """ + dict = {} + for key in self.__dict__.keys(): + if key.startswith('_'): + continue + else: + dict[key] = self.__dict__[key] + dict['namespace'] = self.namespace + return dict + + def get_name(self): + """ + Return the name of the configuration + """ + return self.name + + def set_name(self, name): + """ + Set the name + """ + self.name = name + + def get_path(self): + """ + Return the path of the configuration resource + """ + return self.path + + def set_path(self, path): + """ + Set the path of the configuration resource, and update the name and ref to correspond + """ + self.path = path + #self.name = utils.resourceref.to_objref(self.path) + self.set_ref(utils.resourceref.to_objref(self.path)) + + #@property + def get_full_path(self): + """ + Return the path of the configuration resource + """ + try: + parentconfig = self._find_parent(type=Configuration) + parent_path = utils.resourceref.get_path(parentconfig.get_path()) + except exceptions.NotFound: + parent_path = "" + + return utils.resourceref.join_refs([parent_path, self.path]) + + def get_layer(self): + """ + Get the layer object where this Configuration is located. + """ + if not hasattr(self, "layer"): + layerpath = utils.resourceref.get_path(self.get_path()) + # hardcoded removal of confml folder from the layer path it is there + layerpath = utils.resourceref.remove_end(layerpath, '/confml') + self.layer = Layer(self.get_storage(), layerpath) + """ Add the sublayers to this layer if they are different from this configuration """ + for configpath in self.list_configurations(): + sublayer_path = utils.resourceref.get_path(self.get_configuration(configpath).get_full_path()) + sublayer_path = utils.resourceref.remove_end(sublayer_path, '/confml') + if sublayer_path != utils.resourceref.get_path(self.get_path()): + self.layer.add_layer(self.get_configuration(configpath).get_layer()) + return self.layer + + def set_namespace(self, namespace): + """ + @param namespace: The new namespace of the object + """ + self._namespace = namespace + #self.root.set_namespace(namespace) + + def get_namespace(self): + """ + @return: The reference of the object. + """ + return self._namespace + + def del_namespace(self): + """ + @return: The reference of the object. + """ + self._namespace = None + namespace = property(get_namespace, set_namespace, del_namespace) + + def list_resources(self, empty_folders=False): + """ + List all resources used in this configuration + """ + """ + 1. First ensure that all configuration resource files are added + 2. Then add all layer resources + 3. Make the list distinct + """ + + + resources = [self.get_full_path()] + for config in self._traverse(type=Configuration): + resources.append(config.get_full_path()) + layer = self.get_layer() + for resref in layer.list_all_resources(empty_folders): + resources.append(utils.resourceref.join_refs([layer.get_current_path(), resref])) + + return utils.distinct_array(resources) + + def get_resource(self, ref, mode="r"): + """ + Get the given resource as a Resource object. The resource is searched relative to the + Configuration path, e.g. Configuration('test/foo/root.confml') => searches from 'test/foo'. + @param ref: the reference path to the requested resource + @return: a instance of Resource. + """ + mypath = utils.resourceref.get_path(self.path) + myref = utils.resourceref.join_refs([mypath, ref]) + return self.get_storage().open_resource(myref, mode) + + def get_all_resources(self): + """ + Get all resources in resource list of Resource objects + """ + resources = [] + res_list = self.list_resources() + for res in res_list: + resources.append(self.get_storage().open_resource(res)) + return resources + + def get_root_resource(self): + """ + Get the configuration reference resource. + """ + return self.get_storage().open_resource(self.get_path()) + + def get_feature(self, ref): + """ + Get a feature object by its reference. + @param ref: The reference to the feature object. + @return: A Feature object + """ + return self._get(ref) + + def add_feature(self, feature, namespace=""): + """ + Add a feature object to the configuration. + @param feature: The Feature object to add. + @param namespace: The sub namespace for the feature. + e.g. to add fea2 under fea1 add_feature(fea2, 'fea1') + @return: None + """ + self._add_to_path(namespace, feature) + + def remove_feature(self, ref): + """ + remove feature by its reference + @param ref: + """ + self._remove(ref) + + def list_features(self): + """ + List immediate features found under the this configuration (the top nodes). + The features are also available via the _default_view of the configuration. + @return: a list of feature references. + """ + return [fea.get_ref() for fea in self._objects(type=Feature)] + + def list_all_features(self): + """ + List all features found under the this configuration. The features are also + available via the _default_view of the configuration. + @return: a list of feature references. + """ + return [fea.fqr for fea in self._traverse(type=Feature)] + + def add_data(self, data, policy=container.REPLACE): + """ + Add a data object to this configuration object. + @param data: The Data object to add. + @return: None + """ + if not self._has(data.attr): + self._add(DataContainer(data.attr, container=True)) + (namespace, name) = utils.dottedref.psplit_ref(data.get_fearef()) + self._get(data.attr)._add_to_path(namespace, data, policy) + + def get_data(self, ref): + """ + Get a data object by its reference. + @param ref: The reference to the data object. + @return: A Data object + """ + return self.data._get(ref) + + def remove_data(self, ref): + """ + remove feature by its reference + @param ref: + """ + self.data._remove(ref) + + def list_datas(self): + """ + List all datas found under the this configuration. + @return: a list of Data references. + """ + if self._has('data'): + return [dataelem.fqr for dataelem in self.data._objects(type=Data)] + else: + return [] + + def get_datas(self): + """ + List immediate datas found under the this configuration (the top nodes). + @return: a list of Data references. + """ + if self._has('data'): + return [dataelem for dataelem in self.data._objects(type=Data)] + else: + return [] + + def list_all_datas(self): + """ + List all Data elements found under the this configuration (or subconfigurations). + @return: a list of Data references. + """ + return [dataelem.fqr for dataelem in self._traverse(type=Data)] + + def get_all_datas(self): + """ + List all Data elements found under the this configuration (or subconfigurations). + @return: a list of Data references. + """ + return [dataelem for dataelem in self._traverse(type=Data)] + + def list_leaf_datas(self): + """ + List all leaf Data elements (i.e. actually modified settings) found under this configuration (or subconfigurations). + @return: A list of Data references. + """ + return [dataelem.fqr for dataelem in self._find_leaves(type=Data)] + + def get_leaf_datas(self): + """ + Get all leaf Data elements (i.e. actually modified settings) found under this configuration (or subconfigurations). + @return: A list of Data objects. + """ + return [dataelem for dataelem in self._find_leaves(type=Data)] + + def get_view(self, ref): + """ + Get a view object by its reference. + @param ref: The reference to the view object. + @return: A View object + """ + # Populate the view object before returning it + view = self._get(ref) + view.populate() + return view + + def add_view(self, viewname): + """ + Add a view object to the configuration. + @param viewname: The name of the view to add. + @return: None + """ + return self._add(View(viewname)) + + def remove_view(self, ref): + """ + Remove a view object from the configuration. + @param ref: The reference to the View. + @return: None + @raise NotFound: when view is not found. + """ + return self._remove(ref) + + def list_views(self): + """ + List all views found under the this configuration. + @return: a list of view references. + """ + return [view._path(self) for view in self._traverse(type=View)] + + def save(self): + """ + Save the object to the permanent Storage object. Calls the save operation of + all the children. + """ + for child in self._objects(): + if isinstance(child, (Configuration,ConfigurationProxy)): + child.save() + self.get_project().unload(self.get_full_path(), self) + + def close(self): + """ + Close the configuration + """ + for child in self._objects(): + if isinstance(child, (Configuration, ConfigurationProxy)): + child.close() +# if self.get_full_path() != "": +# self.get_project().unload(self.get_full_path(), self) + + def add(self, child, policy=container.REPLACE): + """ + A generic add function to add child objects. The function is intended to act as + proxy function that call the correct add function based on the child objects class. + + Example: obj.add(Feature("test")), actually obj.add_feature(Feature("test")) + @param child: the child object to add + @raise IncorrectClassError: if the given class cannot be added to this object. + """ + if isinstance(child, Feature): + self.add_feature(child) + elif isinstance(child, View): + self._add(child) + elif isinstance(child, (Data)): + self.add_data(child) + else: + super(Configuration, self).add(child) + + def get_default_view(self): + """ + Get the default view from this configuration hierarchy. + This returns always the view from the Root configuration point of view. + """ + try: + parent = self._find_parent_or_default() + if parent and isinstance(parent, Configuration): + return parent.get_default_view() + else: + if not hasattr(self, '_default_view'): + self._create_default_view() + return self._default_view + except exceptions.NotFound, e: + raise e + # raise exceptions.NotFound("Default View is not found! No root configuration?") + + def recreate_default_view(self): + try: + parent = self._find_parent_or_default() + if parent and isinstance(parent, Configuration): + parent.recreate_default_view() + else: + self._create_default_view() + except exceptions.NotFound, e: + raise e + # raise exceptions.NotFound("Default View is not found! No root configuration?") + + def _create_default_view(self): + # Rebuild the default view for this Configuration + self._default_view = View("_default_view", data=True) + self._default_view._parent= self + # First add all features of the configuration to the view. + # Then add all data elements under the features + for child in self._traverse(type=Feature): + self._default_view.add_feature(child, child.namespace) + for child in self._traverse(type=Data): + #parent_config = child._find_parent_or_default(type=Configuration) + #print "Adding data %s: fqr: %s from file %s." % (child.get_value(), child.fqr, parent_config.get_path()) + try: + fea = self._default_view.get_feature(child.fqr) + fea.add_data(child) + except exceptions.NotFound, e: + data_parent_config = child._find_parent_or_default(type=Configuration) + logging.getLogger('cone').info("Warning: Feature '%s' for data in %s not found." % (child.fqr, data_parent_config.get_path())) + +class ConfigurationProxy(container.LoadProxy): + """ + Configuration loading proxy. Loads the configuration from the given reference, when needed. + """ + def __init__(self, path, **kwargs): + """ + The ConfigurationProxy that represents a configuration that is included in another configuration. + @param ref: the reference to the storage resource + The ConfigurationProxy trust to get the store_interface from the parent object with get_storage() function. + + """ + container.LoadProxy.__init__(self, path) + self.set('_name', utils.resourceref.to_objref(path)) + + def _clone(self, **kwargs): + """ + A ConfigurationProxy specific implementation for cloning. + Copies all (public) members in dictionary. + To clone call the actual object that is proxied as well if the reqursion is on. + @param recursion: Boolean to define recursion on or off + @param recursion_depth: positive integer to define recursion depth. default is -1 which will + perform recursion to all objects. + """ + dict = self._dict() + obj = self.__class__(**dict) + # handle the recursion argument + recursion = kwargs.get('recursion', False) + if recursion: + recursion_depth = kwargs.get('recursion_depth', -1) + if recursion_depth < 0 or recursion_depth > 0: + # decrease the recursion + kwargs['recursion_depth'] = recursion_depth - 1 + newobj = self._get_obj()._clone(**kwargs) + obj._set_obj(newobj) + return obj + + def _dict(self): + """ + Return the public variables in a dictionary + """ + dict = {} + for key in self.__dict__.keys(): + if key.startswith('_'): + continue + else: + dict[key] = self.__dict__[key] + return dict + + def _get_mapper(self,modelname): + """ + Return a instance of appropriate mapper for given model. + """ + return mapping.BaseMapper() + +class Group(Base): + """ + A Group class. Group is used in View to group up other Group/Feature objects. + """ + def __init__(self, ref="", **kwargs): + super(Group, self).__init__(ref, **kwargs) + self.name = ref + self.support_data = kwargs.get("data", False) + + def _supported_type(self, obj): + if isinstance(obj, (Group, \ + Base, \ + _FeatureProxy, \ + FeatureLink)): + return True + else: + return False + + def _default_object(self, name): + return Group(name) + + def get_name(self): + """ + Return the name of the configuration + """ + return self.name + + def set_name(self, name): + """ + Set the name + """ + self.name + + def add(self, child, policy=container.REPLACE): + """ + A generic add function to add child objects. The function is intended to act as + proxy function that call the correct add function based on the child objects class. + + Example: obj.add(Feature("test")), actually obj.add_feature(Feature("test")) + @param child: the child object to add + @raise IncorrectClassError: if the given class cannot be added to this object. + """ + if isinstance(child, (Group, \ + Base, \ + _FeatureProxy, \ + FeatureLink)): + self._add(child) + else: + raise exceptions.IncorrectClassError("Cannot add %s to %s" % (child, self)) + + def get_name(self): + """ + Return the name of the configuration + """ + return self.name + + def set_name(self, name): + """ + Set the name + """ + self.name = name + + def add_feature(self, feature, path=""): + """ + Add feature to this Group. + """ + if not isinstance(feature, Feature): + raise exceptions.IncorrectClassError("add_feature requires instance of Feature!! Given %s" % feature) + if not self.support_data: + self._add_to_path(path, _FeatureProxy(feature._name, feature)) + else: + self._add_to_path(path, _FeatureDataProxy(feature._name, feature)) + + def remove_feature(self, ref): + """ + remove a given feature from this view by reference. + @param ref: + """ + self._remove(ref) + + def get_feature(self, ref): + """ + @param path: The path (ref) to the given feature + """ + try: + return self._get(ref) + except exceptions.NotFound: + raise exceptions.NotFound("Feature '%s' not found." % ref) + + def get_features(self, ref, **kwargs): + """ + Get a list of features that match the ref. + Example1: get_features('foo.bar') would be the same as get_feature('foo.bar'), but this returns + always a list []. + Example2: get_features('foo.*') would try to retrieve a list of all foo children. + Example3: get_features('foo.*', type='') would try to retrieve a list of all foo children, + that have a defined type. + @param path: The path (ref) to the given feature or xpath like expression + @return: A list of features. + """ + (startref, last) = utils.dottedref.psplit_ref(ref) + startelem = self._get(startref) + if last == '**': + return [fea for fea in startelem._traverse(**kwargs)] + elif last == '*': + return [fea for fea in startelem._objects(**kwargs)] + else: + return [self._get(ref)] + + def list_features(self): + """ + Return a array of all Feature children references under this object. + """ + return [fea.get_ref() for fea in self._objects(type=(_FeatureProxy))] + + def list_all_features(self): + """ + Return a array of all Feature children references under this object. + """ + return [fea.fqr for fea in self._traverse(type=(_FeatureProxy))] + + def add_group(self, groupname): + """ + """ + self._add(Group(groupname)) + + def remove_group(self, ref): + """ + remove a given feature from this view by reference. + @param ref: + """ + self._remove(ref) + + def get_group(self, ref): + """ + @param path: The path (ref) to the given feature + """ + return self._get(ref) + + def list_groups(self): + """ + """ + return [group.get_name() for group in self._objects(type=Group)] + + def populate(self): + """ + Populate or fetch the link to the actual feature for this featureproxy. + This method fetches the feature to the _obj member variable and populates also + subfeatures. + """ + for child in self._traverse(type=FeatureLink): + child.populate() + + + +class View(Group): + """ + A View class. View is intended to create new or different hierarchies of existing features. A View can contain Group and/or Feature objects. + """ + def __init__(self, ref="", **kwargs): + super(View, self).__init__(self.to_ref(ref), **kwargs) + self.name = ref + self.container = True + + @classmethod + def to_ref(cls, ref): + """ + return a view reference converted from name + """ + return ref.replace('.', '').replace('/', '') + + +class Feature(Base): + """ + A Feature class. Feature is the base for all Configurable items in a Configuration. + """ + PROPERTIES = ['value'] + def __init__(self, ref="", **kwargs): + super(Feature, self).__init__(ref, **kwargs) + self.name = kwargs.get('name', ref) + self.type = kwargs.get('type', None) + self._dataproxy = None + + def __copy__(self): + dict = {} + for key in self.__dict__.keys(): + if key.startswith('_') or key == 'ref': + continue + else: + dict[key] = self.__dict__[key] + fea = self.__class__(self.ref, **dict) + return fea + + + def _supported_type(self, obj): + # For now support added for desc element via support for Base + if isinstance(obj, (Feature, Option, Base)): + return True + else: + return False + + def add(self, child, policy=container.REPLACE): + """ + A generic add function to add child objects. The function is intended to act as + proxy function that call the correct add function based on the child objects class. + + Example: obj.add(Feature("test")), actually obj.add_feature(Feature("test")) + @param child: the child object to add + @raise IncorrectClassError: if the given class cannot be added to this object. + """ + if isinstance(child, Feature): + self.add_feature(child) + elif isinstance(child, Option): + self._add(child, policy) + elif isinstance(child, Base): + self._add(child, policy) + else: + raise exceptions.IncorrectClassError("Cannot add %s to %s" % (child, self)) + + def get_name(self): + """ + Return the name of the configuration + """ + return self.name + + def set_name(self, name): + """ + Set the name + """ + self.name = name + + def get_type(self): + return self.type + + def set_type(self, type): + self.type = type + + def add_feature(self, feature, path=""): + """ + @param feature: The Feature object to add + """ + configuration = self.find_parent(type=Configuration) + if configuration: + feapath = utils.dottedref.join_refs([self._path(configuration), path]) + configuration.add_feature(feature, feapath) + else: + self._add_to_path(path, feature) + + def get_feature(self, path): + """ + @param path: The path (ref) to the given feature + """ + return self._get(path) + + def remove_feature(self, ref): + """ + remove a given feature from this view by reference. + @param ref: + """ + configuration = self.find_parent(type=Configuration) + if configuration: + fullfqr = utils.dottedref.join_refs([self._path(configuration), ref]) + configuration.remove_feature(fullfqr) + else: + self._remove(ref) + + def list_features(self): + """ + Return a array of all Feature children references under this object. + """ + return [fea.get_ref() for fea in self._objects(type=Feature)] + + def list_all_features(self): + """ + Return a array of all Feature children references under this object. + """ + return [fea._path(self) for fea in self._traverse(type=Feature)] + + def add_option(self, option): + """ + @param option: option object + """ + if not isinstance(option, Option): + raise TypeError("%r is not an instance of Option!" % option) + self._add(option) + + def create_option(self, name, value): + """ + @param name: option name + @param value: option value + """ + self._add(Option(name, value)) + + def get_option(self, ref): + """ + @param name: The option reference of the option (as returned by list_options()) + """ + real_ref = 'opt_' + ref + obj = self._get(real_ref) + if not isinstance(obj, Option): + raise TypeError('Object %r is not an instance of Option (%r instead)' % (real_ref, type(obj))) + return obj + + def remove_option(self, ref): + """ + remove a given option from this feature by option reference. + """ + real_ref = 'opt_' + ref + obj = self._get(real_ref) + if not isinstance(obj, Option): + 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))) + self._remove(real_ref) + + def list_options(self): + """ + Return a array of all Option children references under this object. + """ + # Return option refs without the leading 'opt_' + return [opt.ref[4:] for opt in self._objects(type=Option)] + + def get_value(self, attr=None): + """ + Get the current value of the feature + @param attr: The attribute name of the data. E.g. attr='data', attr='rfs' + """ + # Do not allow getting of setting of sequence values directly with Feature object + if not self.is_sequence(): + return self.get_value_cast(self.dataproxy._get_value(attr), attr) + else: + """ get the feature specific data from sequence => a column of data table """ + coldata = [] + feasequence = self.get_sequence_parent() + feapath = self._path(feasequence) + for row in feasequence.data: + feadata = row.get_feature(feapath) + coldata.append(feadata.value) + return coldata + + def set_value(self, value, attr=None): + """ + Set the current value for this feature. Set the value on the topmost layer. + @param value: the value to set + """ + # Do not allow setting of setting of sequence values directly with Feature object + if not self.is_sequence(): + value = self.set_value_cast(value, attr) + self.dataproxy._set_value(value, attr) + + def del_value(self, attr=None): + """ + Delete the topmost value for this feature. + """ + if not self.is_sequence(): + self.dataproxy._del_value(attr) + + def get_value_cast(self, value, attr=None): + """ + A function to perform the value type casting in get operation + @param value: the value to cast + @param attr: the attribute which is fetched from model (normally in confml either None='data' or 'rfs') + """ + return value + + def set_value_cast(self, value, attr=None): + """ + A function to perform the value type casting in the set operation + @param value: the value to cast + @param attr: the attribute which is fetched from model (normally in confml either None='data' or 'rfs') + """ + return value + + def get_original_value(self, attr=None): + """ + Get the current value of the feature + @param attr: The attribute name of the data. E.g. attr='data', attr='rfs' + """ + # Do not allow getting of setting of sequence values directly with Feature object + if not self.is_sequence(): + return self.dataproxy._get_value(attr) + else: + """ get the feature specific data from sequence => a column of data table """ + coldata = [] + feasequence = self.get_sequence_parent() + feapath = self._path(feasequence.data) + for row in feasequence.data: + feadata = row.get_feature(feapath) + coldata.append(feadata.value) + return coldata + + def add_data(self, data): + """ + Add a data value. + @param data: A Data object + """ + try: + return self.dataproxy._add_data(data) + except AttributeError: + self.dataproxy = self.get_default_view().get_feature(self.get_fullfqr()) + return self.dataproxy._add_data(data) + + def get_data(self, attr=None): + """ + Helper function to get the topmost data value from the default view. + @param attr: The attribute name of the data. E.g. attr='data', attr='rfs' + """ + try: + return self.dataproxy._get_data(attr) + except AttributeError: + self.dataproxy = self.get_default_view().get_feature(self.get_fullfqr()) + return self.dataproxy._get_data(attr) + + def get_datas(self): + """ + Helper function to get the data values from the default view. + """ + try: + return self.dataproxy._get_datas() + except AttributeError: + self.dataproxy = self.get_default_view().get_feature(self.get_fullfqr()) + return self.dataproxy._get_datas() + + def get_valueset(self): + """ + Get the ValueSet object for this feature, that has the list of available values. + """ + if self.get_type() == 'boolean': + return ValueSet([True, False]) + elif self.get_type() == 'int': + return ValueRange(0, sys.maxint) + elif self.get_type() == 'string': + return ValueRe('.*') + elif self.get_type() in ('selection', 'multiSelection'): + values = [] + for opt in self._objects(type=Option): + v = opt.get_value() + if v is not None: values.append(v) + return ValueSet(values) + + def is_sequence(self): + """ Return true if the feature is a sequence or part of a sequence """ + try: + return self._parent.is_sequence() + except AttributeError: + return False + + def get_sequence_parent(self): + """ Try to get a FeatureSequence object for this Feature if it is found """ + try: + return self._parent.get_sequence_parent() + except AttributeError: + return None + + def getdataproxy(self): + if self._dataproxy == None: + self.dataproxy = self.get_default_view().get_feature(self.get_fullfqr()) + return self._dataproxy + def setdataproxy(self, value): self._dataproxy = value + def deldataproxy(self): self._dataproxy = None + dataproxy = property(getdataproxy, setdataproxy, deldataproxy) + value = property(get_value, set_value, del_value) + +class FeatureSequence(Feature): + POLICY_REPLACE = 0 + POLICY_APPEND = 1 + POLICY_PREPEND = 2 + """ + A Feature class. Feature is the base for all Configurable items in a Configuration. + """ + dataelem_name = '?datarows' + template_name = '?template' + def __init__(self, ref="", **kwargs): + super(FeatureSequence, self).__init__(ref) + self.name = kwargs.get('name', ref) + self.type = 'sequence' + self.mapKey = kwargs.get('mapKey') + self.mapValue = kwargs.get('mapValue') + self._templatedata = None + + def _get_policy(self, data): + """ + parse the policy from a policy string and return a constant + @return: POLICY_* constant + """ + try: + containerdata = utils.get_list(data._get_parent()._get(data.get_ref())) + firstdata = containerdata[0] + except AttributeError: + firstdata = data + + if firstdata.policy == 'append': + return self.POLICY_APPEND + elif firstdata.policy == 'prefix': + return self.POLICY_PREPEND + elif firstdata == data: + # otherwise the policy is either replace or undefined + # (firstdata.policy == 'replace' or firstdata.policy == ''): + return self.POLICY_REPLACE + else: + return self.POLICY_APPEND + + def _set_template_data(self, data=None): + """ + Set the template of the feature sequence + """ + # If template data is not existing, create it + if data != None: + self._templatedata = data + for feaname in self.list_features(): + if self._templatedata._has(feaname): + self.get_feature(feaname)._templatedata = self._templatedata._get(feaname) + else: + subdata = Data(ref=feaname) + self.get_feature(feaname)._templatedata = subdata + self._templatedata._add(subdata) + + def _add_datarow(self, dataobj=None, policy=POLICY_APPEND): + """ + Add a feature data row for a new data in this sequence + """ + if dataobj == None: + dataobj = Data(fqr=self.fqr) + elif dataobj.attr != 'data': + # Add data rows only for data objects (not e.g. RFS) + return + fea = FeatureSequenceSub(self.dataelem_name) + rowproxy = _FeatureDataProxy(fea._name, fea) + """ the imaginary features share the parent relation of the proxy objects """ + self.dataproxy._add(rowproxy, policy) + fea._parent = rowproxy._parent + rowproxy._add_data(dataobj) + """ update the FeatureSequenceSub index from the index number of dataproxy """ + fea._index = utils.get_list(self.dataproxy._get(self.dataelem_name)).index(rowproxy) + # Create a the subfeatures / columns for the parent feature and + # add a data element under each feature. + for feaname in self.list_all_features(): + (pathto_fea, fearef) = utils.dottedref.psplit_ref(feaname) + rowproxy.add_feature(FeatureSequenceSub(fearef), pathto_fea) + subproxy = rowproxy.get_feature(feaname) + subproxy._obj._parent = subproxy._parent + if not dataobj._has(feaname): + dataobj._add_to_path(pathto_fea, Data(ref=fearef)) + subproxy._add_data(dataobj._get(feaname)) + + def add(self, child, policy=container.REPLACE): + """ + A generic add function to add child objects. The function is intended to act as + proxy function that call the correct add function based on the child objects class. + + Example: obj.add(Feature("test")), actually obj.add_feature(Feature("test")) + @param child: the child object to add + @raise IncorrectClassError: if the given class cannot be added to this object. + """ + if isinstance(child, Feature): + self.add_feature(child) + elif isinstance(child, Option): + self._add(child) + elif isinstance(child, Base): + self._add(child) + else: + raise exceptions.IncorrectClassError("Cannot add %s to %s" % (child, self)) + + def add_sequence(self, data=None, policy=POLICY_APPEND): + """ + Add a feature data row for a new data in this sequence + """ + self._add_datarow(None, policy) + # set the initial data if it is given + rowproxy = utils.get_list(self.dataproxy._get(self.dataelem_name))[-1] + if data != None: + for index in range(len(data)): + rowproxy[index].set_value(data[index]) + # add the new data sequence/row to the last configuration layer + dataobj = rowproxy._get_data() + last_config = self.get_root_configuration().get_last_configuration() + last_config.add_data(dataobj, container.APPEND) + return dataobj + + def set_template(self, data=None): + """ + Set the template of the feature sequence + """ + # If template data is not existing, create it + if self._templatedata == None: + self._set_template_data(Data(ref=self.ref, template=True)) + # Add the template data to parent config + pconfig = self.find_parent(type=Configuration) + pconfig.add_data(self._templatedata) + + if data != None: + templdatas = self._templatedata._objects() + for index in range(len(data)): + templdatas[index].set_value(data[index]) + + def get_template(self): + """ + Add a feature data row for a new data in this sequence + """ + #self._set_template(None) + # set the initial data if it is given + if self._templatedata: + return [data.get_value() for data in self._templatedata._objects()] + else: + return None + + def get_data(self): + """ + Helper function to get the topmost data value from the default view. + """ + if self.dataproxy._has(self.dataelem_name): + return utils.get_list(self.dataproxy._get(self.dataelem_name)) + else: + return [] + + def add_data(self, data): + """ + Add a data value. + @param data: A Data object + """ + # Skip template data adding + if data.template: + self._set_template_data(data) + else: + # Get the data index + self._add_datarow(data, self._get_policy(data)) + return + + def get_map_key(self): + """ + Returns the setting that corresponds to mapKey attribute of this sequence feature. + """ + if self.mapKey != None: + mapkey = self.get_feature(self.mapKey) + return mapkey + else: + return None + + def get_map_key_value(self,key): + """ + Returns the setting that corresponds to mapKey attribute of this sequence feature. + """ + value = None + if self.mapKey != None and self.mapValue != None: + data = self.get_data() + for item in data: + kv = item.get_feature(self.mapKey).get_value() + if kv == key: + value = item.get_feature(self.mapValue).get_value() + return value + + def get_map_value(self): + """ + Returns the setting that corresponds to mapValue attribute of this sequence feature. + """ + if self.mapValue != None: + mapvalue = self.get_feature(self.mapValue) + return mapvalue + else: + return None + + def get_value(self, attr=None): + """ + Helper function to get the topmost data value from the default view. + """ + datatable = self.get_data() + rettable = [] + for row in datatable: + rowvalues = row.value + rettable.append(rowvalues) + return rettable + + def set_value(self, value, attr=None): + """ + Set the current value for this feature. Set the value on the topmost layer. + @param value: the value to set. The value must be a two dimensional array (e.g. matrix) + """ + # sets the first data element to replace policy + try: + self.add_sequence(value.pop(0), self.POLICY_REPLACE) + # ignore the index error of an empty list + except IndexError: + pass + for row in value: + self.add_sequence(row) + + def is_sequence(self): + """ Return always true from a sequence object """ + return True + + def get_sequence_parent(self): + """ Return this object as a sequence parent """ + return self + + value = property(get_value, set_value) + data = property(get_data) + +class FeatureSequenceCell(Feature): + """ + A Feature class. Feature is the base for all Configurable items in a Configuration. + """ + def __init__(self, ref="", **kwargs): + super(Feature, self).__init__(ref) + self.name = kwargs.get('name', ref) + self.type = 'seqcell' + + def get_value(self, attr=None): + """ + Get the current value of the feature + @param attr: The attribute name of the data. E.g. attr='data', attr='rfs' + """ + return self.dataproxy._get_value(attr) + + def set_value(self, value): + """ + Set the current value for this feature. Set the value on the topmost layer. + @param value: the value to set + """ + # The sequence cell only updates the latest value in the proxy + self.dataproxy.get_data().set_value(value) + + value = property(get_value, set_value) + +class FeatureSequenceSub(Feature): + """ + A Feature class. Feature is the base for all Configurable items in a Configuration. + """ + def __init__(self, ref="", **kwargs): + super(Feature, self).__init__(ref) + self.name = kwargs.get('name', ref) + self.type = 'subseq' + self._index = 0 + + def get_index(self): + """ + @return : the index of the data element for sequential data defined inside the same configuration. + 0 for normal data. + """ + return self._index + + def set_value(self, value, attr=None): + """ + Set the current value for this sequence row. + @param value: the value row to set + """ + if utils.is_list(value): + for subindex in range(0, len(value)): + self.dataproxy[subindex].get_data().set_value(value[subindex]) + else: + self.dataproxy.get_data().set_value(value) + + def get_value(self, attr=None): + """ + Set the current value for this feature. Set the value on the topmost layer. + @param value: the value to set + """ + # dataproxy = self.get_default_view().get_feature(self.get_fullfqr()) + # The sequence cell only updates the latest value in the proxy + childdatas = self.dataproxy._objects() + if len(childdatas) > 0: + return [subdata.value for subdata in childdatas] + else: + return self.dataproxy._get_value(attr=attr) + + value = property(get_value, set_value) + + +class FeatureLink(Base): + """ + A _FeatureProxy class. _FeatureProxy is the object that is added to View as a + link to the actual Feature object. + """ + def __init__(self, link="", **kwargs): + # Store the fully qualified reference to this object + self.link = link + ref = link.replace('.', '_') + super(FeatureLink, self).__init__(ref) + self._obj = None + self._populated = False + + @property + def fqr(self): + return self.link + + def populate(self): + """ + Populate or fetch the link to the actual feature for this featureproxy. + This method fetches the feature to the _obj member variable and populates also + subfeatures. + """ + try: + if not self._populated: + feas = self.get_default_view().get_features(self.link) + # add the found features to the parent + for fea in feas: + self._get_parent().add_feature(fea._obj) + except exceptions.NotFound, e: + parent_view = self._find_parent_or_default(type=View) + view_name = parent_view.get_name() + logging.getLogger('cone').info("Warning: Feature '%s' in view '%s' not found." % (self.link, view_name)) + + +class _FeatureProxy(container.ObjectProxyContainer, Base): + """ + A _FeatureProxy class. _FeatureProxy is the object that is added to View as a + link to the actual Feature object. + """ + def __init__(self, ref="", obj=None, **kwargs): + super(_FeatureProxy, self).__init__(obj, ref) + Base.__init__(self, ref) + self.support_data = False + + def __getattr__(self, name): + """ + First check if the requested attr is a children then + direct all not found attribute calls to the sub object getattr + """ + try: + return self.__dict__['_children'][name] + except KeyError: + return getattr(self._obj, name) + + def __getitem__(self, index): + return self._objects()[index] + + def __setitem__(self, index, value): + raise exceptions.NotSupported() + + def __delitem__(self, index): + item = self.__getitem__(index) + return self._remove(item.get_ref()) + + def __len__(self): + return len(self._order) + + def _supported_type(self, obj): + if isinstance(obj, _FeatureProxy): + return True + else: + return False + + def _default_object(self, name): + return Group(name) + + def _set_parent(self, newparent): + """ + @param newparent: The new parent object + @return: None + """ + self._parent = newparent + + def add_feature(self, feature, path=""): + """ + """ + if not isinstance(feature, Feature): + raise exceptions.IncorrectClassError("add_feature requires instance of Feature!! Given %s" % feature) + if not self.support_data: + self._add_to_path(path, _FeatureProxy(feature._name, feature)) + else: + self._add_to_path(path, _FeatureDataProxy(feature._name, feature)) + + def remove_feature(self, ref): + """ + remove a given feature from this view by reference. + @param ref: + """ + self._remove(ref) + + def get_feature(self, path): + """ + @param path: The path (ref) to the given feature + """ + return self._get(path) + + def list_features(self): + """ + Return a array of all Feature children references under this object. + """ + return self._list() + + def list_all_features(self): + """ + Return a array of all Feature children references under this object. + """ + return [fea._path(self) for fea in self._traverse(type=_FeatureProxy)] + + def populate(self): + """ + Dummy implementation of populate + """ + pass + + +class _FeatureDataProxy(_FeatureProxy): + """ + A Feature class. Feature is the base for all Configurable items in a Configuration. + """ + DEFAULT_KEY = 'data' + def __init__(self, ref="", obj=None, **kwargs): + # Initialize _obj to None, because __getattr__(), __setattr__() + # and __delattr__() access it. + # Note that we cannot use self._obj = None here, since that would + # invoke __setattr__(), causing a kind of a chicken-and-egg problem + object.__setattr__(self, '_obj', None) + + super(_FeatureDataProxy, self).__init__(ref, obj) + self.support_data = True + """ Create the data container of all types of data. Add the key for the default key. """ + + self.defaultkey = _FeatureDataProxy.DEFAULT_KEY + self.datas = {self.defaultkey : []} + + def __getattr__(self, name): + """ + """ + if object.__getattribute__(self, '_obj') is not None: + self._obj.dataproxy = self + + if name in Feature.PROPERTIES: + return getattr(self._obj, name) + else: + return super(_FeatureDataProxy, self).__getattr__(name) + + def __setattr__(self, name, value): + """ + """ + if object.__getattribute__(self, '_obj') is not None: + self._obj.dataproxy = self + + if name in Feature.PROPERTIES: + return setattr(self._obj, name, value) + else: + super(_FeatureDataProxy, self).__setattr__(name, value) + + def __delattr__(self, name): + """ + """ + if name in Feature.PROPERTIES: + return delattr(self._obj, name) + else: + return super(_FeatureDataProxy, self).__delattr__(name) + + def _add_data(self, data): + """ + Add a data value. + @param data: A Data object + """ + try: + self.datas[data.attr].append(data) + except KeyError: + """ Create a list object for missing attribute """ + self.datas[data.attr] = [] + self.datas[data.attr].append(data) + + def _get_data(self, attr=None): + """ + Get the data value. in sequence setting cases returns an array of data. + """ + dataattr = attr or self.defaultkey + try: + if len(self.datas[dataattr]) > 0: + return self.datas[dataattr][-1] + else: + return None + except KeyError: + """ return None for missing attribute """ + return None + + def _get_datas(self, attr=None): + """ + Get the entire data array. + """ + dataattr = attr or self.defaultkey + return self.datas[dataattr] + + def _get_value(self, attr=None): + """ + Get the topmost data value. + """ + if self._get_data(attr): + return self._get_data(attr).get_value() + else: + return None + + def _set_value(self, datavalue, attr=None): + """ + Set the value for the feature the last configuration in the current hierarchy + @param value: The value for the feature. + @return: The created Data object. + """ + # Make sure that data value exists only once the the last configuration layer + # So if last_data exists on last layer, update the value of that data element. + # otherwise create a new data elem to the topmost layer + dataobj = self._get_data(attr) + last_config = self.get_root_configuration().get_last_configuration() + if dataobj and dataobj.find_parent(type=Configuration) == last_config: + dataobj.set_value(datavalue) + else: + dataobj = Data(fqr=self.fqr, value=datavalue, attr=attr) + last_config.add_data(dataobj) + self._add_data(dataobj) + return dataobj + + def _del_value(self, attr=None): + """ + Remove the + """ + data = self._get_data(attr) + if data: + dataattr = attr or self.defaultkey + parentconfig = data.find_parent(type=Configuration) + if parentconfig: + parentconfig.remove_data(data.get_fullfqr()) + del self.datas[dataattr][-1] + + def _get_values(self, attr=None): + """ + Get the topmost data value. + """ + dataattr = attr or self.defaultkey + return [dataelem.get_value() for dataelem in self.datas[dataattr]] + + +class DataBase(Base): + def __init__(self, ref="", **kwargs): + super(DataBase, self).__init__(ref, **kwargs) + + def _supported_type(self, obj): + if isinstance(obj, (DataContainer, DataBase)): + return True + else: + return False + + def _default_object(self, name): + return Data(ref=name) + + def count(self): + return len(self._objects()) + + def add(self, child, policy=container.REPLACE): + """ + A generic add function to add child objects. The function is intended to act as + proxy function that call the correct add function based on the child objects class. + + Example: obj.add(Feature("test")), actually obj.add_feature(Feature("test")) + @param child: the child object to add + @raise IncorrectClassError: if the given class cannot be added to this object. + """ + if isinstance(child, (Data)): + self._add(child, container.APPEND) + else: + raise exceptions.IncorrectClassError("Cannot add %s object to %s" % (child, self)) + + +class DataContainer(DataBase): + def __init__(self, ref="", **kwargs): + super(DataContainer, self).__init__(ref, **kwargs) + + +class Data(DataBase): + """ + The data element can contain any data setting for a feature. The data element can be + a value definition for any type of data. It basically just links some data to a feature. + The default Data attribute is 'data', but it can be any string. For example current use case + is 'rfs'. + """ + def __init__(self, **kwargs): + """ + @param ref: the reference to the feature. E.g. foo + @param fqr: the full reference to the feature. E.g. 'foo.bar' + @param value: the value of the data + @param attr: the attribute which the Data object defines. e.g. default is 'data'. But could be + for example 'rfs' + """ + name = kwargs.get('ref', '') + self.fearef = kwargs.get('fqr', None) + if self.fearef: + (namespace, name) = utils.dottedref.psplit_ref(self.fearef) + super(Data, self).__init__(name) + self.value = kwargs.get('value', None) + self.attr = kwargs.get('attr') or 'data' + self.policy = kwargs.get('policy', '') + self.template = kwargs.get('template', False) + self.map = kwargs.get('map') + + def get_fearef(self): + if self.fearef: + return self.fearef + else: + return self.fqr + + def get_value(self): + if self.map != None: + ref = utils.resourceref.to_dref(self.get_map_ref()) + key = self.get_map_key_value() + dview = self.get_root_configuration().get_default_view() + fea = dview.get_feature(ref) + return fea.get_map_key_value(key) + else: + return self.value + + def get_map(self): + return self.map + + def set_map(self, map): + self.map = map + if self.value: + #Either value or mapping can be defined. Not both. + self.value = None + + def get_map_ref(self): + if self.map != None: + return utils.DataMapRef.get_feature_ref(self.map) + else: + return None + + def get_map_key_value(self): + if self.map != None: + return utils.DataMapRef.get_key_value(self.map) + else: + return None + + def set_value(self, value): + self.value = value + if self.map: + #Either value or mapping can be defined. Not both. + self.map = None + + def get_policy(self): return self._policy + def set_policy(self, value): self._policy = value + def del_policy(self): self._policy = None + policy = property(get_policy, set_policy, del_policy) + + +class ValueSet(sets.Set): + """ + A value set object to indicate a set of possible values for a feature. + e.g. A boolean feature ValueSet([True, False]) + """ + def __init__(self, initial_set=None): + super(ValueSet, self).__init__(initial_set or []) + + +class ValueRange(object): + """ + """ + def __init__(self, fromvalue, tovalue, step=1): + self.fromvalue = fromvalue + self.tovalue = tovalue + self.step = step + + def __contains__(self, value): + return self.fromvalue <= value and value <= self.tovalue and (value-self.fromvalue) % self.step == 0 + + +class ValueRe(object): + """ + """ + def __init__(self, regexp): + self.regexp = re.compile(regexp) + + def __contains__(self, value): + if isinstance(value, str): + return self.regexp.match(value) + else: + return False + + +class Option(Base): + """ + Confml option class. + """ + def __init__(self, name, value, **kwargs): + super(Option, self).__init__(Option.to_optref(value, kwargs.get('map', None))) + self.name = name + self.value = value + self.map = kwargs.get('map', None) + self.relevant = kwargs.get('relevant', None) + + @classmethod + def to_optref(cls, value, map): + """ + @return: An option reference converted from value or map, depending + on which one is not None. + """ + if value is not None: + return "opt_value_%s" % value.replace('.', '').replace('/', '').replace(' ', '') + elif map is not None: + return "opt_map_%s" % map.replace('.', '').replace('/', '').replace(' ', '') + else: + raise ValueError("Both value and map are None!") + + def get_name(self): + return self.name + + def get_value(self): + return self.value + + def __cmp__(self, other): + try: + ref = getattr(other, 'ref') + except AttributeError: + ref = other + if self.ref < ref: + return -1 + elif self.ref == ref: + return 0 + else: + return 1 + + +class Storage(object): + """ + A general base class for all storage type classes + """ + """ File open modes """ + MODE_UNKNOWN= -1 + MODE_READ = 1 + MODE_WRITE = 2 + MODE_APPEND = 3 + MODE_DELETE = 4 + + def __init__(self, path): + """ + @param path: the reference to the root of the storage. + """ + self.rootpath = path + self.curpath = "" + self.container = True + self.__opened_res__ = {} + + def __opened__(self, res): + """ + Internal function to add a newly opened Resource object to the list of open resources. + @param res: The resource object + """ + if self.__opened_res__.has_key(res.path): + self.__opened_res__[res.path].append(res) + else: + self.__opened_res__[res.path] = [res] + + def __closed__(self, res): + """ + Internal function to remove a Resource object from the list of open resources. + @param res: The resource object to remove + @raise StorageException if the given resource object is not found: + """ + try: + self.__opened_res__[res.path].remove(res) + if len(self.__opened_res__[res.path]) == 0: + del self.__opened_res__[res.path] + except KeyError, e: + raise exceptions.StorageException("No such %s open resource! %s" % (res, e)) + + def __has_open__(self, ref): + """ + Internal function to find out if any Resource objects are open from given ref. + @param ref: The resource ref + @return: True if resources found. Otherwise False. + """ + return self.__opened_res__.has_key(ref) + + def __get_open__(self, path): + """ + Internal function to get all resource opened on a certain ref . + @param ref: The resource ref + @return: A list of open resources. Empty list if nothing is found + """ + if self.__has_open__(path): + # return a copy of currently open resources + return self.__opened_res__[path][:] + else: + return [] + + def __has_resource__(self, res): + """ + Internal function to find out if the given Resource objects is open in this storage. + @param ref: The resource object + @return: True if resources found. Otherwise False. + """ + try: + res = self.__opened_res__[res.path].index(res) + return True + except KeyError, e: + return False + + @classmethod + def open(cls,path, mode="r", **kwargs): + """ + Class method for opening an instance of Storage + @param path: path to storage, which will determine what type of storage is initiated. + """ + # import all storage instances + from cone.storage import storages + for storagename in storages: + storagemodule = 'cone.storage.'+storagename + module = __import__(storagemodule) + for storage_class in utils.all_subclasses(Storage): + if storage_class.supported_storage(path): + if hasattr(storage_class, '__open__'): + return storage_class.__open__(path, mode, **kwargs) + else: + return storage_class(path, mode, **kwargs) + + obj = Storage(path) + return obj + + @classmethod + def supported_storage(cls, path): + """ + Class method for determing if the given clas supports a storage by given path. + E.g. foo.zip, foo.cpd, foo/bar, http://foo.com/ + @param path: + @return: Boolean value. True if the storage of the path is supported. False if not. + """ + return False + + def set_path(self, path): + """ + """ + self.rootpath = path + + def get_path(self): + """ + """ + return self.rootpath + + def set_current_path(self, path): + """ + @param path: the current path under the Storage. + """ + self.curpath = utils.resourceref.remove_end_slash(utils.resourceref.remove_begin_slash(path)) + + def get_current_path(self): + """ + get the current path under the Storage. + """ + return self.curpath + + def close(self): + """ + Close the repository, which will save and close all open resources. + """ + for openref in self.__opened_res__.keys(): + for res in self.__get_open__(openref): + self.close_resource(res) + + def save(self): + """ + Flush changes from all resources to the repository. + """ + for openref in self.__opened_res__.keys(): + for res in self.__get_open__(openref): + self.save_resource(res) + + def open_resource(self, path, mode="r"): + """ + Open the given resource and return a File object. + @param path : reference to the resource + @param mode : the mode in which to open. Can be one of r = read, w = write, a = append. + raises a NotResource exception if the ref item is not a resource. + """ + raise exceptions.NotSupportedException() + + def delete_resource(self, path): + """ + Delete the given resource from storage + @param path: reference to the resource + raises a NotSupportedException exception if delete operation is not supported by the storage + """ + raise exceptions.NotSupportedException() + + def close_resource(self, path): + """ + Close a given resource instance. Normally this is called by the Resource object + in its own close. + @param path the reference to the resource to close. + """ + raise exceptions.NotSupportedException() + + def is_resource(self, path): + """ + Return true if the ref is a resource + @param ref : reference to path where resources are searched + """ + raise exceptions.NotSupportedException() + + def list_resources(self, path, recurse=False): + """ + find the resources under certain ref/path + @param ref : reference to path where resources are searched + @param recurse : defines whether to return resources directly under the path or does the listing recurse to subfolders. + Default value is False. Set to True to enable recursion. + """ + return [] + + def import_resources(self, paths, storage): + """ + import resources from a list of resources to this storage + @param paths : a list of Resourse objects. + @param storage : the external storage from which files are imported. + """ + raise exceptions.NotSupportedException() + + def export_resources(self, paths, storage): + """ + export resources from this storage based on a list of reference to this storage + @param path : a list of resource paths in this storage (references). + @param storage : the external storage where to export. + """ + raise exceptions.NotSupportedException() + + def close_resource(self, path): + """ + Close a given resource instance. Normally this is called by the Resource object + in its own close. + @param ref the reference to the resource to close. + """ + raise exceptions.NotSupportedException() + + def save_resource(self, path): + """ + Flush the changes of a given resource instance. Normally this is called by the Resource object + in its own save. + @param ref the reference to the resource to close. + """ + raise exceptions.NotSupportedException() + + def create_folder(self, path): + """ + Create a folder entry to a path + @param path : path to the folder + """ + raise exceptions.NotSupportedException() + + def delete_folder(self, path): + """ + Delete a folder entry from a path. The path must be empty. + @param path : path to the folder + """ + raise exceptions.NotSupportedException() + + def is_folder(self, path): + """ + Check if the given path is an existing folder in the storage + @param path : path to the folder + """ + raise exceptions.NotSupportedException() + + def get_mode(self, mode_str): + if mode_str.find("w") != -1: + return self.MODE_WRITE + elif mode_str.find("r") != -1: + return self.MODE_READ + elif mode_str.find("a") != -1: + return self.MODE_APPEND + elif mode_str.find("d") != -1: + return self.MODE_DELETE + else: + return self.MODE_UNKNOWN + + def unload(self, path, object): + """ + Dump a given object to the storage + @param object: The object to dump to the storage, which is expected to be an instance + of Base class. + @param path: The reference where to store the object + @param object: The object instance to dump + @raise StorageException: if the given object cannot be dumped to this storage + """ + raise exceptions.NotSupportedException() + + def load(self, path): + """ + Load an object from a reference. + @param path: The reference where to load the object + @raise StorageException: if the given object cannot be loaded as an object from this storage + """ + raise exceptions.NotSupportedException() + + path = property(get_path, set_path) + +class Resource(object): + STATE_OPEN = 0 + STATE_CLOSE = 1 + def __init__(self, storage, path, mode=Storage.MODE_READ): + self.storage = storage + self.path = path + self.mode = mode + self.state = Resource.STATE_OPEN + self.content_info = None + + def get_path(self): + return self.path + + def close(self): + """ + Close the resource. + Note1: the resource object cannot be accessed anymore after it has been closed. + Note2: the changes are not automatically saved. The save operation must be explicitly called, + to save data. + """ + self.storage.close_resource(self.path) + self.state = Resource.STATE_OPEN + + def read(self, bytes=0): + """ + Read data. + """ + raise exceptions.NotSupportedException() + + def write(self, string): + """ + Write data. + """ + raise exceptions.NotSupportedException() + + def truncate(self, size=0): + """ + Trunkate this resource data to the given size. + @param size: The size to trunkate. Default value is zero, which make the resource empty. + """ + raise NotSupportedException() + + def save(self, size=0): + """ + Save all changes to data to storage. + """ + raise NotSupportedException() + + def get_mode(self): + return self.storage.get_mode(self.mode) + + def get_size(self): + """ + Return the size of this resource in bytes. + + Note that this does not work in write mode. + @return: The resource size in bytes: + @raise exceptions.StorageException: The resource was opened in write mode. + """ + raise exceptions.NotSupportedException() + + def get_content_info(self): + """ + Return the ContentInfo class that contains content information about + resource. + """ + raise exceptions.NotSupportedException() + +class ContentInfo(object): + """ + A ContentInfo object is used to describe content of Resource. + """ + logger = logging.getLogger('cone.contentinfo') + + + def __init__(self, mimetype, mimesubtype): + #: MIME Media type (http://www.iana.org/assignments/media-types/) + #: as a string. E.g. 'image' or 'application' + self.mimetype = mimetype + #: MIME Media subtype as a string. E.g. 'svg+xml' or 'bmp'. + self.mimesubtype = mimesubtype + + @property + def content_type(self): + """ + Returns MIME Media type (http://www.iana.org/assignments/media-types/) + and subtype as a string. E.g. 'image/bmp'. + """ + return self.mimetype + '/' + self.mimesubtype + +class ImageContentInfo(ContentInfo): + + """ + A ImageContentInfo object is used to describe content of image Resources. + """ + def __init__(self): + ContentInfo.__init__(self, 'image', '') + +class BmpImageContentInfo(ImageContentInfo): + """ + A BmpImageContentInfo object is used to describe content of bmp image + Resources. + """ + + _BMP_BITS_PER_PIXEL_OFFSET_ = int('0x1C', 16) + + def __init__(self, resource, data): + ContentInfo.__init__(self, 'image', 'bmp') + + #: Color depth as bits per pixel. + self.color_depth = None + if (resource != None): + try: + self.color_depth = ord(data[self._BMP_BITS_PER_PIXEL_OFFSET_]) + except Exception, e: + self.logger.warning("Invalid BMP-file: %s" % resource.get_path()) + +class Folder(object): + """ + A Folder object is a subfolder of a Storage, offering access to part of the Storages resources. + """ + def __init__(self, storage, path): + """ + Create a layer folder to the storage if it does not exist. + """ + #if not storage.is_folder(path): + # storage.create_folder(path) + self.storage = copy.copy(storage) + self.storage.set_current_path(path) + + def __getattr__(self, name): + return getattr(self.storage, name) + +class CompositeLayer(object): + """ + A base class for composite Configuration objects. + """ + def __init__(self, path="", **kwargs): + self.layers = kwargs.get('layers', []) + self.path = path + + def add_layer(self, layer): + self.layers.append(layer) + + def remove_layer(self, path): + if self.get_layer(path): + self.layers.remove(self.get_layer(path)) + else: + raise exceptions.NotFound('Layer with given path %s not found!' % path) + + def get_layer(self, path): + for layer in self.layers: + if layer.get_current_path() == path: + return layer + return None + + def list_layers(self): + return [layer.get_current_path() for layer in self.layers] + + def list_confml(self): + """ + @return: array of confml file references. + """ + lres = [] + for layerpath in self.list_layers(): + for respath in self.get_layer(layerpath).list_confml(): + lres.append(utils.resourceref.join_refs([layerpath, respath])) + return lres + + def list_implml(self): + """ + @return: array of implml file references. + """ + lres = [] + for layerpath in self.list_layers(): + for respath in self.get_layer(layerpath).list_implml(): + lres.append(utils.resourceref.join_refs([layerpath, respath])) + return lres + + def list_content(self): + """ + @return: array of content file references. + """ + lres = [] + for layerpath in self.list_layers(): + for respath in self.get_layer(layerpath).list_content(): + lres.append(utils.resourceref.join_refs([layerpath, respath])) + return lres + + def list_doc(self): + """ + @return: array of document file references. + """ + lres = [] + for layerpath in self.list_layers(): + for respath in self.get_layer(layerpath).list_doc(): + lres.append(utils.resourceref.join_refs([layerpath, respath])) + return lres + + def list_all_resources(self, empty_folders=False): + """ + Returns a list of all layer related resource paths with full path in the storage. + """ + lres = [] + for layerpath in self.list_layers(): + sublayer = self.get_layer(layerpath) + for respath in sublayer.list_all_resources(empty_folders): + lres.append(utils.resourceref.join_refs([layerpath, respath])) + + return lres + +class Layer(CompositeLayer): + """ + A Layer object is a subfolder of a Storage, offering access to part of the Storages resources. + """ + def __init__(self, storage, path, **kwargs): + """ + Create a layer folder to the storage if it does not exist. + @param storage: a reference to the Storage object + @param path: path for the layer + @param confml_path: optional parameter for confml files path (give in confml_path="something") + @param imlpml_path: optional parameter for implml files path (give in implml_path="something") + @param content_path: optional parameter for content files path (give in content_path="something") + @param doc_path: optional parameter for doc files path (give in doc_path="something") + """ + super(Layer, self).__init__(path, **kwargs) + #if not storage.is_folder(path): + # storage.create_folder(path) + self.storage = copy.copy(storage) + self.storage.set_current_path(path) + self.predefined = {'confml_path' : 'confml', + 'implml_path' : 'implml', + 'content_path' : 'content', + 'doc_path' : 'doc'} + # list through all "hardcoded" paths and check whether the + # hardcoded or given path exists under this Layer. + # if it does then create a folder instance to that path + for (pretag, prevalue) in self.predefined.items(): + self.predefined[pretag] = kwargs.get(pretag, prevalue) + + def __getattr__(self, name): + return getattr(self.storage, name) + + def list_confml(self): + """ + @return: array of confml file references. + """ + res = self.storage.list_resources(self.predefined['confml_path'], True) + res += super(Layer, self).list_confml() + return res + + def list_implml(self): + """ + @return: array of implml file references. + """ + res = self.storage.list_resources(self.predefined['implml_path'], True) + res += super(Layer, self).list_implml() + return res + + def list_content(self): + """ + @return: array of content file references. + """ + res = self.storage.list_resources(self.predefined['content_path'], True) + res += super(Layer, self).list_content() + return res + + def list_doc(self): + """ + @return: array of document file references. + """ + res = self.storage.list_resources(self.predefined['doc_path'], True) + res += super(Layer, self).list_doc() + return res + + def confml_folder(self): + cpath = self.storage.get_current_path() + spath = self.predefined['confml_path'] + return Folder(self.storage, utils.resourceref.join_refs([cpath, spath])) + + def implml_folder(self): + cpath = self.storage.get_current_path() + spath = self.predefined['implml_path'] + return Folder(self.storage, utils.resourceref.join_refs([cpath, spath])) + + def content_folder(self): + cpath = self.storage.get_current_path() + spath = self.predefined['content_path'] + return Folder(self.storage, utils.resourceref.join_refs([cpath, spath])) + + def doc_folder(self): + cpath = self.storage.get_current_path() + spath = self.predefined['doc_path'] + return Folder(self.storage, utils.resourceref.join_refs([cpath, spath])) + + def list_all_resources(self, empty_folders=False): + """ + Returns a list of all layer related resource paths with full path in the storage. + """ + lres = [] + mypath = self.get_current_path() + + for folderpath in sorted(self.predefined.values()): + lres += self.storage.list_resources(folderpath, recurse=True, empty_folders=empty_folders) + + lres += super(Layer, self).list_all_resources(empty_folders) + + return lres + + def list_all_related(self, empty_folders=False): + """ + Returns a list of all (non confml) layer related resource paths with full path in the storage. + """ + lres = [] + predef = self.predefined.copy() + del predef['confml_path'] + mypath = self.get_current_path() + for folderpath in sorted(predef.values()): + lres += self.storage.list_resources(folderpath, recurse=True, empty_folders=empty_folders) + lres += super(Layer, self).list_all_resources(empty_folders=empty_folders) + + return lres + + +class Rule(object): + """ + Base class for Rules in the system. + """ + def __init__(self): + raise exceptions.NotSupportedException() + + +class FactoryBase(object): + pass + +class Factory(object): + def __getattr__(self, name): + """ + The Factory getattr find all subclasses for the Factory and searches for given attr + in those. + """ + for sub_factory in utils.all_subclasses(FactoryBase): + try: + return getattr(sub_factory(), name) + except AttributeError: + continue + raise AttributeError("type object %s has no attribute '%s'" % (self.__class__, name)) + +def get_mapper(modelname): + """ + Return a instance of appropriate mapper for given model. + """ + mapmodule = __import__('cone.public.mapping') + return mapmodule.public.mapping.BaseMapper() + + +################################################################## +class NullHandler(logging.Handler): + """ + Default handler that does not do anything. + """ + def emit(self, record): + pass + +#Initialization of default logger that contains NullHandler. +logger = logging.getLogger('cone') +logger.addHandler(NullHandler())