--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/configurationengine/source/cone/validation/builtinvalidators/confml.py Tue Aug 10 14:29:28 2010 +0300
@@ -0,0 +1,233 @@
+#
+# 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:
+#
+
+import logging
+
+from cone.public import api, exceptions, container, utils
+from cone.confml import model
+
+from cone.validation.confmlvalidation import ValidatorBase, FixerBase
+
+class SettingValidatorBase(ValidatorBase):
+ """
+ Base class for validators that validate ConfML settings
+ (sub-classes of cone.confml.model.ConfmlSetting).
+ """
+ def validate(self):
+ for ref, feature in self.context.feature_dict.iteritems():
+ if isinstance(feature._obj, model.ConfmlSetting):
+ self.validate_setting(ref, feature)
+
+ def validate_setting(self, ref, setting):
+ raise NotImplementedError()
+
+class LengthConstraintValidator(SettingValidatorBase):
+ """
+ Validator for validating xs:length, xs:minLength and xs:maxLength
+ constraints on setting data values.
+ """
+ PROBLEM_TYPES = ['model.confml.invalid_value.length',
+ 'model.confml.invalid_value.minlength',
+ 'model.confml.invalid_value.maxlength']
+
+ def validate_setting(self, ref, setting):
+ if setting.length:
+ value = setting.get_value()
+ if isinstance(value, basestring) and len(value) != setting.length:
+ self._add_problem(
+ setting = setting,
+ msg = "Setting %s: Exact number of characters must be %s (value has %s)" % (ref, setting.length, len(value)),
+ prob_type = self.PROBLEM_TYPES[0])
+
+ if setting.minLength:
+ value = setting.get_value()
+ if isinstance(value, basestring) and len(value) < setting.minLength:
+ self._add_problem(
+ setting = setting,
+ msg = "Setting %s: Minimum number of characters is %s (value has %s)" % (ref, setting.minLength, len(value)),
+ prob_type = self.PROBLEM_TYPES[1])
+
+ if setting.maxLength:
+ value = setting.get_value()
+ if isinstance(value, basestring) and len(value) > setting.maxLength:
+ self._add_problem(
+ setting = setting,
+ msg = "Setting %s: Maximum number of characters is %s (value has %s)" % (ref, setting.maxLength, len(value)),
+ prob_type = self.PROBLEM_TYPES[2])
+
+ def _add_problem(self, setting, msg, prob_type):
+ dataobj = setting.datas['data'][-1]
+ prob = api.Problem(
+ msg = msg,
+ type = prob_type,
+ line = dataobj.lineno,
+ file = dataobj.get_configuration_path())
+ self.context.problems.append(prob)
+
+class MissingFeatureForDataValidator(ValidatorBase):
+ """
+ Validator for validating data elements that do not have a
+ corresponding feature/setting in the configuration.
+ """
+ PROBLEM_TYPES = ['model.confml.missing_feature_for_data']
+
+ def validate(self):
+ for dataobj in self.context.configuration._traverse(type=api.Data):
+ try:
+ self.context.dview.get_feature(dataobj.fqr)
+ except exceptions.NotFound:
+ prob = api.Problem(
+ msg = "Feature '%s' not found" % dataobj.fqr,
+ type = self.PROBLEM_TYPES[0],
+ line = dataobj.lineno,
+ file = dataobj.get_configuration_path())
+ self.context.problems.append(prob)
+
+class MissingDescriptionValidator(SettingValidatorBase):
+ """
+ Validator for validating missing descriptions in feature/setting in the configuration.
+ """
+ PROBLEM_TYPES = ['model.confml.missing_desc']
+
+ def validate_setting(self, ref, setting):
+ print 'Validating missing desc!'
+ if not setting.desc or setting.desc == '':
+ prob = api.Problem(
+ msg = "Setting/Feature %s: has no description" % (ref),
+ type = self.PROBLEM_TYPES[0],
+ line = setting.lineno,
+ file = setting.get_configuration_path(),
+ severity = api.Problem.SEVERITY_WARNING)
+ self.context.problems.append(prob)
+
+class DuplicateSettingValidator(ValidatorBase):
+ """
+ Validator for validating that there are no settings with same ref in given
+ configuration.
+ """
+ PROBLEM_TYPES = ['model.confml.duplicate.setting']
+
+ def validate(self):
+ settings_container = container.DataContainer()
+ # Traverse through the configuration model and store each feature to
+ # the settings_container.
+ for setting in self.context.configuration._traverse(type=model.ConfmlSetting):
+ settings_container.add_value(setting.fqr, setting)
+ # Go though the settings_container to see if any features have more than one
+ # definition and report those as problems
+ for fqr in settings_container.list_keys():
+ if len(settings_container.get_values(fqr)) > 1:
+ files = [setting.get_configuration_path() for setting in settings_container.get_values(fqr)]
+ prob = api.Problem(
+ msg = "Feature %s has '%s' definitions in files %s" % (fqr, len(settings_container.get_values(fqr)), files),
+ type = self.PROBLEM_TYPES[0],
+ severity = api.Problem.SEVERITY_WARNING,
+ line = settings_container.get_value(fqr).lineno,
+ file = files[-1],
+ problem_data = settings_container.get_values(fqr))
+ self.context.problems.append(prob)
+
+class DuplicateFeatureValidator(ValidatorBase):
+ """
+ Validator for validating that there are no features with same ref in given
+ configuration.
+ """
+ PROBLEM_TYPES = ['model.confml.duplicate.feature']
+
+ def validate(self):
+ settings_container = container.DataContainer()
+ # Traverse through the configuration model and store each feature to
+ # the settings_container.
+ for setting in self.context.configuration._traverse(type=model.ConfmlFeature):
+ settings_container.add_value(setting.fqr, setting)
+ # Go though the settings_container to see if any features have more than one
+ # definition and report those as problems
+ for fqr in settings_container.list_keys():
+ if len(settings_container.get_values(fqr)) > 1:
+ files = [setting.get_configuration_path() for setting in settings_container.get_values(fqr)]
+ prob = api.Problem(
+ msg = "Feature %s has '%s' definitions in files %s" % (fqr, len(settings_container.get_values(fqr)), files),
+ type = self.PROBLEM_TYPES[0],
+ severity = api.Problem.SEVERITY_INFO,
+ line = settings_container.get_value(fqr).lineno,
+ file = files[-1],
+ problem_data = settings_container.get_values(fqr))
+ self.context.problems.append(prob)
+
+class DuplicateSettingFixer(FixerBase):
+ """
+ A Fix class for duplicate settings that removes all but the last definition of the element.
+ """
+ PROBLEM_TYPES = ['model.confml.duplicate.setting']
+
+ def fix(self, context):
+ for problem in self.filter_problems(context.problems, self.PROBLEM_TYPES[0]):
+ logging.getLogger('cone.validation').info("Fixing problem %s" % problem.msg)
+ context.fixes.append("Fixed problem: %s" % problem.msg)
+ # The problem data is expected to have those duplicate settings and the
+ # actual setting as a last element
+ for setting in problem.problem_data[0:-1]:
+ parent_fea = setting.find_parent(type=api.Feature)
+ logging.getLogger('cone.validation').info("Remove setting %s from %s" % (setting.fqr, parent_fea.get_configuration_path()))
+ try:
+ parent_fea.remove_feature(setting.ref)
+ except exceptions.NotFound:
+ logging.getLogger('cone.validation').info("Already removed %s from %s" % (setting.ref, parent_fea.get_configuration_path()))
+
+class DuplicateFeatureFixer(FixerBase):
+ """
+ A Fix class for duplicate features that merges all setting under a duplicate feature
+ to the first instance of the feature and removes the duplicates.
+ """
+ PROBLEM_TYPES = ['model.confml.duplicate.feature']
+
+ def fix(self, context):
+ for problem in self.filter_problems(context.problems, self.PROBLEM_TYPES[0]):
+ logging.getLogger('cone.validation').info("Fixing problem %s" % problem.msg)
+ context.fixes.append("Fixed problem: %s" % problem.msg)
+ # The problem data is expected to have those duplicate settings and the
+ # actual setting as a last element
+ target_feature = problem.problem_data[0]
+ target_config = target_feature.find_parent(type=api.Configuration)
+ for feature in problem.problem_data[1:]:
+ logging.getLogger('cone.validation').info("Move settings from Feature %s in %s to %s" % \
+ (feature.fqr, feature.get_configuration_path(), target_feature.get_configuration_path()))
+ for setting_ref in feature.list_features():
+ setting = feature.get_feature(setting_ref)
+ # Get the path from feature to the parent of this setting
+ # (pathto,ref) = utils.dottedref.psplit_ref(setting_ref)
+ if target_feature.has_ref(setting_ref):
+ target_feature.remove_feature(setting_ref)
+ target_feature.add_feature(setting)
+
+ config = feature.find_parent(type=api.Configuration)
+ logging.getLogger('cone.validation').info("Remove feature %s from %s" % (feature.fqr, config.get_full_path()))
+ config.remove_feature(feature.ref)
+
+
+#: List of all built-in ConfML validator classes
+VALIDATOR_CLASSES = [
+ MissingFeatureForDataValidator,
+ LengthConstraintValidator,
+ DuplicateSettingValidator,
+ DuplicateFeatureValidator,
+# MissingDescriptionValidator,
+]
+
+#: List of all built-in ConfML fixer classes
+FIXER_CLASSES = [
+ DuplicateFeatureFixer,
+]