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