configurationengine/source/cone/public/api.py
changeset 3 e7e0ae78773e
parent 0 2e8eeb919028
child 4 0951727b8815
equal deleted inserted replaced
2:87cfa131b535 3:e7e0ae78773e
       
     1 from __future__ import with_statement
     1 #
     2 #
     2 # Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
     3 # Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
     3 # All rights reserved.
     4 # All rights reserved.
     4 # This component and the accompanying materials are made available
     5 # This component and the accompanying materials are made available
     5 # under the terms of "Eclipse Public License v1.0"
     6 # under the terms of "Eclipse Public License v1.0"
    16 """
    17 """
    17 Cone public API.
    18 Cone public API.
    18 The core interface to the ConE functionality.
    19 The core interface to the ConE functionality.
    19 """
    20 """
    20 
    21 
    21 import os
       
    22 import re
    22 import re
    23 import sys
    23 import sys
    24 import logging
    24 import logging
    25 import copy
    25 import mimetypes
    26 import sets
    26 
    27 
    27 from cone.public import exceptions, utils, container, mapping
    28 import exceptions, utils, container, mapping
    28 
       
    29 def get_file_logger():
       
    30     return logger
    29 
    31 
    30 class Base(container.ObjectContainer):
    32 class Base(container.ObjectContainer):
    31     """
    33     """
    32     The Base class is intended for capturing same kind of naming scheme.
    34     The Base class is intended for capturing same kind of naming scheme.
    33     """
    35     """
    36     def __init__(self, ref="", **kwargs):
    38     def __init__(self, ref="", **kwargs):
    37         if len(utils.dottedref.split_ref(ref)) > 1:
    39         if len(utils.dottedref.split_ref(ref)) > 1:
    38             raise exceptions.InvalidRef("Invalid reference for Base object %s!" % ref)
    40             raise exceptions.InvalidRef("Invalid reference for Base object %s!" % ref)
    39         self.ref = ref
    41         self.ref = ref
    40         container.ObjectContainer.__init__(self, ref)
    42         container.ObjectContainer.__init__(self, ref)
    41         for arg in kwargs.keys():
    43         try:
    42             if kwargs.get(arg) != None:
    44             for arg in kwargs.keys():
    43                 setattr(self, arg, kwargs.get(arg))
    45                 if kwargs.get(arg) != None:
    44 
    46                     setattr(self, arg, kwargs.get(arg))
       
    47         except AttributeError,e:
       
    48             raise e
       
    49         
    45     def __repr__(self):
    50     def __repr__(self):
    46         dict = self._dict()
    51         dict = self._dict()
    47         return "%s(%s)" % (self.__class__.__name__, dict)
    52         return "%s(%s)" % (self.__class__.__name__, dict)
    48 
    53 
       
    54 #    def __reduce_ex__(self, protocol_version):
       
    55 #        tpl = super(Base, self).__reduce_ex__(protocol_version)
       
    56 #        return tpl
       
    57 
       
    58 #    def __getstate__(self):
       
    59 #        state = self._dict(internals=True, ignore_empty=True)
       
    60 #        # pop out the _name so that it wont appear as redundant data (ref is the same)
       
    61 #        state.pop('_name', None)
       
    62 #        state.pop('_parent', None)
       
    63 #        return state
       
    64 #
       
    65 #    def __setstate__(self, state):
       
    66 #        super(Base, self).__setstate__(state)
       
    67 #        self.ref = state.get('ref','')
       
    68 #        for arg in state.keys():
       
    69 #            self.__dict__.setdefault(arg, state.get(arg))
       
    70         
    49     def _get_mapper(self,modelname):
    71     def _get_mapper(self,modelname):
    50         """
    72         """
    51         Return a instance of appropriate mapper for given model.
    73         Return a instance of appropriate mapper for given model.
    52         """
    74         """
    53         return mapping.BaseMapper()
    75         return mapping.BaseMapper()
   106                 kwargs['recursion_depth'] = recursion_depth - 1 
   128                 kwargs['recursion_depth'] = recursion_depth - 1 
   107                 for child in self._objects():
   129                 for child in self._objects():
   108                     obj._add(child._clone(**kwargs), container.APPEND)
   130                     obj._add(child._clone(**kwargs), container.APPEND)
   109         return obj
   131         return obj
   110 
   132 
   111     def _dict(self):
   133     def _dict(self, **kwargs):
   112         """
   134         """
   113         Return the public variables in a dictionary
   135         Return the public variables in a dictionary
   114         """
   136         """
   115         dict = {}
   137         dict = {}
   116         for key in self.__dict__.keys():
   138         # loop through the items in this object internal __dict__
   117             if key.startswith('_'):
   139         # and add all except internal variables and function overrides  
       
   140         for (key,value) in self.__dict__.items():
       
   141             if not kwargs.get('internals', False) and key.startswith('_'):
   118                 continue
   142                 continue
       
   143             elif not kwargs.get('callables', False) and callable(value):
       
   144                 continue
       
   145             elif kwargs.get('ignore_empty') and not value:
       
   146                 # ignore empty values
       
   147                 pass
   119             else:
   148             else:
   120                 dict[key] = self.__dict__[key]
   149                 dict[key] = value
   121         return dict
   150         return dict
   122 
   151 
   123     def _default_object(self, name):
   152     def _default_object(self, name):
   124         return Base(name)
   153         return Base(name)
   125 
   154 
   150         if parentcontainer:
   179         if parentcontainer:
   151             paths.append(parentcontainer.namespace)
   180             paths.append(parentcontainer.namespace)
   152         paths.reverse()
   181         paths.reverse()
   153         return utils.dottedref.join_refs(paths)
   182         return utils.dottedref.join_refs(paths)
   154 
   183 
       
   184     def path(self, toparent=None):
       
   185         """
       
   186         Get the path to this Base object..
       
   187         @param toparent: the _parent object up to which the path is relative. Default value is None.,
       
   188         which gives the fully qualified path in the topology.
       
   189         @return: The path to this Base object from toparent
       
   190         """
       
   191         return self._path(toparent)
       
   192 
       
   193     def parent_path(self, toparent=None):
       
   194         """
       
   195         Get the path to the parent of this Base object..
       
   196         @param toparent: the _parent object up to which the path is relative. Default value is None.,
       
   197         which gives the fully qualified path in the topology.
       
   198         @return: The path to this Base object from toparent
       
   199         """
       
   200         if self._parent != None:
       
   201             return self._parent.path(toparent)
       
   202         else:
       
   203             return ''
       
   204 
   155     def get_fullref(self):
   205     def get_fullref(self):
   156         """
   206         """
   157         Return a full reference, reference including a 
   207         Return a full reference, reference including a 
   158         possible index of the object in list. 
   208         possible index of the object in list. 
   159         e.g. ref can be bar[1] or just the normal bar. 
   209         e.g. ref can be bar[1] or just the normal bar. 
   238         elif isinstance(self, Configuration):
   288         elif isinstance(self, Configuration):
   239             return self
   289             return self
   240         else:
   290         else:
   241             return None
   291             return None
   242 
   292 
       
   293     def get_configuration(self):
       
   294         """
       
   295         Return the containing configuration of this object.
       
   296         """
       
   297         parent = self._find_parent_or_default(type=Configuration)
       
   298         return parent
       
   299 
       
   300     def get_configuration_path(self):
       
   301         """
       
   302         Return the path of containing configuration of this object.
       
   303         """
       
   304         parent = self._find_parent_or_default(type=Configuration)
       
   305         try:
       
   306             return parent.get_full_path()
       
   307         except AttributeError:
       
   308             return None
       
   309     
   243     def get_index(self):
   310     def get_index(self):
   244         """
   311         """
   245         @return : the index of the data element for sequential data defined inside the same configuration.
   312         @return : the index of the data element for sequential data defined inside the same configuration.
   246         0 for normal data.
   313         0 for normal data.
   247         """
   314         """
   291         @param fqr: the fully qualified ref to the object
   358         @param fqr: the fully qualified ref to the object
   292         @raise AttributeError: if the given ref is not found.  
   359         @raise AttributeError: if the given ref is not found.  
   293         """
   360         """
   294         return None
   361         return None
   295 
   362 
       
   363     def get_store_interface(self):
       
   364         # if the project cannot be retrieved return None
       
   365         try:
       
   366             return self.get_project()
       
   367         except exceptions.NotFound:
       
   368             return None
       
   369     
       
   370     def get_id(self): 
       
   371         try:
       
   372             return self._id
       
   373         except AttributeError:
       
   374             return None
       
   375 
       
   376     def set_id(self,value): 
       
   377         self._id = value
       
   378 
       
   379     def del_id(self): 
       
   380         delattr(self,'_id')
       
   381 
       
   382     """ The id as a property """
       
   383     id = property(get_id,set_id, del_id)
   296 
   384 
   297 class Project(Base):
   385 class Project(Base):
   298     """
   386     """
   299     A project is a container that can hold several Configuration objects.
   387     A project is a container that can hold several Configuration objects.
   300     """
   388     """
   307         """ Try to set the model and tet the actual configuration class """
   395         """ Try to set the model and tet the actual configuration class """
   308         try:
   396         try:
   309             self._model = storage.persistentmodule.MODEL
   397             self._model = storage.persistentmodule.MODEL
   310         except AttributeError:
   398         except AttributeError:
   311             self._model = None
   399             self._model = None
   312         
   400         self.loaded = {}
   313         self.set_storage(storage)
   401         self.set_storage(storage)
   314         self.update()
   402         self.update()
   315         self.loaded = {}
   403 
       
   404     def __getstate__(self):
       
   405         state = {}
       
   406         state['storage'] = self.storage
       
   407         return state
       
   408 
       
   409     def __setstate__(self, state):
       
   410         self.__init__(state['storage'])
   316 
   411 
   317     def __add_loaded__(self, ref, obj):
   412     def __add_loaded__(self, ref, obj):
   318         """
   413         """
   319         Add the object to loaded 
   414         Add the object to loaded 
   320         """
   415         """
   351             if self.loaded[ref]['counter'] == 0:
   446             if self.loaded[ref]['counter'] == 0:
   352                 del self.loaded[ref]
   447                 del self.loaded[ref]
   353                 return True
   448                 return True
   354             else: 
   449             else: 
   355                 return False
   450                 return False
   356         else: 
   451         else:
   357             return True
   452             # Return False in case the object is loaded at all in this project 
       
   453             # increases performance as unloading is not done on unchanged objects
       
   454             return False
   358         
   455         
   359     def _supported_type(self, obj):
   456     def _supported_type(self, obj):
   360         if isinstance(obj, Configuration) \
   457         if isinstance(obj, Configuration) \
   361         or isinstance(obj, ConfigurationProxy): 
   458         or isinstance(obj, ConfigurationProxy): 
   362             return True
   459             return True
   381 
   478 
   382     def set_storage(self, storage):
   479     def set_storage(self, storage):
   383         """
   480         """
   384         Set the Storage instance of this Project.
   481         Set the Storage instance of this Project.
   385         """
   482         """
   386         if isinstance(storage, Storage):
   483         if storage != None and not isinstance(storage, Storage):
   387             self.storage = storage
       
   388         else:
       
   389             raise exceptions.StorageException("The given storage is not a instance of Storage!")
   484             raise exceptions.StorageException("The given storage is not a instance of Storage!")
       
   485         self.storage = storage
   390 
   486 
   391     def list_configurations(self, filter_or_filters=None):
   487     def list_configurations(self, filter_or_filters=None):
   392         """
   488         """
   393         List the direct child objects of the project (Root configurations)
   489         List the direct child objects of the project (Root configurations)
   394         @param filter_or_filters: A regular expression or list of regular expressions
   490         @param filter_or_filters: A regular expression or list of regular expressions
   416     def list_all_configurations(self):
   512     def list_all_configurations(self):
   417         """
   513         """
   418         List all configuration objects of the project (all configurations)
   514         List all configuration objects of the project (all configurations)
   419         @return: a list for configuration file paths
   515         @return: a list for configuration file paths
   420         """
   516         """
   421         return [obj.get_path() for obj in self._traverse(type=(Configuration, ConfigurationProxy))]
   517         # TODO
       
   518         # huge performance problem 
       
   519         return [obj.get_full_path() for obj in self._traverse(type=(Configuration, ConfigurationProxy))]
   422 
   520 
   423     def get_configuration(self, path):
   521     def get_configuration(self, path):
   424         """
   522         """
   425         Get a configuration object from the given path
   523         Get a configuration object from the given path
   426         @param path: path to configuration 
   524         @param path: path to configuration 
   443         @param path: path to configuration 
   541         @param path: path to configuration 
   444         @return: Boolean return value.
   542         @return: Boolean return value.
   445         """
   543         """
   446         # Changed from list_all_configurations to list_configurations
   544         # Changed from list_all_configurations to list_configurations
   447         # (list_all_configurations causes a insane performance problem with _traverse)
   545         # (list_all_configurations causes a insane performance problem with _traverse)
   448         return path in self.list_configurations()
   546         #
   449 
   547         try:
   450     def add_configuration(self, config):
   548             return self.storage.is_resource(path)
       
   549         except exceptions.NotSupportedException:
       
   550             return path in self.list_configurations()
       
   551         
       
   552     def add_configuration(self, config, overwrite_existing=False):
   451         """
   553         """
   452         Add a Configuration object to this project
   554         Add a Configuration object to this project
   453         """
   555         @param config: The configuration object to add
       
   556         @param overwrite_existing: When this is set true any existing configuration is 
       
   557         overwritten. 
       
   558         """ 
   454         if isinstance(config, Configuration):
   559         if isinstance(config, Configuration):
   455             if self.is_configuration(config.get_path()):
   560             if not overwrite_existing and self.is_configuration(config.get_path()):
   456                 raise exceptions.AlreadyExists("%s" % config.get_path())
   561                 raise exceptions.AlreadyExists("%s" % config.get_path())
   457             self._add(config)
   562             
       
   563             proxy = ConfigurationProxy(config.path)
       
   564             proxy._set_obj(config)
       
   565             self._add(proxy)
       
   566             #self._add(config)
   458             self.__add_loaded__(config.get_path(), config)
   567             self.__add_loaded__(config.get_path(), config)
   459             self.__loaded__(config.get_path())
   568             self.__loaded__(config.get_path())
   460         else:
   569         else:
   461             raise exceptions.IncorrectClassError("Only Configuration instance can be added to Project!")
   570             raise exceptions.IncorrectClassError("Only Configuration instance can be added to Project!")
   462 
   571 
   463     def create_configuration(self, path, namespace=""):
   572     def create_configuration(self, path, overwrite_existing=False, **kwargs):
   464         """
   573         """
   465         Create a Configuration object to this project
   574         Create a Configuration object to this project
   466         """
   575         @param path: The path of the new configuration file
   467         config = self.get_configuration_class()(utils.resourceref.norm(path), namespace=namespace)
   576         @param overwrite_existing: When this is set true any existing configuration is 
   468         self.add_configuration(config)
   577         overwritten. 
       
   578         @param **kwargs: normal keyword arguments that are passed on to the newly 
       
   579         created Configuration object. See Configuration object constructor description on what
       
   580         you can pass on here.  
       
   581         """
       
   582         config = self.get_configuration_class()(utils.resourceref.norm(path), **kwargs)
       
   583         self.add_configuration(config, overwrite_existing)
   469         return config
   584         return config
   470 
   585 
   471     def remove_configuration(self, path):
   586     def remove_configuration(self, path):
   472         """
   587         """
   473         Remove a Configuration by its reference
   588         Remove a Configuration by its reference
   485         Import a configuration object from another storage
   600         Import a configuration object from another storage
   486         """
   601         """
   487         self.storage.import_resources(configuration.list_resources(), configuration.get_storage())
   602         self.storage.import_resources(configuration.list_resources(), configuration.get_storage())
   488         return
   603         return
   489 
   604 
   490     def export_configuration(self, configuration, export_storage, empty_folders=False):
   605     def export_configuration(self, configuration, export_storage, **kwargs):
   491         """
   606         """
   492         Export a configuration object to another storage
   607         Export a configuration object to another storage
   493         """
   608         """
   494         # First clone the configuration and then import the rest of the configuration resources
   609         # First clone the configuration and then import the rest of the configuration resources
   495         if isinstance(configuration, ConfigurationProxy):
   610         if isinstance(configuration, ConfigurationProxy):
   502         #If the configuration is not in the root of the project adding the path 
   617         #If the configuration is not in the root of the project adding the path 
   503         #to final exporting source path.
   618         #to final exporting source path.
   504         #l = []
   619         #l = []
   505         cpath = utils.resourceref.get_path(configuration.get_path()) 
   620         cpath = utils.resourceref.get_path(configuration.get_path()) 
   506         resr = [utils.resourceref.join_refs([cpath,related]) \
   621         resr = [utils.resourceref.join_refs([cpath,related]) \
   507                 for related in configuration.get_layer().list_all_related(empty_folders)]        
   622                 for related in configuration.get_layer().list_all_related(**kwargs)]
   508         self.storage.export_resources(resr ,export_storage, empty_folders)
   623         
       
   624         self.storage.export_resources(resr ,export_storage, kwargs.get("empty_folders", False))
   509         return
   625         return
   510 
   626 
   511     def get_configuration_class(self):
   627     def get_configuration_class(self):
   512         """
   628         """
   513         return the default configuration class that is used with the model. 
   629         return the default configuration class that is used with the model. 
   543         object from this storage 
   659         object from this storage 
   544         """
   660         """
   545         if not self.__get_loaded__(path):
   661         if not self.__get_loaded__(path):
   546             configuration = self.get_storage().load(path)
   662             configuration = self.get_storage().load(path)
   547             if configuration.get_ref() == 'unknown':
   663             if configuration.get_ref() == 'unknown':
   548                  configuration.set_ref(utils.resourceref.to_dref(path))
   664                 configuration.set_ref(utils.resourceref.to_dref(path))
   549             self.__add_loaded__(path, configuration)
   665             self.__add_loaded__(path, configuration)
   550         """ increase the ref counter """
   666         """ increase the ref counter """
   551         self.__loaded__(path)
   667         self.__loaded__(path)
   552         return self.__get_loaded__(path)
   668         return self.__get_loaded__(path)
   553 
   669 
   558         @param object: The object instance to dump 
   674         @param object: The object instance to dump 
   559         @raise StorageException: if the given object cannot be dumped to this storage 
   675         @raise StorageException: if the given object cannot be dumped to this storage 
   560         """
   676         """
   561         if self.__unloaded__(path):
   677         if self.__unloaded__(path):
   562             self.get_storage().unload(path, object)
   678             self.get_storage().unload(path, object)
       
   679             # remove the configuration from this this project, 
       
   680             # with proxy set the _obj reference to None
       
   681             try:
       
   682                 conf =  self.get_configuration(path)
       
   683                 if isinstance(conf, ConfigurationProxy):
       
   684                     conf._set_obj(None)
       
   685             except exceptions.NotFound:
       
   686                 # if the configuration is not found at all then ignore the resetting
       
   687                 pass
   563 
   688 
   564     def get_path(self):
   689     def get_path(self):
   565         """
   690         """
   566         Return the path of the project, which is always root
   691         Return the path of the project, which is always root
   567         """
   692         """
   576 #        self.meta       = {}
   701 #        self.meta       = {}
   577 #        self.desc       = ""
   702 #        self.desc       = ""
   578         super(CompositeConfiguration, self).__init__(ref, **kwargs)
   703         super(CompositeConfiguration, self).__init__(ref, **kwargs)
   579         self.container = True
   704         self.container = True
   580 
   705 
       
   706     def _configuration_class(self):
       
   707         return Configuration
       
   708 
   581     def add_configuration(self, config):
   709     def add_configuration(self, config):
   582         """
   710         """
   583         Add an existing Configuration to this configuration
   711         Add an existing Configuration to this configuration
   584         @param config: A Configuration instance:
   712         @param config: A Configuration instance:
   585         @return: None 
   713         @return: None 
   586         """
   714         """
   587         """
   715         """
   588         Merge the default view features from added config to this configs _default_view.
   716         Merge the default view features from added config to this configs _default_view.
   589         """
   717         """
   590         self._add(config)
   718         # if the Configuration has a separate resource path, add it automatically behind proxy 
   591 
   719         if utils.resourceref.is_path(config.path) and isinstance(config, Configuration):
   592     def include_configuration(self, configref):
   720             proxy = ConfigurationProxy(config.path)
       
   721             proxy._set_obj(config)
       
   722             self._add(proxy)
       
   723             # Add the new configuration to the list of "modified/loaded" configurations
       
   724             try:
       
   725                 prj = self.get_project()
       
   726                 prj.__add_loaded__(config.get_full_path(), config)
       
   727                 prj.__loaded__(config.get_full_path())
       
   728             except exceptions.NotFound:
       
   729                 # if the parent is not found this configuration is not (yet) a part of project and cant be stored 
       
   730                 pass
       
   731         else:
       
   732             self._add(config)
       
   733 
       
   734     def include_configuration(self, configref, policy=0):
   593         """
   735         """
   594         Add an existing Configuration to this configuration by its resource reference
   736         Add an existing Configuration to this configuration by its resource reference
   595         @param config: A Configuration instance:
   737         @param config: A Configuration instance:
   596         @return: None 
   738         @return: None 
   597         """
   739         """
   598         # add the configuration load proxy to this configuration instead 
   740         # add the configuration load proxy to this configuration instead 
   599         # adding the configuration directly
   741         # adding the configuration directly
   600         self._add(ConfigurationProxy(configref))
   742         self._add(ConfigurationProxy(configref), policy)
   601 
   743 
   602     def create_configuration(self, path):
   744     def create_configuration(self, path):
   603         """
   745         """
   604         Create a new configuration by its name to the Configuration. 
   746         Create a new configuration by its name to the Configuration. 
   605         1. Create new Configuration object
   747         1. Create new Configuration object
   609         @param path: The reference of the configuration to create
   751         @param path: The reference of the configuration to create
   610         @return: The new configuration object.
   752         @return: The new configuration object.
   611         """
   753         """
   612         # normalise the path
   754         # normalise the path
   613         normpath = utils.resourceref.norm(path)
   755         normpath = utils.resourceref.norm(path)
   614         cklass = self.get_configuration_class()
   756         cklass = self._configuration_class()
   615         conf = cklass(normpath, namespace=self.namespace)
   757         conf = cklass(normpath, namespace=self.namespace)
   616         proxy = ConfigurationProxy(normpath)
   758         self.add_configuration(conf)
   617         self.add_configuration(proxy)
       
   618         proxy._set_obj(conf)
       
   619         return conf
   759         return conf
   620 
   760 
   621     def remove_configuration(self, path):
   761     def remove_configuration(self, path):
   622         """
   762         """
   623         Remove a Layer object from the Configuration by its reference.
   763         Remove a Layer object from the Configuration by its reference.
   634     def list_all_configurations(self):
   774     def list_all_configurations(self):
   635         """
   775         """
   636         List all Layer objects in the Configuration
   776         List all Layer objects in the Configuration
   637         @return: a copy array of layer references.
   777         @return: a copy array of layer references.
   638         """
   778         """
   639         # TODO
   779         return [config.get_path_for_parent(self) for config in self._traverse(type=(Configuration, ConfigurationProxy))] 
   640         # huge performance problem 
       
   641         return [config.get_path() for config in self._traverse(type=(Configuration, ConfigurationProxy))] 
       
   642 
   780 
   643     def get_configuration(self, path):
   781     def get_configuration(self, path):
   644         """
   782         """
   645         Get a Layer object by if path
   783         Get a Layer object by if path
   646         @return: a Layer object
   784         @return: a Layer object
   665             last_config = last_config.get_configuration_by_index(-1)
   803             last_config = last_config.get_configuration_by_index(-1)
   666             return last_config.get_last_configuration()
   804             return last_config.get_last_configuration()
   667         except IndexError:
   805         except IndexError:
   668             return self 
   806             return self 
   669 
   807 
   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):
   808     def add(self, child, policy=container.REPLACE):
   682         """
   809         """
   683         A generic add function to add child objects. The function is intended to act as
   810         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.
   811         proxy function that call the correct add function based on the child objects class.
   685         
   812         
   688         @raise IncorrectClassError: if the given class cannot be added to this object.  
   815         @raise IncorrectClassError: if the given class cannot be added to this object.  
   689         """
   816         """
   690         if isinstance(child, Configuration):
   817         if isinstance(child, Configuration):
   691             self.add_configuration(child)
   818             self.add_configuration(child)
   692         elif isinstance(child, ConfigurationProxy):
   819         elif isinstance(child, ConfigurationProxy):
   693             self.add_configuration(child)
   820             self._add(child)
   694         elif isinstance(child, Base):
   821         elif isinstance(child, Base):
   695             self._add(child)
   822             self._add(child)
   696         else:
   823         else:
   697             raise exceptions.IncorrectClassError("Cannot add %s to %s" % (child, self))
   824             raise exceptions.IncorrectClassError("Cannot add %s to %s" % (child, self))
   698 
   825 
   699     def layered_content(self, layers=None):
   826     def layered_resources(self, layers=None, empty_folders=False, folder=None, resource_type=None):
   700         """
   827         """
   701         fetch content from first to last and override content 
   828         Fetch resource paths by layers so that if a resource with the same name
   702         if it is found from a later layer 
   829         exists on multiple layers, the one on the latest layer is the active one.
   703         Create an array of the layers based on the layer indexes.
   830         @param layers: List of layer indices to specify the layer to use, None
   704         """
   831             for all layers.
       
   832         @param empty_folders: If True, empty folders are returned also.
       
   833         @param folder: Name of a specific folder from which to get resources, or None.
       
   834             If None, resource_type must be specified.
       
   835         @param resource_type: Type of the resources to find. Must be one of
       
   836             ('confml', 'implml', 'content', 'doc') or None.
       
   837             If None, folder must be specified.
       
   838         @return: A container.DataContainer instance containing the resource paths.
       
   839             For example: {'foo.txt': ['layer1/content/foo.txt',
       
   840                                       'layer2/content/foo.txt'],
       
   841                           'bar.txt': ['layer1/content/bar.txt']}
       
   842         """
       
   843         MAPPING = {'confml':    lambda layer: layer.confml_folder(),
       
   844                    'implml':    lambda layer: layer.implml_folder(),
       
   845                    'content':   lambda layer: layer.content_folder(),
       
   846                    'doc':       lambda layer: layer.doc_folder()}
       
   847         if resource_type is not None and resource_type not in MAPPING:
       
   848             raise ValueError("Invalid resource type %r, should be one of %r" % (resource_type, MAPPING.keys()))
       
   849         
       
   850         if folder and resource_type:
       
   851             raise ValueError('Only one of folder and resource_type must be specified!')
       
   852         if not folder and not resource_type:
       
   853             raise ValueError('Either folder or resource_type must be specified!')
       
   854         
   705         configuration_array = []
   855         configuration_array = []
   706         if layers == None:
   856         if layers == None:
   707             configuration_array = self.list_configurations()
   857             configuration_array = self.list_configurations()
   708         else:
   858         else:
   709             all = self.list_configurations()
   859             all = self.list_configurations()
   710             for i in layers:
   860             for i in layers:
   711                 configuration_array.append(all[i])
   861                 configuration_array.append(all[i])
   712 
   862         
   713         content = container.DataContainer()
   863         # Add the current configuration as the last one in the list, in case
       
   864         # the current configuration happens to be a layer root itself
       
   865         configuration_array.append('')
       
   866         
       
   867         # Set up the get_folder function based on the parameters
       
   868         if resource_type:
       
   869             get_folder = MAPPING[resource_type]
       
   870         else:
       
   871             def get_folder(layer):
       
   872                 cpath = layer.get_current_path()
       
   873                 return Folder(layer.storage, utils.resourceref.join_refs([cpath, folder]))
       
   874         
       
   875         result = container.DataContainer()
   714         for configuration_path in configuration_array:
   876         for configuration_path in configuration_array:
   715             content_folder = self.get_configuration(configuration_path).get_layer().content_folder()
   877             folder_obj = get_folder(self.get_configuration(configuration_path).get_layer())
   716             content_path = content_folder.get_current_path()
   878             folder_path = folder_obj.get_current_path()
   717             for content_file in content_folder.list_resources("", True):
   879             for res_path in folder_obj.list_resources("", recurse=True, empty_folders=empty_folders):
   718                 source_file = utils.resourceref.join_refs([content_path, content_file])
   880                 if res_path == '': continue # ZipStorage sometimes returns empty paths
   719                 content.add_value(content_file, source_file)
   881                 res_fullpath = utils.resourceref.join_refs([folder_path, res_path])
   720                 
   882                 result.add_value(res_path, res_fullpath)
   721         return content
   883         return result
   722 
   884     
   723 
   885     def layered_confml(self, layers=None, empty_folders=False):
       
   886         return self.layered_resources(layers, empty_folders, resource_type='confml')
       
   887     
       
   888     def layered_implml(self, layers=None, empty_folders=False):
       
   889         return self.layered_resources(layers, empty_folders, resource_type='implml')
       
   890     
       
   891     def layered_content(self, layers=None, empty_folders=False):
       
   892         return self.layered_resources(layers, empty_folders, resource_type='content')
       
   893     
       
   894     def layered_doc(self, layers=None, empty_folders=False):
       
   895         return self.layered_resources(layers, empty_folders, resource_type='doc')
       
   896     
       
   897     
       
   898     
   724 class Configuration(CompositeConfiguration):
   899 class Configuration(CompositeConfiguration):
   725     """
   900     """
   726     A Configuration is a container that can hold several Layer objects.
   901     A Configuration is a container that can hold several Layer objects.
   727     """
   902     """
   728 
   903 
   729     def __init__(self, ref="", **kwargs):
   904     def __init__(self, ref="", **kwargs):
   730         self.path = kwargs.get('path') or ref
   905         self.path = kwargs.get('path') or ref
   731         self.namespace = kwargs.get('namespace', '')
   906         self.namespace = kwargs.get('namespace', '')
   732         self.name = utils.resourceref.to_objref(self.path)
   907         self.name = kwargs.get('name',utils.resourceref.to_objref(self.path))
   733         super(Configuration, self).__init__(utils.resourceref.to_objref(self.path))
   908         self.version = kwargs.get('version')
       
   909         super(Configuration, self).__init__(utils.resourceref.to_objref(self.path), **kwargs)
   734         self.container = True
   910         self.container = True
   735 
   911 
       
   912     def __reduce_ex__(self, protocol_version):
       
   913         """
       
   914         Make the Configuration pickle a ConfigurationProxy object that would load this configuration
       
   915         """
       
   916         proxy = ConfigurationProxy(self.path, store_interface = self.get_project())
       
   917         tpl = proxy.__reduce_ex__(protocol_version)
       
   918         return tpl
       
   919 
       
   920     def __getstate__(self):
       
   921         return None
       
   922 
   736     def _default_object(self, name):
   923     def _default_object(self, name):
   737         return Feature(name)
   924         return self._default_class()(name)
       
   925 
       
   926     def _default_class(self):
       
   927         return self._feature_class()
       
   928 
       
   929     def _feature_class(self):
       
   930         return Feature
       
   931 
       
   932     def _view_class(self):
       
   933         return View
   738 
   934 
   739     def _supported_type(self, obj):
   935     def _supported_type(self, obj):
   740         if isinstance(obj, Configuration) \
   936         if isinstance(obj, Configuration) \
   741         or isinstance(obj, Feature) \
   937         or isinstance(obj, Feature) \
   742         or isinstance(obj, Data) \
   938         or isinstance(obj, Data) \
   745         or isinstance(obj, Base): 
   941         or isinstance(obj, Base): 
   746             return True
   942             return True
   747         else:
   943         else:
   748             return False
   944             return False
   749 
   945 
   750     def _dict(self):
   946     def _dict(self, **kwargs):
   751         """
   947         """
   752         Return the public variables in a dictionary
   948         Return the public variables in a dictionary
   753         """
   949         """
   754         dict = {}
   950         dict = super(Configuration, self)._dict(**kwargs)
   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
   951         dict['namespace'] = self.namespace
   761         return dict
   952         return dict
   762     
   953     
   763     def get_name(self):
   954     def get_name(self):
   764         """
   955         """
   781     def set_path(self, path):
   972     def set_path(self, path):
   782         """
   973         """
   783         Set the path of the configuration resource, and update the name and ref to correspond
   974         Set the path of the configuration resource, and update the name and ref to correspond
   784         """
   975         """
   785         self.path = path
   976         self.path = path
   786         #self.name = utils.resourceref.to_objref(self.path)
       
   787         self.set_ref(utils.resourceref.to_objref(self.path))
   977         self.set_ref(utils.resourceref.to_objref(self.path))
   788 
   978 
   789     #@property
       
   790     def get_full_path(self):
   979     def get_full_path(self):
   791         """
   980         """
   792         Return the path of the configuration resource
   981         Return the path of the configuration resource
   793         """
   982         """
   794         try:
   983         try:
   795             parentconfig = self._find_parent(type=Configuration)
   984             parentconfig = self._find_parent(type=Configuration)
   796             parent_path = utils.resourceref.get_path(parentconfig.get_path()) 
   985             parent_path = utils.resourceref.get_path(parentconfig.get_path()) 
   797         except exceptions.NotFound:
   986         except exceptions.NotFound:
   798             parent_path = ""
   987             parent_path = ""
   799 
   988         return utils.resourceref.join_refs([parent_path, self.path])
       
   989 
       
   990     def get_path_for_parent(self, parent):
       
   991         """
       
   992         Return the path to this configuration for a defined parent Configuration object.
       
   993         """
       
   994         parent_path = ""
       
   995         try:
       
   996             parentconfig = self._find_parent(type=Configuration)
       
   997             if parent != parentconfig:
       
   998                 parent_path = utils.resourceref.get_path(parentconfig.get_path_for_parent(parent)) 
       
   999         except exceptions.NotFound:
       
  1000             pass
   800         return utils.resourceref.join_refs([parent_path, self.path])
  1001         return utils.resourceref.join_refs([parent_path, self.path])
   801 
  1002 
   802     def get_layer(self):
  1003     def get_layer(self):
   803         """
  1004         """
   804         Get the layer object where this Configuration is located. 
  1005         Get the layer object where this Configuration is located. 
   819     def set_namespace(self, namespace):
  1020     def set_namespace(self, namespace):
   820         """
  1021         """
   821         @param namespace: The new namespace of the object
  1022         @param namespace: The new namespace of the object
   822         """
  1023         """
   823         self._namespace =  namespace
  1024         self._namespace =  namespace
   824         #self.root.set_namespace(namespace)
       
   825 
  1025 
   826     def get_namespace(self):
  1026     def get_namespace(self):
   827         """
  1027         """
   828         @return: The reference of the object.
  1028         @return: The reference of the object.
   829         """
  1029         """
   834         @return: The reference of the object.
  1034         @return: The reference of the object.
   835         """
  1035         """
   836         self._namespace = None
  1036         self._namespace = None
   837     namespace = property(get_namespace, set_namespace, del_namespace)
  1037     namespace = property(get_namespace, set_namespace, del_namespace)
   838 
  1038 
   839     def list_resources(self, empty_folders=False):
  1039     def list_resources(self, **kwargs):
   840         """
  1040         """
   841         List all resources used in this configuration
  1041         List all resources used in this configuration
   842         """
  1042         """
   843         """
  1043         """
   844         1. First ensure that all configuration resource files are added 
  1044         1. First ensure that all configuration resource files are added 
   846         3. Make the list distinct
  1046         3. Make the list distinct
   847         """
  1047         """
   848         
  1048         
   849         
  1049         
   850         resources = [self.get_full_path()]
  1050         resources = [self.get_full_path()]
   851         for config in self._traverse(type=Configuration):
  1051         for config in self._traverse(type=(Configuration,ConfigurationProxy)):
   852             resources.append(config.get_full_path())
  1052             resources.append(config.get_full_path())
   853         layer = self.get_layer()
  1053         layer = self.get_layer()
   854         for resref in layer.list_all_resources(empty_folders):
  1054         for resref in layer.list_all_resources():
   855             resources.append(utils.resourceref.join_refs([layer.get_current_path(), resref]))
  1055             resources.append(utils.resourceref.join_refs([layer.get_current_path(), resref]))
   856     
  1056     
   857         return utils.distinct_array(resources)
  1057         return utils.distinct_array(resources)
   858 
  1058 
   859     def get_resource(self, ref, mode="r"):
  1059     def get_resource(self, ref, mode="r"):
   889         @param ref: The reference to the feature object.
  1089         @param ref: The reference to the feature object.
   890         @return: A Feature object
  1090         @return: A Feature object
   891         """
  1091         """
   892         return self._get(ref)
  1092         return self._get(ref)
   893 
  1093 
       
  1094     def create_feature(self, ref, **kwargs):
       
  1095         """
       
  1096         Create a feature object to the configuration.
       
  1097         @param ref: The ref for the Feature object.
       
  1098         @param **kwargs: keyword arguments  
       
  1099         e.g. to add fea2 under fea1 add_feature(fea2, 'fea1')
       
  1100         @return: the new feature object.
       
  1101         """
       
  1102         fea = self._feature_class()(ref, **kwargs)
       
  1103         self._add(fea)
       
  1104         return fea
       
  1105 
   894     def add_feature(self, feature, namespace=""):
  1106     def add_feature(self, feature, namespace=""):
   895         """
  1107         """
   896         Add a feature object to the configuration.
  1108         Add a feature object to the configuration.
   897         @param feature: The Feature object to add.
  1109         @param feature: The Feature object to add.
   898         @param namespace: The sub namespace for the feature. 
  1110         @param namespace: The sub namespace for the feature. 
   899         e.g. to add fea2 under fea1 add_feature(fea2, 'fea1')
  1111         e.g. to add fea2 under fea1 add_feature(fea2, 'fea1')
   900         @return: None
  1112         @return: None
   901         """
  1113         """
   902         self._add_to_path(namespace, feature)
  1114         if namespace and self._has(namespace):
       
  1115             # Add the feature directly with an existing feature's add_feature functionality
       
  1116             self.get_feature(namespace).add_feature(feature)
       
  1117         else:
       
  1118             self._add_to_path(namespace, feature)
   903 
  1119 
   904     def remove_feature(self, ref):
  1120     def remove_feature(self, ref):
   905         """
  1121         """
   906         remove feature by its reference
  1122         remove feature by its reference
   907         @param ref: 
  1123         @param ref: 
   925         return [fea.fqr for fea in self._traverse(type=Feature)]
  1141         return [fea.fqr for fea in self._traverse(type=Feature)]
   926 
  1142 
   927     def add_data(self, data, policy=container.REPLACE):
  1143     def add_data(self, data, policy=container.REPLACE):
   928         """
  1144         """
   929         Add a data object to this configuration object.
  1145         Add a data object to this configuration object.
   930         @param data: The Data object to add.
  1146         @param data: The Data object or list of Data objects to add.
   931         @return: None
  1147         @return: None
   932         """ 
  1148         """
   933         if not self._has(data.attr):
  1149         data_objs = utils.get_list(data)
   934             self._add(DataContainer(data.attr, container=True))
  1150         
   935         (namespace, name) = utils.dottedref.psplit_ref(data.get_fearef())
  1151         if policy == container.PREPEND:
   936         self._get(data.attr)._add_to_path(namespace, data, policy)
  1152             data_objs = reversed(data_objs)
       
  1153             policy_first = container.PREPEND
       
  1154             policy_rest = container.PREPEND
       
  1155         else:
       
  1156             policy_first = policy
       
  1157             policy_rest = container.APPEND
       
  1158         
       
  1159         for i, data_obj in enumerate(data_objs):
       
  1160             if not self._has(data_obj.attr):
       
  1161                 self._add(DataContainer(data_obj.attr, container=True))
       
  1162             (namespace, name) = utils.dottedref.psplit_ref(data_obj.get_fearef())
       
  1163             
       
  1164             if i == 0:  p = policy_first
       
  1165             else:       p = policy_rest
       
  1166             self._get(data_obj.attr)._add_to_path(namespace, data_obj, p)
   937 
  1167 
   938     def get_data(self, ref):
  1168     def get_data(self, ref):
   939         """
  1169         """
   940         Get a data object by its reference.
  1170         Get a data object by its reference.
   941         @param ref: The reference to the data object.
  1171         @param ref: The reference to the data object.
  1007         # Populate the view object before returning it
  1237         # Populate the view object before returning it
  1008         view = self._get(ref)
  1238         view = self._get(ref)
  1009         view.populate()
  1239         view.populate()
  1010         return view
  1240         return view
  1011 
  1241 
  1012     def add_view(self, viewname):
  1242     def create_view(self, viewname):
       
  1243         """
       
  1244         Create a view object to the configuration.
       
  1245         @param viewname: The name of the view to add. 
       
  1246         @return: view object
       
  1247         """
       
  1248         viewobj = self._view_class()(viewname)
       
  1249         self.add_view(viewobj)
       
  1250         return viewobj
       
  1251 
       
  1252     def add_view(self, viewobj):
  1013         """
  1253         """
  1014         Add a view object to the configuration.
  1254         Add a view object to the configuration.
  1015         @param viewname: The name of the view to add. 
  1255         @param viewobj: The existing view object to add. 
  1016         @return: None
  1256         @return: None
  1017         """
  1257         """
  1018         return self._add(View(viewname))
  1258         assert(isinstance(viewobj, View))
       
  1259         return self._add(viewobj)
  1019 
  1260 
  1020     def remove_view(self, ref):
  1261     def remove_view(self, ref):
  1021         """
  1262         """
  1022         Remove a view object from the configuration.
  1263         Remove a view object from the configuration.
  1023         @param ref: The reference to the View. 
  1264         @param ref: The reference to the View. 
  1036     def save(self):
  1277     def save(self):
  1037         """
  1278         """
  1038         Save the object to the permanent Storage object. Calls the save operation of 
  1279         Save the object to the permanent Storage object. Calls the save operation of 
  1039         all the children.
  1280         all the children.
  1040         """
  1281         """
       
  1282         # Change the recursion order so that the current object 
       
  1283         # is saved first and then its childen.
       
  1284         # This increases performance in cases where this object requires information about its childen (no unload -> load cases)
       
  1285         self.get_project().unload(self.get_full_path(), self)
  1041         for child in self._objects():
  1286         for child in self._objects():
  1042             if isinstance(child, (Configuration,ConfigurationProxy)):
  1287             if isinstance(child, (Configuration,ConfigurationProxy)):
  1043                 child.save()
  1288                 child.save()
  1044         self.get_project().unload(self.get_full_path(), self)
       
  1045 
  1289 
  1046     def close(self):
  1290     def close(self):
  1047         """
  1291         """
  1048         Close the configuration
  1292         Close the configuration
  1049         """
  1293         """
  1075         """
  1319         """
  1076         Get the default view from this configuration hierarchy.
  1320         Get the default view from this configuration hierarchy.
  1077         This returns always the view from the Root configuration point of view.
  1321         This returns always the view from the Root configuration point of view.
  1078         """
  1322         """
  1079         try:
  1323         try:
  1080             parent = self._find_parent_or_default() 
  1324             parent = self._find_parent_or_default()
  1081             if parent and isinstance(parent, Configuration):
  1325             if parent and isinstance(parent, Configuration):
  1082                 return parent.get_default_view()
  1326                 return parent.get_default_view()
  1083             else:
  1327             else:
  1084                 if not hasattr(self, '_default_view'):
  1328                 if not self._has('?default_view'):
  1085                     self._create_default_view()
  1329                     self._create_default_view()
  1086                 return self._default_view
  1330                 return self._get('?default_view')
  1087         except exceptions.NotFound, e:
  1331         except exceptions.NotFound, e:
  1088             raise e
  1332             raise e
  1089         # raise exceptions.NotFound("Default View is not found! No root configuration?")
  1333         # raise exceptions.NotFound("Default View is not found! No root configuration?")
  1090     
  1334     
  1091     def recreate_default_view(self):
  1335     def recreate_default_view(self):
  1099             raise e
  1343             raise e
  1100         # raise exceptions.NotFound("Default View is not found! No root configuration?")
  1344         # raise exceptions.NotFound("Default View is not found! No root configuration?")
  1101     
  1345     
  1102     def _create_default_view(self):
  1346     def _create_default_view(self):
  1103         # Rebuild the default view for this Configuration
  1347         # Rebuild the default view for this Configuration
  1104         self._default_view = View("_default_view", data=True)
  1348         default_view = View("?default_view", data=True)
  1105         self._default_view._parent= self
  1349         #self._default_view._parent= self
       
  1350         self._add(default_view)
  1106         # First add all features of the configuration to the view. 
  1351         # First add all features of the configuration to the view. 
  1107         # Then add all data elements under the features
  1352         # Then add all data elements under the features
  1108         for child in self._traverse(type=Feature):
  1353         for child in self._traverse(type=Feature):
  1109             self._default_view.add_feature(child, child.namespace)
  1354             # TODO print "Adding : %s -> %s" % (child.namespace, child)
       
  1355             default_view.add_feature(child, child.namespace)
  1110         for child in self._traverse(type=Data):
  1356         for child in self._traverse(type=Data):
  1111             #parent_config = child._find_parent_or_default(type=Configuration)
  1357             #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())
  1358             #print "Adding data %s: fqr: %s from file %s." % (child.get_value(), child.fqr, parent_config.get_path())
  1113             try:
  1359             try:
  1114                 fea = self._default_view.get_feature(child.fqr)
  1360                 fea = default_view.get_feature(child.fqr)
  1115                 fea.add_data(child)
  1361                 fea.add_data(child)
  1116             except exceptions.NotFound, e:
  1362             except exceptions.NotFound, e:
  1117                 data_parent_config = child._find_parent_or_default(type=Configuration)
  1363                 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()))
  1364                 logging.getLogger('cone').info("Warning: Feature '%s' for data in %s not found." % (child.fqr, data_parent_config.get_path()))
  1119 
  1365 
  1126         The ConfigurationProxy that represents a configuration that is included in another configuration.
  1372         The ConfigurationProxy that represents a configuration that is included in another configuration.
  1127         @param ref: the reference to the storage resource 
  1373         @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.
  1374         The ConfigurationProxy trust to get the store_interface from the parent object with get_storage() function.
  1129         
  1375         
  1130         """
  1376         """
  1131         container.LoadProxy.__init__(self, path)
  1377         super(ConfigurationProxy,self).__init__(path, **kwargs)
  1132         self.set('_name', utils.resourceref.to_objref(path))
  1378         self.set('_name', utils.resourceref.to_objref(path))
  1133 
  1379 
       
  1380     def __reduce_ex__(self, protocol_version):
       
  1381         """
       
  1382         Make the Configuration pickle a ConfigurationProxy object that would load this configuration
       
  1383         """
       
  1384         return super(ConfigurationProxy, self).__reduce_ex__(protocol_version)
       
  1385     
  1134     def _clone(self, **kwargs):
  1386     def _clone(self, **kwargs):
  1135         """
  1387         """
  1136         A ConfigurationProxy specific implementation for cloning.
  1388         A ConfigurationProxy specific implementation for cloning.
  1137         Copies all (public) members in dictionary.
  1389         Copies all (public) members in dictionary.
  1138         To clone call the actual object that is proxied as well if the reqursion is on.
  1390         To clone call the actual object that is proxied as well if the reqursion is on.
  1150                 # decrease the recursion
  1402                 # decrease the recursion
  1151                 kwargs['recursion_depth'] = recursion_depth - 1
  1403                 kwargs['recursion_depth'] = recursion_depth - 1
  1152                 newobj = self._get_obj()._clone(**kwargs) 
  1404                 newobj = self._get_obj()._clone(**kwargs) 
  1153                 obj._set_obj(newobj)
  1405                 obj._set_obj(newobj)
  1154         return obj
  1406         return obj
  1155 
  1407     
  1156     def _dict(self):
  1408     def _dict(self):
  1157         """
  1409         """
  1158         Return the public variables in a dictionary
  1410         Return the public variables in a dictionary
  1159         """
  1411         """
  1160         dict = {}
  1412         dict = {}
  1162             if key.startswith('_'):
  1414             if key.startswith('_'):
  1163                 continue
  1415                 continue
  1164             else:
  1416             else:
  1165                 dict[key] = self.__dict__[key]
  1417                 dict[key] = self.__dict__[key]
  1166         return dict
  1418         return dict
  1167 
  1419     
  1168     def _get_mapper(self,modelname):
  1420     def _get_mapper(self,modelname):
  1169         """
  1421         """
  1170         Return a instance of appropriate mapper for given model.
  1422         Return a instance of appropriate mapper for given model.
  1171         """
  1423         """
  1172         return mapping.BaseMapper()
  1424         return mapping.BaseMapper()
  1175     """
  1427     """
  1176     A Group class. Group is used in View to group up other Group/Feature objects.
  1428     A Group class. Group is used in View to group up other Group/Feature objects.
  1177     """
  1429     """
  1178     def __init__(self, ref="", **kwargs):
  1430     def __init__(self, ref="", **kwargs):
  1179         super(Group, self).__init__(ref, **kwargs)
  1431         super(Group, self).__init__(ref, **kwargs)
  1180         self.name = ref
  1432         self.name = kwargs.get('name', ref)
  1181         self.support_data = kwargs.get("data", False)
  1433         self.support_data = kwargs.get("data", False)
  1182 
  1434 
  1183     def _supported_type(self, obj):
  1435     def _supported_type(self, obj):
  1184         if isinstance(obj, (Group, \
  1436         if isinstance(obj, (Group, \
  1185                            Base, \
  1437                            Base, \
  1186                            _FeatureProxy, \
  1438                            _FeatureProxy, \
  1187                            FeatureLink)): 
  1439                            FeatureLink, \
       
  1440                            ConfigurationProxy)): 
  1188             return True
  1441             return True
  1189         else:
  1442         else:
  1190             return False
  1443             return False
  1191 
  1444 
  1192     def _default_object(self, name):
  1445     def _default_object(self, name):
  1193         return Group(name)
  1446         return self._group_class()(name)
  1194 
  1447 
  1195     def get_name(self):
  1448     def _group_class(self):
  1196         """
  1449         return Group
  1197         Return the name of the configuration
  1450 
  1198         """
  1451     def _featurelink_class(self):
  1199         return self.name
  1452         return FeatureLink
  1200 
       
  1201     def set_name(self, name):
       
  1202         """
       
  1203         Set the name
       
  1204         """
       
  1205         self.name
       
  1206 
  1453 
  1207     def add(self, child, policy=container.REPLACE):
  1454     def add(self, child, policy=container.REPLACE):
  1208         """
  1455         """
  1209         A generic add function to add child objects. The function is intended to act as
  1456         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.
  1457         proxy function that call the correct add function based on the child objects class.
  1211         
  1458         
  1212         Example: obj.add(Feature("test")), actually obj.add_feature(Feature("test"))
  1459         Example: obj.add(Feature("test")), actually obj.add_feature(Feature("test"))
  1213         @param child: the child object to add
  1460         @param child: the child object to add
  1214         @raise IncorrectClassError: if the given class cannot be added to this object.  
  1461         @raise IncorrectClassError: if the given class cannot be added to this object.  
  1215         """
  1462         """
  1216         if isinstance(child, (Group, \
  1463         if self._supported_type(child):
  1217                               Base, \
       
  1218                               _FeatureProxy, \
       
  1219                               FeatureLink)):
       
  1220             self._add(child)
  1464             self._add(child)
  1221         else:
  1465         else:
  1222             raise exceptions.IncorrectClassError("Cannot add %s to %s" % (child, self))
  1466             raise exceptions.IncorrectClassError("Cannot add %s to %s" % (child, self))
  1223 
  1467 
  1224     def get_name(self):
  1468     def get_name(self):
  1231         """
  1475         """
  1232         Set the name
  1476         Set the name
  1233         """
  1477         """
  1234         self.name = name
  1478         self.name = name
  1235 
  1479 
       
  1480     def create_featurelink(self, feature_ref, **kwargs):
       
  1481         """
       
  1482         create a feature link object to this element, with the given ref
       
  1483         @param feature_ref: the reference for the featurelink which should
       
  1484         point to a exising feature in the configuration.
       
  1485         @param **kwargs: keyword arguments are passed to the featurelink object 
       
  1486         directly.
       
  1487         """
       
  1488         fealink = self._featurelink_class()(feature_ref, **kwargs)
       
  1489         self.add(fealink)
       
  1490         return fealink
       
  1491 
       
  1492     def get_featurelink(self, ref):
       
  1493         return self._get(FeatureLink.get_featurelink_ref(ref))
       
  1494     
  1236     def add_feature(self, feature, path=""):
  1495     def add_feature(self, feature, path=""):
  1237         """
  1496         """
  1238         Add feature to this Group.
  1497         Add feature to this Group.
  1239         """
  1498         """
  1240         if not isinstance(feature, Feature):
  1499         if not isinstance(feature, Feature):
  1258         try:
  1517         try:
  1259             return self._get(ref)
  1518             return self._get(ref)
  1260         except exceptions.NotFound:
  1519         except exceptions.NotFound:
  1261             raise exceptions.NotFound("Feature '%s' not found." % ref)
  1520             raise exceptions.NotFound("Feature '%s' not found." % ref)
  1262 
  1521 
  1263     def get_features(self, ref, **kwargs):
  1522     def get_features(self, refs, **kwargs):
  1264         """
  1523         """
  1265         Get a list of features that match the ref. 
  1524         Get a list of features that match the ref. 
       
  1525         
       
  1526         @param refs: The paths (refs) to the given feature or xpath like expression. The refs
       
  1527         argument can be a single reference or a list of references to features. 
       
  1528         @return: A list of features.
       
  1529         
       
  1530         NOTE! the invalid references will not raise an exception.
       
  1531          
  1266         Example1: get_features('foo.bar') would be the same as get_feature('foo.bar'), but this returns 
  1532         Example1: get_features('foo.bar') would be the same as get_feature('foo.bar'), but this returns 
  1267         always a list [<Feature>].
  1533         always a list [<Feature>].
  1268         Example2: get_features('foo.*') would try to retrieve a list of all foo children.
  1534         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, 
  1535         Example3: get_features('foo.*', type='') would try to retrieve a list of all foo children, 
  1270         that have a defined type.
  1536         that have a defined type.
  1271         @param path: The path (ref) to the given feature or xpath like expression 
  1537         Example4: get_features(['foo','bar.set1']) would try to retrieve a foo and then bar.set1.
       
  1538         
       
  1539         """
       
  1540         
       
  1541         if utils.is_list(refs):
       
  1542             features = []
       
  1543             for ref in refs:
       
  1544                 features += self.get_matching_features(ref, **kwargs)
       
  1545             return features
       
  1546         else:
       
  1547             return self.get_matching_features(refs, **kwargs)
       
  1548 
       
  1549     def get_matching_features(self, ref, **kwargs):
       
  1550         """
       
  1551         Get a list of features that match the ref. 
       
  1552         
       
  1553         @param refs: The paths (refs) to the given feature or xpath like expression. The refs
       
  1554         argument can be a single reference or a list of references to features. 
  1272         @return: A list of features.
  1555         @return: A list of features.
  1273         """
  1556 
  1274         (startref, last) = utils.dottedref.psplit_ref(ref)
  1557         NOTE! the invalid references will not raise an exception but return an empty list.
  1275         startelem = self._get(startref)
  1558         
  1276         if last == '**':
  1559         Example1: get_features('foo.bar') would be the same as get_feature('foo.bar'), but this returns 
  1277             return [fea for fea in startelem._traverse(**kwargs)]
  1560         always a list [<Feature>].
  1278         elif last == '*':
  1561         Example2: get_features('foo.*') would try to retrieve a list of all foo children.
  1279             return [fea for fea in startelem._objects(**kwargs)] 
  1562         Example3: get_features('foo.*', type='') would try to retrieve a list of all foo children, 
  1280         else:
  1563         that have a defined type.
  1281             return [self._get(ref)]
  1564         
  1282 
  1565         """
       
  1566         try:
       
  1567             (startref, last) = utils.dottedref.psplit_ref(ref)
       
  1568             startelem = self._get(startref)
       
  1569             kwargs['type'] = _FeatureProxy
       
  1570             if last == '**':
       
  1571                 return [fea for fea in startelem._traverse(**kwargs)]
       
  1572             elif last == '*':
       
  1573                 return [fea for fea in startelem._objects(**kwargs)] 
       
  1574             elif ref != "":
       
  1575                 return [self._get(ref)]
       
  1576             else:
       
  1577                 return []
       
  1578         except exceptions.NotFound:
       
  1579             return []
       
  1580          
  1283     def list_features(self):
  1581     def list_features(self):
  1284         """
  1582         """
  1285         Return a array of all Feature children references under this object.
  1583         Return a array of all Feature children references under this object.
  1286         """
  1584         """
  1287         return [fea.get_ref() for fea in self._objects(type=(_FeatureProxy))]
  1585         return [fea.get_ref() for fea in self._objects(type=(_FeatureProxy))]
  1290         """
  1588         """
  1291         Return a array of all Feature children references under this object.
  1589         Return a array of all Feature children references under this object.
  1292         """
  1590         """
  1293         return [fea.fqr for fea in self._traverse(type=(_FeatureProxy))]
  1591         return [fea.fqr for fea in self._traverse(type=(_FeatureProxy))]
  1294 
  1592 
  1295     def add_group(self, groupname):
  1593     def create_group(self, groupname, **kwargs):
  1296         """
  1594         """
  1297         """
  1595         create a group object to this element with given group name.
  1298         self._add(Group(groupname))
  1596         @param groupname: the name for the new group
       
  1597         @param **kwargs: keyword arguments are passed on to the new group object.  
       
  1598         """
       
  1599         grp = self._group_class()(groupname, **kwargs)
       
  1600         self.add_group(grp)
       
  1601         return grp
       
  1602 
       
  1603     def add_group(self, grp):
       
  1604         """
       
  1605         """
       
  1606         self._add(grp)
  1299 
  1607 
  1300     def remove_group(self, ref):
  1608     def remove_group(self, ref):
  1301         """
  1609         """
  1302         remove a given feature from this view by reference. 
  1610         remove a given feature from this view by reference. 
  1303         @param ref: 
  1611         @param ref: 
  1311         return self._get(ref)
  1619         return self._get(ref)
  1312 
  1620 
  1313     def list_groups(self):
  1621     def list_groups(self):
  1314         """
  1622         """
  1315         """
  1623         """
  1316         return [group.get_name() for group in self._objects(type=Group)]
  1624         return [group.ref for group in self._objects(type=Group)]
  1317 
  1625 
  1318     def populate(self):
  1626     def populate(self):
  1319         """
  1627         """
  1320         Populate or fetch the link to the actual feature for this featureproxy.
  1628         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 
  1629         This method fetches the feature to the _obj member variable and populates also 
  1330     """
  1638     """
  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.
  1639     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     """
  1640     """
  1333     def __init__(self, ref="", **kwargs):
  1641     def __init__(self, ref="", **kwargs):
  1334         super(View, self).__init__(self.to_ref(ref), **kwargs)
  1642         super(View, self).__init__(self.to_ref(ref), **kwargs)
  1335         self.name = ref
       
  1336         self.container = True
  1643         self.container = True
  1337 
  1644 
  1338     @classmethod
  1645     @classmethod
  1339     def to_ref(cls, ref):
  1646     def to_ref(cls, ref):
  1340         """ 
  1647         """ 
  1348     A Feature class. Feature is the base for all Configurable items in a Configuration.
  1655     A Feature class. Feature is the base for all Configurable items in a Configuration.
  1349     """
  1656     """
  1350     PROPERTIES = ['value']
  1657     PROPERTIES = ['value']
  1351     def __init__(self, ref="", **kwargs):
  1658     def __init__(self, ref="", **kwargs):
  1352         super(Feature, self).__init__(ref, **kwargs)
  1659         super(Feature, self).__init__(ref, **kwargs)
  1353         self.name = kwargs.get('name', ref)
  1660         self.name = kwargs.get('name', None)
  1354         self.type = kwargs.get('type', None)
  1661         self.type = kwargs.get('type', None)
       
  1662         self.relevant = kwargs.get('relevant', None)
       
  1663         self.constraint = kwargs.get('constraint', None)
  1355         self._dataproxy = None
  1664         self._dataproxy = None
  1356 
  1665 
  1357     def __copy__(self):
  1666     def __copy__(self):
  1358         dict = {}
  1667         dict = {}
  1359         for key in self.__dict__.keys():
  1668         for key in self.__dict__.keys():
  1362             else:
  1671             else:
  1363                 dict[key] = self.__dict__[key]
  1672                 dict[key] = self.__dict__[key]
  1364         fea = self.__class__(self.ref, **dict)
  1673         fea = self.__class__(self.ref, **dict)
  1365         return fea
  1674         return fea
  1366 
  1675 
  1367 
  1676     def __getstate__(self):
       
  1677         state = super(Feature, self).__getstate__()
       
  1678         # remove the dataproxy value so that it is not stored in serializings
       
  1679         state.pop('_dataproxy', None)
       
  1680         return state
       
  1681 
       
  1682     def __setstate__(self, state):
       
  1683         super(Feature, self).__setstate__(state)
       
  1684         self._dataproxy = None
       
  1685 
       
  1686         
  1368     def _supported_type(self, obj):
  1687     def _supported_type(self, obj):
  1369         # For now support added for desc element via support for Base
  1688         # For now support added for desc element via support for Base
  1370         if isinstance(obj, (Feature, Option, Base)):
  1689         if isinstance(obj, (Feature, Option, Base)):
  1371             return True
  1690             return True
  1372         else:
  1691         else:
  1373             return False
  1692             return False
       
  1693 
       
  1694     def _feature_class(self):
       
  1695         return Feature
  1374 
  1696 
  1375     def add(self, child, policy=container.REPLACE):
  1697     def add(self, child, policy=container.REPLACE):
  1376         """
  1698         """
  1377         A generic add function to add child objects. The function is intended to act as
  1699         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.
  1700         proxy function that call the correct add function based on the child objects class.
  1385             self.add_feature(child)
  1707             self.add_feature(child)
  1386         elif isinstance(child, Option):
  1708         elif isinstance(child, Option):
  1387             self._add(child, policy)
  1709             self._add(child, policy)
  1388         elif isinstance(child, Base):
  1710         elif isinstance(child, Base):
  1389             self._add(child, policy)
  1711             self._add(child, policy)
       
  1712         elif isinstance(child, Property):
       
  1713             self._add(child, policy)
  1390         else:
  1714         else:
  1391             raise exceptions.IncorrectClassError("Cannot add %s to %s" % (child, self))
  1715             raise exceptions.IncorrectClassError("Cannot add %s to %s" % (child, self))
  1392 
  1716 
  1393     def get_name(self):
  1717     def get_name(self):
  1394         """
  1718         """
  1399     def set_name(self, name):
  1723     def set_name(self, name):
  1400         """
  1724         """
  1401         Set the name
  1725         Set the name
  1402         """
  1726         """
  1403         self.name = name
  1727         self.name = name
       
  1728 
       
  1729     def get_relevant(self):
       
  1730         """
       
  1731         Return the relevant attribute of the feature
       
  1732         """
       
  1733         return self.relevant
       
  1734 
       
  1735     def set_relevant(self, relevant):
       
  1736         """
       
  1737         Set the relevant attribute
       
  1738         """
       
  1739         self.relevant = relevant
       
  1740 
       
  1741     def get_constraint(self):
       
  1742         """
       
  1743         Return the constraint attribute of the feature
       
  1744         """
       
  1745         return self.constraint
       
  1746 
       
  1747     def set_constraint(self, constraint):
       
  1748         """
       
  1749         Set the constraint attribute
       
  1750         """
       
  1751         self.constraint = constraint
  1404 
  1752 
  1405     def get_type(self):
  1753     def get_type(self):
  1406         return self.type
  1754         return self.type
  1407 
  1755 
  1408     def set_type(self, type):
  1756     def set_type(self, type):
  1409         self.type = type
  1757         self.type = type
  1410 
  1758 
       
  1759     def create_feature(self, ref, **kwargs):
       
  1760         """
       
  1761         Create a feature object to the configuration.
       
  1762         @param ref: The ref for the Feature object.
       
  1763         @param **kwargs: keyword arguments  
       
  1764         e.g. to add fea2 under fea1 add_feature(fea2, 'fea1')
       
  1765         @return: the new feature object.
       
  1766         """
       
  1767         fea = self._feature_class()(ref, **kwargs)
       
  1768         self.add_feature(fea)
       
  1769         return fea
       
  1770 
  1411     def add_feature(self, feature, path=""):
  1771     def add_feature(self, feature, path=""):
  1412         """
  1772         """
  1413         @param feature: The Feature object to add 
  1773         @param feature: The Feature object to add 
  1414         """
  1774         """
  1415         configuration = self.find_parent(type=Configuration)
  1775         self._add_to_path(path, feature)
  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 
  1776 
  1422     def get_feature(self, path):
  1777     def get_feature(self, path):
  1423         """
  1778         """
  1424         @param path: The path (ref) to the given feature 
  1779         @param path: The path (ref) to the given feature 
  1425         """
  1780         """
  1428     def remove_feature(self, ref):
  1783     def remove_feature(self, ref):
  1429         """
  1784         """
  1430         remove a given feature from this view by reference. 
  1785         remove a given feature from this view by reference. 
  1431         @param ref: 
  1786         @param ref: 
  1432         """
  1787         """
  1433         configuration = self.find_parent(type=Configuration)
  1788         self._remove(ref)
  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 
  1789 
  1440     def list_features(self):
  1790     def list_features(self):
  1441         """
  1791         """
  1442         Return a array of all Feature children references under this object.
  1792         Return a array of all Feature children references under this object.
  1443         """
  1793         """
  1489         Return a array of all Option children references under this object.
  1839         Return a array of all Option children references under this object.
  1490         """
  1840         """
  1491         # Return option refs without the leading 'opt_'
  1841         # Return option refs without the leading 'opt_'
  1492         return [opt.ref[4:] for opt in self._objects(type=Option)]
  1842         return [opt.ref[4:] for opt in self._objects(type=Option)]
  1493 
  1843 
       
  1844     def add_property(self, property):
       
  1845         """
       
  1846         @param property: property object to add
       
  1847         """
       
  1848         if not isinstance(property, Property):
       
  1849             raise TypeError("%r is not an instance of Property!" % property)
       
  1850         self._add(property)
       
  1851 
       
  1852     def create_property(self, **kwargs):
       
  1853         """
       
  1854         @param name=str: property name 
       
  1855         @param value=str: property value
       
  1856         @param unit=str: property unit, e.g. kB
       
  1857         """
       
  1858         self._add(Property(**kwargs))
       
  1859 
       
  1860 
       
  1861     def get_property(self, ref):
       
  1862         """
       
  1863         @param ref: The ref of the property
       
  1864         """
       
  1865         obj = self._get(Property.to_propertyref(ref))
       
  1866         
       
  1867         if not isinstance(obj, Property):
       
  1868             raise TypeError('Object %r is not an instance of Property (%r instead)' % (Property.to_propertyref(ref), type(obj)))
       
  1869         return obj
       
  1870 
       
  1871     def remove_property(self, ref):
       
  1872         """
       
  1873         remove a given property from this feature by ref. 
       
  1874         @param ref: 
       
  1875         """
       
  1876         obj = self._get(Property.to_propertyref(ref))
       
  1877         if not isinstance(obj, Property):
       
  1878             raise TypeError('Trying to remove property with ref %r, but object with ref %r is not an instance of Property (%s instead)' % (ref, Property.to_propertyref(ref), type(obj)))
       
  1879         self._remove(Property.to_propertyref(ref))
       
  1880 
       
  1881     def list_properties(self):
       
  1882         """
       
  1883         Return a array of all Feature properties under this object.
       
  1884         """
       
  1885         
       
  1886         return [Property.to_normref(property.ref) for property in self._objects(type=Property)]
       
  1887 
  1494     def get_value(self, attr=None):
  1888     def get_value(self, attr=None):
  1495         """
  1889         """
  1496         Get the current value of the feature
  1890         Get the current value of the feature. 
  1497         @param attr: The attribute name of the data. E.g. attr='data', attr='rfs' 
  1891         @param attr: The attribute name of the data. E.g. attr='data', attr='rfs' 
  1498         """
  1892         """
  1499         # Do not allow getting of setting of sequence values directly with Feature object
  1893         return self.convert_data_to_value(self.dataproxy._get_datas(attr=attr), cast=True, attr=attr)
  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 
  1894 
  1512     def set_value(self, value, attr=None):
  1895     def set_value(self, value, attr=None):
  1513         """
  1896         """
  1514         Set the current value for this feature. Set the value on the topmost layer.
  1897         Set the current value for this feature. Set the value on the topmost layer.
  1515         @param value: the value to set
  1898         @param value: the value to set
  1516         """
  1899         """
  1517         # Do not allow setting of setting of sequence values directly with Feature object
  1900         data_objs = self.convert_value_to_data(value, attr)
  1518         if not self.is_sequence():
  1901         
  1519             value = self.set_value_cast(value, attr)
  1902         # Set the created data objects to the dataproxy and the
  1520             self.dataproxy._set_value(value, attr)
  1903         # last configuration, overriding any existing elements
  1521 
  1904         self.dataproxy._set_datas(data_objs, attr)
       
  1905         last_config = self.get_root_configuration().get_last_configuration()
       
  1906         last_config.add_data(data_objs, container.REPLACE)
       
  1907     
       
  1908     def convert_data_to_value(self, data_objects, cast=True, attr=None):
       
  1909         """
       
  1910         Convert the given list of Data objects into a suitable value
       
  1911         for this setting.
       
  1912         @param data_objects: The Data object list.
       
  1913         @param cast: If True, the value should be cast to its correct Python type
       
  1914             (e.g. int), if False, the value should remain in the string form
       
  1915             it was in the data objects.
       
  1916         @param attr: The attribute name of the data. E.g. attr='data', attr='rfs'
       
  1917         @return: The converted value.
       
  1918         """
       
  1919         if not data_objects:    return None
       
  1920         
       
  1921         data_obj = data_objects[-1]
       
  1922         if data_obj.map:
       
  1923             value = self._resolve_name_id_mapped_value(data_obj.map, cast_value=cast)
       
  1924         else:
       
  1925             value = data_obj.value
       
  1926             if cast: value = self.get_value_cast(value, attr)
       
  1927         return value
       
  1928     
       
  1929     def convert_value_to_data(self, value, attr=None):
       
  1930         """
       
  1931         Convert the given value to a list of Data objects that can be placed
       
  1932         in the configuration's last layer's data section (DataContainer object).
       
  1933         @param value: The value to convert.
       
  1934         @param attr: The attribute name of the data. E.g. attr='data', attr='rfs'
       
  1935         @return: The converted Data objects.
       
  1936         """
       
  1937         value = self.set_value_cast(value, attr)
       
  1938         return [Data(fqr=self.fqr, value=value, attr=attr)]
       
  1939     
  1522     def del_value(self, attr=None):
  1940     def del_value(self, attr=None):
  1523         """
  1941         """
  1524         Delete the topmost value for this feature.
  1942         Delete the topmost value for this feature.
  1525         """
  1943         """
  1526         if not self.is_sequence():
  1944         self.dataproxy._del_value(attr)
  1527             self.dataproxy._del_value(attr)
       
  1528 
  1945 
  1529     def get_value_cast(self, value, attr=None):
  1946     def get_value_cast(self, value, attr=None):
  1530         """
  1947         """
  1531         A function to perform the value type casting in get operation  
  1948         A function to perform the value type casting in get operation  
  1532         @param value: the value to cast 
  1949         @param value: the value to cast 
  1545     def get_original_value(self, attr=None):
  1962     def get_original_value(self, attr=None):
  1546         """
  1963         """
  1547         Get the current value of the feature
  1964         Get the current value of the feature
  1548         @param attr: The attribute name of the data. E.g. attr='data', attr='rfs' 
  1965         @param attr: The attribute name of the data. E.g. attr='data', attr='rfs' 
  1549         """
  1966         """
  1550         # Do not allow getting of setting of sequence values directly with Feature object
  1967         return self.convert_data_to_value(self.dataproxy._get_datas(attr=attr), cast=False, attr=attr)
  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 
  1968 
  1563     def add_data(self, data):
  1969     def add_data(self, data):
  1564         """
  1970         """
  1565         Add a data value.
  1971         Add a data value.
  1566         @param data: A Data object  
  1972         @param data: A Data object  
  1614         try:
  2020         try:
  1615             return self._parent.is_sequence()
  2021             return self._parent.is_sequence()
  1616         except AttributeError:
  2022         except AttributeError:
  1617             return False
  2023             return False
  1618 
  2024 
       
  2025     def is_sequence_root(self):
       
  2026         """ Return true if this feature is a sequence object it self """
       
  2027         return False
       
  2028 
  1619     def get_sequence_parent(self):
  2029     def get_sequence_parent(self):
  1620         """ Try to get a FeatureSequence object for this Feature if it is found """
  2030         """ Try to get a FeatureSequence object for this Feature if it is found """
  1621         try:
  2031         try:
  1622             return self._parent.get_sequence_parent()
  2032             return self._parent.get_sequence_parent()
  1623         except AttributeError:
  2033         except AttributeError:
  1628             self.dataproxy = self.get_default_view().get_feature(self.get_fullfqr())
  2038             self.dataproxy = self.get_default_view().get_feature(self.get_fullfqr())
  1629         return self._dataproxy
  2039         return self._dataproxy
  1630     def setdataproxy(self, value): self._dataproxy = value
  2040     def setdataproxy(self, value): self._dataproxy = value
  1631     def deldataproxy(self): self._dataproxy = None
  2041     def deldataproxy(self): self._dataproxy = None
  1632     dataproxy = property(getdataproxy, setdataproxy, deldataproxy)
  2042     dataproxy = property(getdataproxy, setdataproxy, deldataproxy)
  1633     value = property(get_value, set_value, del_value)
  2043     """ Use custom OProperty to enable overriding value methods in subclasses """
       
  2044     value = utils.OProperty(get_value, set_value, del_value)
       
  2045 
       
  2046     def get_column_value(self, attr=None):
       
  2047         """
       
  2048         Get the value of the featuresequence column
       
  2049         @param ref: the reference to the column   
       
  2050         @param attr: The attribute name of the data. E.g. attr='data', attr='rfs' 
       
  2051         """
       
  2052         """ get the feature specific data from sequence => a column of data table """
       
  2053         seq_parent = self.get_sequence_parent()
       
  2054         if seq_parent._has_empty_sequence_marker():
       
  2055             return []
       
  2056         
       
  2057         coldata =  []
       
  2058         colref = self.path(seq_parent)
       
  2059         for row in seq_parent.data:
       
  2060             feadata = row.get_feature(colref)
       
  2061             coldata.append(feadata.get_value(attr))
       
  2062         return coldata
       
  2063     
       
  2064     def get_column_original_value(self, attr=None):
       
  2065         """
       
  2066         Get the value of the featuresequence column
       
  2067         @param feasequence: the feature sequence object
       
  2068         @param ref: the reference to the column   
       
  2069         @param attr: The attribute name of the data. E.g. attr='data', attr='rfs' 
       
  2070         """
       
  2071         """ get the feature specific data from sequence => a column of data table """
       
  2072         seq_parent = self.get_sequence_parent()
       
  2073         if seq_parent._has_empty_sequence_marker():
       
  2074             return []
       
  2075         
       
  2076         coldata =  []
       
  2077         colref = self.path(seq_parent)
       
  2078         for row in seq_parent.data:
       
  2079             feadata = row.get_feature(colref)
       
  2080             coldata.append(feadata.get_original_value(attr))
       
  2081         return coldata
       
  2082     
       
  2083     def set_column_value(self, value, attr=None):
       
  2084         """
       
  2085         Get the value of the featuresequence column
       
  2086         @param feasequence: the feature sequence object
       
  2087         @param ref: the reference to the column   
       
  2088         @param value: the value to set. This must be a list instance. 
       
  2089         @param attr: The attribute name of the data. E.g. attr='data', attr='rfs' 
       
  2090         """
       
  2091         seq_parent = self.get_sequence_parent()
       
  2092         colref = self.path(seq_parent)
       
  2093         
       
  2094         if not isinstance(value,list): 
       
  2095             raise exceptions.ConeException("The value for feature sequence '%s' column '%s' must be a list instance. Got %r" % (self.get_sequence_parent().fqr, colref, value))
       
  2096         
       
  2097         # Handle the special case where the sequence is marked as empty
       
  2098         # with the empty sequence marker (single empty data element)
       
  2099         if seq_parent._has_empty_sequence_marker():
       
  2100             seqrows = []
       
  2101         else:
       
  2102             seqrows = seq_parent.data
       
  2103         
       
  2104         if len(seqrows) < len(value):
       
  2105             raise exceptions.ConeException("Too many values for feature sequence '%s' column '%s'. Sequence holds only %d rows. Got %d data values in %r" % (self.get_sequence_parent().fqr, colref, len(seqrows), len(value), value))
       
  2106         for i in range(0, len(value)):
       
  2107             feadata = seqrows[i].get_feature(colref)
       
  2108             feadata.set_value(value[i])
       
  2109 
       
  2110     def add_sequence_feature(self, feature, path=""):
       
  2111         """
       
  2112         Override of the add_feature function in sequence to set the sequence childs to act 
       
  2113         as columns of the feature sequence
       
  2114         @param feature: The Feature object to add 
       
  2115         @param path: path to feature if it not added directly under parent_fea 
       
  2116         """
       
  2117         # modify all possible children of feature
       
  2118         for fea in feature._traverse(type=Feature):
       
  2119             to_sequence_feature(fea)
       
  2120             
       
  2121         # Finally modify and add this feature to parent_feat
       
  2122         to_sequence_feature(feature)
       
  2123         self._add_to_path(path, feature)
       
  2124     
       
  2125     def _resolve_name_id_mapped_value(self, mapping_string, cast_value=True):
       
  2126         """
       
  2127         Resolve the name-ID mapped value based on the given mapping string.
       
  2128         @param mapping_string: The name-ID mapping string in the data element, e.g.
       
  2129             "FooFeature/FooSequence[@key='123']"
       
  2130         @param cast_value: If True, the resolved value will be cast to the corresponding
       
  2131             Python type, otherwise the raw string representation of the value in the
       
  2132             data element will be returned.
       
  2133         @return: The resolved value.
       
  2134         """
       
  2135         def fail(msg): raise exceptions.NameIdMappingError(msg)
       
  2136         
       
  2137         pattern = r"^([\w/]+)\[@key='(.*)'\]$"
       
  2138         m = re.match(pattern, mapping_string)
       
  2139         if m is None: fail("Malformed mapping expression: %s" % mapping_string)
       
  2140         
       
  2141         source_seq_ref = m.group(1).replace('/', '.')
       
  2142         mapping_key = m.group(2)
       
  2143         
       
  2144         dview = self.get_root_configuration().get_default_view()
       
  2145         
       
  2146         try:
       
  2147             source_seq = dview.get_feature(source_seq_ref)
       
  2148         except exceptions.NotFound:
       
  2149             fail("Mapping source sequence '%s' does not exist" % source_seq_ref)
       
  2150         
       
  2151         if source_seq.type != 'sequence':
       
  2152             fail("Mapping source setting '%s' is not a sequence setting" % source_seq_ref)
       
  2153         if not source_seq.mapKey or not source_seq.mapValue:
       
  2154             fail("Source sequence '%s' must have both mapKey and mapValue specified" % source_seq_ref)
       
  2155         
       
  2156         def get_subsetting(ref):
       
  2157             """
       
  2158             Return the sub-setting by the given mapKey or mapValue ref from the
       
  2159             source sequence.
       
  2160             @param ref: The reference in the format it is in the ConfML file.
       
  2161                 E.g. 'SubSetting', 'FileSubSetting/localPath', 'FileSubSetting/targetPath'
       
  2162             """
       
  2163             subsetting = source_seq.get_feature(ref.replace('/', '.'))
       
  2164             # Use localPath for file and folder settings by default
       
  2165             if subsetting.type in ('file', 'folder'):
       
  2166                 subsetting = subsetting.get_feature('localPath')
       
  2167             return subsetting
       
  2168         
       
  2169         try:
       
  2170             key_subsetting = get_subsetting(source_seq.mapKey)
       
  2171         except exceptions.NotFound:
       
  2172             fail("Invalid mapKey in source sequence '%s': no sub-setting with ref '%s'" % (source_seq_ref, source_seq.mapKey))
       
  2173         
       
  2174         
       
  2175         # Get possible override for mapValue from options
       
  2176         value_subsetting_ref = source_seq.mapValue
       
  2177         value_subsetting_ref_overridden = False
       
  2178         for opt in self._objects(type=Option):
       
  2179             if not opt.map or not opt.map_value: continue
       
  2180             if opt.map.replace('/', '.') == source_seq_ref:
       
  2181                 value_subsetting_ref = opt.map_value
       
  2182                 value_subsetting_ref_overridden = True
       
  2183         
       
  2184         try:
       
  2185             value_subsetting = get_subsetting(value_subsetting_ref)
       
  2186         except exceptions.NotFound:
       
  2187             if value_subsetting_ref_overridden:
       
  2188                 fail("Invalid mapValue override in option: sub-setting '%s' does not exist under source sequence '%s'" % (value_subsetting_ref, source_seq_ref))
       
  2189             else:
       
  2190                 fail("Invalid mapValue in source sequence '%s': no sub-setting with ref '%s'" % (source_seq_ref, value_subsetting_ref))
       
  2191         
       
  2192         key_list = key_subsetting.get_original_value()
       
  2193         if mapping_key not in key_list:
       
  2194             fail("No item-setting in source sequence '%s' matches key '%s'" % (source_seq_ref, mapping_key))
       
  2195         
       
  2196         if cast_value:  value_list = value_subsetting.get_value()
       
  2197         else:           value_list = value_subsetting.get_original_value()
       
  2198         return value_list[key_list.index(mapping_key)]
       
  2199 
  1634 
  2200 
  1635 class FeatureSequence(Feature):
  2201 class FeatureSequence(Feature):
  1636     POLICY_REPLACE = 0
  2202     POLICY_REPLACE = 0
  1637     POLICY_APPEND = 1
  2203     POLICY_APPEND = 1
  1638     POLICY_PREPEND = 2
  2204     POLICY_PREPEND = 2
  1640     A Feature class. Feature is the base for all Configurable items in a Configuration.
  2206     A Feature class. Feature is the base for all Configurable items in a Configuration.
  1641     """
  2207     """
  1642     dataelem_name = '?datarows'
  2208     dataelem_name = '?datarows'
  1643     template_name = '?template'
  2209     template_name = '?template'
  1644     def __init__(self, ref="", **kwargs):
  2210     def __init__(self, ref="", **kwargs):
  1645         super(FeatureSequence, self).__init__(ref)
  2211         super(FeatureSequence, self).__init__(ref, **kwargs)
  1646         self.name = kwargs.get('name', ref)
  2212         self.name = kwargs.get('name', ref)
  1647         self.type = 'sequence'
  2213         self.type = 'sequence'
  1648         self.mapKey   = kwargs.get('mapKey')
       
  1649         self.mapValue = kwargs.get('mapValue')
       
  1650         self._templatedata = None
  2214         self._templatedata = None
  1651 
  2215 
  1652     def _get_policy(self, data):
  2216     def _get_policy(self, data):
  1653         """
  2217         """
  1654         parse the policy from a policy string and return a constant
  2218         parse the policy from a policy string and return a constant
  1663         if firstdata.policy == 'append':
  2227         if firstdata.policy == 'append':
  1664             return self.POLICY_APPEND
  2228             return self.POLICY_APPEND
  1665         elif firstdata.policy == 'prefix':
  2229         elif firstdata.policy == 'prefix':
  1666             return self.POLICY_PREPEND
  2230             return self.POLICY_PREPEND
  1667         elif firstdata == data:
  2231         elif firstdata == data:
  1668              # otherwise the policy is either replace or undefined
  2232             # otherwise the policy is either replace or undefined
  1669              # (firstdata.policy == 'replace' or firstdata.policy == ''):
  2233             # (firstdata.policy == 'replace' or firstdata.policy == ''):
  1670             return self.POLICY_REPLACE
  2234             return self.POLICY_REPLACE
  1671         else:
  2235         else:
  1672             return self.POLICY_APPEND
  2236             return self.POLICY_APPEND
  1673         
  2237         
  1674     def _set_template_data(self, data=None):
  2238     def _set_template_data(self, data=None):
  1675         """
  2239         """
  1676         Set the template of the feature sequence  
  2240         Set the template of the feature sequence  
  1677         """
  2241         """
  1678         # If template data is not existing, create it
       
  1679         if data != None:
  2242         if data != None:
  1680             self._templatedata = data
  2243             self._templatedata = data
  1681             for feaname in self.list_features():
  2244             for feaname in self.list_features():
  1682                 if self._templatedata._has(feaname):
  2245                 if self._templatedata._has(feaname):
  1683                     self.get_feature(feaname)._templatedata = self._templatedata._get(feaname)
  2246                     self.get_feature(feaname)._templatedata = self._templatedata._get(feaname)
  1688 
  2251 
  1689     def _add_datarow(self, dataobj=None, policy=POLICY_APPEND):
  2252     def _add_datarow(self, dataobj=None, policy=POLICY_APPEND):
  1690         """
  2253         """
  1691         Add a feature data row for a new data in this sequence 
  2254         Add a feature data row for a new data in this sequence 
  1692         """
  2255         """
       
  2256         create_sub_data_objs = True
  1693         if dataobj == None:
  2257         if dataobj == None:
  1694             dataobj = Data(fqr=self.fqr)
  2258             dataobj = Data(fqr=self.fqr)
  1695         elif dataobj.attr != 'data':
  2259         elif dataobj.attr != 'data':
  1696             # Add data rows only for data objects (not e.g. RFS)
  2260             # Add data rows only for data objects (not e.g. RFS)
  1697             return
  2261             return
       
  2262         else:
       
  2263             # If the data object is given, but it doesn't contain any child
       
  2264             # elements, don't add them automatically. This is to account for the
       
  2265             # case where there is only one empty data element that specifies
       
  2266             # that the sequence is set to be empty
       
  2267             if len(dataobj._order) == 0:
       
  2268                 create_sub_data_objs = False
  1698         fea = FeatureSequenceSub(self.dataelem_name)
  2269         fea = FeatureSequenceSub(self.dataelem_name)
  1699         rowproxy = _FeatureDataProxy(fea._name, fea)
  2270         rowproxy = _FeatureDataProxy(fea._name, fea)
  1700         """ the imaginary features share the parent relation of the proxy objects """
  2271         """ the imaginary features share the parent relation of the proxy objects """
  1701         self.dataproxy._add(rowproxy, policy)
  2272         self.dataproxy._add(rowproxy, policy)
  1702         fea._parent = rowproxy._parent
  2273         fea._parent = rowproxy._parent
  1705         fea._index = utils.get_list(self.dataproxy._get(self.dataelem_name)).index(rowproxy)
  2276         fea._index = utils.get_list(self.dataproxy._get(self.dataelem_name)).index(rowproxy)
  1706         # Create a the subfeatures / columns for the parent feature and 
  2277         # Create a the subfeatures / columns for the parent feature and 
  1707         # add a data element under each feature.
  2278         # add a data element under each feature.
  1708         for feaname in self.list_all_features():
  2279         for feaname in self.list_all_features():
  1709             (pathto_fea, fearef) = utils.dottedref.psplit_ref(feaname)
  2280             (pathto_fea, fearef) = utils.dottedref.psplit_ref(feaname)
  1710             rowproxy.add_feature(FeatureSequenceSub(fearef), pathto_fea)
  2281             subfea = self.get_feature(feaname)
       
  2282             cellfea = FeatureSequenceSub(fearef)
       
  2283             cellfea.set_value_cast = subfea.set_value_cast
       
  2284             cellfea.get_value_cast = subfea.get_value_cast
       
  2285             cellfea.convert_data_to_value = subfea.convert_data_to_value
       
  2286             cellfea.convert_value_to_data = subfea.convert_value_to_data
       
  2287             rowproxy.add_feature(cellfea, pathto_fea)
  1711             subproxy = rowproxy.get_feature(feaname)
  2288             subproxy = rowproxy.get_feature(feaname)
  1712             subproxy._obj._parent = subproxy._parent 
  2289             subproxy._obj._parent = subproxy._parent
  1713             if not dataobj._has(feaname):
  2290             if create_sub_data_objs and not dataobj._has(feaname):
  1714                 dataobj._add_to_path(pathto_fea, Data(ref=fearef))
  2291                 dataobj._add_to_path(pathto_fea, Data(ref=fearef))
  1715             subproxy._add_data(dataobj._get(feaname))
  2292             subproxy._add_data(dataobj._get(feaname))
  1716 
  2293         return dataobj
       
  2294     
       
  2295     def _has_empty_sequence_marker(self):
       
  2296         """
       
  2297         Return True if the sequence setting has the empty sequence marker (a single
       
  2298         empty data element), which denotes that the sequence is set to empty.
       
  2299         """
       
  2300         datatable = self.get_data()
       
  2301         if len(datatable) == 1:
       
  2302             data_elem = datatable[0].get_datas()[0]
       
  2303             if len(data_elem._order) == 0:
       
  2304                 return True
       
  2305         return False
       
  2306     
  1717     def add(self, child, policy=container.REPLACE):
  2307     def add(self, child, policy=container.REPLACE):
  1718         """
  2308         """
  1719         A generic add function to add child objects. The function is intended to act as
  2309         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.
  2310         proxy function that call the correct add function based on the child objects class.
  1721         
  2311         
  1729             self._add(child)
  2319             self._add(child)
  1730         elif isinstance(child, Base):
  2320         elif isinstance(child, Base):
  1731             self._add(child)
  2321             self._add(child)
  1732         else:
  2322         else:
  1733             raise exceptions.IncorrectClassError("Cannot add %s to %s" % (child, self))
  2323             raise exceptions.IncorrectClassError("Cannot add %s to %s" % (child, self))
       
  2324     
       
  2325     def add_feature(self, feature, path=""):
       
  2326         """
       
  2327         Override of the add_feature function in sequence to set the sequence childs to act 
       
  2328         as columns of the feature sequence
       
  2329         @param feature: The Feature object to add 
       
  2330         """
       
  2331         add_sequence_feature(self, feature, path)
  1734 
  2332 
  1735     def add_sequence(self, data=None, policy=POLICY_APPEND):
  2333     def add_sequence(self, data=None, policy=POLICY_APPEND):
  1736         """
  2334         """
  1737         Add a feature data row for a new data in this sequence 
  2335         Add a feature data row for a new data in this sequence 
  1738         """
  2336         """
  1739         self._add_datarow(None, policy)
  2337         if self._has_empty_sequence_marker():
       
  2338             # We currently have the empty sequence marker (single empty data
       
  2339             # element), so this one that we are adding should replace it
       
  2340             policy = self.POLICY_REPLACE
       
  2341             
       
  2342         datarow = self._add_datarow(None, policy)
       
  2343         # add the new data sequence/row to the last configuration layer
       
  2344         last_config = self.get_root_configuration().get_last_configuration()
       
  2345         
       
  2346         container_policy = {self.POLICY_REPLACE: container.REPLACE,
       
  2347                             self.POLICY_APPEND:  container.APPEND,
       
  2348                             self.POLICY_PREPEND: container.PREPEND}[policy]
       
  2349         last_config.add_data(datarow, container_policy)
       
  2350         
  1740         # set the initial data if it is given
  2351         # set the initial data if it is given
  1741         rowproxy = utils.get_list(self.dataproxy._get(self.dataelem_name))[-1]
  2352         rowproxy = utils.get_list(self.dataproxy._get(self.dataelem_name))[-1]
  1742         if data != None:
  2353         if data != None:
  1743             for index in range(len(data)):
  2354             for index in range(len(data)):
  1744                 rowproxy[index].set_value(data[index])
  2355                 if data[index] != None:
  1745         # add the new data sequence/row to the last configuration layer
  2356                     rowproxy[index].set_value(data[index])
  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 
  2357 
  1751     def set_template(self, data=None):
  2358     def set_template(self, data=None):
  1752         """
  2359         """
  1753         Set the template of the feature sequence  
  2360         Set the template of the feature sequence  
  1754         """
  2361         """
  1755         # If template data is not existing, create it
  2362         if data is None:
  1756         if self._templatedata == None:
  2363             self._templatedata = None
  1757             self._set_template_data(Data(ref=self.ref, template=True))
  2364             return
  1758             # Add the template data to parent config
  2365         
  1759             pconfig = self.find_parent(type=Configuration)
  2366         if not isinstance(data, list):
  1760             pconfig.add_data(self._templatedata)
  2367             raise TypeError('data must be a list (got %r)' % data)
  1761 
  2368         
  1762         if data != None:
  2369         # Create the new template data object
  1763             templdatas = self._templatedata._objects()
  2370         templatedata = Data(fqr=self.fqr, template=True)
  1764             for index in range(len(data)):
  2371         
  1765                 templdatas[index].set_value(data[index])
  2372         # Add all sub-objects to the data object
       
  2373         def add_data_objects(feature, data_obj, value_list):
       
  2374             refs = feature.list_features()
       
  2375             if len(refs) != len(value_list):
       
  2376                 raise ValueError("Data value list is invalid")
       
  2377             for i, ref in enumerate(refs):
       
  2378                 value = value_list[i]
       
  2379                 subfea = feature.get_feature(ref)
       
  2380                 if isinstance(value, list):
       
  2381                     subdata = Data(ref=ref)
       
  2382                     data_obj.add(subdata)
       
  2383                     add_data_objects(feature.get_feature(ref), subdata, value)
       
  2384                 else:
       
  2385                     if value is not None:
       
  2386                         subdata = Data(ref=ref, value=subfea.set_value_cast(value))
       
  2387                         data_obj.add(subdata)
       
  2388         add_data_objects(self, templatedata, data)
       
  2389         
       
  2390         self._set_template_data(templatedata)
       
  2391         
       
  2392         # Remove any existing template data
       
  2393         pconfig = self.find_parent(type=Configuration)
       
  2394         dataobjs = pconfig._traverse(type=Data, filters=[lambda x: x.template and x.fqr == self.fqr])
       
  2395         if dataobjs:
       
  2396             for dataobj in dataobjs:
       
  2397                 dataobj._parent._remove(dataobj.get_fullref())
       
  2398         
       
  2399         # Add the template data to the parent config (beginning of the data section)
       
  2400         pconfig.add_data(self._templatedata, policy=container.PREPEND)
  1766 
  2401 
  1767     def get_template(self):
  2402     def get_template(self):
  1768         """
  2403         """
  1769         Add a feature data row for a new data in this sequence 
  2404         Add a feature data row for a new data in this sequence 
  1770         """
  2405         """
  1771         #self._set_template(None)
  2406         #self._set_template(None)
  1772         # set the initial data if it is given
  2407         # set the initial data if it is given
  1773         if self._templatedata:
  2408         if self._templatedata:
  1774             return [data.get_value() for data in self._templatedata._objects()]
  2409             def get_data_items(feature, data_obj):
       
  2410                 refs = feature.list_features()
       
  2411                 if refs:
       
  2412                     result = []
       
  2413                     for ref in refs:
       
  2414                         if data_obj._has(ref):
       
  2415                             result.append(get_data_items(feature.get_feature(ref), data_obj._get(ref)))
       
  2416                         else:
       
  2417                             result.append(None)
       
  2418                     return result
       
  2419                 else:
       
  2420                     return data_obj.value
       
  2421             return get_data_items(self, self._templatedata)
  1775         else:
  2422         else:
  1776             return None
  2423             return None
  1777 
  2424 
  1778     def get_data(self):
  2425     def get_data(self):
  1779         """
  2426         """
  1794             self._set_template_data(data)
  2441             self._set_template_data(data)
  1795         else:
  2442         else:
  1796             # Get the data index
  2443             # Get the data index
  1797             self._add_datarow(data, self._get_policy(data))
  2444             self._add_datarow(data, self._get_policy(data))
  1798         return
  2445         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         
  2446         
  1833     def get_value(self, attr=None):
  2447     def get_value(self, attr=None):
  1834         """
  2448         """
  1835         Helper function to get the topmost data value from the default view.
  2449         Helper function to get the topmost data value from the default view.
  1836         """
  2450         """
       
  2451         if self._has_empty_sequence_marker():
       
  2452             return []
       
  2453         
  1837         datatable =  self.get_data()
  2454         datatable =  self.get_data()
  1838         rettable = [] 
  2455         rettable = []
  1839         for row in datatable:
  2456         for row in datatable:
  1840             rowvalues = row.value
  2457             rowvalues = row.value
  1841             rettable.append(rowvalues)
  2458             rettable.append(rowvalues)
  1842         return rettable
  2459         return rettable
  1843 
  2460 
       
  2461     def get_original_value(self, attr=None):
       
  2462         """
       
  2463         Get the current value of the feature
       
  2464         @param attr: The attribute name of the data. E.g. attr='data', attr='rfs' 
       
  2465         """
       
  2466         if self._has_empty_sequence_marker():
       
  2467             return []
       
  2468         
       
  2469         datatable =  self.get_data()
       
  2470         rettable = []
       
  2471         for row in datatable:
       
  2472             rowvalues = row.get_original_value()
       
  2473             rettable.append(rowvalues)
       
  2474         return rettable
       
  2475     
  1844     def set_value(self, value, attr=None):
  2476     def set_value(self, value, attr=None):
  1845         """
  2477         """
  1846         Set the current value for this feature. Set the value on the topmost layer.
  2478         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)
  2479         @param value: the value to set. The value must be a two dimensional array (e.g. matrix)
  1848         """
  2480         """
  1849         # sets the first data element to replace policy
  2481         if value:
  1850         try:
  2482             # Add the first item with replace policy
  1851             self.add_sequence(value.pop(0), self.POLICY_REPLACE)
  2483             self.add_sequence(value[0], self.POLICY_REPLACE)
  1852         # ignore the index error of an empty list
  2484             for row in value[1:]:
  1853         except IndexError:
  2485                 self.add_sequence(row)
  1854             pass
  2486         else:
  1855         for row in value:
  2487             # Setting the sequence to empty, so add one empty item-setting
  1856             self.add_sequence(row)
  2488             # to signify that
       
  2489             self.add_sequence(None, self.POLICY_REPLACE)
       
  2490             
       
  2491             # Strip all sub-elements from the data element just created,
       
  2492             # since the ConfML spec says that an empty sequence is denoted
       
  2493             # by a single empty data element
       
  2494             data_elem = self.get_data()[0].get_datas()[0]
       
  2495             for r in list(data_elem._order):
       
  2496                 data_elem._remove(r)
  1857 
  2497 
  1858     def is_sequence(self):
  2498     def is_sequence(self):
  1859         """ Return always true from a sequence object """
  2499         """ Return always true from a sequence object """
  1860         return True
  2500         return True
  1861 
  2501 
       
  2502     def is_sequence_root(self):
       
  2503         """ Return true if this feature is a sequence object it self """
       
  2504         return True
       
  2505 
       
  2506     def get_column_features(self):
       
  2507         """ Return a list of sequence subfeature, which are the columns of the sequence """
       
  2508         columns = []
       
  2509         for subref in self.list_features():
       
  2510             columns.append(self.get_feature(subref))
       
  2511         return columns
       
  2512 
  1862     def get_sequence_parent(self):
  2513     def get_sequence_parent(self):
  1863         """ Return this object as a sequence parent """
  2514         """ Return this object as a sequence parent """
  1864         return self
  2515         return self
  1865 
  2516 
  1866     value = property(get_value, set_value)
  2517     value = property(get_value, set_value)
  1867     data = property(get_data)
  2518     data = property(get_data)
  1868 
  2519 
       
  2520 
       
  2521 def add_sequence_feature(parent_feature, feature, path=""):
       
  2522     """
       
  2523     Override of the add_feature function in sequence to set the sequence childs to act 
       
  2524     as columns of the feature sequence
       
  2525     @param parent_feature: The parent feature where the feature object is added 
       
  2526     @param feature: The Feature object to add 
       
  2527     @param path: path to feature if it not added directly under parent_fea 
       
  2528     """
       
  2529     # modify all possible children of feature
       
  2530     for fea in feature._traverse(type=Feature):
       
  2531         to_sequence_feature(fea)
       
  2532         
       
  2533     # Finally modify and add this feature to parent_feat
       
  2534     to_sequence_feature(feature)
       
  2535     parent_feature._add_to_path(path, feature)
       
  2536 
       
  2537 def to_sequence_feature(feature):
       
  2538     """
       
  2539     modify a Feature object to sequence feature that will return column like data from a sequence.
       
  2540     @param feature: The Feature object for which is modified.
       
  2541     """
       
  2542     feature.get_value = feature.get_column_value 
       
  2543     feature.get_original_value = feature.get_column_original_value
       
  2544     feature.set_value = feature.set_column_value
       
  2545     feature.add_feature = feature.add_sequence_feature
       
  2546 
       
  2547 def get_column_value(feasequence, ref, attr=None):
       
  2548     """
       
  2549     Get the value of the featuresequence column
       
  2550     @param feasequence: the feature sequence object
       
  2551     @param ref: the reference to the column   
       
  2552     @param attr: The attribute name of the data. E.g. attr='data', attr='rfs' 
       
  2553     """
       
  2554     """ get the feature specific data from sequence => a column of data table """
       
  2555     coldata =  []
       
  2556     for row in feasequence.data:
       
  2557         feadata = row.get_feature(ref)
       
  2558         coldata.append(feadata.get_value(attr))
       
  2559     return coldata
       
  2560 
       
  2561 def get_column_original_value(feasequence, ref, attr=None):
       
  2562     """
       
  2563     Get the value of the featuresequence column
       
  2564     @param feasequence: the feature sequence object
       
  2565     @param ref: the reference to the column   
       
  2566     @param attr: The attribute name of the data. E.g. attr='data', attr='rfs' 
       
  2567     """
       
  2568     """ get the feature specific data from sequence => a column of data table """
       
  2569     coldata =  []
       
  2570     for row in feasequence.data:
       
  2571         feadata = row.get_feature(ref)
       
  2572         coldata.append(feadata.get_original_value(attr))
       
  2573     return coldata
       
  2574 
       
  2575 def set_column_value(feasequence, ref, value, attr=None):
       
  2576     """
       
  2577     Get the value of the featuresequence column
       
  2578     @param feasequence: the feature sequence object
       
  2579     @param ref: the reference to the column   
       
  2580     @param value: the value to set. This must be a list instance. 
       
  2581     @param attr: The attribute name of the data. E.g. attr='data', attr='rfs' 
       
  2582     """
       
  2583     if not isinstance(value,list): 
       
  2584         raise exceptions.ConeException("The value for feature sequence '%s' column '%s' must be a list instance. Got %r" % (feasequence.fqr, ref, value))
       
  2585     seqrows = feasequence.data
       
  2586     if len(seqrows) < len(value): 
       
  2587         raise exceptions.ConeException("Too many values for feature sequence '%s' column '%s'. Sequence holds only %d rows. Got %d data values in %r" % (feasequence.fqr, ref, len(seqrows), len(value), value))
       
  2588     for i in range(0, len(value)):
       
  2589         feadata = seqrows[i].get_feature(ref)
       
  2590         feadata.set_value(value[i])
       
  2591 
  1869 class FeatureSequenceCell(Feature):
  2592 class FeatureSequenceCell(Feature):
  1870     """
  2593     """
  1871     A Feature class. Feature is the base for all Configurable items in a Configuration.
  2594     A Feature class. Feature is the base for all Configurable items in a Configuration.
  1872     """
  2595     """
  1873     def __init__(self, ref="", **kwargs):
  2596     def __init__(self, ref="", **kwargs):
  1874         super(Feature, self).__init__(ref)
  2597         super(FeatureSequenceCell, self).__init__(ref)
  1875         self.name = kwargs.get('name', ref)
  2598         self.name = kwargs.get('name', ref)
  1876         self.type = 'seqcell'
  2599         self.type = 'seqcell'
  1877  
  2600  
  1878     def get_value(self, attr=None):
  2601     def get_value(self, attr=None):
  1879         """
  2602         """
  1895 class FeatureSequenceSub(Feature):
  2618 class FeatureSequenceSub(Feature):
  1896     """
  2619     """
  1897     A Feature class. Feature is the base for all Configurable items in a Configuration.
  2620     A Feature class. Feature is the base for all Configurable items in a Configuration.
  1898     """
  2621     """
  1899     def __init__(self, ref="", **kwargs):
  2622     def __init__(self, ref="", **kwargs):
  1900         super(Feature, self).__init__(ref)
  2623         super(FeatureSequenceSub, self).__init__(ref)
  1901         self.name = kwargs.get('name', ref)
  2624         self.name = kwargs.get('name', ref)
  1902         self.type = 'subseq'
  2625         self.type = 'subseq'
  1903         self._index = 0
  2626         self._index = 0
  1904 
  2627 
       
  2628     def __getstate__(self):
       
  2629         state = super(FeatureSequenceSub,self).__getstate__()
       
  2630         state['_children'].pop('?datarows', None)
       
  2631         return state
       
  2632 
  1905     def get_index(self):
  2633     def get_index(self):
  1906         """
  2634         """
  1907         @return : the index of the data element for sequential data defined inside the same configuration.
  2635         @return : the index of the data element for sequential data defined inside the same configuration.
  1908         0 for normal data.
  2636         0 for normal data.
  1909         """
  2637         """
  1913         """
  2641         """
  1914         Set the current value for this sequence row.
  2642         Set the current value for this sequence row.
  1915         @param value: the value row to set
  2643         @param value: the value row to set
  1916         """
  2644         """
  1917         if utils.is_list(value):
  2645         if utils.is_list(value):
  1918             for subindex in range(0, len(value)):  
  2646             for subindex in range(0, len(value)):
  1919                 self.dataproxy[subindex].get_data().set_value(value[subindex])
  2647                 self.dataproxy[subindex].get_data().set_value(value[subindex])
  1920         else: 
  2648         else:
  1921             self.dataproxy.get_data().set_value(value)
  2649             data_objs = self.convert_value_to_data(value)
       
  2650             data_object_where_to_add = self._parent._get_data()
       
  2651             
       
  2652             self.dataproxy._set_datas(data_objs, attr)
       
  2653             data_object_where_to_add._add(data_objs, container.REPLACE)
  1922 
  2654 
  1923     def get_value(self, attr=None):
  2655     def get_value(self, attr=None):
  1924         """
  2656         """
  1925         Set the current value for this feature. Set the value on the topmost layer.
  2657         Set the current value for this feature. Set the value on the topmost layer.
  1926         @param value: the value to set
  2658         @param value: the value to set
  1927         """
  2659         """
  1928         # dataproxy = self.get_default_view().get_feature(self.get_fullfqr())
  2660         # Handle empty sequences
       
  2661         if self.get_sequence_parent()._has_empty_sequence_marker():
       
  2662             return []
       
  2663         
  1929         # The sequence cell only updates the latest value in the proxy
  2664         # The sequence cell only updates the latest value in the proxy
  1930         childdatas = self.dataproxy._objects()
  2665         childdatas = self.dataproxy._objects()
  1931         if len(childdatas) > 0:
  2666         if len(childdatas) > 0:
  1932             return [subdata.value for subdata in childdatas]
  2667             return [subdata.value for subdata in childdatas]
  1933         else: 
  2668         else: 
  1934             return self.dataproxy._get_value(attr=attr)
  2669             return super(FeatureSequenceSub,self).get_value(attr)
  1935 
  2670 
       
  2671     def get_original_value(self, attr=None):
       
  2672         """
       
  2673         Get the current value of the feature
       
  2674         @param attr: The attribute name of the data. E.g. attr='data', attr='rfs' 
       
  2675         """
       
  2676         # Handle empty sequences
       
  2677         if self.get_sequence_parent()._has_empty_sequence_marker():
       
  2678             return []
       
  2679         
       
  2680         childdatas = self.dataproxy._objects()
       
  2681         if len(childdatas) > 0:
       
  2682             return [subdata.get_original_value() for subdata in childdatas]
       
  2683         else:
       
  2684             return self.dataproxy._get_value(attr)
       
  2685         
  1936     value = property(get_value, set_value)
  2686     value = property(get_value, set_value)
  1937 
  2687 
  1938 
  2688 
  1939 class FeatureLink(Base):
  2689 class FeatureLink(Base):
  1940     """
  2690     """
  1941     A _FeatureProxy class. _FeatureProxy is the object that is added to View as a 
  2691     A _FeatureProxy class. _FeatureProxy is the object that is added to View as a 
  1942     link to the actual Feature object. 
  2692     link to the actual Feature object. 
  1943     """
  2693     """
  1944     def __init__(self, link="", **kwargs):
  2694     """ class variable for defining the override attributes"""
       
  2695     override_attributes = ['name']
       
  2696     ref_prefix = 'link_'
       
  2697     PROXYREF_PREFIX = 'proxy_'
       
  2698     
       
  2699     def __init__(self, ref="", **kwargs):
  1945         # Store the fully qualified reference to this object
  2700         # Store the fully qualified reference to this object
  1946         self.link = link
  2701         self.link = kwargs.get('link', ref)
  1947         ref = link.replace('.', '_')
  2702         self.name = kwargs.get('name', None)
  1948         super(FeatureLink, self).__init__(ref)
  2703         ref = self.get_featurelink_ref(self.link)
       
  2704         # the reference of this particular object
       
  2705         super(FeatureLink, self).__init__(ref, **kwargs)
  1949         self._obj = None
  2706         self._obj = None
  1950         self._populated = False
  2707         self._populated = False
       
  2708 
       
  2709     def add(self, child, policy=container.REPLACE):
       
  2710         """
       
  2711         Add an override to enable adding any override attribute to a featurelink object.
       
  2712         
       
  2713         A generic add function to add child objects. The function is intended to act as
       
  2714         proxy function that call the correct add function based on the child objects class.
       
  2715         
       
  2716         Example: obj.add(Feature("test")), actually obj.add_feature(Feature("test"))
       
  2717         @param child: the child object to add
       
  2718         @raise IncorrectClassError: if the given class cannot be added to this object.  
       
  2719         """
       
  2720         if isinstance(child, Base):
       
  2721             self._add(child, policy)
       
  2722         else:
       
  2723             raise exceptions.IncorrectClassError("Cannot add %s to %s" % (child, self))
       
  2724 
       
  2725     def get_name(self):
       
  2726         """
       
  2727         Return the name of the featurelink
       
  2728         """
       
  2729         return self.name
       
  2730 
       
  2731     def set_name(self, name):
       
  2732         """
       
  2733         Set the name
       
  2734         """
       
  2735         self.name = name
  1951 
  2736 
  1952     @property
  2737     @property
  1953     def fqr(self):
  2738     def fqr(self):
  1954         return self.link
  2739         return self.link
  1955 
  2740 
  1960         subfeatures. 
  2745         subfeatures. 
  1961         """
  2746         """
  1962         try:
  2747         try:
  1963             if not self._populated:
  2748             if not self._populated:
  1964                 feas = self.get_default_view().get_features(self.link)
  2749                 feas = self.get_default_view().get_features(self.link)
       
  2750                 # get the non wildcard part of ref
       
  2751                 static_ref = utils.dottedref.get_static_ref(self.link)
  1965                 # add the found features to the parent
  2752                 # add the found features to the parent
  1966                 for fea in feas:
  2753                 for fea in feas:
  1967                     self._get_parent().add_feature(fea._obj)
  2754                     override_attrs = {}
       
  2755                     # override the FeatureProxy object with exactly same reference 
       
  2756                     # (in feat/* case dont override the children features)
       
  2757                     if fea.fqr == static_ref:
       
  2758                         override_attrs = self.get_attributes()
       
  2759                     feature = fea._obj
       
  2760                     proxy_ref = self.get_featureproxy_ref(feature.fqr)
       
  2761                     proxy = _FeatureProxy(proxy_ref, feature, **override_attrs)
       
  2762                     self._get_parent()._add(proxy)
       
  2763                     
  1968         except exceptions.NotFound, e:
  2764         except exceptions.NotFound, e:
  1969                 parent_view = self._find_parent_or_default(type=View)
  2765                 parent_view = self._find_parent_or_default(type=View)
  1970                 view_name = parent_view.get_name()
  2766                 view_name = parent_view.get_name()
  1971                 logging.getLogger('cone').info("Warning: Feature '%s' in view '%s' not found." % (self.link, view_name))
  2767                 logging.getLogger('cone').info("Warning: Feature '%s' in view '%s' not found." % (self.link, view_name))
  1972 
  2768 
       
  2769     def get_attributes(self):
       
  2770         """
       
  2771         Returns a list of FeatureLink attributes that override settings of the original feature.
       
  2772         @return: a dictionary of attribute key : value pairs.
       
  2773         """
       
  2774         attrs = {}
       
  2775         for attr in self.override_attributes:
       
  2776             # try to get the attribute from this object
       
  2777             # and set it to the attribute list if it not None
       
  2778             try:
       
  2779                 value = getattr(self, attr)
       
  2780                 if value != None: attrs[attr] = value
       
  2781             except AttributeError:
       
  2782                 pass
       
  2783         return attrs
       
  2784 
       
  2785     @classmethod
       
  2786     def get_featurelink_ref(cls, ref):
       
  2787         """
       
  2788         return a featurelink ref from a feature ref. 
       
  2789         This is needed to make the featurelink object refs unique in a container
       
  2790         that has Features. 
       
  2791         """
       
  2792         return cls.ref_prefix + ref.replace('.', '_').replace('/','_')
       
  2793     
       
  2794     @classmethod
       
  2795     def get_featureproxy_ref(cls, ref):
       
  2796         """
       
  2797         Return a ref for a given setting fqr to be used under a group.
       
  2798         This is needed to make the featureproxy object refs unique in a container
       
  2799         that has Features. 
       
  2800         """
       
  2801         return cls.PROXYREF_PREFIX + ref.replace('.', '_').replace('/','_')
  1973 
  2802 
  1974 class _FeatureProxy(container.ObjectProxyContainer, Base):
  2803 class _FeatureProxy(container.ObjectProxyContainer, Base):
  1975     """
  2804     """
  1976     A _FeatureProxy class. _FeatureProxy is the object that is added to View as a 
  2805     A _FeatureProxy class. _FeatureProxy is the object that is added to View as a 
  1977     link to the actual Feature object. 
  2806     link to the actual Feature object. 
  1978     """
  2807     """
  1979     def __init__(self, ref="", obj=None, **kwargs):
  2808     def __init__(self, ref="", obj=None, **kwargs):
  1980         super(_FeatureProxy, self).__init__(obj, ref)
  2809         container.ObjectProxyContainer.__init__(self, obj, ref)
  1981         Base.__init__(self, ref)
  2810         Base.__init__(self, ref, **kwargs)
  1982         self.support_data = False
  2811         self.support_data = False
  1983 
  2812         
  1984     def __getattr__(self, name):
  2813     def __getattr__(self, name):
  1985         """
  2814         """
  1986         First check if the requested attr is a children then 
  2815         First check if the requested attr is a children then 
  1987         direct all not found attribute calls to the sub object getattr
  2816         direct all not found attribute calls to the sub object getattr
  1988         """
  2817         """
  1993 
  2822 
  1994     def __getitem__(self, index):
  2823     def __getitem__(self, index):
  1995         return self._objects()[index]
  2824         return self._objects()[index]
  1996 
  2825 
  1997     def __setitem__(self, index, value):
  2826     def __setitem__(self, index, value):
  1998         raise exceptions.NotSupported()
  2827         raise exceptions.NotSupportedException()
  1999 
  2828 
  2000     def __delitem__(self, index):
  2829     def __delitem__(self, index):
  2001         item = self.__getitem__(index)
  2830         item = self.__getitem__(index)
  2002         return self._remove(item.get_ref())
  2831         return self._remove(item.get_ref())
  2003 
  2832 
  2017         """
  2846         """
  2018         @param newparent:  The new parent object
  2847         @param newparent:  The new parent object
  2019         @return: None
  2848         @return: None
  2020         """
  2849         """
  2021         self._parent = newparent
  2850         self._parent = newparent
       
  2851 
       
  2852     def get_proxied_obj(self):
       
  2853         """
       
  2854         @return: Returns proxied object.
       
  2855         """
       
  2856         return self._obj
  2022 
  2857 
  2023     def add_feature(self, feature, path=""):
  2858     def add_feature(self, feature, path=""):
  2024         """
  2859         """
  2025         """
  2860         """
  2026         if not isinstance(feature, Feature):
  2861         if not isinstance(feature, Feature):
  2059         """
  2894         """
  2060         Dummy implementation of populate
  2895         Dummy implementation of populate
  2061         """
  2896         """
  2062         pass
  2897         pass
  2063 
  2898 
  2064 
  2899     def has_attribute(self, name):
       
  2900         """
       
  2901         Perform a check whether an attribute with given name is stored inside the 
       
  2902         _FeatureProxy. The check does not extend to the proxied (_obj) insanses or 
       
  2903         children of this proxy.
       
  2904         
       
  2905         @return: True when an attribute is a real attribute in this _FeatureProxy object. 
       
  2906         """
       
  2907         return self.__dict__.has_key(name)
       
  2908 
       
  2909     def get_option(self, ref):
       
  2910         """
       
  2911         @param name: The option reference of the option (as returned by list_options()) 
       
  2912         """
       
  2913         real_ref = 'opt_' + ref
       
  2914         for op in self.options.values():
       
  2915             if op.ref == real_ref:
       
  2916                 return op
       
  2917         else:
       
  2918             
       
  2919             obj = self.get_proxied_obj()._get(real_ref)
       
  2920             if not isinstance(obj, Option):
       
  2921                 raise TypeError('Object %r is not an instance of Option (%r instead)' % (real_ref, type(obj)))
       
  2922             return obj
       
  2923 
       
  2924     def list_options(self):
       
  2925         """
       
  2926         Return a array of all Option children references under this object.
       
  2927         """
       
  2928         opts = self.get_proxied_obj().list_options()
       
  2929         
       
  2930         for opt in self.options:
       
  2931             opts.append(self.options[opt].ref[4:])
       
  2932         
       
  2933         return opts
       
  2934 
       
  2935     def get_property(self, ref):
       
  2936         """
       
  2937         @param name: The property reference of the property (as returned by list_properties()) 
       
  2938         """
       
  2939         for prop in self.properties.values():
       
  2940             if prop.ref == Property.to_propertyref(ref):
       
  2941                 return prop
       
  2942         else:
       
  2943             obj = self.get_proxied_obj()._get(Property.to_propertyref(ref))
       
  2944             return obj
       
  2945 
       
  2946     def list_properties(self):
       
  2947         """
       
  2948         Return a array of all Property children references under this object.
       
  2949         """
       
  2950         props = self.get_proxied_obj().list_properties()
       
  2951         
       
  2952         for pr in self.properties:
       
  2953             props.append(Property.to_normref(self.properties[pr].ref))
       
  2954         
       
  2955         return props
       
  2956 
       
  2957     
  2065 class _FeatureDataProxy(_FeatureProxy):
  2958 class _FeatureDataProxy(_FeatureProxy):
  2066     """
  2959     """
  2067     A Feature class. Feature is the base for all Configurable items in a Configuration.
  2960     A Feature class. Feature is the base for all Configurable items in a Configuration.
  2068     """
  2961     """
  2069     DEFAULT_KEY = 'data'
  2962     DEFAULT_KEY = 'data'
  2083 
  2976 
  2084     def __getattr__(self, name):
  2977     def __getattr__(self, name):
  2085         """
  2978         """
  2086         """
  2979         """
  2087         if object.__getattribute__(self, '_obj') is not None:
  2980         if object.__getattribute__(self, '_obj') is not None:
  2088             self._obj.dataproxy = self
  2981             self.get_proxied_obj().dataproxy = self
  2089         
  2982         
  2090         if name in Feature.PROPERTIES:
  2983         if name in Feature.PROPERTIES:
  2091             return getattr(self._obj, name)
  2984             return getattr(self.get_proxied_obj(), name)
  2092         else:
  2985         else:
  2093             return super(_FeatureDataProxy, self).__getattr__(name)
  2986             return super(_FeatureDataProxy, self).__getattr__(name)
  2094     
  2987     
  2095     def __setattr__(self, name, value):
  2988     def __setattr__(self, name, value):
  2096         """
  2989         """
  2097         """
  2990         """
  2098         if object.__getattribute__(self, '_obj') is not None:
  2991         if object.__getattribute__(self, '_obj') is not None:
  2099             self._obj.dataproxy = self
  2992             self.get_proxied_obj().dataproxy = self
  2100             
  2993             
  2101         if name in Feature.PROPERTIES:
  2994         if name in Feature.PROPERTIES:
  2102             return setattr(self._obj, name, value)
  2995             return setattr(self.get_proxied_obj(), name, value)
  2103         else:
  2996         else:
  2104             super(_FeatureDataProxy, self).__setattr__(name, value)
  2997             super(_FeatureDataProxy, self).__setattr__(name, value)
  2105 
  2998 
  2106     def __delattr__(self, name):
  2999     def __delattr__(self, name):
  2107         """
  3000         """
  2108         """
  3001         """
  2109         if name in Feature.PROPERTIES:
  3002         if name in Feature.PROPERTIES:
  2110             return delattr(self._obj, name)
  3003             return delattr(self.get_proxied_obj(), name)
  2111         else:
  3004         else:
  2112             return super(_FeatureDataProxy, self).__delattr__(name)
  3005             return super(_FeatureDataProxy, self).__delattr__(name)
  2113 
  3006 
  2114     def _add_data(self, data):
  3007     def _add_data(self, data):
  2115         """
  3008         """
  2116         Add a data value.
  3009         Add a data value or a list of data values.
  2117         @param data: A Data object  
  3010         @param data: A Data object  
  2118         """
  3011         """
       
  3012         if isinstance(data, list):
       
  3013             for d in data: self._add_data(d)
       
  3014             return
       
  3015         
  2119         try:
  3016         try:
  2120             self.datas[data.attr].append(data)
  3017             self.datas[data.attr].append(data)
  2121         except KeyError:
  3018         except KeyError:
  2122             """ Create a list object for missing attribute """ 
  3019             """ Create a list object for missing attribute """ 
  2123             self.datas[data.attr] = []
  3020             self.datas[data.attr] = []
  2140     def _get_datas(self, attr=None):
  3037     def _get_datas(self, attr=None):
  2141         """
  3038         """
  2142         Get the entire data array.
  3039         Get the entire data array.
  2143         """
  3040         """
  2144         dataattr = attr or self.defaultkey
  3041         dataattr = attr or self.defaultkey
  2145         return self.datas[dataattr]
  3042         return self.datas.get(dataattr, [])
       
  3043     
       
  3044     def _set_datas(self, datas, attr=None):
       
  3045         """
       
  3046         Set the entire data array.
       
  3047         """
       
  3048         dataattr = attr or self.defaultkey
       
  3049         self.datas[dataattr] = list(datas)
  2146 
  3050 
  2147     def _get_value(self, attr=None):
  3051     def _get_value(self, attr=None):
  2148         """
  3052         """
  2149         Get the topmost data value.
  3053         Get the topmost data value.
  2150         """
  3054         """
  2151         if self._get_data(attr):
  3055         if self._get_data(attr):
  2152             return self._get_data(attr).get_value()
  3056             return self._get_data(attr).get_value()
  2153         else:
  3057         else:
  2154             return None
  3058             return None
  2155 
  3059     
  2156     def _set_value(self, datavalue, attr=None):
  3060     def _set_value(self, datavalue, attr=None):
  2157         """
  3061         """
  2158         Set the value for the feature the last configuration in the current hierarchy
  3062         Set the value for the feature the last configuration in the current hierarchy
  2159         @param value: The value for the feature.
  3063         @param value: The value for the feature.
  2160         @return: The created Data object.  
  3064         @return: The created Data object.  
  2251         self.value  = kwargs.get('value', None)
  3155         self.value  = kwargs.get('value', None)
  2252         self.attr   = kwargs.get('attr') or 'data'
  3156         self.attr   = kwargs.get('attr') or 'data'
  2253         self.policy = kwargs.get('policy', '')
  3157         self.policy = kwargs.get('policy', '')
  2254         self.template = kwargs.get('template', False)
  3158         self.template = kwargs.get('template', False)
  2255         self.map    = kwargs.get('map')
  3159         self.map    = kwargs.get('map')
       
  3160         self.empty  = kwargs.get('empty', False)
       
  3161         self.lineno = None
       
  3162 
       
  3163     def __setstate__(self, state):
       
  3164         super(Data, self).__setstate__(state)
       
  3165         self.value = state.get('value', None)
       
  3166         self.attr = state.get('attr', None)
       
  3167         self.policy = state.get('policy', '')
       
  3168         self.map = state.get('map', None)
       
  3169         self.template = state.get('template', False)
       
  3170         self.lineno = state.get('lineno', None)
       
  3171         self.fearef = state.get('fearef', None)
  2256 
  3172 
  2257     def get_fearef(self):
  3173     def get_fearef(self):
  2258         if self.fearef:
  3174         if self.fearef:
  2259             return self.fearef
  3175             return self.fearef
  2260         else:
  3176         else:
  2261             return self.fqr
  3177             return self.fqr
  2262 
  3178 
  2263     def get_value(self):
  3179     def get_value(self):
  2264         if self.map != None:
  3180         return self.value
  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 
  3181 
  2273     def get_map(self):
  3182     def get_map(self):
  2274         return self.map
  3183         return self.map
  2275 
  3184 
  2276     def set_map(self, map):
  3185     def set_map(self, map):
  2277         self.map = map
  3186         self.map = map
  2278         if self.value:
  3187         if self.value:
  2279             #Either value or mapping can be defined. Not both.
  3188             #Either value or mapping can be defined. Not both.
  2280             self.value = None
  3189             self.value = None
  2281 
  3190 
  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):
  3191     def set_value(self, value):
  2295         self.value = value
  3192         self.value = value
  2296         if self.map:
  3193         if self.map:
  2297             #Either value or mapping can be defined. Not both.
  3194             #Either value or mapping can be defined. Not both.
  2298             self.map = None
  3195             self.map = None
  2301     def set_policy(self, value): self._policy = value
  3198     def set_policy(self, value): self._policy = value
  2302     def del_policy(self):  self._policy = None
  3199     def del_policy(self):  self._policy = None
  2303     policy = property(get_policy, set_policy, del_policy)
  3200     policy = property(get_policy, set_policy, del_policy)
  2304 
  3201 
  2305 
  3202 
  2306 class ValueSet(sets.Set):
  3203 class ValueSet(set):
  2307     """
  3204     """
  2308     A value set object to indicate a set of possible values for a feature. 
  3205     A value set object to indicate a set of possible values for a feature. 
  2309     e.g. A boolean feature ValueSet([True, False])
  3206     e.g. A boolean feature ValueSet([True, False])
  2310     """
  3207     """
  2311     def __init__(self, initial_set=None):
  3208     def __init__(self, initial_set=None):
  2334         if isinstance(value, str):
  3231         if isinstance(value, str):
  2335             return self.regexp.match(value)
  3232             return self.regexp.match(value)
  2336         else:
  3233         else:
  2337             return False
  3234             return False
  2338     
  3235     
       
  3236 
       
  3237 class Property(Base):
       
  3238     """
       
  3239     Confml property class
       
  3240     """
       
  3241     def __init__(self, **kwargs):
       
  3242         """
       
  3243         @param name=str: name string (mandatory)
       
  3244         @param value=str: value for the property, string 
       
  3245         @param unit=str: unit of the property
       
  3246         """
       
  3247         if kwargs.get('name',None) == None:
       
  3248             raise ValueError("Property name cannot be None!")
       
  3249         super(Property,self).__init__(Property.to_propertyref(kwargs.get('name',None)))
       
  3250         self.name = kwargs.get('name',None)
       
  3251         self.value = kwargs.get('value',None)
       
  3252         self.unit = kwargs.get('unit',None)
       
  3253 
       
  3254     @classmethod
       
  3255     def to_propertyref(cls, name):
       
  3256         """ 
       
  3257         @param name: name of the property 
       
  3258         @return: A property reference.
       
  3259         """
       
  3260         if name is not None:
       
  3261             return "property_%s" % name
       
  3262         else:
       
  3263             raise ValueError("Property name cannot be None!")
       
  3264     
       
  3265     @classmethod
       
  3266     def to_normref(cls, ref):
       
  3267         """
       
  3268         @param ref: a property reference 
       
  3269         @return: normalized property reference
       
  3270         """
       
  3271         return ref[9:]
       
  3272 
       
  3273     def get_name(self):
       
  3274         return self.name
       
  3275 
       
  3276     def get_value(self):
       
  3277         return self.value
       
  3278 
       
  3279     def get_unit(self):
       
  3280         return self.unit
  2339 
  3281 
  2340 class Option(Base):
  3282 class Option(Base):
  2341     """
  3283     """
  2342     Confml option class.
  3284     Confml option class.
  2343     """
  3285     """
  2345         super(Option, self).__init__(Option.to_optref(value, kwargs.get('map', None)))
  3287         super(Option, self).__init__(Option.to_optref(value, kwargs.get('map', None)))
  2346         self.name = name
  3288         self.name = name
  2347         self.value = value
  3289         self.value = value
  2348         self.map = kwargs.get('map', None)
  3290         self.map = kwargs.get('map', None)
  2349         self.relevant = kwargs.get('relevant', None)
  3291         self.relevant = kwargs.get('relevant', None)
       
  3292         self.map_value = kwargs.get('map_value', None)
       
  3293         self.display_name = kwargs.get('display_name', None)
  2350 
  3294 
  2351     @classmethod
  3295     @classmethod
  2352     def to_optref(cls, value, map):
  3296     def to_optref(cls, value, map):
  2353         """ 
  3297         """ 
  2354         @return: An option reference converted from value or map, depending
  3298         @return: An option reference converted from value or map, depending
  2389     MODE_READ   = 1
  3333     MODE_READ   = 1
  2390     MODE_WRITE  = 2
  3334     MODE_WRITE  = 2
  2391     MODE_APPEND = 3
  3335     MODE_APPEND = 3
  2392     MODE_DELETE = 4
  3336     MODE_DELETE = 4
  2393 
  3337 
  2394     def __init__(self, path):
  3338     def __init__(self, path, mode=''):
  2395         """
  3339         """
  2396         @param path: the reference to the root of the storage.
  3340         @param path: the reference to the root of the storage.
  2397         """
  3341         """
  2398         self.rootpath = path
  3342         self.rootpath = path
  2399         self.curpath = ""
  3343         self.curpath = ""
  2400         self.container = True
  3344         self.container = True
  2401         self.__opened_res__ = {}
  3345         self.__opened_res__ = {}
  2402 
  3346         self.mode = mode
       
  3347         self.cpath_stack = []
       
  3348     
       
  3349     def __reduce_ex__(self, protocol_version):
       
  3350         return  (open_storage, 
       
  3351                  (self.path, self.mode),
       
  3352                  None,
       
  3353                  None,
       
  3354                  None)
       
  3355         
  2403     def __opened__(self, res):
  3356     def __opened__(self, res):
  2404         """
  3357         """
  2405         Internal function to add a newly opened Resource object to the list of open resources.
  3358         Internal function to add a newly opened Resource object to the list of open resources.
  2406         @param res: The resource object 
  3359         @param res: The resource object 
  2407         """
  3360         """
  2494     def get_path(self):
  3447     def get_path(self):
  2495         """
  3448         """
  2496         """
  3449         """
  2497         return self.rootpath
  3450         return self.rootpath
  2498 
  3451 
       
  3452     def push(self, path):
       
  3453         """
       
  3454         Set the current path under the Storage to the given path and push the possible existing path to a stack. 
       
  3455         The current path can be reverted with pop method.
       
  3456         
       
  3457         @return: None 
       
  3458         @param path: The path which is set as current path.
       
  3459         """
       
  3460         self.cpath_stack.append(self.curpath)
       
  3461         self.curpath = path
       
  3462 
       
  3463     def pop(self):
       
  3464         """
       
  3465         Pop a path from path stack and set the current path to the popped element. The path can be pushed to the 
       
  3466         current path stack with push. 
       
  3467         
       
  3468         NOTE! if the pop is called when the current path stack is empty, the path will just remain is empty path 
       
  3469         keeping the active path in the storages root path. 
       
  3470         
       
  3471         @return: The new path.
       
  3472         """
       
  3473         try:
       
  3474             path = self.cpath_stack.pop()
       
  3475             self.curpath = path
       
  3476         except IndexError:
       
  3477             pass
       
  3478         return self.curpath
       
  3479 
  2499     def set_current_path(self, path):
  3480     def set_current_path(self, path):
  2500         """
  3481         """
  2501         @param path: the current path under the Storage. 
  3482         @param path: the current path under the Storage. 
  2502         """
  3483         """
  2503         self.curpath = utils.resourceref.remove_end_slash(utils.resourceref.remove_begin_slash(path))
  3484         self.curpath = utils.resourceref.remove_end_slash(utils.resourceref.remove_begin_slash(path))
  2554         Return true if the ref is a resource
  3535         Return true if the ref is a resource
  2555         @param ref : reference to path where resources are searched
  3536         @param ref : reference to path where resources are searched
  2556         """
  3537         """
  2557         raise exceptions.NotSupportedException()
  3538         raise exceptions.NotSupportedException()
  2558 
  3539 
  2559     def list_resources(self, path, recurse=False):
  3540     def list_resources(self, path, **kwargs):
  2560         """
  3541         """
  2561         find the resources under certain ref/path 
  3542         find the resources under certain ref/path 
  2562         @param ref : reference to path where resources are searched
  3543         @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. 
  3544         @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.
  3545         Default value is False. Set to True to enable recursion.
  2577         """
  3558         """
  2578         export resources from this storage based on a list of reference to this storage
  3559         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).
  3560         @param path : a list of resource paths in this storage (references).
  2580         @param storage : the external storage where to export.
  3561         @param storage : the external storage where to export.
  2581         """  
  3562         """  
  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()
  3563         raise exceptions.NotSupportedException()
  2591 
  3564 
  2592     def save_resource(self, path):
  3565     def save_resource(self, path):
  2593         """
  3566         """
  2594         Flush the changes of a given resource instance. Normally this is called by the Resource object 
  3567         Flush the changes of a given resource instance. Normally this is called by the Resource object 
  2689     def truncate(self, size=0):
  3662     def truncate(self, size=0):
  2690         """
  3663         """
  2691         Trunkate this resource data to the given size.
  3664         Trunkate this resource data to the given size.
  2692         @param size: The size to trunkate. Default value is zero, which make the resource empty. 
  3665         @param size: The size to trunkate. Default value is zero, which make the resource empty. 
  2693         """
  3666         """
  2694         raise NotSupportedException()
  3667         raise exceptions.NotSupportedException()
  2695 
  3668 
  2696     def save(self, size=0):
  3669     def save(self, size=0):
  2697         """
  3670         """
  2698         Save all changes to data to storage.
  3671         Save all changes to data to storage.
  2699         """
  3672         """
  2700         raise NotSupportedException()
  3673         raise exceptions.NotSupportedException()
  2701 
  3674 
  2702     def get_mode(self):
  3675     def get_mode(self):
  2703         return self.storage.get_mode(self.mode)
  3676         return self.storage.get_mode(self.mode)
  2704     
  3677     
  2705     def get_size(self):
  3678     def get_size(self):
  2766             try:
  3739             try:
  2767                 self.color_depth = ord(data[self._BMP_BITS_PER_PIXEL_OFFSET_])
  3740                 self.color_depth = ord(data[self._BMP_BITS_PER_PIXEL_OFFSET_])
  2768             except Exception, e:
  3741             except Exception, e:
  2769                 self.logger.warning("Invalid BMP-file: %s" % resource.get_path())
  3742                 self.logger.warning("Invalid BMP-file: %s" % resource.get_path())
  2770         
  3743         
       
  3744 
       
  3745 class currentdir(object):
       
  3746     def __init__(self, storage, curdir):
       
  3747         self.storage = storage
       
  3748         # make sure that the curdir does not contain path prefix
       
  3749         self.curdir = curdir.lstrip('/')
       
  3750 
       
  3751     def __enter__(self):
       
  3752         self.storage.push(self.curdir)
       
  3753 
       
  3754     def __exit__(self, type, value, tb):
       
  3755         self.storage.pop()
       
  3756 
       
  3757 
  2771 class Folder(object):
  3758 class Folder(object):
  2772     """
  3759     """
  2773     A Folder object is a subfolder of a Storage, offering access to part of the Storages resources.
  3760     A Folder object is a subfolder of a Storage, offering access to part of the Storages resources.
  2774     """
  3761     """
  2775     def __init__(self, storage, path):
  3762     def __init__(self, storage, path, **kwargs):
  2776         """
  3763         """
  2777         Create a layer folder to the storage if it does not exist.
  3764         Create a layer folder to the storage if it does not exist.
  2778         """
  3765         """
  2779         #if not storage.is_folder(path):
  3766         self.curpath = path
  2780         #    storage.create_folder(path)
  3767         self.storage = storage
  2781         self.storage = copy.copy(storage)
  3768 
  2782         self.storage.set_current_path(path)
  3769     def set_path(self, path):
  2783 
  3770         """
  2784     def __getattr__(self, name):
  3771         """
  2785         return getattr(self.storage, name)
  3772         self.curpath = path
  2786 
  3773 
  2787 class CompositeLayer(object):
  3774     def get_path(self):
       
  3775         """
       
  3776         """
       
  3777         return self.curpath
       
  3778 
       
  3779     def set_current_path(self, path):
       
  3780         """
       
  3781         @param path: the current path under the Storage. 
       
  3782         """
       
  3783         self.curpath = utils.resourceref.remove_end_slash(utils.resourceref.remove_begin_slash(path))
       
  3784 
       
  3785     def get_current_path(self):
       
  3786         """
       
  3787         get the current path under the Storage. 
       
  3788         """
       
  3789         return self.curpath
       
  3790 
       
  3791     def close(self):
       
  3792         """
       
  3793         Close the repository, which will save and close all open resources.  
       
  3794         """
       
  3795         self.storage.close()
       
  3796 
       
  3797     def save(self):
       
  3798         """
       
  3799         Flush changes from all resources to the repository.  
       
  3800         """        
       
  3801         return self.storage.save()
       
  3802 
       
  3803     def open_resource(self, path, mode="r"):
       
  3804         """
       
  3805         Open the given resource and return a File object.
       
  3806         @param path : reference to the resource 
       
  3807         @param mode : the mode in which to open. Can be one of r = read, w = write, a = append.
       
  3808         raises a NotResource exception if the ref item is not a resource.
       
  3809         """  
       
  3810         with currentdir(self.storage, self.curpath):
       
  3811             res = self.storage.open_resource(path, mode)
       
  3812             return res
       
  3813         
       
  3814     def delete_resource(self, path):
       
  3815         """
       
  3816         Delete the given resource from storage
       
  3817         @param path: reference to the resource 
       
  3818         raises a NotSupportedException exception if delete operation is not supported by the storage
       
  3819         """  
       
  3820         with currentdir(self.storage, self.curpath):
       
  3821             res = self.storage.delete_resource(path)
       
  3822             return res
       
  3823 
       
  3824     def close_resource(self, path):
       
  3825         """
       
  3826         Close a given resource instance. Normally this is called by the Resource object 
       
  3827         in its own close.
       
  3828         @param path the reference to the resource to close. 
       
  3829         """
       
  3830         with currentdir(self.storage, self.curpath):
       
  3831             res = self.storage.close_resource(path)
       
  3832             return res
       
  3833 
       
  3834     def is_resource(self, path):
       
  3835         """
       
  3836         Return true if the ref is a resource
       
  3837         @param ref : reference to path where resources are searched
       
  3838         """
       
  3839         with currentdir(self.storage, self.curpath):
       
  3840             res = self.storage.is_resource(path)
       
  3841             return res
       
  3842 
       
  3843     def list_resources(self, path, **kwargs):
       
  3844         """
       
  3845         find the resources under certain ref/path 
       
  3846         @param ref : reference to path where resources are searched
       
  3847         @param recurse : defines whether to return resources directly under the path or does the listing recurse to subfolders. 
       
  3848         Default value is False. Set to True to enable recursion.
       
  3849         """  
       
  3850         with currentdir(self.storage, self.curpath):
       
  3851             res = self.storage.list_resources(path, **kwargs)
       
  3852             return res
       
  3853 
       
  3854     def import_resources(self, paths, storage):
       
  3855         """
       
  3856         import resources from a list of resources to this storage
       
  3857         @param paths : a list of Resourse objects.
       
  3858         @param storage : the external storage from which files are imported.
       
  3859         """  
       
  3860         with currentdir(self.storage, self.curpath):
       
  3861             res = self.storage.import_resources(paths, storage)
       
  3862             return res
       
  3863 
       
  3864     def export_resources(self, paths, storage):
       
  3865         """
       
  3866         export resources from this storage based on a list of reference to this storage
       
  3867         @param path : a list of resource paths in this storage (references).
       
  3868         @param storage : the external storage where to export.
       
  3869         """  
       
  3870         with currentdir(self.storage, self.curpath):
       
  3871             res = self.storage.export_resources(paths, storage)
       
  3872             return res
       
  3873 
       
  3874     def save_resource(self, path):
       
  3875         """
       
  3876         Flush the changes of a given resource instance. Normally this is called by the Resource object 
       
  3877         in its own save.
       
  3878         @param ref the reference to the resource to close. 
       
  3879         """
       
  3880         with currentdir(self.storage, self.curpath):
       
  3881             res = self.storage.save_resource(path)
       
  3882             return res
       
  3883 
       
  3884     def create_folder(self, path):
       
  3885         """
       
  3886         Create a folder entry to a path
       
  3887         @param path : path to the folder
       
  3888         """  
       
  3889         with currentdir(self.storage, self.curpath):
       
  3890             res = self.storage.create_folder(path)
       
  3891             return res
       
  3892 
       
  3893     def delete_folder(self, path):
       
  3894         """
       
  3895         Delete a folder entry from a path. The path must be empty.
       
  3896         @param path : path to the folder
       
  3897         """  
       
  3898         with currentdir(self.storage, self.curpath):
       
  3899             res = self.storage.delete_folder(path)
       
  3900             return res
       
  3901 
       
  3902     def is_folder(self, path):
       
  3903         """
       
  3904         Check if the given path is an existing folder in the storage
       
  3905         @param path : path to the folder
       
  3906         """
       
  3907         with currentdir(self.storage, self.curpath):
       
  3908             res = self.storage.is_folder(path)
       
  3909             return res
       
  3910 
       
  3911     def get_mode(self, mode_str):
       
  3912         return self.storage.get_mode()
       
  3913 
       
  3914     def unload(self, path, object):
       
  3915         """
       
  3916         Dump a given object to the storage 
       
  3917         @param object: The object to dump to the storage, which is expected to be an instance 
       
  3918         of Base class.
       
  3919         @param path: The reference where to store the object 
       
  3920         @param object: The object instance to dump 
       
  3921         @raise StorageException: if the given object cannot be dumped to this storage 
       
  3922         """
       
  3923         with currentdir(self.storage, self.curpath):
       
  3924             res = self.storage.unload(path, object)
       
  3925             return res
       
  3926 
       
  3927     def load(self, path):
       
  3928         """
       
  3929         Load an object from a reference.
       
  3930         @param path: The reference where to load the object 
       
  3931         @raise StorageException: if the given object cannot be loaded as an object from this storage 
       
  3932         """
       
  3933         with currentdir(self.storage, self.curpath):
       
  3934             res = self.storage.load(path)
       
  3935             return res
       
  3936 
       
  3937     path = property(get_path, set_path)
       
  3938 
       
  3939 
       
  3940 class CompositeLayer(Folder):
  2788     """
  3941     """
  2789     A base class for composite Configuration objects.  
  3942     A base class for composite Configuration objects.  
  2790     """
  3943     """
  2791     def __init__(self, path="", **kwargs):
  3944     def __init__(self, storage, path="", **kwargs):
       
  3945         super(CompositeLayer, self).__init__(storage, path, **kwargs)
  2792         self.layers = kwargs.get('layers', [])
  3946         self.layers = kwargs.get('layers', [])
  2793         self.path = path
  3947         self.path = path
  2794 
  3948 
  2795     def add_layer(self, layer):
  3949     def add_layer(self, layer):
  2796         self.layers.append(layer)
  3950         self.layers.append(layer)
  2848         for layerpath in self.list_layers():
  4002         for layerpath in self.list_layers():
  2849             for respath in self.get_layer(layerpath).list_doc():
  4003             for respath in self.get_layer(layerpath).list_doc():
  2850                 lres.append(utils.resourceref.join_refs([layerpath, respath]))
  4004                 lres.append(utils.resourceref.join_refs([layerpath, respath]))
  2851         return lres
  4005         return lres
  2852 
  4006 
  2853     def list_all_resources(self, empty_folders=False):
  4007     def list_all_resources(self, **kwargs):
  2854         """
  4008         """
  2855         Returns a list of all layer related resource paths with full path in the storage.
  4009         Returns a list of all layer related resource paths with full path in the storage.
  2856         """
  4010         """
  2857         lres = []
  4011         lres = []
  2858         for layerpath in self.list_layers():
  4012         for layerpath in self.list_layers():
  2859             sublayer = self.get_layer(layerpath)
  4013             sublayer = self.get_layer(layerpath)
  2860             for respath in sublayer.list_all_resources(empty_folders):
  4014             for respath in sublayer.list_all_resources(**kwargs):
  2861                 lres.append(utils.resourceref.join_refs([layerpath, respath]))
  4015                 lres.append(utils.resourceref.join_refs([layerpath, respath]))
  2862                 
       
  2863         return lres
  4016         return lres
  2864 
  4017     
       
  4018     def list_all_related(self, **kwargs):
       
  4019         """
       
  4020         Returns a list of all (non confml) layer related resource paths with full path in the storage.
       
  4021         """
       
  4022         lres = []
       
  4023         for layerpath in self.list_layers():
       
  4024             sublayer = self.get_layer(layerpath)
       
  4025             for respath in sublayer.list_all_related(**kwargs):                
       
  4026                 lres.append(utils.resourceref.join_refs([layerpath, respath]))
       
  4027         
       
  4028         return lres
       
  4029     
  2865 class Layer(CompositeLayer):
  4030 class Layer(CompositeLayer):
  2866     """
  4031     """
  2867     A Layer object is a subfolder of a Storage, offering access to part of the Storages resources.
  4032     A Layer object is a subfolder of a Storage, offering access to part of the Storages resources.
  2868     """
  4033     """
  2869     def __init__(self, storage, path, **kwargs):
  4034     def __init__(self, storage, path, **kwargs):
  2874         @param confml_path: optional parameter for confml files path (give in confml_path="something") 
  4039         @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")
  4040         @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")
  4041         @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")
  4042         @param doc_path: optional parameter for doc files path (give in doc_path="something")
  2878         """
  4043         """
  2879         super(Layer, self).__init__(path, **kwargs)
  4044         super(Layer, self).__init__(storage, path, **kwargs)
  2880         #if not storage.is_folder(path):
  4045         #if not storage.is_folder(path):
  2881         #    storage.create_folder(path)
  4046         #    storage.create_folder(path)
  2882         self.storage = copy.copy(storage)
       
  2883         self.storage.set_current_path(path)
       
  2884         self.predefined = {'confml_path' : 'confml', 
  4047         self.predefined = {'confml_path' : 'confml', 
  2885                            'implml_path' : 'implml', 
  4048                            'implml_path' : 'implml', 
  2886                            'content_path' : 'content', 
  4049                            'content_path' : 'content', 
  2887                            'doc_path' : 'doc'}
  4050                            'doc_path' : 'doc'}
  2888         # list through all "hardcoded" paths and check whether the 
  4051         # list through all "hardcoded" paths and check whether the 
  2892             self.predefined[pretag] = kwargs.get(pretag, prevalue)
  4055             self.predefined[pretag] = kwargs.get(pretag, prevalue)
  2893 
  4056 
  2894     def __getattr__(self, name):
  4057     def __getattr__(self, name):
  2895         return getattr(self.storage, name)
  4058         return getattr(self.storage, name)
  2896 
  4059 
       
  4060     def __getstate__(self):
       
  4061         state = {}
       
  4062         state['predefined'] = self.predefined
       
  4063         state['path'] = self.path
       
  4064         state['layers'] = self.layers
       
  4065         return state
       
  4066 
       
  4067     def __setstate__(self, state):
       
  4068         state = {}
       
  4069         self.predefined = state.get('predefined',{})
       
  4070         self.path = state.get('path','')
       
  4071         self.layers = state.get('layers',[])
       
  4072         
       
  4073         return state
       
  4074     
  2897     def list_confml(self):
  4075     def list_confml(self):
  2898         """
  4076         """
  2899         @return: array of confml file references.
  4077         @return: array of confml file references.
  2900         """
  4078         """
  2901         res = self.storage.list_resources(self.predefined['confml_path'], True)
  4079         res = self.list_resources(self.predefined['confml_path'], recurse=True)
  2902         res += super(Layer, self).list_confml()
  4080         res += super(Layer, self).list_confml()
  2903         return res 
  4081         return res 
  2904 
  4082 
  2905     def list_implml(self):
  4083     def list_implml(self):
  2906         """
  4084         """
  2907         @return: array of implml file references.
  4085         @return: array of implml file references.
  2908         """
  4086         """
  2909         res = self.storage.list_resources(self.predefined['implml_path'], True)
  4087         res = self.list_resources(self.predefined['implml_path'], recurse=True)
  2910         res += super(Layer, self).list_implml()
  4088         res += super(Layer, self).list_implml()
  2911         return res 
  4089         return res 
  2912 
  4090 
  2913     def list_content(self):
  4091     def list_content(self):
  2914         """
  4092         """
  2915         @return: array of content file references.
  4093         @return: array of content file references.
  2916         """
  4094         """
  2917         res = self.storage.list_resources(self.predefined['content_path'], True)
  4095         res = self.list_resources(self.predefined['content_path'], recurse=True)
  2918         res += super(Layer, self).list_content()
  4096         res += super(Layer, self).list_content()
  2919         return res
  4097         return res
  2920 
  4098 
  2921     def list_doc(self):
  4099     def list_doc(self):
  2922         """
  4100         """
  2923         @return: array of document file references.
  4101         @return: array of document file references.
  2924         """
  4102         """
  2925         res = self.storage.list_resources(self.predefined['doc_path'], True)
  4103         res = self.list_resources(self.predefined['doc_path'], recurse=True)
  2926         res += super(Layer, self).list_doc()
  4104         res += super(Layer, self).list_doc()
  2927         return res
  4105         return res
  2928 
  4106 
  2929     def confml_folder(self):
  4107     def confml_folder(self):
  2930         cpath = self.storage.get_current_path()
  4108         cpath = self.get_current_path()
  2931         spath = self.predefined['confml_path']
  4109         spath = self.predefined['confml_path']
  2932         return Folder(self.storage,  utils.resourceref.join_refs([cpath, spath]))
  4110         return Folder(self.storage,  utils.resourceref.join_refs([cpath, spath]))
  2933 
  4111 
  2934     def implml_folder(self):
  4112     def implml_folder(self):
  2935         cpath = self.storage.get_current_path()
  4113         cpath = self.get_current_path()
  2936         spath = self.predefined['implml_path']
  4114         spath = self.predefined['implml_path']
  2937         return Folder(self.storage,  utils.resourceref.join_refs([cpath, spath]))
  4115         return Folder(self.storage,  utils.resourceref.join_refs([cpath, spath]))
  2938 
  4116 
  2939     def content_folder(self):
  4117     def content_folder(self):
  2940         cpath = self.storage.get_current_path()
  4118         cpath = self.get_current_path()
  2941         spath = self.predefined['content_path']
  4119         spath = self.predefined['content_path']
  2942         return Folder(self.storage,  utils.resourceref.join_refs([cpath, spath]))
  4120         return Folder(self.storage,  utils.resourceref.join_refs([cpath, spath]))
  2943 
  4121 
  2944     def doc_folder(self):
  4122     def doc_folder(self):
  2945         cpath = self.storage.get_current_path()
  4123         cpath = self.get_current_path()
  2946         spath = self.predefined['doc_path']
  4124         spath = self.predefined['doc_path']
  2947         return Folder(self.storage,  utils.resourceref.join_refs([cpath, spath]))
  4125         return Folder(self.storage,  utils.resourceref.join_refs([cpath, spath]))
  2948 
  4126 
  2949     def list_all_resources(self, empty_folders=False):
  4127     def list_all_resources(self, **kwargs):
  2950         """
  4128         """
  2951         Returns a list of all layer related resource paths with full path in the storage.
  4129         Returns a list of all layer related resource paths with full path in the storage.
  2952         """
  4130         """
  2953         lres = []
  4131         lres = []
  2954         mypath = self.get_current_path()
       
  2955         
       
  2956         for folderpath in sorted(self.predefined.values()):
  4132         for folderpath in sorted(self.predefined.values()):
  2957             lres += self.storage.list_resources(folderpath, recurse=True, empty_folders=empty_folders)
  4133             lres += self.list_resources(folderpath, recurse=True)
  2958                  
  4134                  
  2959         lres += super(Layer, self).list_all_resources(empty_folders)
  4135         lres += super(Layer, self).list_all_resources()
  2960         
       
  2961         return lres
  4136         return lres
  2962 
  4137 
  2963     def list_all_related(self, empty_folders=False):
  4138     def list_all_related(self, **kwargs):
  2964         """
  4139         """
  2965         Returns a list of all (non confml) layer related resource paths with full path in the storage.
  4140         Returns a list of all (non confml) layer related resource paths with full path in the storage.
  2966         """
  4141         """
       
  4142         
  2967         lres = []
  4143         lres = []
       
  4144         exclude_filters = kwargs.get('exclude_filters', {})
       
  4145         kwargs['recurse'] = True
  2968         predef = self.predefined.copy()
  4146         predef = self.predefined.copy()
  2969         del predef['confml_path']
  4147         del predef['confml_path']
  2970         mypath = self.get_current_path()
       
  2971         for folderpath in sorted(predef.values()):
  4148         for folderpath in sorted(predef.values()):
  2972             lres += self.storage.list_resources(folderpath, recurse=True, empty_folders=empty_folders)
  4149             filter = exclude_filters.get(folderpath, None)
  2973         lres += super(Layer, self).list_all_resources(empty_folders=empty_folders)
  4150             resources = self.list_resources(folderpath, **kwargs)
       
  4151             if filter:
       
  4152                 lres += [res for res in resources if not re.search(filter, res, re.IGNORECASE)]
       
  4153             else:            
       
  4154                 lres += resources
       
  4155         lres += super(Layer, self).list_all_related(**kwargs)
  2974        
  4156        
  2975         return lres
  4157         return lres
       
  4158 
       
  4159 
       
  4160 class Include(Base, container.LoadLink):
       
  4161     """
       
  4162     A common include element that automatically loads a resource 
       
  4163     and its object under this include element.
       
  4164     """
       
  4165     def __init__(self, ref="", **kwargs):
       
  4166         path = kwargs.get('path') or ref
       
  4167         store_interface = kwargs.get('store_interface',None)
       
  4168         ref = utils.resourceref.to_objref(path)
       
  4169         container.LoadLink.__init__(self, path, store_interface)
       
  4170         Base.__init__(self, ref)
       
  4171     
       
  4172     def get_store_interface(self):
       
  4173         if not self._storeint and self._parent:
       
  4174             try:
       
  4175                 self._storeint = self._parent.get_store_interface()
       
  4176             except exceptions.NotFound:
       
  4177                 # If project is not found, let the store interface be None 
       
  4178                 pass
       
  4179         return self._storeint
  2976 
  4180 
  2977 
  4181 
  2978 class Rule(object):
  4182 class Rule(object):
  2979     """
  4183     """
  2980     Base class for Rules in the system.
  4184     Base class for Rules in the system.
  3005     """
  4209     """
  3006     mapmodule = __import__('cone.public.mapping')
  4210     mapmodule = __import__('cone.public.mapping')
  3007     return mapmodule.public.mapping.BaseMapper()
  4211     return mapmodule.public.mapping.BaseMapper()
  3008 
  4212 
  3009 
  4213 
  3010 ##################################################################
  4214 class Problem(object):
       
  4215     SEVERITY_ERROR      = "error"
       
  4216     SEVERITY_WARNING    = "warning"
       
  4217     SEVERITY_INFO       = "info"
       
  4218     
       
  4219     def __init__(self, msg, **kwargs):
       
  4220         self.msg = msg
       
  4221         self.type = kwargs.get('type', '')
       
  4222         self.line = kwargs.get('line', None)
       
  4223         self.file = kwargs.get('file', None)
       
  4224         self.severity = kwargs.get('severity', self.SEVERITY_ERROR)
       
  4225         self.traceback = kwargs.get('traceback', None)
       
  4226         # A slot for any problem specific data 
       
  4227         self.problem_data = kwargs.get('problem_data', None)
       
  4228     
       
  4229     def log(self, logger, current_file=None):
       
  4230         """
       
  4231         Log this problem with the given logger.
       
  4232         """
       
  4233         file = self.file or current_file
       
  4234         if self.line is None:
       
  4235             msg = "(%s) %s" % (file, self.msg)
       
  4236         else:
       
  4237             msg = "(%s:%d) %s" % (file, self.line, self.msg)
       
  4238         
       
  4239         mapping = {self.SEVERITY_ERROR:   logging.ERROR,
       
  4240                    self.SEVERITY_WARNING: logging.WARNING,
       
  4241                    self.SEVERITY_INFO:    logging.INFO}
       
  4242         level = mapping.get(self.severity, logging.ERROR)
       
  4243         logger.log(level, msg)
       
  4244         
       
  4245         if self.traceback:
       
  4246             logger.debug(self.traceback)
       
  4247     
       
  4248     @classmethod
       
  4249     def from_exception(cls, ex):
       
  4250         """
       
  4251         Create a Problem object from an exception instance.
       
  4252         
       
  4253         If the exception is a sub-class of ConeException, then it may contain
       
  4254         extra information (like a line number) for the problem.
       
  4255         """
       
  4256         if isinstance(ex, exceptions.ConeException):
       
  4257             return Problem(msg      = ex.problem_msg or unicode(ex),
       
  4258                            type     = ex.problem_type or '',
       
  4259                            line     = ex.problem_lineno,
       
  4260                            severity = cls.SEVERITY_ERROR)
       
  4261         else:
       
  4262             return Problem(msg      = unicode(ex),
       
  4263                            severity = cls.SEVERITY_ERROR)
       
  4264     
       
  4265     def __repr__(self):
       
  4266         var_data = []
       
  4267         for varname in ('msg', 'type', 'line', 'file', 'severity'):
       
  4268             var_data.append("%s=%r" % (varname, getattr(self, varname)))
       
  4269         return "%s(%s)" % (self.__class__.__name__, ', '.join(var_data))
       
  4270     
       
  4271     def __eq__(self, other):
       
  4272         if not isinstance(other, Problem):
       
  4273             return False
       
  4274         for varname in ('msg', 'type', 'line', 'file', 'severity'):
       
  4275             self_val = getattr(self, varname)
       
  4276             other_val = getattr(other, varname)
       
  4277             if self_val != other_val:
       
  4278                 return False
       
  4279         return True
       
  4280     
       
  4281     def __ne__(self, other):
       
  4282         return self == other
       
  4283     
       
  4284     def __lt__(self, other):
       
  4285         if not isinstance(other, Problem):
       
  4286             return False
       
  4287         return (self.file, self.line) < (other.file, other.line)
       
  4288 
       
  4289 def make_content_info(resource, data):
       
  4290     """
       
  4291     Factory for ContentInfo
       
  4292     """
       
  4293     cnt_inf = None
       
  4294     
       
  4295     if resource != None:
       
  4296         guessed_type = mimetypes.guess_type(resource.get_path())
       
  4297         mimetype = None
       
  4298         mimesubtype = None
       
  4299         
       
  4300         if guessed_type != None:
       
  4301             mimetype, mimesubtype = guessed_type[0].split('/') 
       
  4302         
       
  4303         if mimetype == 'image' and mimesubtype == 'x-ms-bmp':
       
  4304             cnt_inf = BmpImageContentInfo(resource, data)
       
  4305         else:
       
  4306             cnt_inf = ContentInfo(mimetype, mimesubtype)
       
  4307     return cnt_inf
       
  4308 
       
  4309 def open_storage(path, mode="r", **kwargs):
       
  4310     return Storage.open(path, mode="r", **kwargs)
       
  4311 
  3011 class NullHandler(logging.Handler):
  4312 class NullHandler(logging.Handler):
  3012     """
  4313     """
  3013     Default handler that does not do anything.
  4314     Default handler that does not do anything.
  3014     """
  4315     """
  3015     def emit(self, record):
  4316     def emit(self, record):