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