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