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 sys, os, shutil
|
|
18 |
import logging
|
|
19 |
from optparse import OptionParser, OptionGroup
|
|
20 |
import cone_common
|
|
21 |
from cone.report import report_util
|
|
22 |
from cone.public import api, exceptions, utils, plugin, parsecontext
|
|
23 |
import cone.validation.parsecontext
|
|
24 |
import cone.validation.schemavalidation
|
|
25 |
import cone.validation.implmlvalidation
|
|
26 |
import cone.validation.confmlvalidation
|
|
27 |
from cone.validation.problem_type_filter import ProblemTypeFilter
|
|
28 |
|
|
29 |
ROOT_PATH = os.path.abspath(os.path.dirname(__file__))
|
|
30 |
|
|
31 |
VERSION = '1.0'
|
|
32 |
|
|
33 |
logger = logging.getLogger('cone')
|
|
34 |
|
|
35 |
REPORT_SHORTCUTS = {
|
|
36 |
'xml': report_util.ReportShortcut(
|
|
37 |
os.path.join(ROOT_PATH, 'validation_report_template.xml'),
|
|
38 |
'validation_report.xml',
|
|
39 |
"Create a validation report of xml type."),
|
|
40 |
|
|
41 |
'html': report_util.ReportShortcut(
|
|
42 |
os.path.join(ROOT_PATH, 'validation_report_template.html'),
|
|
43 |
'validation_report.html',
|
|
44 |
"Create a validation report of html type."),
|
|
45 |
}
|
|
46 |
|
|
47 |
def main():
|
|
48 |
""" Validate a configuration, or individual confml/implml files. """
|
|
49 |
shortcut_container = report_util.ReportShortcutContainer(REPORT_SHORTCUTS,
|
|
50 |
'html')
|
|
51 |
|
|
52 |
parser = OptionParser(version="ConE validate %s" % VERSION)
|
|
53 |
|
|
54 |
parser.add_options(cone_common.COMMON_OPTIONS)
|
|
55 |
|
|
56 |
parser.add_option("-c", "--configuration",
|
|
57 |
dest="configuration",
|
|
58 |
help="Defines the name of the configuration for the action",
|
|
59 |
metavar="CONFIG")
|
|
60 |
|
|
61 |
parser.add_option("-p", "--project",
|
|
62 |
dest="project",
|
|
63 |
default=".",
|
|
64 |
help="defines the location of current project. Default is the "\
|
|
65 |
"current working directory.",
|
|
66 |
metavar="STORAGE")
|
|
67 |
|
|
68 |
group = OptionGroup(parser, 'Validate options',
|
|
69 |
'The validate action is intended for performing validation on a '\
|
|
70 |
'configuration or individual files. ')
|
|
71 |
|
|
72 |
group.add_option('--confml-file',
|
|
73 |
action="append",
|
|
74 |
help='Validate only the given single ConfML file.',
|
|
75 |
metavar="FILE",
|
|
76 |
default=None)
|
|
77 |
|
|
78 |
group.add_option('--implml-file',
|
|
79 |
action="append",
|
|
80 |
help='Validate only the given single ImplML file.',
|
|
81 |
metavar="FILE",
|
|
82 |
default=None)
|
|
83 |
|
|
84 |
group.add_option("--template",
|
|
85 |
help="Template used in report generation. "\
|
|
86 |
"Example: --template=report_template.html.",
|
|
87 |
metavar="FILE",
|
|
88 |
default=None)
|
|
89 |
|
|
90 |
group.add_option("--report-type",
|
|
91 |
help="The type of the report to generate. This is a convenience "\
|
|
92 |
"switch for setting the used template. If --template is given, this option has no effect. "\
|
|
93 |
"Possible values: "\
|
|
94 |
+ shortcut_container.get_shortcut_help_text(),
|
|
95 |
metavar="TYPE",\
|
|
96 |
default=None)
|
|
97 |
|
|
98 |
group.add_option("--report",
|
|
99 |
help="Specifies the location of the validation report. "\
|
|
100 |
"Example --report=report.html.",
|
|
101 |
metavar="FILE",
|
|
102 |
default=None)
|
|
103 |
|
|
104 |
group.add_option("--dump-schema-files",
|
|
105 |
help="Dump the XML schema files used for validation into the specified directory.",
|
|
106 |
metavar="DIR",
|
|
107 |
default=None)
|
|
108 |
|
|
109 |
group.add_option("--exclude-filter",
|
|
110 |
action="append",
|
|
111 |
help="Exclude validation problems by given filter. "\
|
|
112 |
"Examples: --exclude-filter=schema, --exclude-filter=schema.implml, --exclude-filter=schema.confml, --exclude-filter=schema.implml.ruleml",
|
|
113 |
default=None)
|
|
114 |
|
|
115 |
group.add_option("--include-filter",
|
|
116 |
action="append",
|
|
117 |
help="Include validation problems by given filter."\
|
|
118 |
"Examples: --include-filter=schema.implml, --include-filter=schema.implml.ruleml",
|
|
119 |
default=None)
|
|
120 |
|
|
121 |
parser.add_option_group(group)
|
|
122 |
(options, _) = parser.parse_args()
|
|
123 |
|
|
124 |
cone_common.handle_common_options(options)
|
|
125 |
|
|
126 |
if not shortcut_container.is_valid_shortcut(options.report_type):
|
|
127 |
parser.error("Invalid report type: %s" % options.report_type)
|
|
128 |
|
|
129 |
if options.dump_schema_files:
|
|
130 |
dump_dir = options.dump_schema_files
|
|
131 |
print "Dumping XML schema files to '%s'" % dump_dir
|
|
132 |
|
|
133 |
cone.validation.schemavalidation.dump_schema_files(dump_dir)
|
|
134 |
return
|
|
135 |
|
|
136 |
pt_filter = ProblemTypeFilter(includes = options.include_filter or [],
|
|
137 |
excludes = options.exclude_filter or [])
|
|
138 |
|
|
139 |
problems = []
|
|
140 |
if options.confml_file or options.implml_file:
|
|
141 |
if options.confml_file:
|
|
142 |
|
|
143 |
func = cone.validation.schemavalidation.validate_confml_data
|
|
144 |
for file in options.confml_file:
|
|
145 |
problems.extend(validate_file(file, func))
|
|
146 |
if options.implml_file:
|
|
147 |
func = cone.validation.schemavalidation.validate_implml_data
|
|
148 |
for file in options.implml_file:
|
|
149 |
problems.extend(validate_file(file, func))
|
|
150 |
else:
|
|
151 |
problems = validate_configuration(options.project, options.configuration, pt_filter)
|
|
152 |
|
|
153 |
if problems is not None:
|
|
154 |
filters = {'filter_by_severity':filter_by_severity}
|
|
155 |
problems = pt_filter.filter(problems)
|
|
156 |
|
|
157 |
print "Total %d problem(s) after filtering" % len(problems)
|
|
158 |
|
|
159 |
print "Generating report..."
|
|
160 |
problems.sort(key=lambda p: (p.severity, p.file, p.line))
|
|
161 |
template, report = shortcut_container.determine_template_and_report(
|
|
162 |
options.report_type,
|
|
163 |
options.template,
|
|
164 |
options.report,
|
|
165 |
'validation_report')
|
|
166 |
report_util.generate_report(template, report, {'problems': problems},extra_filters=filters)
|
|
167 |
|
|
168 |
def extend_without_duplicates(source, target):
|
|
169 |
for item in source:
|
|
170 |
if item not in target:
|
|
171 |
target.append(item)
|
|
172 |
|
|
173 |
def validate_file(filename, validator_func):
|
|
174 |
if not os.path.isfile(filename):
|
|
175 |
print "'%s' does not exist or is not a file!" % filename
|
|
176 |
return
|
|
177 |
else:
|
|
178 |
print "Validating file '%s'" % filename
|
|
179 |
|
|
180 |
f = open(filename, 'rb')
|
|
181 |
try: data = f.read()
|
|
182 |
finally: f.close()
|
|
183 |
|
|
184 |
problems = []
|
|
185 |
try:
|
|
186 |
validator_func(data)
|
|
187 |
except exceptions.ParseError, e:
|
|
188 |
problems.append(api.Problem.from_exception(e))
|
|
189 |
print "Found %d problem(s)" % len(problems)
|
|
190 |
|
|
191 |
for p in problems:
|
|
192 |
p.file = filename
|
|
193 |
return problems
|
|
194 |
|
|
195 |
def validate_configuration(project_path, config_path, problem_type_filter):
|
|
196 |
print "Project: %s" % project_path
|
|
197 |
try:
|
|
198 |
project = api.Project(api.Storage.open(project_path, 'r'))
|
|
199 |
except exceptions.StorageException:
|
|
200 |
print "No such project."
|
|
201 |
return
|
|
202 |
|
|
203 |
confml_parse_context = cone.validation.parsecontext.ValidationParseContext()
|
|
204 |
parsecontext.set_confml_context(confml_parse_context)
|
|
205 |
|
|
206 |
if config_path is None:
|
|
207 |
config_path = project.get_storage().get_active_configuration()
|
|
208 |
if config_path is None:
|
|
209 |
print "Project does not have an active configuration, please specify the configuration using --configuration."
|
|
210 |
return
|
|
211 |
print "Configuration: %s" % config_path
|
|
212 |
|
|
213 |
try:
|
|
214 |
config = project.get_configuration(config_path)
|
|
215 |
except exceptions.NotFound:
|
|
216 |
print "No such configuration in project."
|
|
217 |
return
|
|
218 |
|
|
219 |
result = []
|
|
220 |
|
|
221 |
|
|
222 |
print "Finding ConfML files in configuration..."
|
|
223 |
configs = config._traverse(type=api.Configuration)
|
|
224 |
print "%d ConfML file(s)" % len(configs)
|
|
225 |
print "%d problem(s) while parsing" % len(confml_parse_context.problems)
|
|
226 |
result.extend(confml_parse_context.problems)
|
|
227 |
|
|
228 |
# Schema-validate ConfML files if not filtered out
|
|
229 |
if problem_type_filter.match('schema.confml'):
|
|
230 |
print "Performing XML schema validation on ConfML files..."
|
|
231 |
schema_problems = cone.validation.schemavalidation.validate_confml_file(config, config_path)
|
|
232 |
for conf in configs:
|
|
233 |
path = conf.get_full_path()
|
|
234 |
problems = cone.validation.schemavalidation.validate_confml_file(config, path)
|
|
235 |
schema_problems.extend(problems)
|
|
236 |
print "%d problem(s)" % len(schema_problems)
|
|
237 |
|
|
238 |
# Add the schema problems with duplicates removed, since XML parse
|
|
239 |
# errors might have already been recorded in the ConfML parsing phase
|
|
240 |
extend_without_duplicates(source=schema_problems, target=result)
|
|
241 |
|
|
242 |
|
|
243 |
# Validate the ConfML model if not filtered out
|
|
244 |
validator_classes = cone.validation.confmlvalidation.get_validator_classes(problem_type_filter)
|
|
245 |
if validator_classes:
|
|
246 |
print "Validating ConfML model..."
|
|
247 |
model_problems = cone.validation.confmlvalidation.validate_configuration(config, validator_classes).problems
|
|
248 |
print "%d problem(s)" % len(model_problems)
|
|
249 |
result.extend(model_problems)
|
|
250 |
|
|
251 |
|
|
252 |
print "Finding ImplML files in configuration..."
|
|
253 |
impls = []
|
|
254 |
for file in config.get_layer().list_implml():
|
|
255 |
if plugin.ImplFactory.is_supported_impl_file(file):
|
|
256 |
impls.append(file)
|
|
257 |
print "Found %d supported files" % len(impls)
|
|
258 |
|
|
259 |
# Schema-validate ImplML files if not filtered out
|
|
260 |
if problem_type_filter.match('schema.implml'):
|
|
261 |
print "Performing XML schema validation on ImplML files..."
|
|
262 |
schema_problems = []
|
|
263 |
for impl in impls:
|
|
264 |
probs = cone.validation.schemavalidation.validate_implml_file(config, impl)
|
|
265 |
schema_problems.extend(probs)
|
|
266 |
print "%d problem(s)" % len(schema_problems)
|
|
267 |
result.extend(schema_problems)
|
|
268 |
|
|
269 |
# Validate the ImplML model if not filtered out
|
|
270 |
if problem_type_filter.match('model.implml'):
|
|
271 |
print "Parsing implementations..."
|
|
272 |
implml_parse_context = cone.validation.parsecontext.ValidationParseContext()
|
|
273 |
parsecontext.set_implml_context(implml_parse_context)
|
|
274 |
impl_set = plugin.create_impl_set(impls, config)
|
|
275 |
|
|
276 |
# Add the model-level problems with duplicates removed, since XML parse
|
|
277 |
# errors might have already been recorded in the schema validation phase
|
|
278 |
extend_without_duplicates(source=implml_parse_context.problems, target=result)
|
|
279 |
|
|
280 |
|
|
281 |
validator_classes = cone.validation.implmlvalidation.get_validator_classes(problem_type_filter)
|
|
282 |
if validator_classes:
|
|
283 |
print "Validating implementations..."
|
|
284 |
impl_problems = cone.validation.implmlvalidation.validate_impl_set(impl_set, config, validator_classes)
|
|
285 |
print "%d problem(s)" % len(impl_problems)
|
|
286 |
result.extend(impl_problems)
|
|
287 |
|
|
288 |
return result
|
|
289 |
|
|
290 |
|
|
291 |
def filter_by_severity(problems, severity):
|
|
292 |
"""
|
|
293 |
Filter problems by severity
|
|
294 |
"""
|
|
295 |
return [p for p in problems if p.severity == severity]
|
|
296 |
|
|
297 |
if __name__ == "__main__":
|
|
298 |
main()
|