configurationengine/source/cone/carbon/persistentjson.py
changeset 0 2e8eeb919028
child 3 e7e0ae78773e
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/configurationengine/source/cone/carbon/persistentjson.py	Thu Mar 11 17:04:37 2010 +0200
@@ -0,0 +1,609 @@
+#
+# Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
+# All rights reserved.
+# This component and the accompanying materials are made available
+# under the terms of "Eclipse Public License v1.0"
+# which accompanies this distribution, and is available
+# at the URL "http://www.eclipse.org/legal/epl-v10.html".
+#
+# Initial Contributors:
+# Nokia Corporation - initial contribution.
+#
+# Contributors:
+#
+# Description:
+#
+## 
+# @author Teemu Rytkonen
+import re
+import logging
+import simplejson
+
+""" cone specific imports """
+from cone.public import persistence, exceptions, api, utils, container
+from cone.carbon import model
+
+MODEL                    = model
+
+def dumps(obj, indent=True):
+    """ Make sure that the object is mapped to an object in this model """
+    mobj = obj._get_mapper('carbon').map_object(obj)
+    writer = get_writer_for_class(mobj.__class__.__name__)
+    dict = writer.dumps(mobj)
+    # Return the data as dict, as it is urlencoded by client
+    return dict
+
+def loads(jsonstr):
+    return CarbonReader().loads(jsonstr)
+
+
+class CarbonResourceMapper(object):
+    def __init__(self):
+        self.CARBON_RESOURCE_TYPE_MAP = {'configurationroot' : self.map_carbon_configurationroot,
+                             'configurationlayer' : self.map_carbon_configurationlayer,
+                             'featurelist' : self.map_carbon_featurelist}
+        self.CONFML_RESOURCE_TYPE_MAP = {'configurationroot' : self.map_confml_configurationroot,
+                             'configurationlayer' : self.map_confml_configurationlayer,
+                             'featurelist' : self.map_confml_featurelist}
+
+    def map_carbon_resource(self, resourcepath):
+        for resourceext in self.CARBON_RESOURCE_TYPE_MAP:
+            if resourcepath.endswith(resourceext):
+                return self.CARBON_RESOURCE_TYPE_MAP[resourceext](resourcepath)
+        return resourcepath
+
+    def map_confml_resource(self, resourcetype, resourcepath):
+        return self.CONFML_RESOURCE_TYPE_MAP[resourcetype](resourcepath)
+
+    def map_carbon_configurationroot(self, resourcepath):
+        return resourcepath.replace('.configurationroot', '.confml')
+
+    def map_carbon_configurationlayer(self, resourcepath):
+        return resourcepath.replace('.configurationlayer', '/root.confml')
+
+    def map_carbon_featurelist(self, resourcepath):
+        return "featurelists/%s" % resourcepath.replace('.featurelist', '.confml')
+
+    def map_confml_configurationroot(self, resourcepath):
+        return resourcepath.replace('.confml', '.configurationroot')
+
+    def map_confml_configurationlayer(self, resourcepath):
+        return resourcepath.replace('/root.confml', '.configurationlayer')
+
+    def map_confml_featurelist(self, resourcepath):
+        path = resourcepath.replace('featurelists/','').replace('.confml', '')
+        version_identifier = 'WORKING'
+        m = re.match('^(.*) \((.*)\)', path)
+        # if the resourcepath does not have version information 
+        # use default WORKING
+        if m:
+            path = m.group(1)
+            version_identifier = m.group(2)
+        return '%s (%s).featurelist' % (path, version_identifier)
+
+class ResourceListReader(persistence.ConeReader):
+    """
+    """ 
+    def loads(self, jsonstr):
+        """
+        @param jsonstr: The json string to read. 
+        """
+        reslist = model.ResourceList()
+        datadict = simplejson.loads(jsonstr)
+        for configuration in datadict.get('configurations', []):
+            reslist.add_resource(model.ConfigurationResource(**configuration))
+        for featurelist in datadict.get('featurelists', []):
+            reslist.add_resource(model.FeatureListResource(**featurelist))
+        return reslist
+
+class HasResourceReader(persistence.ConeReader):
+    """
+    """ 
+    def loads(self, jsonstr):
+        """
+        @param jsonstr: The json string to read. 
+        """
+        try:
+            datadict = simplejson.loads(jsonstr)
+            return datadict.get('has_resource',False)
+        except ValueError,e:
+            logging.getLogger('cone').error("Failed to parser json from %s" % jsonstr)
+            raise e
+
+
+class CarbonWriter(persistence.ConeWriter):
+    """
+    """ 
+    def dumps(self, obj):
+        """
+        @param obj: The object 
+        """
+        """ Make sure that the object is mapped to an object in this model """
+        mobj = obj._get_mapper('carbon').map_object(obj)
+        writer = get_writer_for_class(mobj.__class__.__name__)
+        return writer.dumps(obj)
+
+
+class CarbonReader(persistence.ConeReader):
+    """
+    """ 
+    def loads(self, jsonstr):
+        """
+        @param xml: The xml which to read. reads only the first object. 
+        """
+        try:
+            datadict = simplejson.loads(jsonstr)
+            for key in datadict:
+                reader = get_reader_for_elem(key)
+                return reader.loads(datadict[key])
+        except (SyntaxError, ValueError),e:
+            utils.log_exception(logging.getLogger('cone'), "Json string parse raised exception: %s!" % (e))
+            raise exceptions.ParseError("Json string %s parse raised exception: %s!" % (jsonstr,e))
+
+class ConfigurationCreateWriter(CarbonWriter):
+    @classmethod
+    def supported_class(cls, classname):
+        """
+        Class method to determine if this CarbonWriter supports writing
+        of the given class name
+        """
+        return False
+
+    def dumps(self, obj):
+        """
+        @param obj: The Configuration object 
+        """
+        featurelists = []
+        included = []
+        # Remove the featurelists and configurations from the creation phase
+#        resmapper = CarbonResourceMapper()
+#        for confpath in obj.list_configurations():
+#            config = obj.get_configuration(confpath)
+#            if config.meta and config.meta.get('type') == 'featurelist':
+#                featurelists.append(resmapper.map_confml_resource('featurelist',confpath))
+#            elif config.meta and config.meta.get('type'):
+#                included.append(resmapper.map_confml_resource(config.meta.get('type'),confpath))
+#            else:
+#                # ignore configs that are not carbon configs
+#                pass
+
+        configuration_dict = {'name' : obj.name,
+                              'parent_path' : '',
+                              'included' : included,
+                              'description' : obj.desc or 'Needs description',
+                              'configuration_type' : 'carbon',
+                              'resource_type' : 'configuration',
+                              'feature_lists' : featurelists, 
+                               }
+        
+        return configuration_dict
+
+class ConfigurationWriter(CarbonWriter):
+    @classmethod
+    def supported_class(cls, classname):
+        """
+        Class method to determine if this CarbonWriter supports writing
+        of the given class name
+        """
+        if classname=="CarbonConfiguration":
+            return True
+        else:
+            return False
+
+    def dumps(self, obj):
+        if obj.meta:
+            if obj.meta.get('type') == 'configurationroot':
+                return self.dumps_root(obj)
+            elif obj.meta.get('type') == 'configurationlayer':
+                return self.dumps_layer(obj)
+        raise Exception("Not supported CarbonConfigruration, %s" % obj)
+
+    def dumps_root(self, obj):
+        """
+        @param obj: The Configuration object 
+        """
+        featurelists = []
+        included = []
+        resmapper = CarbonResourceMapper()
+        for confpath in obj.list_configurations():
+            config = obj.get_configuration(confpath)
+            if config.meta:
+                if config.meta.get('type') == 'featurelist':
+                    featurelists.append(resmapper.map_confml_resource('featurelist',confpath))
+                else:
+                    included.append(resmapper.map_confml_resource(config.meta.get('type'),confpath))
+            else:
+                # This default case could also be identified as error
+                included.append(confpath)
+
+        configuration_dict = {'feature_lists': featurelists,
+                             'parent_config': None, 
+                             'configuration_name': obj.name, 
+                             'version_identifier': obj.version_identifier, 
+                             'included': included, 
+                             'ref': obj.ref}
+        
+        return configuration_dict
+
+    def dumps_layer(self, obj):
+        """
+        @param obj: The Configuration object 
+        """
+        configuration_dict = {'version_identifier': obj.version_identifier}
+
+        datawriter = DataWriter()
+        data = datawriter.dumps(obj)
+        configuration_dict['data'] =  data
+        
+        return configuration_dict
+
+class ConfigurationRootReader(CarbonReader):
+    """
+    """ 
+    @classmethod
+    def supported_elem(cls, elemname, parent=None):
+        """
+        Class method to determine if this ConfmlWriter supports writing
+        of the given elem name
+        """
+        if elemname=="configurationroot":
+            return True
+        else:
+            return False
+
+    def __init__(self):
+        pass
+
+    def loads(self, dict):
+        """
+        @param obj: The Configuration object 
+        """
+        name = dict.get('configuration_name')
+        path = name + ".confml"
+        conf = model.CarbonConfiguration(dict.get('ref'), path=path, type='configurationroot')
+        conf.name = name
+        conf.version = dict.get('version_identifier')
+        resmapper = CarbonResourceMapper()
+        
+        """ Read the featurelists as included configurations """
+        for fealist in dict.get('feature_lists',[]):
+            conf.include_configuration(resmapper.map_carbon_resource(fealist))
+        """ Read the included configurations """
+        for includedconfig in dict.get('included',[]):
+            conf.include_configuration(resmapper.map_carbon_resource(includedconfig))
+        return conf
+
+class ConfigurationLayerReader(CarbonReader):
+    """
+    """ 
+    @classmethod
+    def supported_elem(cls, elemname, parent=None):
+        """
+        Class method to determine if this ConfmlWriter supports writing
+        of the given elem name
+        """
+        if elemname=="configurationlayer":
+            return True
+        else:
+            return False
+
+    def __init__(self):
+        pass
+
+    def loads(self, dict):
+        """
+        @param obj: The Configuration object 
+        """
+        name = dict.get('configuration_name')
+        path = name + ".confml"
+        conf = model.CarbonConfiguration(dict.get('ref'), path=path, type='configurationlayer')
+        conf.name = name
+        
+        conf.version = dict.get('version_identifier')
+        
+        """ Last read the data of this configuration and add it as a configuration """
+        data_reader = DataReader()
+        datacont = data_reader.loads(dict.get('data', {}))
+        proxy = api.ConfigurationProxy(datacont.path)
+        conf.add_configuration(proxy)
+        proxy._set_obj(datacont)
+        
+        return conf
+
+class FeatureListCreateWriter(CarbonWriter):
+    @classmethod
+    def supported_class(cls, classname):
+        """
+        Feature list create writer is supported only explicitly
+        """
+        return False
+
+    def dumps(self, obj):
+        """
+        @param obj: The FeatureList object 
+        """
+        """ Make sure that the object is mapped to an object in this model """
+        mobj = obj._get_mapper('carbon').map_object(obj)
+        featurelist_dict = {'type' : 'featurelist',
+                            'flv_description' : mobj.desc or 'Needs description',
+                            'version_identifier' : mobj.version_identifier
+                            }
+        if hasattr(mobj, 'responsible'):
+            featurelist_dict['responsible'] = mobj.responsible
+        return featurelist_dict
+
+class FeatureListWriter(CarbonWriter):
+    @classmethod
+    def supported_class(cls, classname):
+        """
+        Feature list create writer is supported only explicitly
+        """
+        if classname == 'FeatureList':
+            return True
+        else:
+            return False
+
+    def dumps(self, obj):
+        """
+        @param obj: The FeatureList object 
+        """
+
+        featurelist_dict = {
+                            'type' : 'featurelist',
+                            'name' : obj.name,
+                            'flv_description' : obj.desc or 'Needs description',
+                            'path' : obj.path,
+                            'features' : []
+                            }
+        if obj.meta.get('version_identifier'):
+            featurelist_dict['version_identifier'] = obj.meta.get('version_identifier')
+        # add all features of the featurelist
+        for fearef in obj.list_features():
+            feature = obj.get_feature(fearef)
+            writer = FeatureWriter()
+            feadict = writer.dumps(feature)
+            featurelist_dict['features'].append(feadict)
+        
+        return featurelist_dict
+
+class FeatureListReader(CarbonReader):
+    """
+    """ 
+    @classmethod
+    def supported_elem(cls, elemname, parent=None):
+        """
+        Class method to determine if this ConfmlWriter supports writing
+        of the given elem name
+        """
+        if elemname=="featurelist":
+            return True
+        else:
+            return False
+
+    def __init__(self):
+        pass
+
+    def loads(self, dict):
+        """
+        @param obj: The Configuration object 
+        """
+        fealist_expanded            = dict.get('expanded')
+        fealist_version             = dict.get('version_identifier')
+        fealist_is_latest_version   = dict.get('is_latest_version')
+        fealist_list_id             = dict.get('list_id')
+        fealist_path                = dict.get('path')
+        fealist_version_title       = dict.get('version_title')
+        fealist_can_be_released     = dict.get('can_be_released')
+        fealist_type                = dict.get('type')
+        fealist_has_external_relations = dict.get('is_latest_version')
+        
+        # Create a configuration object from the featurelist
+        conf = model.FeatureList(path='featurelists/'+fealist_version_title+'.confml')
+        conf.meta.add('version_identifier', fealist_version)
+        
+        for feature in dict.get('features'):
+            reader = FeatureReader()
+            fea = reader.loads(feature)
+            if fea != None:
+                conf.add_feature(fea)
+        
+        for feafqr in conf.list_all_features():
+            # Add empty data object to featurelist configuration
+            conf.add_data(api.Data(fqr=feafqr))
+            
+        return conf
+
+
+class FeatureWriter(CarbonWriter):
+    CONFML_TO_CARBON_TYPE = {
+                             'boolean'   : 'BOOLEAN',
+                             'int'       : 'INTEGER',
+                             'selection' : 'SELECTION',
+                             'string'    : 'STRING',
+                             None : None,
+                             '' : ''
+                             }
+    @classmethod
+    def supported_class(cls, classname):
+        """
+        Class method to determine if this ConfmlWriter supports writing
+        of the given class name
+        """
+        if classname=="Feature" or\
+            classname=="CarbonBooleanSetting" or\
+            classname=="CarbonIntSetting" or\
+            classname=="CarbonStringSetting" or\
+            classname=="CarbonSelectSetting"or\
+            classname=="CarbonSetting":
+            return True
+        else:
+            return False
+
+    def dumps(self, obj):
+        """
+        @param obj: The Feature object 
+        """
+        """ Make sure that the object is mapped to an object in this model """
+        mobj = obj._get_mapper('carbon').map_object(obj)
+        
+        featuredict = {'type' : 'feature',
+                       'status' : 'APPROVED',
+                       'title' : mobj.name,
+                       'ref' : mobj.ref,
+                       'description' : mobj.desc or 'Needs description',
+                       'responsible' : None,
+                       'value_type' : self.CONFML_TO_CARBON_TYPE[mobj.type],
+                       'children' : []}
+        if featuredict['value_type'] != None:
+            featuredict['type_object'] = 'carbon_feature_type_normal'
+        if mobj.type == 'selection':
+            featuredict['options'] = mobj.options.keys() 
+
+        writer = FeatureWriter()
+        for fearef in mobj.list_features():
+            feaobj = obj.get_feature(fearef)
+            childdict = writer.dumps(feaobj)
+            featuredict['children'].append(childdict)
+        return featuredict
+
+
+class FeatureReader(CarbonReader):
+    """
+    """ 
+    @classmethod
+    def supported_elem(cls, elemname, parent=None):
+        """
+        Class method to determine if this ConfmlWriter supports writing
+        of the given elem name
+        """
+        if elemname=="features":
+            return True
+        else:
+            return False
+
+    def __init__(self):
+        pass
+
+    def loads(self, dict):
+        """
+        @param obj: The Configuration object 
+        """
+        id = dict.get('id')
+        name = dict.get('title')
+        ref = dict.get('ref')
+        ref = utils.resourceref.to_objref(ref)
+        status = dict.get('status')
+        value_type = dict.get('value_type')
+        description = dict.get('description')
+        
+        if value_type == 'boolean':
+            fea = model.CarbonBooleanSetting(ref, type=value_type)
+        elif value_type == 'integer':
+            fea = model.CarbonIntSetting(ref, type=value_type)
+        elif value_type == 'string':
+            fea = model.CarbonStringSetting(ref, type=value_type)
+        elif value_type == 'selection':
+            fea = model.CarbonSelectionSetting(ref, type=value_type)
+            for option in dict.get('options'):
+                fea.add_option(option,option)
+        elif value_type == '':
+            fea = model.CarbonFeature(ref, type=value_type)
+        else:
+            fea = model.CarbonFeature(ref)
+
+        
+        fea.name = name
+        fea.status = status
+        fea.desc = description
+         
+        for childdict in dict.get('children',[]):
+            reader = FeatureReader()
+            subfea = reader.loads(childdict)
+            if subfea != None:
+                fea.add_feature(subfea)
+        return fea
+
+class DataWriter(CarbonWriter):
+    @classmethod
+    def supported_class(cls, classname):
+        """
+        Class method to determine if this ConfmlWriter supports writing
+        of the given class name
+        """
+        if  classname=="Data" or \
+            classname=="DataContainer":
+            return True
+        else:
+            return False
+
+    def dumps(self, obj):
+        """
+        @param obj: The DataContainer object 
+        """
+        datadict = {}
+        for dataelem in obj._traverse(type=api.Data):
+            if dataelem.get_value() != None:
+                datadict[dataelem.get_fearef()] = map_confml2carbon_value(dataelem.get_value())
+        return datadict
+
+
+class DataReader(CarbonReader):
+    """
+    """ 
+    @classmethod
+    def supported_elem(cls, elemname, parent=None):
+        """
+        Class method to determine if this ConfmlWriter supports writing
+        of the given elem name
+        """
+        if elemname=="data":
+            return True
+        else:
+            return False
+
+    def __init__(self):
+        pass
+
+    def loads(self, dict):
+        """
+        @param obj: The Configuration object 
+        """
+        datacont  = api.Configuration('confml/data.confml')
+        for dataref in dict.keys():
+            # Ignore null values
+            if dict[dataref]:
+                refs = []
+                for elem in dataref.split('.'):
+                    refs.append(utils.resourceref.to_objref(elem))
+                newref = '.'.join(refs)
+                dataelem = api.Data(fqr=newref, value=map_carbon2confml_value(dict[dataref]))
+                datacont.add_data(dataelem)
+        return datacont
+
+def map_carbon2confml_value(value):
+    if value == 'DEFINED':
+        return 'true'
+    elif value == 'UNDEFINED':
+        return 'false'
+    else:
+        return value
+
+def map_confml2carbon_value(value):
+    if value == 'true':
+        return 'DEFINED'
+    elif value == 'false':
+        return 'UNDEFINED'
+    else:
+        return value
+
+def get_reader_for_elem(elemname, parent=None):
+    for reader in CarbonReader.__subclasses__():
+        if reader.supported_elem(elemname,parent):
+            return reader()
+    raise exceptions.ConePersistenceError("No reader for given elem %s under %s found!" % (elemname, parent))
+
+def get_writer_for_class(classname):
+    for writer in CarbonWriter.__subclasses__():
+        if writer.supported_class(classname):
+            return writer ()
+    raise exceptions.ConePersistenceError("No writer for given class found! %s" % classname)