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