1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.maven.model.validation;
20
21 import javax.inject.Inject;
22 import javax.inject.Named;
23 import javax.inject.Singleton;
24
25 import java.io.File;
26 import java.util.Arrays;
27 import java.util.Deque;
28 import java.util.HashMap;
29 import java.util.HashSet;
30 import java.util.Iterator;
31 import java.util.LinkedList;
32 import java.util.List;
33 import java.util.Map;
34 import java.util.Objects;
35 import java.util.Optional;
36 import java.util.Set;
37 import java.util.function.Consumer;
38 import java.util.function.Supplier;
39 import java.util.regex.Matcher;
40 import java.util.regex.Pattern;
41 import java.util.stream.Collectors;
42 import java.util.stream.StreamSupport;
43
44 import org.apache.maven.model.Activation;
45 import org.apache.maven.model.Build;
46 import org.apache.maven.model.BuildBase;
47 import org.apache.maven.model.Dependency;
48 import org.apache.maven.model.DependencyManagement;
49 import org.apache.maven.model.DistributionManagement;
50 import org.apache.maven.model.Exclusion;
51 import org.apache.maven.model.InputLocation;
52 import org.apache.maven.model.InputLocationTracker;
53 import org.apache.maven.model.Model;
54 import org.apache.maven.model.Parent;
55 import org.apache.maven.model.Plugin;
56 import org.apache.maven.model.PluginExecution;
57 import org.apache.maven.model.PluginManagement;
58 import org.apache.maven.model.Profile;
59 import org.apache.maven.model.ReportPlugin;
60 import org.apache.maven.model.Reporting;
61 import org.apache.maven.model.Repository;
62 import org.apache.maven.model.Resource;
63 import org.apache.maven.model.building.ModelBuildingRequest;
64 import org.apache.maven.model.building.ModelProblem.Severity;
65 import org.apache.maven.model.building.ModelProblem.Version;
66 import org.apache.maven.model.building.ModelProblemCollector;
67 import org.apache.maven.model.building.ModelProblemCollectorRequest;
68 import org.apache.maven.model.interpolation.ModelVersionProcessor;
69 import org.codehaus.plexus.util.StringUtils;
70
71
72
73
74 @Named
75 @Singleton
76 public class DefaultModelValidator implements ModelValidator {
77
78 private static final Pattern CI_FRIENDLY_EXPRESSION = Pattern.compile("\\$\\{(.+?)}");
79 private static final Pattern EXPRESSION_PROJECT_NAME_PATTERN = Pattern.compile("\\$\\{(project.+?)}");
80
81 private static final String ILLEGAL_FS_CHARS = "\\/:\"<>|?*";
82
83 private static final String ILLEGAL_VERSION_CHARS = ILLEGAL_FS_CHARS;
84
85 private static final String ILLEGAL_REPO_ID_CHARS = ILLEGAL_FS_CHARS;
86
87 private static final String EMPTY = "";
88
89 private final Set<String> validIds = new HashSet<>();
90
91 private ModelVersionProcessor versionProcessor;
92
93 @Inject
94 public DefaultModelValidator(ModelVersionProcessor versionProcessor) {
95 this.versionProcessor = versionProcessor;
96 }
97
98 @Override
99 public void validateRawModel(Model m, ModelBuildingRequest request, ModelProblemCollector problems) {
100 Parent parent = m.getParent();
101 if (parent != null) {
102 validateStringNotEmpty(
103 "parent.groupId", problems, Severity.FATAL, Version.BASE, parent.getGroupId(), parent);
104
105 validateStringNotEmpty(
106 "parent.artifactId", problems, Severity.FATAL, Version.BASE, parent.getArtifactId(), parent);
107
108 validateStringNotEmpty(
109 "parent.version", problems, Severity.FATAL, Version.BASE, parent.getVersion(), parent);
110
111 if (equals(parent.getGroupId(), m.getGroupId()) && equals(parent.getArtifactId(), m.getArtifactId())) {
112 addViolation(
113 problems,
114 Severity.FATAL,
115 Version.BASE,
116 "parent.artifactId",
117 null,
118 "must be changed"
119 + ", the parent element cannot have the same groupId:artifactId as the project.",
120 parent);
121 }
122
123 if (equals("LATEST", parent.getVersion()) || equals("RELEASE", parent.getVersion())) {
124 addViolation(
125 problems,
126 Severity.WARNING,
127 Version.BASE,
128 "parent.version",
129 null,
130 "is either LATEST or RELEASE (both of them are being deprecated)",
131 parent);
132 }
133 }
134
135 if (request.getValidationLevel() >= ModelBuildingRequest.VALIDATION_LEVEL_MAVEN_2_0) {
136 Severity errOn30 = getSeverity(request, ModelBuildingRequest.VALIDATION_LEVEL_MAVEN_3_0);
137
138
139
140
141
142
143
144 validateStringNotEmpty("modelVersion", problems, Severity.ERROR, Version.V20, m.getModelVersion(), m);
145
146 validateModelVersion(problems, m.getModelVersion(), m, "4.0.0");
147
148 validateStringNoExpression("groupId", problems, Severity.WARNING, Version.V20, m.getGroupId(), m);
149 if (parent == null) {
150 validateStringNotEmpty("groupId", problems, Severity.FATAL, Version.V20, m.getGroupId(), m);
151 }
152
153 validateStringNoExpression("artifactId", problems, Severity.WARNING, Version.V20, m.getArtifactId(), m);
154 validateStringNotEmpty("artifactId", problems, Severity.FATAL, Version.V20, m.getArtifactId(), m);
155
156 validateVersionNoExpression("version", problems, Severity.WARNING, Version.V20, m.getVersion(), m);
157 if (parent == null) {
158 validateStringNotEmpty("version", problems, Severity.FATAL, Version.V20, m.getVersion(), m);
159 }
160
161 validate20RawDependencies(problems, m.getDependencies(), "dependencies.dependency.", EMPTY, request);
162
163 validate20RawDependenciesSelfReferencing(
164 problems, m, m.getDependencies(), "dependencies.dependency", request);
165
166 if (m.getDependencyManagement() != null) {
167 validate20RawDependencies(
168 problems,
169 m.getDependencyManagement().getDependencies(),
170 "dependencyManagement.dependencies.dependency.",
171 EMPTY,
172 request);
173 }
174
175 validateRawRepositories(problems, m.getRepositories(), "repositories.repository.", EMPTY, request);
176
177 validateRawRepositories(
178 problems, m.getPluginRepositories(), "pluginRepositories.pluginRepository.", EMPTY, request);
179
180 Build build = m.getBuild();
181 if (build != null) {
182 validate20RawPlugins(problems, build.getPlugins(), "build.plugins.plugin.", EMPTY, request);
183
184 PluginManagement mgmt = build.getPluginManagement();
185 if (mgmt != null) {
186 validate20RawPlugins(
187 problems, mgmt.getPlugins(), "build.pluginManagement.plugins.plugin.", EMPTY, request);
188 }
189 }
190
191 Set<String> profileIds = new HashSet<>();
192
193 for (Profile profile : m.getProfiles()) {
194 String prefix = "profiles.profile[" + profile.getId() + "].";
195
196 if (!profileIds.add(profile.getId())) {
197 addViolation(
198 problems,
199 errOn30,
200 Version.V20,
201 "profiles.profile.id",
202 null,
203 "must be unique but found duplicate profile with id " + profile.getId(),
204 profile);
205 }
206
207 validate30RawProfileActivation(problems, profile.getActivation(), prefix);
208
209 validate20RawDependencies(
210 problems, profile.getDependencies(), prefix, "dependencies.dependency.", request);
211
212 if (profile.getDependencyManagement() != null) {
213 validate20RawDependencies(
214 problems,
215 profile.getDependencyManagement().getDependencies(),
216 prefix,
217 "dependencyManagement.dependencies.dependency.",
218 request);
219 }
220
221 validateRawRepositories(
222 problems, profile.getRepositories(), prefix, "repositories.repository.", request);
223
224 validateRawRepositories(
225 problems,
226 profile.getPluginRepositories(),
227 prefix,
228 "pluginRepositories.pluginRepository.",
229 request);
230
231 BuildBase buildBase = profile.getBuild();
232 if (buildBase != null) {
233 validate20RawPlugins(problems, buildBase.getPlugins(), prefix, "plugins.plugin.", request);
234
235 PluginManagement mgmt = buildBase.getPluginManagement();
236 if (mgmt != null) {
237 validate20RawPlugins(
238 problems, mgmt.getPlugins(), prefix, "pluginManagement.plugins.plugin.", request);
239 }
240 }
241 }
242 }
243 }
244
245 private void validate30RawProfileActivation(ModelProblemCollector problems, Activation activation, String prefix) {
246 if (activation == null) {
247 return;
248 }
249 class ActivationFrame {
250 String location;
251 Optional<? extends InputLocationTracker> parent;
252
253 ActivationFrame(String location, Optional<? extends InputLocationTracker> parent) {
254 this.location = location;
255 this.parent = parent;
256 }
257 }
258 final Deque<ActivationFrame> stk = new LinkedList<>();
259
260 final Supplier<String> pathSupplier = () -> {
261 final boolean parallel = false;
262 return StreamSupport.stream(((Iterable<ActivationFrame>) stk::descendingIterator).spliterator(), parallel)
263 .map(f -> f.location)
264 .collect(Collectors.joining("."));
265 };
266 final Supplier<InputLocation> locationSupplier = () -> {
267 if (stk.size() < 2) {
268 return null;
269 }
270 Iterator<ActivationFrame> f = stk.iterator();
271
272 String location = f.next().location;
273 ActivationFrame parent = f.next();
274
275 return parent.parent.map(p -> p.getLocation(location)).orElse(null);
276 };
277 final Consumer<String> validator = s -> {
278 if (hasProjectExpression(s)) {
279 String path = pathSupplier.get();
280 Matcher matcher = EXPRESSION_PROJECT_NAME_PATTERN.matcher(s);
281 while (matcher.find()) {
282 String propertyName = matcher.group(0);
283
284 if (path.startsWith("activation.file.") && "${project.basedir}".equals(propertyName)) {
285 continue;
286 }
287 addViolation(
288 problems,
289 Severity.WARNING,
290 Version.V30,
291 prefix + path,
292 null,
293 "Failed to interpolate profile activation property " + s + ": " + propertyName
294 + " expressions are not supported during profile activation.",
295 locationSupplier.get());
296 }
297 }
298 };
299 Optional<Activation> root = Optional.of(activation);
300 stk.push(new ActivationFrame("activation", root));
301 root.map(Activation::getFile).ifPresent(fa -> {
302 stk.push(new ActivationFrame("file", Optional.of(fa)));
303 stk.push(new ActivationFrame("exists", Optional.empty()));
304 validator.accept(fa.getExists());
305 stk.peek().location = "missing";
306 validator.accept(fa.getMissing());
307 stk.pop();
308 stk.pop();
309 });
310 root.map(Activation::getOs).ifPresent(oa -> {
311 stk.push(new ActivationFrame("os", Optional.of(oa)));
312 stk.push(new ActivationFrame("arch", Optional.empty()));
313 validator.accept(oa.getArch());
314 stk.peek().location = "family";
315 validator.accept(oa.getFamily());
316 stk.peek().location = "name";
317 validator.accept(oa.getName());
318 stk.peek().location = "version";
319 validator.accept(oa.getVersion());
320 stk.pop();
321 stk.pop();
322 });
323 root.map(Activation::getProperty).ifPresent(pa -> {
324 stk.push(new ActivationFrame("property", Optional.of(pa)));
325 stk.push(new ActivationFrame("name", Optional.empty()));
326 validator.accept(pa.getName());
327 stk.peek().location = "value";
328 validator.accept(pa.getValue());
329 stk.pop();
330 stk.pop();
331 });
332 root.map(Activation::getJdk).ifPresent(jdk -> {
333 stk.push(new ActivationFrame("jdk", Optional.empty()));
334 validator.accept(jdk);
335 stk.pop();
336 });
337 }
338
339 private void validate20RawPlugins(
340 ModelProblemCollector problems,
341 List<Plugin> plugins,
342 String prefix,
343 String prefix2,
344 ModelBuildingRequest request) {
345 Severity errOn31 = getSeverity(request, ModelBuildingRequest.VALIDATION_LEVEL_MAVEN_3_1);
346
347 Map<String, Plugin> index = new HashMap<>();
348
349 for (Plugin plugin : plugins) {
350 if (plugin.getGroupId() == null
351 || (plugin.getGroupId() != null
352 && plugin.getGroupId().trim().isEmpty())) {
353 addViolation(
354 problems,
355 Severity.FATAL,
356 Version.V20,
357 prefix + prefix2 + "(groupId:artifactId)",
358 null,
359 "groupId of a plugin must be defined. ",
360 plugin);
361 }
362
363 if (plugin.getArtifactId() == null
364 || (plugin.getArtifactId() != null
365 && plugin.getArtifactId().trim().isEmpty())) {
366 addViolation(
367 problems,
368 Severity.FATAL,
369 Version.V20,
370 prefix + prefix2 + "(groupId:artifactId)",
371 null,
372 "artifactId of a plugin must be defined. ",
373 plugin);
374 }
375
376
377 if (plugin.getVersion() != null && plugin.getVersion().trim().isEmpty()) {
378 addViolation(
379 problems,
380 Severity.FATAL,
381 Version.V20,
382 prefix + prefix2 + "(groupId:artifactId)",
383 null,
384 "version of a plugin must be defined. ",
385 plugin);
386 }
387
388 String key = plugin.getKey();
389
390 Plugin existing = index.get(key);
391
392 if (existing != null) {
393 addViolation(
394 problems,
395 errOn31,
396 Version.V20,
397 prefix + prefix2 + "(groupId:artifactId)",
398 null,
399 "must be unique but found duplicate declaration of plugin " + key,
400 plugin);
401 } else {
402 index.put(key, plugin);
403 }
404
405 Set<String> executionIds = new HashSet<>();
406
407 for (PluginExecution exec : plugin.getExecutions()) {
408 if (!executionIds.add(exec.getId())) {
409 addViolation(
410 problems,
411 Severity.ERROR,
412 Version.V20,
413 prefix + prefix2 + "[" + plugin.getKey() + "].executions.execution.id",
414 null,
415 "must be unique but found duplicate execution with id " + exec.getId(),
416 exec);
417 }
418 }
419 }
420 }
421
422 @Override
423 @SuppressWarnings("checkstyle:MethodLength")
424 public void validateEffectiveModel(Model m, ModelBuildingRequest request, ModelProblemCollector problems) {
425 validateStringNotEmpty("modelVersion", problems, Severity.ERROR, Version.BASE, m.getModelVersion(), m);
426
427 validateId("groupId", problems, m.getGroupId(), m);
428
429 validateId("artifactId", problems, m.getArtifactId(), m);
430
431 validateStringNotEmpty("packaging", problems, Severity.ERROR, Version.BASE, m.getPackaging(), m);
432
433 if (!m.getModules().isEmpty()) {
434 if (!"pom".equals(m.getPackaging())) {
435 addViolation(
436 problems,
437 Severity.ERROR,
438 Version.BASE,
439 "packaging",
440 null,
441 "with value '" + m.getPackaging() + "' is invalid. Aggregator projects "
442 + "require 'pom' as packaging.",
443 m);
444 }
445
446 for (int i = 0, n = m.getModules().size(); i < n; i++) {
447 String module = m.getModules().get(i);
448 if (StringUtils.isBlank(module)) {
449 addViolation(
450 problems,
451 Severity.ERROR,
452 Version.BASE,
453 "modules.module[" + i + "]",
454 null,
455 "has been specified without a path to the project directory.",
456 m.getLocation("modules"));
457 }
458 }
459 }
460
461 validateStringNotEmpty("version", problems, Severity.ERROR, Version.BASE, m.getVersion(), m);
462
463 Severity errOn30 = getSeverity(request, ModelBuildingRequest.VALIDATION_LEVEL_MAVEN_3_0);
464
465 validateEffectiveDependencies(problems, m, m.getDependencies(), false, request);
466
467 DependencyManagement mgmt = m.getDependencyManagement();
468 if (mgmt != null) {
469 validateEffectiveDependencies(problems, m, mgmt.getDependencies(), true, request);
470 }
471
472 if (request.getValidationLevel() >= ModelBuildingRequest.VALIDATION_LEVEL_MAVEN_2_0) {
473 Set<String> modules = new HashSet<>();
474 for (int i = 0, n = m.getModules().size(); i < n; i++) {
475 String module = m.getModules().get(i);
476 if (!modules.add(module)) {
477 addViolation(
478 problems,
479 Severity.ERROR,
480 Version.V20,
481 "modules.module[" + i + "]",
482 null,
483 "specifies duplicate child module " + module,
484 m.getLocation("modules"));
485 }
486 }
487
488 Severity errOn31 = getSeverity(request, ModelBuildingRequest.VALIDATION_LEVEL_MAVEN_3_1);
489
490 validateBannedCharacters(
491 EMPTY, "version", problems, errOn31, Version.V20, m.getVersion(), null, m, ILLEGAL_VERSION_CHARS);
492 validate20ProperSnapshotVersion("version", problems, errOn31, Version.V20, m.getVersion(), null, m);
493
494 Build build = m.getBuild();
495 if (build != null) {
496 for (Plugin p : build.getPlugins()) {
497 validateStringNotEmpty(
498 "build.plugins.plugin.artifactId",
499 problems,
500 Severity.ERROR,
501 Version.V20,
502 p.getArtifactId(),
503 p);
504
505 validateStringNotEmpty(
506 "build.plugins.plugin.groupId", problems, Severity.ERROR, Version.V20, p.getGroupId(), p);
507
508 validate20PluginVersion(
509 "build.plugins.plugin.version", problems, p.getVersion(), p.getKey(), p, request);
510
511 validateBoolean(
512 "build.plugins.plugin.inherited",
513 EMPTY,
514 problems,
515 errOn30,
516 Version.V20,
517 p.getInherited(),
518 p.getKey(),
519 p);
520
521 validateBoolean(
522 "build.plugins.plugin.extensions",
523 EMPTY,
524 problems,
525 errOn30,
526 Version.V20,
527 p.getExtensions(),
528 p.getKey(),
529 p);
530
531 validate20EffectivePluginDependencies(problems, p, request);
532 }
533
534 validate20RawResources(problems, build.getResources(), "build.resources.resource.", request);
535
536 validate20RawResources(
537 problems, build.getTestResources(), "build.testResources.testResource.", request);
538 }
539
540 Reporting reporting = m.getReporting();
541 if (reporting != null) {
542 for (ReportPlugin p : reporting.getPlugins()) {
543 validateStringNotEmpty(
544 "reporting.plugins.plugin.artifactId",
545 problems,
546 Severity.ERROR,
547 Version.V20,
548 p.getArtifactId(),
549 p);
550
551 validateStringNotEmpty(
552 "reporting.plugins.plugin.groupId",
553 problems,
554 Severity.ERROR,
555 Version.V20,
556 p.getGroupId(),
557 p);
558 }
559 }
560
561 for (Repository repository : m.getRepositories()) {
562 validate20EffectiveRepository(problems, repository, "repositories.repository.", request);
563 }
564
565 for (Repository repository : m.getPluginRepositories()) {
566 validate20EffectiveRepository(problems, repository, "pluginRepositories.pluginRepository.", request);
567 }
568
569 DistributionManagement distMgmt = m.getDistributionManagement();
570 if (distMgmt != null) {
571 if (distMgmt.getStatus() != null) {
572 addViolation(
573 problems,
574 Severity.ERROR,
575 Version.V20,
576 "distributionManagement.status",
577 null,
578 "must not be specified.",
579 distMgmt);
580 }
581
582 validate20EffectiveRepository(
583 problems, distMgmt.getRepository(), "distributionManagement.repository.", request);
584 validate20EffectiveRepository(
585 problems,
586 distMgmt.getSnapshotRepository(),
587 "distributionManagement.snapshotRepository.",
588 request);
589 }
590 }
591 }
592
593 private void validate20RawDependencies(
594 ModelProblemCollector problems,
595 List<Dependency> dependencies,
596 String prefix,
597 String prefix2,
598 ModelBuildingRequest request) {
599 Severity errOn30 = getSeverity(request, ModelBuildingRequest.VALIDATION_LEVEL_MAVEN_3_0);
600 Severity errOn31 = getSeverity(request, ModelBuildingRequest.VALIDATION_LEVEL_MAVEN_3_1);
601
602 Map<String, Dependency> index = new HashMap<>();
603
604 for (Dependency dependency : dependencies) {
605 String key = dependency.getManagementKey();
606
607 if ("import".equals(dependency.getScope())) {
608 if (!"pom".equals(dependency.getType())) {
609 addViolation(
610 problems,
611 Severity.WARNING,
612 Version.V20,
613 prefix + prefix2 + "type",
614 key,
615 "must be 'pom' to import the managed dependencies.",
616 dependency);
617 } else if (StringUtils.isNotEmpty(dependency.getClassifier())) {
618 addViolation(
619 problems,
620 errOn30,
621 Version.V20,
622 prefix + prefix2 + "classifier",
623 key,
624 "must be empty, imported POM cannot have a classifier.",
625 dependency);
626 }
627 } else if ("system".equals(dependency.getScope())) {
628
629 if (request.getValidationLevel() >= ModelBuildingRequest.VALIDATION_LEVEL_MAVEN_3_1) {
630 addViolation(
631 problems,
632 Severity.WARNING,
633 Version.V31,
634 prefix + prefix2 + "scope",
635 key,
636 "declares usage of deprecated 'system' scope ",
637 dependency);
638 }
639
640 String sysPath = dependency.getSystemPath();
641 if (StringUtils.isNotEmpty(sysPath)) {
642 if (!hasExpression(sysPath)) {
643 addViolation(
644 problems,
645 Severity.WARNING,
646 Version.V20,
647 prefix + prefix2 + "systemPath",
648 key,
649 "should use a variable instead of a hard-coded path " + sysPath,
650 dependency);
651 } else if (sysPath.contains("${basedir}") || sysPath.contains("${project.basedir}")) {
652 addViolation(
653 problems,
654 Severity.WARNING,
655 Version.V20,
656 prefix + prefix2 + "systemPath",
657 key,
658 "should not point at files within the project directory, " + sysPath
659 + " will be unresolvable by dependent projects",
660 dependency);
661 }
662 }
663 }
664
665 if (equals("LATEST", dependency.getVersion()) || equals("RELEASE", dependency.getVersion())) {
666 addViolation(
667 problems,
668 Severity.WARNING,
669 Version.BASE,
670 prefix + prefix2 + "version",
671 key,
672 "is either LATEST or RELEASE (both of them are being deprecated)",
673 dependency);
674 }
675
676 Dependency existing = index.get(key);
677
678 if (existing != null) {
679 String msg;
680 if (equals(existing.getVersion(), dependency.getVersion())) {
681 msg = "duplicate declaration of version " + Objects.toString(dependency.getVersion(), "(?)");
682 } else {
683 msg = "version " + Objects.toString(existing.getVersion(), "(?)") + " vs "
684 + Objects.toString(dependency.getVersion(), "(?)");
685 }
686
687 addViolation(
688 problems,
689 errOn31,
690 Version.V20,
691 prefix + prefix2 + "(groupId:artifactId:type:classifier)",
692 null,
693 "must be unique: " + key + " -> " + msg,
694 dependency);
695 } else {
696 index.put(key, dependency);
697 }
698 }
699 }
700
701 private void validate20RawDependenciesSelfReferencing(
702 ModelProblemCollector problems,
703 Model m,
704 List<Dependency> dependencies,
705 String prefix,
706 ModelBuildingRequest request) {
707
708
709
710
711
712 for (Dependency dependency : dependencies) {
713 String key = dependency.getGroupId() + ":" + dependency.getArtifactId() + ":" + dependency.getVersion()
714 + (dependency.getClassifier() != null ? ":" + dependency.getClassifier() : EMPTY);
715 String mKey = m.getGroupId() + ":" + m.getArtifactId() + ":" + m.getVersion();
716 if (key.equals(mKey)) {
717
718
719
720 addViolation(
721 problems,
722 Severity.FATAL,
723 Version.V31,
724 prefix + "[" + key + "]",
725 key,
726 "is referencing itself.",
727 dependency);
728 }
729 }
730 }
731
732 private void validateEffectiveDependencies(
733 ModelProblemCollector problems,
734 Model m,
735 List<Dependency> dependencies,
736 boolean management,
737 ModelBuildingRequest request) {
738 Severity errOn30 = getSeverity(request, ModelBuildingRequest.VALIDATION_LEVEL_MAVEN_3_0);
739
740 String prefix = management ? "dependencyManagement.dependencies.dependency." : "dependencies.dependency.";
741
742 for (Dependency d : dependencies) {
743 validateEffectiveDependency(problems, d, management, prefix, request);
744
745 if (request.getValidationLevel() >= ModelBuildingRequest.VALIDATION_LEVEL_MAVEN_2_0) {
746 validateBoolean(
747 prefix, "optional", problems, errOn30, Version.V20, d.getOptional(), d.getManagementKey(), d);
748
749 if (!management) {
750 validateVersion(
751 prefix, "version", problems, errOn30, Version.V20, d.getVersion(), d.getManagementKey(), d);
752
753
754
755
756
757 validateEnum(
758 prefix,
759 "scope",
760 problems,
761 Severity.WARNING,
762 Version.V20,
763 d.getScope(),
764 d.getManagementKey(),
765 d,
766 "provided",
767 "compile",
768 "runtime",
769 "test",
770 "system");
771
772 validateEffectiveModelAgainstDependency(prefix, problems, m, d, request);
773 } else {
774 validateEnum(
775 prefix,
776 "scope",
777 problems,
778 Severity.WARNING,
779 Version.V20,
780 d.getScope(),
781 d.getManagementKey(),
782 d,
783 "provided",
784 "compile",
785 "runtime",
786 "test",
787 "system",
788 "import");
789 }
790 }
791 }
792 }
793
794 private void validateEffectiveModelAgainstDependency(
795 String prefix, ModelProblemCollector problems, Model m, Dependency d, ModelBuildingRequest request) {
796 String key = d.getGroupId() + ":" + d.getArtifactId() + ":" + d.getVersion()
797 + (d.getClassifier() != null ? ":" + d.getClassifier() : EMPTY);
798 String mKey = m.getGroupId() + ":" + m.getArtifactId() + ":" + m.getVersion();
799 if (key.equals(mKey)) {
800
801
802
803 addViolation(
804 problems, Severity.FATAL, Version.V31, prefix + "[" + key + "]", key, "is referencing itself.", d);
805 }
806 }
807
808 private void validate20EffectivePluginDependencies(
809 ModelProblemCollector problems, Plugin plugin, ModelBuildingRequest request) {
810 List<Dependency> dependencies = plugin.getDependencies();
811
812 if (!dependencies.isEmpty()) {
813 String prefix = "build.plugins.plugin[" + plugin.getKey() + "].dependencies.dependency.";
814
815 Severity errOn30 = getSeverity(request, ModelBuildingRequest.VALIDATION_LEVEL_MAVEN_3_0);
816
817 for (Dependency d : dependencies) {
818 validateEffectiveDependency(problems, d, false, prefix, request);
819
820 validateVersion(
821 prefix, "version", problems, errOn30, Version.BASE, d.getVersion(), d.getManagementKey(), d);
822
823 validateEnum(
824 prefix,
825 "scope",
826 problems,
827 errOn30,
828 Version.BASE,
829 d.getScope(),
830 d.getManagementKey(),
831 d,
832 "compile",
833 "runtime",
834 "system");
835 }
836 }
837 }
838
839 private void validateEffectiveDependency(
840 ModelProblemCollector problems,
841 Dependency d,
842 boolean management,
843 String prefix,
844 ModelBuildingRequest request) {
845 validateId(
846 prefix,
847 "artifactId",
848 problems,
849 Severity.ERROR,
850 Version.BASE,
851 d.getArtifactId(),
852 d.getManagementKey(),
853 d);
854
855 validateId(prefix, "groupId", problems, Severity.ERROR, Version.BASE, d.getGroupId(), d.getManagementKey(), d);
856
857 if (!management) {
858 validateStringNotEmpty(
859 prefix, "type", problems, Severity.ERROR, Version.BASE, d.getType(), d.getManagementKey(), d);
860
861 validateDependencyVersion(problems, d, prefix);
862 }
863
864 if ("system".equals(d.getScope())) {
865 String systemPath = d.getSystemPath();
866
867 if (StringUtils.isEmpty(systemPath)) {
868 addViolation(
869 problems,
870 Severity.ERROR,
871 Version.BASE,
872 prefix + "systemPath",
873 d.getManagementKey(),
874 "is missing.",
875 d);
876 } else {
877 File sysFile = new File(systemPath);
878 if (!sysFile.isAbsolute()) {
879 addViolation(
880 problems,
881 Severity.ERROR,
882 Version.BASE,
883 prefix + "systemPath",
884 d.getManagementKey(),
885 "must specify an absolute path but is " + systemPath,
886 d);
887 } else if (!sysFile.isFile()) {
888 String msg = "refers to a non-existing file " + sysFile.getAbsolutePath();
889 systemPath = systemPath.replace('/', File.separatorChar).replace('\\', File.separatorChar);
890 String jdkHome =
891 request.getSystemProperties().getProperty("java.home", EMPTY) + File.separator + "..";
892 if (systemPath.startsWith(jdkHome)) {
893 msg += ". Please verify that you run Maven using a JDK and not just a JRE.";
894 }
895 addViolation(
896 problems,
897 Severity.WARNING,
898 Version.BASE,
899 prefix + "systemPath",
900 d.getManagementKey(),
901 msg,
902 d);
903 }
904 }
905 } else if (StringUtils.isNotEmpty(d.getSystemPath())) {
906 addViolation(
907 problems,
908 Severity.ERROR,
909 Version.BASE,
910 prefix + "systemPath",
911 d.getManagementKey(),
912 "must be omitted." + " This field may only be specified for a dependency with system scope.",
913 d);
914 }
915
916 if (request.getValidationLevel() >= ModelBuildingRequest.VALIDATION_LEVEL_MAVEN_2_0) {
917 for (Exclusion exclusion : d.getExclusions()) {
918 if (request.getValidationLevel() < ModelBuildingRequest.VALIDATION_LEVEL_MAVEN_3_0) {
919 validateId(
920 prefix,
921 "exclusions.exclusion.groupId",
922 problems,
923 Severity.WARNING,
924 Version.V20,
925 exclusion.getGroupId(),
926 d.getManagementKey(),
927 exclusion);
928
929 validateId(
930 prefix,
931 "exclusions.exclusion.artifactId",
932 problems,
933 Severity.WARNING,
934 Version.V20,
935 exclusion.getArtifactId(),
936 d.getManagementKey(),
937 exclusion);
938 } else {
939 validateIdWithWildcards(
940 prefix,
941 "exclusions.exclusion.groupId",
942 problems,
943 Severity.WARNING,
944 Version.V30,
945 exclusion.getGroupId(),
946 d.getManagementKey(),
947 exclusion);
948
949 validateIdWithWildcards(
950 prefix,
951 "exclusions.exclusion.artifactId",
952 problems,
953 Severity.WARNING,
954 Version.V30,
955 exclusion.getArtifactId(),
956 d.getManagementKey(),
957 exclusion);
958 }
959 }
960 }
961 }
962
963
964
965
966 protected void validateDependencyVersion(ModelProblemCollector problems, Dependency d, String prefix) {
967 validateStringNotEmpty(
968 prefix, "version", problems, Severity.ERROR, Version.BASE, d.getVersion(), d.getManagementKey(), d);
969 }
970
971 private void validateRawRepositories(
972 ModelProblemCollector problems,
973 List<Repository> repositories,
974 String prefix,
975 String prefix2,
976 ModelBuildingRequest request) {
977 Map<String, Repository> index = new HashMap<>();
978
979 for (Repository repository : repositories) {
980 validateStringNotEmpty(
981 prefix, prefix2, "id", problems, Severity.ERROR, Version.V20, repository.getId(), null, repository);
982
983 validateStringNotEmpty(
984 prefix,
985 prefix2,
986 "[" + repository.getId() + "].url",
987 problems,
988 Severity.ERROR,
989 Version.V20,
990 repository.getUrl(),
991 null,
992 repository);
993
994 String key = repository.getId();
995
996 Repository existing = index.get(key);
997
998 if (existing != null) {
999 Severity errOn30 = getSeverity(request, ModelBuildingRequest.VALIDATION_LEVEL_MAVEN_3_0);
1000
1001 addViolation(
1002 problems,
1003 errOn30,
1004 Version.V20,
1005 prefix + prefix2 + "id",
1006 null,
1007 "must be unique: " + repository.getId() + " -> " + existing.getUrl() + " vs "
1008 + repository.getUrl(),
1009 repository);
1010 } else {
1011 index.put(key, repository);
1012 }
1013 }
1014 }
1015
1016 private void validate20EffectiveRepository(
1017 ModelProblemCollector problems, Repository repository, String prefix, ModelBuildingRequest request) {
1018 if (repository != null) {
1019 Severity errOn31 = getSeverity(request, ModelBuildingRequest.VALIDATION_LEVEL_MAVEN_3_1);
1020
1021 validateBannedCharacters(
1022 prefix,
1023 "id",
1024 problems,
1025 errOn31,
1026 Version.V20,
1027 repository.getId(),
1028 null,
1029 repository,
1030 ILLEGAL_REPO_ID_CHARS);
1031
1032 if ("local".equals(repository.getId())) {
1033 addViolation(
1034 problems,
1035 errOn31,
1036 Version.V20,
1037 prefix + "id",
1038 null,
1039 "must not be 'local'" + ", this identifier is reserved for the local repository"
1040 + ", using it for other repositories will corrupt your repository metadata.",
1041 repository);
1042 }
1043
1044 if ("legacy".equals(repository.getLayout())) {
1045 addViolation(
1046 problems,
1047 Severity.WARNING,
1048 Version.V20,
1049 prefix + "layout",
1050 repository.getId(),
1051 "uses the unsupported value 'legacy', artifact resolution might fail.",
1052 repository);
1053 }
1054 }
1055 }
1056
1057 private void validate20RawResources(
1058 ModelProblemCollector problems, List<Resource> resources, String prefix, ModelBuildingRequest request) {
1059 Severity errOn30 = getSeverity(request, ModelBuildingRequest.VALIDATION_LEVEL_MAVEN_3_0);
1060
1061 for (Resource resource : resources) {
1062 validateStringNotEmpty(
1063 prefix,
1064 "directory",
1065 problems,
1066 Severity.ERROR,
1067 Version.V20,
1068 resource.getDirectory(),
1069 null,
1070 resource);
1071
1072 validateBoolean(
1073 prefix,
1074 "filtering",
1075 problems,
1076 errOn30,
1077 Version.V20,
1078 resource.getFiltering(),
1079 resource.getDirectory(),
1080 resource);
1081 }
1082 }
1083
1084
1085
1086
1087
1088 private boolean validateId(
1089 String fieldName, ModelProblemCollector problems, String id, InputLocationTracker tracker) {
1090 return validateId(EMPTY, fieldName, problems, Severity.ERROR, Version.BASE, id, null, tracker);
1091 }
1092
1093 @SuppressWarnings("checkstyle:parameternumber")
1094 private boolean validateId(
1095 String prefix,
1096 String fieldName,
1097 ModelProblemCollector problems,
1098 Severity severity,
1099 Version version,
1100 String id,
1101 String sourceHint,
1102 InputLocationTracker tracker) {
1103 if (validIds.contains(id)) {
1104 return true;
1105 }
1106 if (!validateStringNotEmpty(prefix, fieldName, problems, severity, version, id, sourceHint, tracker)) {
1107 return false;
1108 } else {
1109 if (!isValidId(id)) {
1110 addViolation(
1111 problems,
1112 severity,
1113 version,
1114 prefix + fieldName,
1115 sourceHint,
1116 "with value '" + id + "' does not match a valid id pattern.",
1117 tracker);
1118 return false;
1119 }
1120 validIds.add(id);
1121 return true;
1122 }
1123 }
1124
1125 private boolean isValidId(String id) {
1126 for (int i = 0; i < id.length(); i++) {
1127 char c = id.charAt(i);
1128 if (!isValidIdCharacter(c)) {
1129 return false;
1130 }
1131 }
1132 return true;
1133 }
1134
1135 private boolean isValidIdCharacter(char c) {
1136 return c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' || c >= '0' && c <= '9' || c == '-' || c == '_' || c == '.';
1137 }
1138
1139 @SuppressWarnings("checkstyle:parameternumber")
1140 private boolean validateIdWithWildcards(
1141 String prefix,
1142 String fieldName,
1143 ModelProblemCollector problems,
1144 Severity severity,
1145 Version version,
1146 String id,
1147 String sourceHint,
1148 InputLocationTracker tracker) {
1149 if (!validateStringNotEmpty(prefix, fieldName, problems, severity, version, id, sourceHint, tracker)) {
1150 return false;
1151 } else {
1152 if (!isValidIdWithWildCards(id)) {
1153 addViolation(
1154 problems,
1155 severity,
1156 version,
1157 prefix + fieldName,
1158 sourceHint,
1159 "with value '" + id + "' does not match a valid id pattern.",
1160 tracker);
1161 return false;
1162 }
1163 return true;
1164 }
1165 }
1166
1167 private boolean isValidIdWithWildCards(String id) {
1168 for (int i = 0; i < id.length(); i++) {
1169 char c = id.charAt(i);
1170 if (!isValidIdWithWildCardCharacter(c)) {
1171 return false;
1172 }
1173 }
1174 return true;
1175 }
1176
1177 private boolean isValidIdWithWildCardCharacter(char c) {
1178 return isValidIdCharacter(c) || c == '?' || c == '*';
1179 }
1180
1181 private boolean validateStringNoExpression(
1182 String fieldName,
1183 ModelProblemCollector problems,
1184 Severity severity,
1185 Version version,
1186 String string,
1187 InputLocationTracker tracker) {
1188 if (!hasExpression(string)) {
1189 return true;
1190 }
1191
1192 addViolation(
1193 problems,
1194 severity,
1195 version,
1196 fieldName,
1197 null,
1198 "contains an expression but should be a constant.",
1199 tracker);
1200
1201 return false;
1202 }
1203
1204 private boolean validateVersionNoExpression(
1205 String fieldName,
1206 ModelProblemCollector problems,
1207 Severity severity,
1208 Version version,
1209 String string,
1210 InputLocationTracker tracker) {
1211 if (!hasExpression(string)) {
1212 return true;
1213 }
1214
1215 Matcher m = CI_FRIENDLY_EXPRESSION.matcher(string.trim());
1216 while (m.find()) {
1217 String property = m.group(1);
1218 if (!versionProcessor.isValidProperty(property)) {
1219 addViolation(
1220 problems,
1221 severity,
1222 version,
1223 fieldName,
1224 null,
1225 "contains an expression but should be a constant.",
1226 tracker);
1227 return false;
1228 }
1229 }
1230
1231 return true;
1232 }
1233
1234 private boolean hasExpression(String value) {
1235 return value != null && value.contains("${");
1236 }
1237
1238 private boolean hasProjectExpression(String value) {
1239 return value != null && value.contains("${project.");
1240 }
1241
1242 private boolean validateStringNotEmpty(
1243 String fieldName,
1244 ModelProblemCollector problems,
1245 Severity severity,
1246 Version version,
1247 String string,
1248 InputLocationTracker tracker) {
1249 return validateStringNotEmpty(EMPTY, fieldName, problems, severity, version, string, null, tracker);
1250 }
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260 @SuppressWarnings("checkstyle:parameternumber")
1261 private boolean validateStringNotEmpty(
1262 String prefix,
1263 String prefix2,
1264 String fieldName,
1265 ModelProblemCollector problems,
1266 Severity severity,
1267 Version version,
1268 String string,
1269 String sourceHint,
1270 InputLocationTracker tracker) {
1271 if (!validateNotNull(prefix, prefix2, fieldName, problems, severity, version, string, sourceHint, tracker)) {
1272 return false;
1273 }
1274
1275 if (!string.isEmpty()) {
1276 return true;
1277 }
1278
1279 addViolation(problems, severity, version, prefix + prefix2 + fieldName, sourceHint, "is missing.", tracker);
1280
1281 return false;
1282 }
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292 @SuppressWarnings("checkstyle:parameternumber")
1293 private boolean validateStringNotEmpty(
1294 String prefix,
1295 String fieldName,
1296 ModelProblemCollector problems,
1297 Severity severity,
1298 Version version,
1299 String string,
1300 String sourceHint,
1301 InputLocationTracker tracker) {
1302 if (!validateNotNull(prefix, fieldName, problems, severity, version, string, sourceHint, tracker)) {
1303 return false;
1304 }
1305
1306 if (string.length() > 0) {
1307 return true;
1308 }
1309
1310 addViolation(problems, severity, version, prefix + fieldName, sourceHint, "is missing.", tracker);
1311
1312 return false;
1313 }
1314
1315
1316
1317
1318
1319
1320
1321
1322 @SuppressWarnings("checkstyle:parameternumber")
1323 private boolean validateNotNull(
1324 String prefix,
1325 String fieldName,
1326 ModelProblemCollector problems,
1327 Severity severity,
1328 Version version,
1329 Object object,
1330 String sourceHint,
1331 InputLocationTracker tracker) {
1332 if (object != null) {
1333 return true;
1334 }
1335
1336 addViolation(problems, severity, version, prefix + fieldName, sourceHint, "is missing.", tracker);
1337
1338 return false;
1339 }
1340
1341
1342
1343
1344
1345
1346
1347
1348 @SuppressWarnings("checkstyle:parameternumber")
1349 private boolean validateNotNull(
1350 String prefix,
1351 String prefix2,
1352 String fieldName,
1353 ModelProblemCollector problems,
1354 Severity severity,
1355 Version version,
1356 Object object,
1357 String sourceHint,
1358 InputLocationTracker tracker) {
1359 if (object != null) {
1360 return true;
1361 }
1362
1363 addViolation(problems, severity, version, prefix + prefix2 + fieldName, sourceHint, "is missing.", tracker);
1364
1365 return false;
1366 }
1367
1368 @SuppressWarnings("checkstyle:parameternumber")
1369 private boolean validateBoolean(
1370 String prefix,
1371 String fieldName,
1372 ModelProblemCollector problems,
1373 Severity severity,
1374 Version version,
1375 String string,
1376 String sourceHint,
1377 InputLocationTracker tracker) {
1378 if (string == null || string.length() <= 0) {
1379 return true;
1380 }
1381
1382 if ("true".equalsIgnoreCase(string) || "false".equalsIgnoreCase(string)) {
1383 return true;
1384 }
1385
1386 addViolation(
1387 problems,
1388 severity,
1389 version,
1390 prefix + fieldName,
1391 sourceHint,
1392 "must be 'true' or 'false' but is '" + string + "'.",
1393 tracker);
1394
1395 return false;
1396 }
1397
1398 @SuppressWarnings("checkstyle:parameternumber")
1399 private boolean validateEnum(
1400 String prefix,
1401 String fieldName,
1402 ModelProblemCollector problems,
1403 Severity severity,
1404 Version version,
1405 String string,
1406 String sourceHint,
1407 InputLocationTracker tracker,
1408 String... validValues) {
1409 if (string == null || string.length() <= 0) {
1410 return true;
1411 }
1412
1413 List<String> values = Arrays.asList(validValues);
1414
1415 if (values.contains(string)) {
1416 return true;
1417 }
1418
1419 addViolation(
1420 problems,
1421 severity,
1422 version,
1423 prefix + fieldName,
1424 sourceHint,
1425 "must be one of " + values + " but is '" + string + "'.",
1426 tracker);
1427
1428 return false;
1429 }
1430
1431 @SuppressWarnings("checkstyle:parameternumber")
1432 private boolean validateModelVersion(
1433 ModelProblemCollector problems, String string, InputLocationTracker tracker, String... validVersions) {
1434 if (string == null || string.length() <= 0) {
1435 return true;
1436 }
1437
1438 List<String> values = Arrays.asList(validVersions);
1439
1440 if (values.contains(string)) {
1441 return true;
1442 }
1443
1444 boolean newerThanAll = true;
1445 boolean olderThanAll = true;
1446 for (String validValue : validVersions) {
1447 final int comparison = compareModelVersions(validValue, string);
1448 newerThanAll = newerThanAll && comparison < 0;
1449 olderThanAll = olderThanAll && comparison > 0;
1450 }
1451
1452 if (newerThanAll) {
1453 addViolation(
1454 problems,
1455 Severity.FATAL,
1456 Version.V20,
1457 "modelVersion",
1458 null,
1459 "of '" + string + "' is newer than the versions supported by this version of Maven: " + values
1460 + ". Building this project requires a newer version of Maven.",
1461 tracker);
1462
1463 } else if (olderThanAll) {
1464
1465 addViolation(
1466 problems,
1467 Severity.FATAL,
1468 Version.V20,
1469 "modelVersion",
1470 null,
1471 "of '" + string + "' is older than the versions supported by this version of Maven: " + values
1472 + ". Building this project requires an older version of Maven.",
1473 tracker);
1474
1475 } else {
1476 addViolation(
1477 problems,
1478 Severity.ERROR,
1479 Version.V20,
1480 "modelVersion",
1481 null,
1482 "must be one of " + values + " but is '" + string + "'.",
1483 tracker);
1484 }
1485
1486 return false;
1487 }
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497 private static int compareModelVersions(String first, String second) {
1498
1499 String[] firstSegments = StringUtils.split(first, ".");
1500 String[] secondSegments = StringUtils.split(second, ".");
1501 for (int i = 0; i < Math.max(firstSegments.length, secondSegments.length); i++) {
1502 int result = Long.valueOf(i < firstSegments.length ? firstSegments[i] : "0")
1503 .compareTo(Long.valueOf(i < secondSegments.length ? secondSegments[i] : "0"));
1504 if (result != 0) {
1505 return result;
1506 }
1507 }
1508 return 0;
1509 }
1510
1511 @SuppressWarnings("checkstyle:parameternumber")
1512 private boolean validateBannedCharacters(
1513 String prefix,
1514 String fieldName,
1515 ModelProblemCollector problems,
1516 Severity severity,
1517 Version version,
1518 String string,
1519 String sourceHint,
1520 InputLocationTracker tracker,
1521 String banned) {
1522 if (string != null) {
1523 for (int i = string.length() - 1; i >= 0; i--) {
1524 if (banned.indexOf(string.charAt(i)) >= 0) {
1525 addViolation(
1526 problems,
1527 severity,
1528 version,
1529 prefix + fieldName,
1530 sourceHint,
1531 "must not contain any of these characters " + banned + " but found " + string.charAt(i),
1532 tracker);
1533 return false;
1534 }
1535 }
1536 }
1537
1538 return true;
1539 }
1540
1541 @SuppressWarnings("checkstyle:parameternumber")
1542 private boolean validateVersion(
1543 String prefix,
1544 String fieldName,
1545 ModelProblemCollector problems,
1546 Severity severity,
1547 Version version,
1548 String string,
1549 String sourceHint,
1550 InputLocationTracker tracker) {
1551 if (string == null || string.length() <= 0) {
1552 return true;
1553 }
1554
1555 if (hasExpression(string)) {
1556 addViolation(
1557 problems,
1558 severity,
1559 version,
1560 prefix + fieldName,
1561 sourceHint,
1562 "must be a valid version but is '" + string + "'.",
1563 tracker);
1564 return false;
1565 }
1566
1567 return validateBannedCharacters(
1568 prefix, fieldName, problems, severity, version, string, sourceHint, tracker, ILLEGAL_VERSION_CHARS);
1569 }
1570
1571 private boolean validate20ProperSnapshotVersion(
1572 String fieldName,
1573 ModelProblemCollector problems,
1574 Severity severity,
1575 Version version,
1576 String string,
1577 String sourceHint,
1578 InputLocationTracker tracker) {
1579 if (string == null || string.length() <= 0) {
1580 return true;
1581 }
1582
1583 if (string.endsWith("SNAPSHOT") && !string.endsWith("-SNAPSHOT")) {
1584 addViolation(
1585 problems,
1586 severity,
1587 version,
1588 fieldName,
1589 sourceHint,
1590 "uses an unsupported snapshot version format, should be '*-SNAPSHOT' instead.",
1591 tracker);
1592 return false;
1593 }
1594
1595 return true;
1596 }
1597
1598 private boolean validate20PluginVersion(
1599 String fieldName,
1600 ModelProblemCollector problems,
1601 String string,
1602 String sourceHint,
1603 InputLocationTracker tracker,
1604 ModelBuildingRequest request) {
1605 if (string == null) {
1606
1607 return true;
1608 }
1609
1610 Severity errOn30 = getSeverity(request, ModelBuildingRequest.VALIDATION_LEVEL_MAVEN_3_0);
1611
1612 if (!validateVersion(EMPTY, fieldName, problems, errOn30, Version.V20, string, sourceHint, tracker)) {
1613 return false;
1614 }
1615
1616 if (string.length() <= 0 || "RELEASE".equals(string) || "LATEST".equals(string)) {
1617 addViolation(
1618 problems,
1619 errOn30,
1620 Version.V20,
1621 fieldName,
1622 sourceHint,
1623 "must be a valid version but is '" + string + "'.",
1624 tracker);
1625 return false;
1626 }
1627
1628 return true;
1629 }
1630
1631 private static void addViolation(
1632 ModelProblemCollector problems,
1633 Severity severity,
1634 Version version,
1635 String fieldName,
1636 String sourceHint,
1637 String message,
1638 InputLocationTracker tracker) {
1639 StringBuilder buffer = new StringBuilder(256);
1640 buffer.append('\'').append(fieldName).append('\'');
1641
1642 if (sourceHint != null) {
1643 buffer.append(" for ").append(sourceHint);
1644 }
1645
1646 buffer.append(' ').append(message);
1647
1648
1649 problems.add(new ModelProblemCollectorRequest(severity, version)
1650 .setMessage(buffer.toString())
1651 .setLocation(getLocation(fieldName, tracker)));
1652
1653 }
1654
1655 private static InputLocation getLocation(String fieldName, InputLocationTracker tracker) {
1656 InputLocation location = null;
1657
1658 if (tracker != null) {
1659 if (fieldName != null) {
1660 Object key = fieldName;
1661
1662 int idx = fieldName.lastIndexOf('.');
1663 if (idx >= 0) {
1664 fieldName = fieldName.substring(idx + 1);
1665 key = fieldName;
1666 }
1667
1668 if (fieldName.endsWith("]")) {
1669 key = fieldName.substring(fieldName.lastIndexOf('[') + 1, fieldName.length() - 1);
1670 try {
1671 key = Integer.valueOf(key.toString());
1672 } catch (NumberFormatException e) {
1673
1674 }
1675 }
1676
1677 location = tracker.getLocation(key);
1678 }
1679
1680 if (location == null) {
1681 location = tracker.getLocation(EMPTY);
1682 }
1683 }
1684
1685 return location;
1686 }
1687
1688 private static boolean equals(String s1, String s2) {
1689 return StringUtils.clean(s1).equals(StringUtils.clean(s2));
1690 }
1691
1692 private static Severity getSeverity(ModelBuildingRequest request, int errorThreshold) {
1693 return getSeverity(request.getValidationLevel(), errorThreshold);
1694 }
1695
1696 private static Severity getSeverity(int validationLevel, int errorThreshold) {
1697 if (validationLevel < errorThreshold) {
1698 return Severity.WARNING;
1699 } else {
1700 return Severity.ERROR;
1701 }
1702 }
1703 }