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 validateDependencyScope(
792 prefix,
793 "scope",
794 problems,
795 Severity.WARNING,
796 Version.V20,
797 d.getScope(),
798 d.getManagementKey(),
799 d,
800 false);
801
802 validateEffectiveModelAgainstDependency(prefix, problems, m, d, request);
803 } else {
804 validateDependencyScope(
805 prefix,
806 "scope",
807 problems,
808 Severity.WARNING,
809 Version.V20,
810 d.getScope(),
811 d.getManagementKey(),
812 d,
813 true);
814 }
815 }
816 }
817 }
818
819 private void validateEffectiveModelAgainstDependency(
820 String prefix, ModelProblemCollector problems, Model m, Dependency d, ModelBuildingRequest request) {
821 String key = d.getGroupId() + ":" + d.getArtifactId() + ":" + d.getVersion()
822 + (d.getClassifier() != null ? ":" + d.getClassifier() : EMPTY);
823 String mKey = m.getGroupId() + ":" + m.getArtifactId() + ":" + m.getVersion();
824 if (key.equals(mKey)) {
825
826
827
828 addViolation(
829 problems, Severity.FATAL, Version.V31, prefix + "[" + key + "]", key, "is referencing itself.", d);
830 }
831 }
832
833 private void validate20EffectivePluginDependencies(
834 ModelProblemCollector problems, Plugin plugin, ModelBuildingRequest request) {
835 List<Dependency> dependencies = plugin.getDependencies();
836
837 if (!dependencies.isEmpty()) {
838 String prefix = "build.plugins.plugin[" + plugin.getKey() + "].dependencies.dependency.";
839
840 Severity errOn30 = getSeverity(request, ModelBuildingRequest.VALIDATION_LEVEL_MAVEN_3_0);
841
842 for (Dependency d : dependencies) {
843 validateEffectiveDependency(problems, d, false, prefix, request);
844
845 validateVersion(
846 prefix, "version", problems, errOn30, Version.BASE, d.getVersion(), d.getManagementKey(), d);
847
848 validateEnum(
849 prefix,
850 "scope",
851 problems,
852 errOn30,
853 Version.BASE,
854 d.getScope(),
855 d.getManagementKey(),
856 d,
857 "compile",
858 "runtime",
859 "system");
860 }
861 }
862 }
863
864 private void validateEffectiveDependency(
865 ModelProblemCollector problems,
866 Dependency d,
867 boolean management,
868 String prefix,
869 ModelBuildingRequest request) {
870 validateId(
871 prefix,
872 "artifactId",
873 problems,
874 Severity.ERROR,
875 Version.BASE,
876 d.getArtifactId(),
877 d.getManagementKey(),
878 d);
879
880 validateId(prefix, "groupId", problems, Severity.ERROR, Version.BASE, d.getGroupId(), d.getManagementKey(), d);
881
882 if (!management) {
883 validateStringNotEmpty(
884 prefix, "type", problems, Severity.ERROR, Version.BASE, d.getType(), d.getManagementKey(), d);
885
886 validateDependencyVersion(problems, d, prefix);
887 }
888
889 if ("system".equals(d.getScope())) {
890 String systemPath = d.getSystemPath();
891
892 if (StringUtils.isEmpty(systemPath)) {
893 addViolation(
894 problems,
895 Severity.ERROR,
896 Version.BASE,
897 prefix + "systemPath",
898 d.getManagementKey(),
899 "is missing.",
900 d);
901 } else {
902 File sysFile = new File(systemPath);
903 if (!sysFile.isAbsolute()) {
904 addViolation(
905 problems,
906 Severity.ERROR,
907 Version.BASE,
908 prefix + "systemPath",
909 d.getManagementKey(),
910 "must specify an absolute path but is " + systemPath,
911 d);
912 } else if (!sysFile.isFile()) {
913 String msg = "refers to a non-existing file " + sysFile.getAbsolutePath();
914 systemPath = systemPath.replace('/', File.separatorChar).replace('\\', File.separatorChar);
915 String jdkHome =
916 request.getSystemProperties().getProperty("java.home", EMPTY) + File.separator + "..";
917 if (systemPath.startsWith(jdkHome)) {
918 msg += ". Please verify that you run Maven using a JDK and not just a JRE.";
919 }
920 addViolation(
921 problems,
922 Severity.WARNING,
923 Version.BASE,
924 prefix + "systemPath",
925 d.getManagementKey(),
926 msg,
927 d);
928 }
929 }
930 } else if (StringUtils.isNotEmpty(d.getSystemPath())) {
931 addViolation(
932 problems,
933 Severity.ERROR,
934 Version.BASE,
935 prefix + "systemPath",
936 d.getManagementKey(),
937 "must be omitted." + " This field may only be specified for a dependency with system scope.",
938 d);
939 }
940
941 if (request.getValidationLevel() >= ModelBuildingRequest.VALIDATION_LEVEL_MAVEN_2_0) {
942 for (Exclusion exclusion : d.getExclusions()) {
943 if (request.getValidationLevel() < ModelBuildingRequest.VALIDATION_LEVEL_MAVEN_3_0) {
944 validateId(
945 prefix,
946 "exclusions.exclusion.groupId",
947 problems,
948 Severity.WARNING,
949 Version.V20,
950 exclusion.getGroupId(),
951 d.getManagementKey(),
952 exclusion);
953
954 validateId(
955 prefix,
956 "exclusions.exclusion.artifactId",
957 problems,
958 Severity.WARNING,
959 Version.V20,
960 exclusion.getArtifactId(),
961 d.getManagementKey(),
962 exclusion);
963 } else {
964 validateIdWithWildcards(
965 prefix,
966 "exclusions.exclusion.groupId",
967 problems,
968 Severity.WARNING,
969 Version.V30,
970 exclusion.getGroupId(),
971 d.getManagementKey(),
972 exclusion);
973
974 validateIdWithWildcards(
975 prefix,
976 "exclusions.exclusion.artifactId",
977 problems,
978 Severity.WARNING,
979 Version.V30,
980 exclusion.getArtifactId(),
981 d.getManagementKey(),
982 exclusion);
983 }
984 }
985 }
986 }
987
988
989
990
991 protected void validateDependencyVersion(ModelProblemCollector problems, Dependency d, String prefix) {
992 validateStringNotEmpty(
993 prefix, "version", problems, Severity.ERROR, Version.BASE, d.getVersion(), d.getManagementKey(), d);
994 }
995
996 private void validateRawRepositories(
997 ModelProblemCollector problems,
998 List<Repository> repositories,
999 String prefix,
1000 String prefix2,
1001 ModelBuildingRequest request) {
1002 Map<String, Repository> index = new HashMap<>();
1003
1004 for (Repository repository : repositories) {
1005 validateStringNotEmpty(
1006 prefix, prefix2, "id", problems, Severity.ERROR, Version.V20, repository.getId(), null, repository);
1007
1008 validateStringNotEmpty(
1009 prefix,
1010 prefix2,
1011 "[" + repository.getId() + "].url",
1012 problems,
1013 Severity.ERROR,
1014 Version.V20,
1015 repository.getUrl(),
1016 null,
1017 repository);
1018
1019 String key = repository.getId();
1020
1021 Repository existing = index.get(key);
1022
1023 if (existing != null) {
1024 Severity errOn30 = getSeverity(request, ModelBuildingRequest.VALIDATION_LEVEL_MAVEN_3_0);
1025
1026 addViolation(
1027 problems,
1028 errOn30,
1029 Version.V20,
1030 prefix + prefix2 + "id",
1031 null,
1032 "must be unique: " + repository.getId() + " -> " + existing.getUrl() + " vs "
1033 + repository.getUrl(),
1034 repository);
1035 } else {
1036 index.put(key, repository);
1037 }
1038 }
1039 }
1040
1041 private void validate20EffectiveRepository(
1042 ModelProblemCollector problems, Repository repository, String prefix, ModelBuildingRequest request) {
1043 if (repository != null) {
1044 Severity errOn31 = getSeverity(request, ModelBuildingRequest.VALIDATION_LEVEL_MAVEN_3_1);
1045
1046 validateBannedCharacters(
1047 prefix,
1048 "id",
1049 problems,
1050 errOn31,
1051 Version.V20,
1052 repository.getId(),
1053 null,
1054 repository,
1055 ILLEGAL_REPO_ID_CHARS);
1056
1057 if ("local".equals(repository.getId())) {
1058 addViolation(
1059 problems,
1060 errOn31,
1061 Version.V20,
1062 prefix + "id",
1063 null,
1064 "must not be 'local'" + ", this identifier is reserved for the local repository"
1065 + ", using it for other repositories will corrupt your repository metadata.",
1066 repository);
1067 }
1068
1069 if ("legacy".equals(repository.getLayout())) {
1070 addViolation(
1071 problems,
1072 Severity.WARNING,
1073 Version.V20,
1074 prefix + "layout",
1075 repository.getId(),
1076 "uses the unsupported value 'legacy', artifact resolution might fail.",
1077 repository);
1078 }
1079 }
1080 }
1081
1082 private void validate20RawResources(
1083 ModelProblemCollector problems, List<Resource> resources, String prefix, ModelBuildingRequest request) {
1084 Severity errOn30 = getSeverity(request, ModelBuildingRequest.VALIDATION_LEVEL_MAVEN_3_0);
1085
1086 for (Resource resource : resources) {
1087 validateStringNotEmpty(
1088 prefix,
1089 "directory",
1090 problems,
1091 Severity.ERROR,
1092 Version.V20,
1093 resource.getDirectory(),
1094 null,
1095 resource);
1096
1097 validateBoolean(
1098 prefix,
1099 "filtering",
1100 problems,
1101 errOn30,
1102 Version.V20,
1103 resource.getFiltering(),
1104 resource.getDirectory(),
1105 resource);
1106 }
1107 }
1108
1109
1110
1111
1112
1113 private boolean validateId(
1114 String fieldName, ModelProblemCollector problems, String id, InputLocationTracker tracker) {
1115 return validateId(EMPTY, fieldName, problems, Severity.ERROR, Version.BASE, id, null, tracker);
1116 }
1117
1118 @SuppressWarnings("checkstyle:parameternumber")
1119 private boolean validateId(
1120 String prefix,
1121 String fieldName,
1122 ModelProblemCollector problems,
1123 Severity severity,
1124 Version version,
1125 String id,
1126 String sourceHint,
1127 InputLocationTracker tracker) {
1128 if (validIds.contains(id)) {
1129 return true;
1130 }
1131 if (!validateStringNotEmpty(prefix, fieldName, problems, severity, version, id, sourceHint, tracker)) {
1132 return false;
1133 } else {
1134 if (!isValidId(id)) {
1135 addViolation(
1136 problems,
1137 severity,
1138 version,
1139 prefix + fieldName,
1140 sourceHint,
1141 "with value '" + id + "' does not match a valid id pattern.",
1142 tracker);
1143 return false;
1144 }
1145 validIds.add(id);
1146 return true;
1147 }
1148 }
1149
1150 private boolean isValidId(String id) {
1151 for (int i = 0; i < id.length(); i++) {
1152 char c = id.charAt(i);
1153 if (!isValidIdCharacter(c)) {
1154 return false;
1155 }
1156 }
1157 return true;
1158 }
1159
1160 private boolean isValidIdCharacter(char c) {
1161 return c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' || c >= '0' && c <= '9' || c == '-' || c == '_' || c == '.';
1162 }
1163
1164 @SuppressWarnings("checkstyle:parameternumber")
1165 private boolean validateIdWithWildcards(
1166 String prefix,
1167 String fieldName,
1168 ModelProblemCollector problems,
1169 Severity severity,
1170 Version version,
1171 String id,
1172 String sourceHint,
1173 InputLocationTracker tracker) {
1174 if (!validateStringNotEmpty(prefix, fieldName, problems, severity, version, id, sourceHint, tracker)) {
1175 return false;
1176 } else {
1177 if (!isValidIdWithWildCards(id)) {
1178 addViolation(
1179 problems,
1180 severity,
1181 version,
1182 prefix + fieldName,
1183 sourceHint,
1184 "with value '" + id + "' does not match a valid id pattern.",
1185 tracker);
1186 return false;
1187 }
1188 return true;
1189 }
1190 }
1191
1192 private boolean isValidIdWithWildCards(String id) {
1193 for (int i = 0; i < id.length(); i++) {
1194 char c = id.charAt(i);
1195 if (!isValidIdWithWildCardCharacter(c)) {
1196 return false;
1197 }
1198 }
1199 return true;
1200 }
1201
1202 private boolean isValidIdWithWildCardCharacter(char c) {
1203 return isValidIdCharacter(c) || c == '?' || c == '*';
1204 }
1205
1206 private boolean validateStringNoExpression(
1207 String fieldName,
1208 ModelProblemCollector problems,
1209 Severity severity,
1210 Version version,
1211 String string,
1212 InputLocationTracker tracker) {
1213 if (!hasExpression(string)) {
1214 return true;
1215 }
1216
1217 addViolation(
1218 problems,
1219 severity,
1220 version,
1221 fieldName,
1222 null,
1223 "contains an expression but should be a constant.",
1224 tracker);
1225
1226 return false;
1227 }
1228
1229 private boolean validateVersionNoExpression(
1230 String fieldName,
1231 ModelProblemCollector problems,
1232 Severity severity,
1233 Version version,
1234 String string,
1235 InputLocationTracker tracker) {
1236 if (!hasExpression(string)) {
1237 return true;
1238 }
1239
1240 Matcher m = CI_FRIENDLY_EXPRESSION.matcher(string.trim());
1241 while (m.find()) {
1242 String property = m.group(1);
1243 if (!versionProcessor.isValidProperty(property)) {
1244 addViolation(
1245 problems,
1246 severity,
1247 version,
1248 fieldName,
1249 null,
1250 "contains an expression but should be a constant.",
1251 tracker);
1252 return false;
1253 }
1254 }
1255
1256 return true;
1257 }
1258
1259 private boolean hasExpression(String value) {
1260 return value != null && value.contains("${");
1261 }
1262
1263 private boolean hasProjectExpression(String value) {
1264 return value != null && value.contains("${project.");
1265 }
1266
1267 private boolean validateStringNotEmpty(
1268 String fieldName,
1269 ModelProblemCollector problems,
1270 Severity severity,
1271 Version version,
1272 String string,
1273 InputLocationTracker tracker) {
1274 return validateStringNotEmpty(EMPTY, fieldName, problems, severity, version, string, null, tracker);
1275 }
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285 @SuppressWarnings("checkstyle:parameternumber")
1286 private boolean validateStringNotEmpty(
1287 String prefix,
1288 String prefix2,
1289 String fieldName,
1290 ModelProblemCollector problems,
1291 Severity severity,
1292 Version version,
1293 String string,
1294 String sourceHint,
1295 InputLocationTracker tracker) {
1296 if (!validateNotNull(prefix, prefix2, fieldName, problems, severity, version, string, sourceHint, tracker)) {
1297 return false;
1298 }
1299
1300 if (!string.isEmpty()) {
1301 return true;
1302 }
1303
1304 addViolation(problems, severity, version, prefix + prefix2 + fieldName, sourceHint, "is missing.", tracker);
1305
1306 return false;
1307 }
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317 @SuppressWarnings("checkstyle:parameternumber")
1318 private boolean validateStringNotEmpty(
1319 String prefix,
1320 String fieldName,
1321 ModelProblemCollector problems,
1322 Severity severity,
1323 Version version,
1324 String string,
1325 String sourceHint,
1326 InputLocationTracker tracker) {
1327 if (!validateNotNull(prefix, fieldName, problems, severity, version, string, sourceHint, tracker)) {
1328 return false;
1329 }
1330
1331 if (!string.isEmpty()) {
1332 return true;
1333 }
1334
1335 addViolation(problems, severity, version, prefix + fieldName, sourceHint, "is missing.", tracker);
1336
1337 return false;
1338 }
1339
1340
1341
1342
1343
1344
1345
1346
1347 @SuppressWarnings("checkstyle:parameternumber")
1348 private boolean validateNotNull(
1349 String prefix,
1350 String fieldName,
1351 ModelProblemCollector problems,
1352 Severity severity,
1353 Version version,
1354 Object object,
1355 String sourceHint,
1356 InputLocationTracker tracker) {
1357 if (object != null) {
1358 return true;
1359 }
1360
1361 addViolation(problems, severity, version, prefix + fieldName, sourceHint, "is missing.", tracker);
1362
1363 return false;
1364 }
1365
1366
1367
1368
1369
1370
1371
1372
1373 @SuppressWarnings("checkstyle:parameternumber")
1374 private boolean validateNotNull(
1375 String prefix,
1376 String prefix2,
1377 String fieldName,
1378 ModelProblemCollector problems,
1379 Severity severity,
1380 Version version,
1381 Object object,
1382 String sourceHint,
1383 InputLocationTracker tracker) {
1384 if (object != null) {
1385 return true;
1386 }
1387
1388 addViolation(problems, severity, version, prefix + prefix2 + fieldName, sourceHint, "is missing.", tracker);
1389
1390 return false;
1391 }
1392
1393 @SuppressWarnings("checkstyle:parameternumber")
1394 private boolean validateBoolean(
1395 String prefix,
1396 String fieldName,
1397 ModelProblemCollector problems,
1398 Severity severity,
1399 Version version,
1400 String string,
1401 String sourceHint,
1402 InputLocationTracker tracker) {
1403 if (string == null || string.length() <= 0) {
1404 return true;
1405 }
1406
1407 if ("true".equalsIgnoreCase(string) || "false".equalsIgnoreCase(string)) {
1408 return true;
1409 }
1410
1411 addViolation(
1412 problems,
1413 severity,
1414 version,
1415 prefix + fieldName,
1416 sourceHint,
1417 "must be 'true' or 'false' but is '" + string + "'.",
1418 tracker);
1419
1420 return false;
1421 }
1422
1423 @SuppressWarnings("checkstyle:parameternumber")
1424 private boolean validateEnum(
1425 String prefix,
1426 String fieldName,
1427 ModelProblemCollector problems,
1428 Severity severity,
1429 Version version,
1430 String string,
1431 String sourceHint,
1432 InputLocationTracker tracker,
1433 String... validValues) {
1434 if (string == null || string.length() <= 0) {
1435 return true;
1436 }
1437
1438 List<String> values = Arrays.asList(validValues);
1439
1440 if (values.contains(string)) {
1441 return true;
1442 }
1443
1444 addViolation(
1445 problems,
1446 severity,
1447 version,
1448 prefix + fieldName,
1449 sourceHint,
1450 "must be one of " + values + " but is '" + string + "'.",
1451 tracker);
1452
1453 return false;
1454 }
1455
1456 @SuppressWarnings("checkstyle:parameternumber")
1457 private boolean validateDependencyScope(
1458 String prefix,
1459 String fieldName,
1460 ModelProblemCollector problems,
1461 Severity severity,
1462 Version version,
1463 String scope,
1464 String sourceHint,
1465 InputLocationTracker tracker,
1466 boolean isDependencyManagement) {
1467 if (scope == null || scope.length() <= 0) {
1468 return true;
1469 }
1470
1471 String[] validScopes;
1472 if (isDependencyManagement) {
1473 validScopes = new String[] {"provided", "compile", "runtime", "test", "system", "import"};
1474 } else {
1475 validScopes = new String[] {"provided", "compile", "runtime", "test", "system"};
1476 }
1477
1478 List<String> values = Arrays.asList(validScopes);
1479
1480 if (values.contains(scope)) {
1481 return true;
1482 }
1483
1484
1485 if ("import".equals(scope) && !isDependencyManagement) {
1486 addViolation(
1487 problems,
1488 severity,
1489 version,
1490 prefix + fieldName,
1491 sourceHint,
1492 "has scope 'import'. The 'import' scope is only valid in <dependencyManagement> sections.",
1493 tracker);
1494 } else {
1495 addViolation(
1496 problems,
1497 severity,
1498 version,
1499 prefix + fieldName,
1500 sourceHint,
1501 "must be one of " + values + " but is '" + scope + "'.",
1502 tracker);
1503 }
1504
1505 return false;
1506 }
1507
1508 @SuppressWarnings("checkstyle:parameternumber")
1509 private boolean validateModelVersion(
1510 ModelProblemCollector problems, String string, InputLocationTracker tracker, String... validVersions) {
1511 if (string == null || string.length() <= 0) {
1512 return true;
1513 }
1514
1515 List<String> values = Arrays.asList(validVersions);
1516
1517 if (values.contains(string)) {
1518 return true;
1519 }
1520
1521 boolean newerThanAll = true;
1522 boolean olderThanAll = true;
1523 for (String validValue : validVersions) {
1524 final int comparison = compareModelVersions(validValue, string);
1525 newerThanAll = newerThanAll && comparison < 0;
1526 olderThanAll = olderThanAll && comparison > 0;
1527 }
1528
1529 if (newerThanAll) {
1530 addViolation(
1531 problems,
1532 Severity.FATAL,
1533 Version.V20,
1534 "modelVersion",
1535 null,
1536 "of '" + string + "' is newer than the versions supported by this version of Maven: " + values
1537 + ". Building this project requires a newer version of Maven.",
1538 tracker);
1539
1540 } else if (olderThanAll) {
1541
1542 addViolation(
1543 problems,
1544 Severity.FATAL,
1545 Version.V20,
1546 "modelVersion",
1547 null,
1548 "of '" + string + "' is older than the versions supported by this version of Maven: " + values
1549 + ". Building this project requires an older version of Maven.",
1550 tracker);
1551
1552 } else {
1553 addViolation(
1554 problems,
1555 Severity.ERROR,
1556 Version.V20,
1557 "modelVersion",
1558 null,
1559 "must be one of " + values + " but is '" + string + "'.",
1560 tracker);
1561 }
1562
1563 return false;
1564 }
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574 private static int compareModelVersions(String first, String second) {
1575
1576 String[] firstSegments = StringUtils.split(first, ".");
1577 String[] secondSegments = StringUtils.split(second, ".");
1578 for (int i = 0; i < Math.max(firstSegments.length, secondSegments.length); i++) {
1579 int result = Long.valueOf(i < firstSegments.length ? firstSegments[i] : "0")
1580 .compareTo(Long.valueOf(i < secondSegments.length ? secondSegments[i] : "0"));
1581 if (result != 0) {
1582 return result;
1583 }
1584 }
1585 return 0;
1586 }
1587
1588 @SuppressWarnings("checkstyle:parameternumber")
1589 private boolean validateBannedCharacters(
1590 String prefix,
1591 String fieldName,
1592 ModelProblemCollector problems,
1593 Severity severity,
1594 Version version,
1595 String string,
1596 String sourceHint,
1597 InputLocationTracker tracker,
1598 String banned) {
1599 if (string != null) {
1600 for (int i = string.length() - 1; i >= 0; i--) {
1601 if (banned.indexOf(string.charAt(i)) >= 0) {
1602 addViolation(
1603 problems,
1604 severity,
1605 version,
1606 prefix + fieldName,
1607 sourceHint,
1608 "must not contain any of these characters " + banned + " but found " + string.charAt(i),
1609 tracker);
1610 return false;
1611 }
1612 }
1613 }
1614
1615 return true;
1616 }
1617
1618 @SuppressWarnings("checkstyle:parameternumber")
1619 private boolean validateVersion(
1620 String prefix,
1621 String fieldName,
1622 ModelProblemCollector problems,
1623 Severity severity,
1624 Version version,
1625 String string,
1626 String sourceHint,
1627 InputLocationTracker tracker) {
1628 if (string == null || string.length() <= 0) {
1629 return true;
1630 }
1631
1632 if (hasExpression(string)) {
1633 addViolation(
1634 problems,
1635 severity,
1636 version,
1637 prefix + fieldName,
1638 sourceHint,
1639 "must be a valid version but is '" + string + "'.",
1640 tracker);
1641 return false;
1642 }
1643
1644 return validateBannedCharacters(
1645 prefix, fieldName, problems, severity, version, string, sourceHint, tracker, ILLEGAL_VERSION_CHARS);
1646 }
1647
1648 private boolean validate20ProperSnapshotVersion(
1649 String fieldName,
1650 ModelProblemCollector problems,
1651 Severity severity,
1652 Version version,
1653 String string,
1654 String sourceHint,
1655 InputLocationTracker tracker) {
1656 if (string == null || string.length() <= 0) {
1657 return true;
1658 }
1659
1660 if (string.endsWith("SNAPSHOT") && !string.endsWith("-SNAPSHOT")) {
1661 addViolation(
1662 problems,
1663 severity,
1664 version,
1665 fieldName,
1666 sourceHint,
1667 "uses an unsupported snapshot version format, should be '*-SNAPSHOT' instead.",
1668 tracker);
1669 return false;
1670 }
1671
1672 return true;
1673 }
1674
1675 private boolean validate20PluginVersion(
1676 String fieldName,
1677 ModelProblemCollector problems,
1678 String string,
1679 String sourceHint,
1680 InputLocationTracker tracker,
1681 ModelBuildingRequest request) {
1682 if (string == null) {
1683
1684 return true;
1685 }
1686
1687 Severity errOn30 = getSeverity(request, ModelBuildingRequest.VALIDATION_LEVEL_MAVEN_3_0);
1688
1689 if (!validateVersion(EMPTY, fieldName, problems, errOn30, Version.V20, string, sourceHint, tracker)) {
1690 return false;
1691 }
1692
1693 if (string.length() <= 0 || "RELEASE".equals(string) || "LATEST".equals(string)) {
1694 addViolation(
1695 problems,
1696 errOn30,
1697 Version.V20,
1698 fieldName,
1699 sourceHint,
1700 "must be a valid version but is '" + string + "'.",
1701 tracker);
1702 return false;
1703 }
1704
1705 return true;
1706 }
1707
1708 private static void addViolation(
1709 ModelProblemCollector problems,
1710 Severity severity,
1711 Version version,
1712 String fieldName,
1713 String sourceHint,
1714 String message,
1715 InputLocationTracker tracker) {
1716 StringBuilder buffer = new StringBuilder(256);
1717 buffer.append('\'').append(fieldName).append('\'');
1718
1719 if (sourceHint != null) {
1720 buffer.append(" for ").append(sourceHint);
1721 }
1722
1723 buffer.append(' ').append(message);
1724
1725
1726 problems.add(new ModelProblemCollectorRequest(severity, version)
1727 .setMessage(buffer.toString())
1728 .setLocation(getLocation(fieldName, tracker)));
1729
1730 }
1731
1732 private static InputLocation getLocation(String fieldName, InputLocationTracker tracker) {
1733 InputLocation location = null;
1734
1735 if (tracker != null) {
1736 if (fieldName != null) {
1737 Object key = fieldName;
1738
1739 int idx = fieldName.lastIndexOf('.');
1740 if (idx >= 0) {
1741 fieldName = fieldName.substring(idx + 1);
1742 key = fieldName;
1743 }
1744
1745 if (fieldName.endsWith("]")) {
1746 key = fieldName.substring(fieldName.lastIndexOf('[') + 1, fieldName.length() - 1);
1747 try {
1748 key = Integer.valueOf(key.toString());
1749 } catch (NumberFormatException e) {
1750
1751 }
1752 }
1753
1754 location = tracker.getLocation(key);
1755 }
1756
1757 if (location == null) {
1758 location = tracker.getLocation(EMPTY);
1759 }
1760 }
1761
1762 return location;
1763 }
1764
1765 private static boolean equals(String s1, String s2) {
1766 return StringUtils.clean(s1).equals(StringUtils.clean(s2));
1767 }
1768
1769 private static Severity getSeverity(ModelBuildingRequest request, int errorThreshold) {
1770 return getSeverity(request.getValidationLevel(), errorThreshold);
1771 }
1772
1773 private static Severity getSeverity(int validationLevel, int errorThreshold) {
1774 if (validationLevel < errorThreshold) {
1775 return Severity.WARNING;
1776 } else {
1777 return Severity.ERROR;
1778 }
1779 }
1780 }