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