|
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() |