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