diff -r 000000000000 -r 2e8eeb919028 configurationengine/source/cone/carbon/persistentjson.py --- /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)