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)) || "project.basedir".equals(m.group(1)))) {
989 validateStringNoExpression(
990 prefix + prefix2 + "[" + repository.getId() + "].url",
991 problems,
992 Severity.ERROR,
993 Version.V40,
994 repository.getUrl(),
995 repository);
996 break;
997 }
998 }
999 }
1000
1001 String key = repository.getId();
1002
1003 Repository existing = index.get(key);
1004
1005 if (existing != null) {
1006 Severity errOn30 = getSeverity(request, ModelBuildingRequest.VALIDATION_LEVEL_MAVEN_3_0);
1007
1008 addViolation(
1009 problems,
1010 errOn30,
1011 Version.V20,
1012 prefix + prefix2 + "id",
1013 null,
1014 "must be unique: " + repository.getId() + " -> " + existing.getUrl() + " vs "
1015 + repository.getUrl(),
1016 repository);
1017 } else {
1018 index.put(key, repository);
1019 }
1020 }
1021 }
1022
1023 private void validate20EffectiveRepository(
1024 ModelProblemCollector problems, Repository repository, String prefix, ModelBuildingRequest request) {
1025 if (repository != null) {
1026 Severity errOn31 = getSeverity(request, ModelBuildingRequest.VALIDATION_LEVEL_MAVEN_3_1);
1027
1028 validateBannedCharacters(
1029 prefix,
1030 "id",
1031 problems,
1032 errOn31,
1033 Version.V20,
1034 repository.getId(),
1035 null,
1036 repository,
1037 ILLEGAL_REPO_ID_CHARS);
1038
1039 if ("local".equals(repository.getId())) {
1040 addViolation(
1041 problems,
1042 errOn31,
1043 Version.V20,
1044 prefix + "id",
1045 null,
1046 "must not be 'local'" + ", this identifier is reserved for the local repository"
1047 + ", using it for other repositories will corrupt your repository metadata.",
1048 repository);
1049 }
1050
1051 if ("legacy".equals(repository.getLayout())) {
1052 addViolation(
1053 problems,
1054 Severity.WARNING,
1055 Version.V20,
1056 prefix + "layout",
1057 repository.getId(),
1058 "uses the unsupported value 'legacy', artifact resolution might fail.",
1059 repository);
1060 }
1061 }
1062 }
1063
1064 private void validate20RawResources(
1065 ModelProblemCollector problems, List<Resource> resources, String prefix, ModelBuildingRequest request) {
1066 Severity errOn30 = getSeverity(request, ModelBuildingRequest.VALIDATION_LEVEL_MAVEN_3_0);
1067
1068 for (Resource resource : resources) {
1069 validateStringNotEmpty(
1070 prefix,
1071 "directory",
1072 problems,
1073 Severity.ERROR,
1074 Version.V20,
1075 resource.getDirectory(),
1076 null,
1077 resource);
1078
1079 validateBoolean(
1080 prefix,
1081 "filtering",
1082 problems,
1083 errOn30,
1084 Version.V20,
1085 resource.getFiltering(),
1086 resource.getDirectory(),
1087 resource);
1088 }
1089 }
1090
1091
1092
1093
1094
1095 private boolean validateCoordinateId(
1096 String fieldName, ModelProblemCollector problems, String id, InputLocationTracker tracker) {
1097 return validateCoordinateId(EMPTY, fieldName, problems, Severity.ERROR, Version.BASE, id, null, tracker);
1098 }
1099
1100 @SuppressWarnings("checkstyle:parameternumber")
1101 private boolean validateCoordinateId(
1102 String prefix,
1103 String fieldName,
1104 ModelProblemCollector problems,
1105 Severity severity,
1106 Version version,
1107 String id,
1108 String sourceHint,
1109 InputLocationTracker tracker) {
1110 if (validCoordinateIds.contains(id)) {
1111 return true;
1112 }
1113 if (!validateStringNotEmpty(prefix, fieldName, problems, severity, version, id, sourceHint, tracker)) {
1114 return false;
1115 } else {
1116 if (!isValidCoordinateId(id)) {
1117 addViolation(
1118 problems,
1119 severity,
1120 version,
1121 prefix + fieldName,
1122 sourceHint,
1123 "with value '" + id + "' does not match a valid coordinate id pattern.",
1124 tracker);
1125 return false;
1126 }
1127 validCoordinateIds.add(id);
1128 return true;
1129 }
1130 }
1131
1132 private boolean isValidCoordinateId(String id) {
1133 for (int i = 0; i < id.length(); i++) {
1134 char c = id.charAt(i);
1135 if (!isValidCoordinateIdCharacter(c)) {
1136 return false;
1137 }
1138 }
1139 return true;
1140 }
1141
1142 private boolean isValidCoordinateIdCharacter(char c) {
1143 return c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' || c >= '0' && c <= '9' || c == '-' || c == '_' || c == '.';
1144 }
1145
1146 @SuppressWarnings("checkstyle:parameternumber")
1147 private boolean validateProfileId(
1148 String prefix,
1149 String fieldName,
1150 ModelProblemCollector problems,
1151 Severity severity,
1152 Version version,
1153 String id,
1154 String sourceHint,
1155 InputLocationTracker tracker) {
1156 if (validProfileIds.contains(id)) {
1157 return true;
1158 }
1159 if (!validateStringNotEmpty(prefix, fieldName, problems, severity, version, id, sourceHint, tracker)) {
1160 return false;
1161 } else {
1162 if (!isValidProfileId(id)) {
1163 addViolation(
1164 problems,
1165 severity,
1166 version,
1167 prefix + fieldName,
1168 sourceHint,
1169 "with value '" + id + "' does not match a valid profile id pattern.",
1170 tracker);
1171 return false;
1172 }
1173 validProfileIds.add(id);
1174 return true;
1175 }
1176 }
1177
1178 private boolean isValidProfileId(String id) {
1179 switch (id.charAt(0)) {
1180 case '+':
1181 case '-':
1182 case '!':
1183 case '?':
1184 return false;
1185 default:
1186 }
1187 return true;
1188 }
1189
1190 @SuppressWarnings("checkstyle:parameternumber")
1191 private boolean validateCoordinateIdWithWildcards(
1192 String prefix,
1193 String fieldName,
1194 ModelProblemCollector problems,
1195 Severity severity,
1196 Version version,
1197 String id,
1198 String sourceHint,
1199 InputLocationTracker tracker) {
1200 if (!validateStringNotEmpty(prefix, fieldName, problems, severity, version, id, sourceHint, tracker)) {
1201 return false;
1202 } else {
1203 if (!isValidCoordinateIdWithWildCards(id)) {
1204 addViolation(
1205 problems,
1206 severity,
1207 version,
1208 prefix + fieldName,
1209 sourceHint,
1210 "with value '" + id + "' does not match a valid coordinate id pattern.",
1211 tracker);
1212 return false;
1213 }
1214 return true;
1215 }
1216 }
1217
1218 private boolean isValidCoordinateIdWithWildCards(String id) {
1219 for (int i = 0; i < id.length(); i++) {
1220 char c = id.charAt(i);
1221 if (!isValidCoordinateIdWithWildCardCharacter(c)) {
1222 return false;
1223 }
1224 }
1225 return true;
1226 }
1227
1228 private boolean isValidCoordinateIdWithWildCardCharacter(char c) {
1229 return isValidCoordinateIdCharacter(c) || c == '?' || c == '*';
1230 }
1231
1232 private boolean validateStringNoExpression(
1233 String fieldName,
1234 ModelProblemCollector problems,
1235 Severity severity,
1236 Version version,
1237 String string,
1238 InputLocationTracker tracker) {
1239 if (!hasExpression(string)) {
1240 return true;
1241 }
1242
1243 addViolation(
1244 problems,
1245 severity,
1246 version,
1247 fieldName,
1248 null,
1249 "contains an expression but should be a constant.",
1250 tracker);
1251
1252 return false;
1253 }
1254
1255 private boolean validateVersionNoExpression(
1256 String fieldName,
1257 ModelProblemCollector problems,
1258 Severity severity,
1259 Version version,
1260 String string,
1261 InputLocationTracker tracker) {
1262 if (!hasExpression(string)) {
1263 return true;
1264 }
1265
1266 Matcher m = EXPRESSION_NAME_PATTERN.matcher(string.trim());
1267 while (m.find()) {
1268 String property = m.group(1);
1269 if (!versionProcessor.isValidProperty(property)) {
1270 addViolation(
1271 problems,
1272 severity,
1273 version,
1274 fieldName,
1275 null,
1276 "contains an expression but should be a constant.",
1277 tracker);
1278
1279 return false;
1280 }
1281 }
1282
1283 return true;
1284 }
1285
1286 private boolean hasExpression(String value) {
1287 return value != null && value.contains("${");
1288 }
1289
1290 private boolean hasProjectExpression(String value) {
1291 return value != null && value.contains("${project.");
1292 }
1293
1294 private boolean validateStringNotEmpty(
1295 String fieldName,
1296 ModelProblemCollector problems,
1297 Severity severity,
1298 Version version,
1299 String string,
1300 InputLocationTracker tracker) {
1301 return validateStringNotEmpty(EMPTY, fieldName, problems, severity, version, string, null, tracker);
1302 }
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312 @SuppressWarnings("checkstyle:parameternumber")
1313 private boolean validateStringNotEmpty(
1314 String prefix,
1315 String prefix2,
1316 String fieldName,
1317 ModelProblemCollector problems,
1318 Severity severity,
1319 Version version,
1320 String string,
1321 String sourceHint,
1322 InputLocationTracker tracker) {
1323 if (!validateNotNull(prefix, prefix2, fieldName, problems, severity, version, string, sourceHint, tracker)) {
1324 return false;
1325 }
1326
1327 if (!string.isEmpty()) {
1328 return true;
1329 }
1330
1331 addViolation(problems, severity, version, prefix + prefix2 + fieldName, sourceHint, "is missing.", tracker);
1332
1333 return false;
1334 }
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344 @SuppressWarnings("checkstyle:parameternumber")
1345 private boolean validateStringNotEmpty(
1346 String prefix,
1347 String fieldName,
1348 ModelProblemCollector problems,
1349 Severity severity,
1350 Version version,
1351 String string,
1352 String sourceHint,
1353 InputLocationTracker tracker) {
1354 if (!validateNotNull(prefix, fieldName, problems, severity, version, string, sourceHint, tracker)) {
1355 return false;
1356 }
1357
1358 if (string.length() > 0) {
1359 return true;
1360 }
1361
1362 addViolation(problems, severity, version, prefix + fieldName, sourceHint, "is missing.", tracker);
1363
1364 return false;
1365 }
1366
1367
1368
1369
1370
1371
1372
1373
1374 @SuppressWarnings("checkstyle:parameternumber")
1375 private boolean validateNotNull(
1376 String prefix,
1377 String fieldName,
1378 ModelProblemCollector problems,
1379 Severity severity,
1380 Version version,
1381 Object object,
1382 String sourceHint,
1383 InputLocationTracker tracker) {
1384 if (object != null) {
1385 return true;
1386 }
1387
1388 addViolation(problems, severity, version, prefix + fieldName, sourceHint, "is missing.", tracker);
1389
1390 return false;
1391 }
1392
1393
1394
1395
1396
1397
1398
1399
1400 @SuppressWarnings("checkstyle:parameternumber")
1401 private boolean validateNotNull(
1402 String prefix,
1403 String prefix2,
1404 String fieldName,
1405 ModelProblemCollector problems,
1406 Severity severity,
1407 Version version,
1408 Object object,
1409 String sourceHint,
1410 InputLocationTracker tracker) {
1411 if (object != null) {
1412 return true;
1413 }
1414
1415 addViolation(problems, severity, version, prefix + prefix2 + fieldName, sourceHint, "is missing.", tracker);
1416
1417 return false;
1418 }
1419
1420 @SuppressWarnings("checkstyle:parameternumber")
1421 private boolean validateBoolean(
1422 String prefix,
1423 String fieldName,
1424 ModelProblemCollector problems,
1425 Severity severity,
1426 Version version,
1427 String string,
1428 String sourceHint,
1429 InputLocationTracker tracker) {
1430 if (string == null || string.length() <= 0) {
1431 return true;
1432 }
1433
1434 if ("true".equalsIgnoreCase(string) || "false".equalsIgnoreCase(string)) {
1435 return true;
1436 }
1437
1438 addViolation(
1439 problems,
1440 severity,
1441 version,
1442 prefix + fieldName,
1443 sourceHint,
1444 "must be 'true' or 'false' but is '" + string + "'.",
1445 tracker);
1446
1447 return false;
1448 }
1449
1450 @SuppressWarnings("checkstyle:parameternumber")
1451 private boolean validateEnum(
1452 String prefix,
1453 String fieldName,
1454 ModelProblemCollector problems,
1455 Severity severity,
1456 Version version,
1457 String string,
1458 String sourceHint,
1459 InputLocationTracker tracker,
1460 String... validValues) {
1461 if (string == null || string.length() <= 0) {
1462 return true;
1463 }
1464
1465 List<String> values = Arrays.asList(validValues);
1466
1467 if (values.contains(string)) {
1468 return true;
1469 }
1470
1471 addViolation(
1472 problems,
1473 severity,
1474 version,
1475 prefix + fieldName,
1476 sourceHint,
1477 "must be one of " + values + " but is '" + string + "'.",
1478 tracker);
1479
1480 return false;
1481 }
1482
1483 @SuppressWarnings("checkstyle:parameternumber")
1484 private boolean validateModelVersion(
1485 ModelProblemCollector problems, String string, InputLocationTracker tracker, List<String> validVersions) {
1486 if (string == null || string.length() <= 0) {
1487 return true;
1488 }
1489
1490 if (validVersions.contains(string)) {
1491 return true;
1492 }
1493
1494 boolean newerThanAll = true;
1495 boolean olderThanAll = true;
1496 for (String validValue : validVersions) {
1497 final int comparison = compareModelVersions(validValue, string);
1498 newerThanAll = newerThanAll && comparison < 0;
1499 olderThanAll = olderThanAll && comparison > 0;
1500 }
1501
1502 if (newerThanAll) {
1503 addViolation(
1504 problems,
1505 Severity.FATAL,
1506 Version.V20,
1507 "modelVersion",
1508 null,
1509 "of '" + string + "' is newer than the versions supported by this version of Maven: "
1510 + validVersions + ". Building this project requires a newer version of Maven.",
1511 tracker);
1512
1513 } else if (olderThanAll) {
1514
1515 addViolation(
1516 problems,
1517 Severity.FATAL,
1518 Version.V20,
1519 "modelVersion",
1520 null,
1521 "of '" + string + "' is older than the versions supported by this version of Maven: "
1522 + validVersions + ". Building this project requires an older version of Maven.",
1523 tracker);
1524
1525 } else {
1526 addViolation(
1527 problems,
1528 Severity.ERROR,
1529 Version.V20,
1530 "modelVersion",
1531 null,
1532 "must be one of " + validVersions + " but is '" + string + "'.",
1533 tracker);
1534 }
1535
1536 return false;
1537 }
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547 private static int compareModelVersions(String first, String second) {
1548
1549 String[] firstSegments = first.split("\\.");
1550 String[] secondSegments = second.split("\\.");
1551 for (int i = 0; i < Math.max(firstSegments.length, secondSegments.length); i++) {
1552 int result = Long.valueOf(i < firstSegments.length ? firstSegments[i] : "0")
1553 .compareTo(Long.valueOf(i < secondSegments.length ? secondSegments[i] : "0"));
1554 if (result != 0) {
1555 return result;
1556 }
1557 }
1558 return 0;
1559 }
1560
1561 @SuppressWarnings("checkstyle:parameternumber")
1562 private boolean validateBannedCharacters(
1563 String prefix,
1564 String fieldName,
1565 ModelProblemCollector problems,
1566 Severity severity,
1567 Version version,
1568 String string,
1569 String sourceHint,
1570 InputLocationTracker tracker,
1571 String banned) {
1572 if (string != null) {
1573 for (int i = string.length() - 1; i >= 0; i--) {
1574 if (banned.indexOf(string.charAt(i)) >= 0) {
1575 addViolation(
1576 problems,
1577 severity,
1578 version,
1579 prefix + fieldName,
1580 sourceHint,
1581 "must not contain any of these characters " + banned + " but found " + string.charAt(i),
1582 tracker);
1583 return false;
1584 }
1585 }
1586 }
1587
1588 return true;
1589 }
1590
1591 @SuppressWarnings("checkstyle:parameternumber")
1592 private boolean validateVersion(
1593 String prefix,
1594 String fieldName,
1595 ModelProblemCollector problems,
1596 Severity severity,
1597 Version version,
1598 String string,
1599 String sourceHint,
1600 InputLocationTracker tracker) {
1601 if (string == null || string.length() <= 0) {
1602 return true;
1603 }
1604
1605 if (hasExpression(string)) {
1606 addViolation(
1607 problems,
1608 severity,
1609 version,
1610 prefix + fieldName,
1611 sourceHint,
1612 "must be a valid version but is '" + string + "'.",
1613 tracker);
1614 return false;
1615 }
1616
1617 return validateBannedCharacters(
1618 prefix, fieldName, problems, severity, version, string, sourceHint, tracker, ILLEGAL_VERSION_CHARS);
1619 }
1620
1621 private boolean validate20ProperSnapshotVersion(
1622 String fieldName,
1623 ModelProblemCollector problems,
1624 Severity severity,
1625 Version version,
1626 String string,
1627 String sourceHint,
1628 InputLocationTracker tracker) {
1629 if (string == null || string.length() <= 0) {
1630 return true;
1631 }
1632
1633 if (string.endsWith("SNAPSHOT") && !string.endsWith("-SNAPSHOT")) {
1634 addViolation(
1635 problems,
1636 severity,
1637 version,
1638 fieldName,
1639 sourceHint,
1640 "uses an unsupported snapshot version format, should be '*-SNAPSHOT' instead.",
1641 tracker);
1642 return false;
1643 }
1644
1645 return true;
1646 }
1647
1648 private boolean validate20PluginVersion(
1649 String fieldName,
1650 ModelProblemCollector problems,
1651 String string,
1652 String sourceHint,
1653 InputLocationTracker tracker,
1654 ModelBuildingRequest request) {
1655 if (string == null) {
1656
1657 return true;
1658 }
1659
1660 Severity errOn30 = getSeverity(request, ModelBuildingRequest.VALIDATION_LEVEL_MAVEN_3_0);
1661
1662 if (!validateVersion(EMPTY, fieldName, problems, errOn30, Version.V20, string, sourceHint, tracker)) {
1663 return false;
1664 }
1665
1666 if (string.length() <= 0 || "RELEASE".equals(string) || "LATEST".equals(string)) {
1667 addViolation(
1668 problems,
1669 errOn30,
1670 Version.V20,
1671 fieldName,
1672 sourceHint,
1673 "must be a valid version but is '" + string + "'.",
1674 tracker);
1675 return false;
1676 }
1677
1678 return true;
1679 }
1680
1681 private static void addViolation(
1682 ModelProblemCollector problems,
1683 Severity severity,
1684 Version version,
1685 String fieldName,
1686 String sourceHint,
1687 String message,
1688 InputLocationTracker tracker) {
1689 StringBuilder buffer = new StringBuilder(256);
1690 buffer.append('\'').append(fieldName).append('\'');
1691
1692 if (sourceHint != null) {
1693 buffer.append(" for ").append(sourceHint);
1694 }
1695
1696 buffer.append(' ').append(message);
1697
1698 problems.add(new ModelProblemCollectorRequest(severity, version)
1699 .setMessage(buffer.toString())
1700 .setLocation(getLocation(fieldName, tracker)));
1701 }
1702
1703 private static org.apache.maven.model.InputLocation getLocation(String fieldName, InputLocationTracker tracker) {
1704 InputLocation location = null;
1705
1706 if (tracker != null) {
1707 if (fieldName != null) {
1708 Object key = fieldName;
1709
1710 int idx = fieldName.lastIndexOf('.');
1711 if (idx >= 0) {
1712 fieldName = fieldName.substring(idx + 1);
1713 key = fieldName;
1714 }
1715
1716 if (fieldName.endsWith("]")) {
1717 key = fieldName.substring(fieldName.lastIndexOf('[') + 1, fieldName.length() - 1);
1718 try {
1719 key = Integer.valueOf(key.toString());
1720 } catch (NumberFormatException e) {
1721
1722 }
1723 }
1724
1725 location = tracker.getLocation(key);
1726 }
1727
1728 if (location == null) {
1729 location = tracker.getLocation(EMPTY);
1730 }
1731 }
1732
1733 return location != null ? new org.apache.maven.model.InputLocation(location) : null;
1734 }
1735
1736 private static boolean equals(String s1, String s2) {
1737 String c1 = s1 == null ? "" : s1.trim();
1738 String c2 = s2 == null ? "" : s2.trim();
1739 return c1.equals(c2);
1740 }
1741
1742 private static Severity getSeverity(ModelBuildingRequest request, int errorThreshold) {
1743 return getSeverity(request.getValidationLevel(), errorThreshold);
1744 }
1745
1746 private static Severity getSeverity(int validationLevel, int errorThreshold) {
1747 if (validationLevel < errorThreshold) {
1748 return Severity.WARNING;
1749 } else {
1750 return Severity.ERROR;
1751 }
1752 }
1753 }