configurationengine/source/cone/validation/builtinvalidators/confml.py
changeset 3 e7e0ae78773e
equal deleted inserted replaced
2:87cfa131b535 3:e7e0ae78773e
       
     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 import logging
       
    18 
       
    19 from cone.public import api, exceptions, container, utils
       
    20 from cone.confml import model
       
    21 
       
    22 from cone.validation.confmlvalidation import ValidatorBase, FixerBase
       
    23 
       
    24 class SettingValidatorBase(ValidatorBase):
       
    25     """
       
    26     Base class for validators that validate ConfML settings
       
    27     (sub-classes of cone.confml.model.ConfmlSetting).
       
    28     """
       
    29     def validate(self):
       
    30         for ref, feature in self.context.feature_dict.iteritems():
       
    31             if isinstance(feature._obj, model.ConfmlSetting):
       
    32                 self.validate_setting(ref, feature)
       
    33     
       
    34     def validate_setting(self, ref, setting):
       
    35         raise NotImplementedError()
       
    36 
       
    37 class LengthConstraintValidator(SettingValidatorBase):
       
    38     """
       
    39     Validator for validating xs:length, xs:minLength and xs:maxLength
       
    40     constraints on setting data values.
       
    41     """
       
    42     PROBLEM_TYPES = ['model.confml.invalid_value.length',
       
    43                      'model.confml.invalid_value.minlength',
       
    44                      'model.confml.invalid_value.maxlength']
       
    45     
       
    46     def validate_setting(self, ref, setting):
       
    47         if setting.length:
       
    48             value = setting.get_value()
       
    49             if isinstance(value, basestring) and len(value) != setting.length:
       
    50                 self._add_problem(
       
    51                     setting = setting,
       
    52                     msg = "Setting %s: Exact number of characters must be %s (value has %s)" % (ref, setting.length, len(value)),
       
    53                     prob_type = self.PROBLEM_TYPES[0])
       
    54                 
       
    55         if setting.minLength:
       
    56             value = setting.get_value()
       
    57             if isinstance(value, basestring) and len(value) < setting.minLength:
       
    58                 self._add_problem(
       
    59                     setting = setting,
       
    60                     msg = "Setting %s: Minimum number of characters is %s (value has %s)" % (ref, setting.minLength, len(value)),
       
    61                     prob_type = self.PROBLEM_TYPES[1])
       
    62         
       
    63         if setting.maxLength:
       
    64             value = setting.get_value()
       
    65             if isinstance(value, basestring) and len(value) > setting.maxLength:
       
    66                 self._add_problem(
       
    67                     setting = setting,
       
    68                     msg = "Setting %s: Maximum number of characters is %s (value has %s)" % (ref, setting.maxLength, len(value)),
       
    69                     prob_type = self.PROBLEM_TYPES[2])
       
    70    
       
    71     def _add_problem(self, setting, msg, prob_type):
       
    72         dataobj = setting.datas['data'][-1]
       
    73         prob = api.Problem(
       
    74             msg = msg,
       
    75             type = prob_type,
       
    76             line = dataobj.lineno,
       
    77             file = dataobj.get_configuration_path())
       
    78         self.context.problems.append(prob)
       
    79 
       
    80 class MissingFeatureForDataValidator(ValidatorBase):
       
    81     """
       
    82     Validator for validating data elements that do not have a
       
    83     corresponding feature/setting in the configuration.
       
    84     """
       
    85     PROBLEM_TYPES = ['model.confml.missing_feature_for_data']
       
    86     
       
    87     def validate(self):
       
    88         for dataobj in self.context.configuration._traverse(type=api.Data):
       
    89             try:
       
    90                 self.context.dview.get_feature(dataobj.fqr)
       
    91             except exceptions.NotFound:
       
    92                 prob = api.Problem(
       
    93                     msg = "Feature '%s' not found" % dataobj.fqr,
       
    94                     type = self.PROBLEM_TYPES[0],
       
    95                     line = dataobj.lineno,
       
    96                     file = dataobj.get_configuration_path())
       
    97                 self.context.problems.append(prob)
       
    98 
       
    99 class MissingDescriptionValidator(SettingValidatorBase):
       
   100     """
       
   101     Validator for validating missing descriptions in feature/setting in the configuration.
       
   102     """
       
   103     PROBLEM_TYPES = ['model.confml.missing_desc']
       
   104     
       
   105     def validate_setting(self, ref, setting):
       
   106         print 'Validating missing desc!'
       
   107         if not setting.desc or setting.desc == '':
       
   108             prob = api.Problem(
       
   109                 msg = "Setting/Feature %s: has no description" % (ref),
       
   110                 type = self.PROBLEM_TYPES[0],
       
   111                 line = setting.lineno,
       
   112                 file = setting.get_configuration_path(),
       
   113                 severity = api.Problem.SEVERITY_WARNING)
       
   114             self.context.problems.append(prob)
       
   115 
       
   116 class DuplicateSettingValidator(ValidatorBase):
       
   117     """
       
   118     Validator for validating that there are no settings with same ref in given 
       
   119     configuration.
       
   120     """
       
   121     PROBLEM_TYPES = ['model.confml.duplicate.setting']
       
   122     
       
   123     def validate(self):
       
   124         settings_container = container.DataContainer()
       
   125         # Traverse through the configuration model and store each feature to 
       
   126         # the settings_container. 
       
   127         for setting in self.context.configuration._traverse(type=model.ConfmlSetting):
       
   128             settings_container.add_value(setting.fqr, setting)
       
   129         # Go though the settings_container to see if any features have more than one 
       
   130         # definition and report those as problems
       
   131         for fqr in settings_container.list_keys():
       
   132             if len(settings_container.get_values(fqr)) > 1:
       
   133                 files = [setting.get_configuration_path() for setting in settings_container.get_values(fqr)]
       
   134                 prob = api.Problem(
       
   135                     msg = "Feature %s has '%s' definitions in files %s" % (fqr, len(settings_container.get_values(fqr)), files),
       
   136                     type = self.PROBLEM_TYPES[0],
       
   137                     severity = api.Problem.SEVERITY_WARNING, 
       
   138                     line = settings_container.get_value(fqr).lineno,
       
   139                     file = files[-1],
       
   140                     problem_data = settings_container.get_values(fqr))
       
   141                 self.context.problems.append(prob)
       
   142 
       
   143 class DuplicateFeatureValidator(ValidatorBase):
       
   144     """
       
   145     Validator for validating that there are no features with same ref in given 
       
   146     configuration.
       
   147     """
       
   148     PROBLEM_TYPES = ['model.confml.duplicate.feature']
       
   149     
       
   150     def validate(self):
       
   151         settings_container = container.DataContainer()
       
   152         # Traverse through the configuration model and store each feature to 
       
   153         # the settings_container. 
       
   154         for setting in self.context.configuration._traverse(type=model.ConfmlFeature):
       
   155             settings_container.add_value(setting.fqr, setting)
       
   156         # Go though the settings_container to see if any features have more than one 
       
   157         # definition and report those as problems
       
   158         for fqr in settings_container.list_keys():
       
   159             if len(settings_container.get_values(fqr)) > 1:
       
   160                 files = [setting.get_configuration_path() for setting in settings_container.get_values(fqr)]
       
   161                 prob = api.Problem(
       
   162                     msg = "Feature %s has '%s' definitions in files %s" % (fqr, len(settings_container.get_values(fqr)), files),
       
   163                     type = self.PROBLEM_TYPES[0],
       
   164                     severity = api.Problem.SEVERITY_INFO,
       
   165                     line = settings_container.get_value(fqr).lineno,
       
   166                     file = files[-1],
       
   167                     problem_data = settings_container.get_values(fqr))
       
   168                 self.context.problems.append(prob)
       
   169 
       
   170 class DuplicateSettingFixer(FixerBase):
       
   171     """
       
   172     A Fix class for duplicate settings that removes all but the last definition of the element.
       
   173     """
       
   174     PROBLEM_TYPES = ['model.confml.duplicate.setting']
       
   175 
       
   176     def fix(self, context):
       
   177         for problem in self.filter_problems(context.problems, self.PROBLEM_TYPES[0]):
       
   178             logging.getLogger('cone.validation').info("Fixing problem %s" % problem.msg)
       
   179             context.fixes.append("Fixed problem: %s" % problem.msg)
       
   180             # The problem data is expected to have those duplicate settings and the 
       
   181             # actual setting as a last element
       
   182             for setting in problem.problem_data[0:-1]:
       
   183                 parent_fea = setting.find_parent(type=api.Feature)
       
   184                 logging.getLogger('cone.validation').info("Remove setting %s from %s" % (setting.fqr, parent_fea.get_configuration_path()))
       
   185                 try:
       
   186                     parent_fea.remove_feature(setting.ref)
       
   187                 except exceptions.NotFound:
       
   188                     logging.getLogger('cone.validation').info("Already removed %s from %s" % (setting.ref, parent_fea.get_configuration_path()))
       
   189 
       
   190 class DuplicateFeatureFixer(FixerBase):
       
   191     """
       
   192     A Fix class for duplicate features that merges all setting under a duplicate feature 
       
   193     to the first instance of the feature and removes the duplicates.
       
   194     """
       
   195     PROBLEM_TYPES = ['model.confml.duplicate.feature']
       
   196 
       
   197     def fix(self, context):
       
   198         for problem in self.filter_problems(context.problems, self.PROBLEM_TYPES[0]):
       
   199             logging.getLogger('cone.validation').info("Fixing problem %s" % problem.msg)
       
   200             context.fixes.append("Fixed problem: %s" % problem.msg)
       
   201             # The problem data is expected to have those duplicate settings and the 
       
   202             # actual setting as a last element
       
   203             target_feature = problem.problem_data[0]
       
   204             target_config = target_feature.find_parent(type=api.Configuration)
       
   205             for feature in problem.problem_data[1:]:
       
   206                 logging.getLogger('cone.validation').info("Move settings from Feature %s in %s to %s" % \
       
   207                                                           (feature.fqr, feature.get_configuration_path(), target_feature.get_configuration_path()))
       
   208                 for setting_ref in feature.list_features():
       
   209                     setting = feature.get_feature(setting_ref)
       
   210                     # Get the path from feature to the parent of this setting
       
   211                     # (pathto,ref) = utils.dottedref.psplit_ref(setting_ref)
       
   212                     if target_feature.has_ref(setting_ref):
       
   213                         target_feature.remove_feature(setting_ref)
       
   214                     target_feature.add_feature(setting)
       
   215                     
       
   216                 config = feature.find_parent(type=api.Configuration)
       
   217                 logging.getLogger('cone.validation').info("Remove feature %s from %s" % (feature.fqr, config.get_full_path()))
       
   218                 config.remove_feature(feature.ref)
       
   219 
       
   220                 
       
   221 #: List of all built-in ConfML validator classes
       
   222 VALIDATOR_CLASSES = [
       
   223     MissingFeatureForDataValidator,
       
   224     LengthConstraintValidator,
       
   225     DuplicateSettingValidator,
       
   226     DuplicateFeatureValidator,
       
   227 #    MissingDescriptionValidator,
       
   228 ]
       
   229 
       
   230 #: List of all built-in ConfML fixer classes
       
   231 FIXER_CLASSES = [
       
   232     DuplicateFeatureFixer,
       
   233 ]