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