diff -r 87cfa131b535 -r e7e0ae78773e configurationengine/source/cone/confml/model.py --- a/configurationengine/source/cone/confml/model.py Fri Mar 12 08:30:17 2010 +0200 +++ b/configurationengine/source/cone/confml/model.py Tue Aug 10 14:29:28 2010 +0300 @@ -21,9 +21,15 @@ All Confml element attributes become attributes of this instance. """ import types +import sys +import re from cone.public import api, exceptions, container, utils class ConfmlElement(api.Base): + def __init__(self, ref="", **kwargs): + super(ConfmlElement,self).__init__(ref, **kwargs) + self.lineno = None + def _get_mapper(self,modelname): """ Return a instance of appropriate mapper for given model. @@ -48,6 +54,18 @@ pass """ The description as a property """ desc = property(get_desc,set_desc,del_desc) + +class ConfmlData(api.Data): + """ + The data element can contain any data setting for a feature. The data element can be + a value definition for any type of data. It basically just links some data to a feature. + The default Data attribute is 'data', but it can be any string. For example current use case + is 'rfs'. + """ + def __init__(self, **kwargs): + super(ConfmlData,self).__init__(**kwargs) + self.lineno = None + class ConfmlConfiguration(ConfmlElement, api.Configuration): """ Confml configuration class. @@ -59,6 +77,14 @@ if kwargs.get('desc'): self.desc = kwargs.get('desc') + def _view_class(self): + return ConfmlView + + def _feature_class(self): + return ConfmlFeature + + def _configuration_class(self): + return ConfmlConfiguration def get_desc(self): """ @@ -84,7 +110,7 @@ def get_meta(self): """ - @return: The description of the Configuration. + @return: The meta element of the Configuration. """ try: meta = getattr(self,ConfmlMeta.refname) @@ -104,88 +130,13 @@ """ The meta element as a property """ meta = property(get_meta,set_meta,del_meta) - -class ConfmlGroup(ConfmlElement, api.Group): - """ - Confml view. +class ConfmlSettingAttributes(ConfmlElement): """ - def __init__(self, ref="", **kwargs): - super(ConfmlGroup,self).__init__(ref,**kwargs) - if kwargs.get('icon'): - self.icon = kwargs.get('icon') - if kwargs.get('desc'): - self.desc = kwargs.get('desc') - - def get_icon(self): - try: - icon = getattr(self,ConfmlIcon.refname) - return icon.href - except AttributeError: - return None - def set_icon(self,value): self._add(ConfmlIcon(value)) - def del_icon(self): - try: - self._remove(ConfmlIcon.refname) - except exceptions.NotFound: - pass - """ The icon as a property """ - icon = property(get_icon,set_icon,del_icon) - - def get_desc(self): - try: - desc = getattr(self,ConfmlDescription.refname) - return desc.text - except AttributeError: - return None - def set_desc(self,value): self._add(ConfmlDescription(value)) - def del_desc(self): - try: - self._remove(ConfmlDescription.refname) - except exceptions.NotFound: - pass - """ The description as a property """ - desc = property(get_desc,set_desc,del_desc) - - -class ConfmlView(api.View): + Abstract base class for setting attributes. This is used as + a base in actual ConfmlSetting and ConfmlFeatureLink. """ - Confml view. - """ - def __init__(self, ref="", **kwargs): - super(ConfmlView,self).__init__(ref,**kwargs) - if kwargs.get('desc'): - self.desc = kwargs.get('desc') - - - def get_desc(self): - try: - desc = getattr(self,ConfmlDescription.refname) - return desc.text - except AttributeError: - return None - def set_desc(self,value): self._add(ConfmlDescription(value)) - def del_desc(self): - try: - self._remove(ConfmlDescription.refname) - except exceptions.NotFound: - pass - """ The description as a property """ - desc = property(get_desc,set_desc,del_desc) - -class ConfmlFeature(ConfmlElement, api.Feature): - pass - -class ConfmlSetting(ConfmlElement, api.Feature): - """ - Confml setting class. Attribute 'options' contains options of this setting. - """ - supported_types = ['int', - 'string', - 'boolean', - 'selection'] def __init__(self, ref,**kwargs): - super(ConfmlSetting,self).__init__(ref,**kwargs) - self.type = kwargs.get('type',None) + super(ConfmlSettingAttributes,self).__init__(ref,**kwargs) if kwargs.get('desc'): self.desc = kwargs.get('desc') if kwargs.get('minOccurs'): @@ -196,10 +147,10 @@ self.maxLength = kwargs.get('maxLength') if kwargs.get('minLength'): self.minLength = kwargs.get('minLength') - if kwargs.get('mapKey'): - self.mapKey = kwargs.get('mapKey') - if kwargs.get('mapValue'): - self.mapValue = kwargs.get('mapValue') + self.mapKey = kwargs.get('mapKey') + self.mapValue = kwargs.get('mapValue') + self.displayName = kwargs.get('displayName') + self.readOnly = kwargs.get('readOnly',None) self.constraint = kwargs.get('constraint',None) @@ -212,39 +163,6 @@ """ return api.ValueRe('.*') - def add_property(self, **kwargs): - """ - @param name=str: property name - @param value=str: property value - @param unit=str: property unit, e.g. kB - """ - self._add(ConfmlProperty(**kwargs), container.APPEND) - - def get_property(self, name): - """ - @param name: The name of the property - """ - for property in utils.get_list(self._get(ConfmlProperty.refname)): - if property.name == name: - return property - raise exceptions.NotFound("ConfmlProperty with name %s not found!" % name) - - def remove_property(self, name): - """ - remove a given option from this feature by name. - @param name: - """ - for property in self._get(ConfmlProperty.refname): - if property.name == name: - return self._remove(property.get_fullref()) - raise exceptions.NotFound("ConfmlProperty with name %s not found!" % name) - - def list_properties(self): - """ - Return a array of all Feature children references under this object. - """ - return [obj.name for obj in utils.get_list(self._get(ConfmlProperty.refname))] - def get_maxlength(self): try: return getattr(self,ConfmlMaxLength.refname).value @@ -259,7 +177,7 @@ self._remove(ConfmlMaxLength.refname) except exceptions.NotFound: pass - """ The description as a property """ + """ The maxLength as a property """ maxLength = property(get_maxlength,set_maxlength,del_maxlength) def get_minlength(self): @@ -276,8 +194,25 @@ self._remove(ConfmlMinLength.refname) except exceptions.NotFound: pass - """ The description as a property """ + """ The minLength as a property """ minLength = property(get_minlength,set_minlength,del_minlength) + + def get_length(self): + try: + return getattr(self,ConfmlLength.refname).value + except AttributeError: + return None + + def set_length(self,value): + self._add(ConfmlLength(value)) + + def del_length(self): + try: + self._remove(ConfmlLength.refname) + except exceptions.NotFound: + pass + """ The length as a property """ + length = property(get_length,set_length,del_length) def get_minInclusive(self): try: @@ -390,22 +325,112 @@ @property def properties(self): - dict = {} - for property in utils.get_list(self._get(ConfmlProperty.refname)): - dict[property.name] = property - return dict + propdict = {} + for prop in self._objects(type=api.Property): + propdict[prop.name] = prop + return propdict - def get_rfs(self,): - return super(ConfmlSetting,self).get_value('rfs') + def get_rfs(self): + return super(ConfmlSettingAttributes,self).get_value('rfs') def set_rfs(self, value): - super(ConfmlSetting,self).set_value('rfs',value) + super(ConfmlSettingAttributes,self).set_value('rfs',value) def del_rfs(self): - super(ConfmlSetting,self).del_value('rfs') + super(ConfmlSettingAttributes,self).del_value('rfs') rfs = property(get_rfs,set_rfs,del_rfs) + +class ConfmlGroup(ConfmlElement, api.Group): + """ + Confml view. + """ + def __init__(self, ref="", **kwargs): + super(ConfmlGroup,self).__init__(ref,**kwargs) + if kwargs.get('icon'): + self.icon = kwargs.get('icon') + if kwargs.get('desc'): + self.desc = kwargs.get('desc') + + def _group_class(self): + return ConfmlGroup + + def _featurelink_class(self): + return ConfmlFeatureLink + + def get_icon(self): + try: + icon = getattr(self,ConfmlIcon.refname) + return icon.href + except AttributeError: + return None + def set_icon(self,value): self._add(ConfmlIcon(value)) + def del_icon(self): + try: + self._remove(ConfmlIcon.refname) + except exceptions.NotFound: + pass + """ The icon as a property """ + icon = property(get_icon,set_icon,del_icon) + + def get_desc(self): + try: + desc = getattr(self,ConfmlDescription.refname) + return desc.text + except AttributeError: + return None + def set_desc(self,value): self._add(ConfmlDescription(value)) + def del_desc(self): + try: + self._remove(ConfmlDescription.refname) + except exceptions.NotFound: + pass + """ The description as a property """ + desc = property(get_desc,set_desc,del_desc) + + +class ConfmlView(api.View, ConfmlGroup): + """ + Confml view. + """ + def __init__(self, ref="", **kwargs): + super(ConfmlView,self).__init__(ref,**kwargs) + if kwargs.get('desc'): + self.desc = kwargs.get('desc') + + def get_desc(self): + try: + desc = getattr(self,ConfmlDescription.refname) + return desc.text + except AttributeError: + return None + def set_desc(self,value): self._add(ConfmlDescription(value)) + def del_desc(self): + try: + self._remove(ConfmlDescription.refname) + except exceptions.NotFound: + pass + """ The description as a property """ + desc = property(get_desc,set_desc,del_desc) + + +class ConfmlFeature(ConfmlElement, api.Feature): + def _feature_class(self): + return ConfmlSetting + +class ConfmlSetting(ConfmlSettingAttributes, api.Feature): + """ + Confml setting class. Attribute 'options' contains options of this setting. + """ + supported_types = ['int', + 'string', + 'boolean', + 'selection'] + def __init__(self, ref,**kwargs): + super(ConfmlSetting,self).__init__(ref,**kwargs) + self.type = kwargs.get('type',None) + def get_value_cast(self, value, attr=None): """ A function to perform the value type casting in get operation @@ -425,13 +450,17 @@ @param value: the value to cast @param attr: the attribute which is fetched from model (normally in confml either None='data' or 'rfs') """ + # Add a exception case for None value, because the data casting will always fail for it + if value == None: + return value + if not attr or attr == 'data': return self.set_data_cast(value) elif attr == 'rfs': return self.set_rfs_cast(value) else: return value - + def get_data_cast(self, value): """ A function to perform the data type casting in get operation @@ -600,48 +629,78 @@ """ Confml setting class for multiSelection type. """ - + + # Pattern for checking whether a data value should be interpreted + # in the old style (e.g. '"opt1" "opt2" "opt3"') + OLD_STYLE_DATA_PATTERN = re.compile(r'"[^"]*([^"]*" ")*[^"]*"') + def __init__(self, ref,**kwargs): kwargs['type'] = 'multiSelection' ConfmlSetting.__init__(self,ref,**kwargs) - + def add_data(self, data): + """ + Add a data value. + @param data: A Data object + """ + # If there are existing data objects added to the proxy, and they + # are not in the same DataContainer (ConfML data section), change the + # policy to replace + if self.dataproxy.datas.get(data.attr): + existing_data_obj = self.dataproxy.datas[data.attr][-1] + existing_obj_parent = existing_data_obj._find_parent_or_default(type=api.DataContainer) + new_obj_parent = data._find_parent_or_default(type=api.DataContainer) + + if existing_obj_parent is not new_obj_parent: + self.dataproxy.datas[data.attr] = [] + + self.dataproxy._add_data(data) + def get_valueset(self): """ Get the ValueSet object for this feature, that has the list of available values. """ return api.Feature.get_valueset(self) - - def get_data_cast(self, value): - """ - A function to perform the value type casting in get operation - """ - try: - if not isinstance(value, types.ListType): - values = value.split('" "') - for i in range(len(values)): - if values[i].startswith('"'): - values[i] = values[i][1:] - if values[i].endswith('"'): - values[i] = values[i][:-1] - return values - return value - except AttributeError: - return None - def set_data_cast(self, value): - """ - A function to perform the value type casting in the set operation - """ + def convert_data_to_value(self, data_objects, cast=True, attr=None): + if len(data_objects) == 1: + d = data_objects[0] + + # Special handling for cases where the data is in the old format + # (pre-2.88 ConfML spec) + if d.value is not None: + if self.OLD_STYLE_DATA_PATTERN.match(d.value): + return tuple([v.rstrip('"').lstrip('"') for v in d.value.split('" "')]) + + # Single data object with empty="true" means that nothing is selected + if d.empty: return () - if isinstance(value, list): - value = " ".join(['"%s"' % elem for elem in value]) - return value + # Read each data value (or name-ID mapped value) into result + result = [] + for data_obj in data_objects: + if data_obj.map: + value = self._resolve_name_id_mapped_value(data_obj.map, cast_value=cast) + else: + value = data_obj.value + result.append(value) + result = utils.distinct_array(result) + + # Handle None in the result (data element with no text data) + if None in result: + # If the empty string is a valid option, change the None to that, + # otherwise ignore + index = result.index(None) + if '' in self.get_valueset(): result[index] = '' + else: del result[index] + + return tuple(result) - def set_value(self, value): - if not isinstance(value, types.ListType): - raise ValueError("Only list types are allowed.") - self.value = value + def convert_value_to_data(self, value, attr=None): + if not isinstance(value, (types.ListType, types.TupleType, types.NoneType)): + raise ValueError("Only list, tuple and None types are allowed.") + + if value: return [api.Data(fqr=self.fqr, value=v, attr=attr) for v in value] + else: return [api.Data(fqr=self.fqr, empty=True, attr=attr)] class ConfmlDateSetting(ConfmlSetting): """ @@ -667,6 +726,32 @@ kwargs['type'] = 'dateTime' ConfmlSetting.__init__(self,ref,**kwargs) +class ConfmlHexBinarySetting(ConfmlSetting): + """ + Confml setting class for hex-binary type. + """ + def __init__(self, ref,**kwargs): + kwargs['type'] = 'hexBinary' + ConfmlSetting.__init__(self,ref,**kwargs) + + def get_valueset(self): + return api.ValueRe(r'^([0123456789ABCDEF]{2})*$') + + def get_data_cast(self, value): + value = value or '' # Handle None + if value not in self.get_valueset(): + raise ValueError("Cannot convert value %r of setting '%s' into binary data: Not a valid hex string", value) + + temp = [] + for i in xrange(len(value) / 2): + start = i * 2 + end = start + 2 + temp.append(chr(int(value[start:end], 16))) + return ''.join(temp) + + def set_data_cast(self, value): + return ''.join(['%02X' % ord(c) for c in value]) + class ConfmlDurationSetting(ConfmlSetting): """ Confml setting class for date type. @@ -711,10 +796,12 @@ class ConfmlLocalPath(ConfmlElement, api.Feature): """ - Confml file class. Attribute setting. + Confml file class. Attribute setting. + The localPath "name" is always the same as its ref 'localPath' """ def __init__(self, ref='localPath', **kwargs): kwargs['type'] = 'string' + kwargs['name'] = ref ConfmlElement.__init__(self, **kwargs) api.Feature.__init__(self, ref, **kwargs) self.readOnly = kwargs.get('readOnly', None) @@ -723,14 +810,45 @@ class ConfmlTargetPath(ConfmlElement, api.Feature): """ Confml file class. Attribute setting. + The targetPath "name" is always the same as its ref 'targetPath' """ def __init__(self, ref='targetPath', **kwargs): kwargs['type'] = 'string' + kwargs['name'] = ref ConfmlElement.__init__(self, **kwargs) api.Feature.__init__(self, ref, **kwargs) self.readOnly = kwargs.get('readOnly', None) +class ConfmlFeatureLink(ConfmlSettingAttributes, api.FeatureLink): + """ + ConfmlFeatureLink object is the setting reference object inside confml + group / view. It can populate the actual FeatureProxy objects under the + particular group / view object. + """ + + """ the override_attributes explicitly states which feature link attributes can be overridden """ + override_attributes = ['name', + 'desc', + 'minLength', + 'maxLength', + 'minOccurs', + 'maxOccurs', + 'minInclusive', + 'maxInclusive', + 'minExclusive', + 'maxExclusive', + 'pattern', + 'totalDigits', + 'options', + 'properties', + 'readOnly' + ] + def __init__(self, ref,**kwargs): + ConfmlSettingAttributes.__init__(self, ref,**kwargs) + api.FeatureLink.__init__(self, ref, **kwargs) + self.type = kwargs.get('type',None) + class ConfmlMeta(api.Base): """ Confml meta element @@ -751,6 +869,9 @@ def __setitem__(self, key, value): self.array[key] = value + def __len__(self): + return len(self.array) + def __str__(self): tempstr = "ConfmlMeta object\n" counter = 0 @@ -790,6 +911,16 @@ def replace(self, index, tag, value, ns=None, dict=None): self.array[index] = ConfmlMetaProperty(tag, value, ns, attrs=dict) + def update(self, data): + """ + Update this the ConfmlMeta object meta with the given data. + @param data: The input ConfmlMeta data to update for this object + """ + if data: + for property in data.array: + self.set_property_by_tag(property.tag, property.value, property.ns, property.attrs) + + def clear(self, value): self.array = [] @@ -807,22 +938,38 @@ return -1 def find_by_attribute(self, name, value): - for item in self.array: - if item.attrs.has_key(name) and item.attrs[name] == value: + for item in self.array: + if item.attrs.has_key(name) and item.attrs[name] == value: return self.array.index(item) return -1 - def get_property_by_tag(self, tag): + def get_property_by_tag(self, tag, attrs={}): """ Try to find the element by its tag in the meta elem array. @param tag: the tag that is searched @return: the ConfmlMetaProperty object if it is found. None if element with tag is not found. - """ + """ for item in self.array: if item.tag == tag: - return item + if not item.attrs or (item.attrs.get("name", None) == attrs.get("name", None)): + return item return None + def set_property_by_tag(self, tag, value, ns=None, attributes=None): + """ + Try to find the element by its tag and set it the meta elem array. + This will either create a new element to the meta or replace first + encountered elem in array. + @param tag: the tag that is searched + @return: the ConfmlMetaProperty object if it is found. None if element with tag is not found. + """ + + if self.get_property_by_tag(tag, attributes): + property = self.get_property_by_tag(tag, attributes) + property.value = value + property.attrs = attributes or {} + else: + self.add(tag, value, ns, attributes) class ConfmlDescription(api.Base): """ @@ -844,21 +991,6 @@ self.href = href -class ConfmlProperty(api.Base): - """ - Confml meta element - """ - refname = "_property" - def __init__(self, **kwargs): - """ - @param name=str: name string - @param value=str: value for the property, string - @param unit=str: unit of the property - """ - super(ConfmlProperty,self).__init__(self.refname) - self.name = kwargs.get('name',None) - self.value = kwargs.get('value',None) - self.unit = kwargs.get('unit',None) class ConfmlMetaProperty(api.Base): @@ -873,10 +1005,7 @@ self.tag = tag self.value = value self.ns = ns - if kwargs.has_key("attrs") and kwargs["attrs"] != None: - self.attrs = dict(kwargs["attrs"]) - else: - self.attrs = {} + self.attrs = dict(kwargs.get('attrs') or {}) def __cmp__(self, other): try: @@ -890,9 +1019,28 @@ def __str__(self): return "Tag: %s Value: %s Namespace: %s Attributes: % s" % (self.tag, self.value, self.ns, repr(self.attrs)) - -class ConfmlLength(api.Base): +class ConfmlNumericValue(api.Base): + """ + Confml base class for all float type properties. + Performs a simple value casting from string to int in value setting. + """ + def __init__(self, ref="", **kwargs): + super(ConfmlNumericValue,self).__init__(ref, **kwargs) + self._value = None + + def get_value(self): return self._value + def del_value(self): self._value = None + def set_value(self, value): + if utils.is_float(value): + self._value = float(value) + else: + self._value = int(value) + """ The value as a property """ + value = property(get_value,set_value,del_value) + + +class ConfmlLength(ConfmlNumericValue): """ Confml length element """ @@ -901,7 +1049,7 @@ super(ConfmlLength,self).__init__(self.refname) self.value = value -class ConfmlMaxLength(api.Base): +class ConfmlMaxLength(ConfmlNumericValue): """ Confml max element """ @@ -910,7 +1058,7 @@ super(ConfmlMaxLength,self).__init__(self.refname) self.value = value -class ConfmlMinLength(api.Base): +class ConfmlMinLength(ConfmlNumericValue): """ Confml min element """ @@ -919,7 +1067,7 @@ super(ConfmlMinLength,self).__init__(self.refname) self.value = value -class ConfmlMinInclusive(api.Base): +class ConfmlMinInclusive(ConfmlNumericValue): """ Confml minInclusive element """ @@ -928,7 +1076,7 @@ super(ConfmlMinInclusive,self).__init__(self.refname) self.value = value -class ConfmlMaxInclusive(api.Base): +class ConfmlMaxInclusive(ConfmlNumericValue): """ Confml minInclusive element """ @@ -937,7 +1085,7 @@ super(ConfmlMaxInclusive,self).__init__(self.refname) self.value = value -class ConfmlMinExclusive(api.Base): +class ConfmlMinExclusive(ConfmlNumericValue): """ Confml minExclusive element """ @@ -946,7 +1094,7 @@ super(ConfmlMinExclusive,self).__init__(self.refname) self.value = value -class ConfmlMaxExclusive(api.Base): +class ConfmlMaxExclusive(ConfmlNumericValue): """ Confml maxExclusive element """ @@ -964,7 +1112,7 @@ super(ConfmlPattern,self).__init__(self.refname) self.value = value -class ConfmlTotalDigits(api.Base): +class ConfmlTotalDigits(ConfmlNumericValue): """ Confml totalDigits element """