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