3
|
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 |
]
|