1 package org.apache.maven.model.validation;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 import java.io.File;
23 import java.util.Arrays;
24 import java.util.HashMap;
25 import java.util.HashSet;
26 import java.util.List;
27 import java.util.Map;
28 import java.util.Set;
29
30 import org.apache.maven.model.Build;
31 import org.apache.maven.model.BuildBase;
32 import org.apache.maven.model.Dependency;
33 import org.apache.maven.model.DependencyManagement;
34 import org.apache.maven.model.DistributionManagement;
35 import org.apache.maven.model.Exclusion;
36 import org.apache.maven.model.InputLocation;
37 import org.apache.maven.model.InputLocationTracker;
38 import org.apache.maven.model.Model;
39 import org.apache.maven.model.Parent;
40 import org.apache.maven.model.Plugin;
41 import org.apache.maven.model.PluginExecution;
42 import org.apache.maven.model.PluginManagement;
43 import org.apache.maven.model.Profile;
44 import org.apache.maven.model.ReportPlugin;
45 import org.apache.maven.model.Reporting;
46 import org.apache.maven.model.Repository;
47 import org.apache.maven.model.Resource;
48 import org.apache.maven.model.building.ModelBuildingRequest;
49 import org.apache.maven.model.building.ModelProblem.Severity;
50 import org.apache.maven.model.building.ModelProblemCollector;
51 import org.codehaus.plexus.component.annotations.Component;
52 import org.codehaus.plexus.util.StringUtils;
53
54
55
56
57 @Component( role = ModelValidator.class )
58 public class DefaultModelValidator
59 implements ModelValidator
60 {
61
62 private static final String ID_REGEX = "[A-Za-z0-9_\\-.]+";
63
64 private static final String ILLEGAL_FS_CHARS = "\\/:\"<>|?*";
65
66 private static final String ILLEGAL_VERSION_CHARS = ILLEGAL_FS_CHARS;
67
68 private static final String ILLEGAL_REPO_ID_CHARS = ILLEGAL_FS_CHARS;
69
70 public void validateRawModel( Model model, ModelBuildingRequest request, ModelProblemCollector problems )
71 {
72 Parent parent = model.getParent();
73 if ( parent != null )
74 {
75 validateStringNotEmpty( "parent.groupId", problems, Severity.FATAL, parent.getGroupId(), parent );
76
77 validateStringNotEmpty( "parent.artifactId", problems, Severity.FATAL, parent.getArtifactId(), parent );
78
79 validateStringNotEmpty( "parent.version", problems, Severity.FATAL, parent.getVersion(), parent );
80
81 if ( equals( parent.getGroupId(), model.getGroupId() )
82 && equals( parent.getArtifactId(), model.getArtifactId() ) )
83 {
84 addViolation( problems, Severity.FATAL, "parent.artifactId", null, "must be changed"
85 + ", the parent element cannot have the same groupId:artifactId as the project.", parent );
86 }
87 }
88
89 if ( request.getValidationLevel() >= ModelBuildingRequest.VALIDATION_LEVEL_MAVEN_2_0 )
90 {
91 Severity errOn30 = getSeverity( request, ModelBuildingRequest.VALIDATION_LEVEL_MAVEN_3_0 );
92
93 validateEnum( "modelVersion", problems, Severity.ERROR, model.getModelVersion(), null, model, "4.0.0" );
94
95 validateStringNoExpression( "groupId", problems, Severity.WARNING, model.getGroupId(), model );
96 if ( parent == null )
97 {
98 validateStringNotEmpty( "groupId", problems, Severity.FATAL, model.getGroupId(), model );
99 }
100
101 validateStringNoExpression( "artifactId", problems, Severity.WARNING, model.getArtifactId(), model );
102 validateStringNotEmpty( "artifactId", problems, Severity.FATAL, model.getArtifactId(), model );
103
104 validateStringNoExpression( "version", problems, Severity.WARNING, model.getVersion(), model );
105 if ( parent == null )
106 {
107 validateStringNotEmpty( "version", problems, Severity.FATAL, model.getVersion(), model );
108 }
109
110 validateRawDependencies( problems, model.getDependencies(), "dependencies.dependency", request );
111
112 if ( model.getDependencyManagement() != null )
113 {
114 validateRawDependencies( problems, model.getDependencyManagement().getDependencies(),
115 "dependencyManagement.dependencies.dependency", request );
116 }
117
118 validateRepositories( problems, model.getRepositories(), "repositories.repository", request );
119
120 validateRepositories( problems, model.getPluginRepositories(), "pluginRepositories.pluginRepository",
121 request );
122
123 Build build = model.getBuild();
124 if ( build != null )
125 {
126 validateRawPlugins( problems, build.getPlugins(), "build.plugins.plugin", request );
127
128 PluginManagement mngt = build.getPluginManagement();
129 if ( mngt != null )
130 {
131 validateRawPlugins( problems, mngt.getPlugins(), "build.pluginManagement.plugins.plugin",
132 request );
133 }
134 }
135
136 Set<String> profileIds = new HashSet<String>();
137
138 for ( Profile profile : model.getProfiles() )
139 {
140 String prefix = "profiles.profile[" + profile.getId() + "]";
141
142 if ( !profileIds.add( profile.getId() ) )
143 {
144 addViolation( problems, errOn30, "profiles.profile.id", null,
145 "must be unique but found duplicate profile with id " + profile.getId(), profile );
146 }
147
148 validateRawDependencies( problems, profile.getDependencies(), prefix + ".dependencies.dependency",
149 request );
150
151 if ( profile.getDependencyManagement() != null )
152 {
153 validateRawDependencies( problems, profile.getDependencyManagement().getDependencies(), prefix
154 + ".dependencyManagement.dependencies.dependency", request );
155 }
156
157 validateRepositories( problems, profile.getRepositories(), prefix + ".repositories.repository",
158 request );
159
160 validateRepositories( problems, profile.getPluginRepositories(), prefix
161 + ".pluginRepositories.pluginRepository", request );
162
163 BuildBase buildBase = profile.getBuild();
164 if ( buildBase != null )
165 {
166 validateRawPlugins( problems, buildBase.getPlugins(), prefix + ".plugins.plugin", request );
167
168 PluginManagement mngt = buildBase.getPluginManagement();
169 if ( mngt != null )
170 {
171 validateRawPlugins( problems, mngt.getPlugins(), prefix + ".pluginManagement.plugins.plugin",
172 request );
173 }
174 }
175 }
176 }
177 }
178
179 private void validateRawPlugins( ModelProblemCollector problems, List<Plugin> plugins, String prefix,
180 ModelBuildingRequest request )
181 {
182 Severity errOn31 = getSeverity( request, ModelBuildingRequest.VALIDATION_LEVEL_MAVEN_3_1 );
183
184 Map<String, Plugin> index = new HashMap<String, Plugin>();
185
186 for ( Plugin plugin : plugins )
187 {
188 String key = plugin.getKey();
189
190 Plugin existing = index.get( key );
191
192 if ( existing != null )
193 {
194 addViolation( problems, errOn31, prefix + ".(groupId:artifactId)", null,
195 "must be unique but found duplicate declaration of plugin " + key, plugin );
196 }
197 else
198 {
199 index.put( key, plugin );
200 }
201
202 Set<String> executionIds = new HashSet<String>();
203
204 for ( PluginExecution exec : plugin.getExecutions() )
205 {
206 if ( !executionIds.add( exec.getId() ) )
207 {
208 addViolation( problems, Severity.ERROR, prefix + "[" + plugin.getKey()
209 + "].executions.execution.id", null, "must be unique but found duplicate execution with id "
210 + exec.getId(), exec );
211 }
212 }
213 }
214 }
215
216 public void validateEffectiveModel( Model model, ModelBuildingRequest request, ModelProblemCollector problems )
217 {
218 validateStringNotEmpty( "modelVersion", problems, Severity.ERROR, model.getModelVersion(), model );
219
220 validateId( "groupId", problems, model.getGroupId(), model );
221
222 validateId( "artifactId", problems, model.getArtifactId(), model );
223
224 validateStringNotEmpty( "packaging", problems, Severity.ERROR, model.getPackaging(), model );
225
226 if ( !model.getModules().isEmpty() )
227 {
228 if ( !"pom".equals( model.getPackaging() ) )
229 {
230 addViolation( problems, Severity.ERROR, "packaging", null, "with value '" + model.getPackaging()
231 + "' is invalid. Aggregator projects " + "require 'pom' as packaging.", model );
232 }
233
234 for ( int i = 0, n = model.getModules().size(); i < n; i++ )
235 {
236 String module = model.getModules().get( i );
237 if ( StringUtils.isBlank( module ) )
238 {
239 addViolation( problems, Severity.WARNING, "modules.module[" + i + "]", null,
240 "has been specified without a path to the project directory.",
241 model.getLocation( "modules" ) );
242 }
243 }
244 }
245
246 validateStringNotEmpty( "version", problems, Severity.ERROR, model.getVersion(), model );
247
248 Severity errOn30 = getSeverity( request, ModelBuildingRequest.VALIDATION_LEVEL_MAVEN_3_0 );
249
250 validateEffectiveDependencies( problems, model.getDependencies(), false, request );
251
252 DependencyManagement mgmt = model.getDependencyManagement();
253 if ( mgmt != null )
254 {
255 validateEffectiveDependencies( problems, mgmt.getDependencies(), true, request );
256 }
257
258 if ( request.getValidationLevel() >= ModelBuildingRequest.VALIDATION_LEVEL_MAVEN_2_0 )
259 {
260 Set<String> modules = new HashSet<String>();
261 for ( int i = 0, n = model.getModules().size(); i < n; i++ )
262 {
263 String module = model.getModules().get( i );
264 if ( !modules.add( module ) )
265 {
266 addViolation( problems, Severity.ERROR, "modules.module[" + i + "]", null,
267 "specifies duplicate child module " + module, model.getLocation( "modules" ) );
268 }
269 }
270
271 Severity errOn31 = getSeverity( request, ModelBuildingRequest.VALIDATION_LEVEL_MAVEN_3_1 );
272
273 validateBannedCharacters( "version", problems, errOn31, model.getVersion(), null, model,
274 ILLEGAL_VERSION_CHARS );
275 validateProperSnapshotVersion( "version", problems, errOn31, model.getVersion(), null, model );
276
277 Build build = model.getBuild();
278 if ( build != null )
279 {
280 for ( Plugin p : build.getPlugins() )
281 {
282 validateStringNotEmpty( "build.plugins.plugin.artifactId", problems, Severity.ERROR,
283 p.getArtifactId(), p );
284
285 validateStringNotEmpty( "build.plugins.plugin.groupId", problems, Severity.ERROR, p.getGroupId(),
286 p );
287
288 validatePluginVersion( "build.plugins.plugin.version", problems, p.getVersion(), p.getKey(), p,
289 request );
290
291 validateBoolean( "build.plugins.plugin.inherited", problems, errOn30, p.getInherited(), p.getKey(),
292 p );
293
294 validateBoolean( "build.plugins.plugin.extensions", problems, errOn30, p.getExtensions(),
295 p.getKey(), p );
296
297 validateEffectivePluginDependencies( problems, p, request );
298 }
299
300 validateResources( problems, build.getResources(), "build.resources.resource", request );
301
302 validateResources( problems, build.getTestResources(), "build.testResources.testResource", request );
303 }
304
305 Reporting reporting = model.getReporting();
306 if ( reporting != null )
307 {
308 for ( ReportPlugin p : reporting.getPlugins() )
309 {
310 validateStringNotEmpty( "reporting.plugins.plugin.artifactId", problems, Severity.ERROR,
311 p.getArtifactId(), p );
312
313 validateStringNotEmpty( "reporting.plugins.plugin.groupId", problems, Severity.ERROR,
314 p.getGroupId(), p );
315
316 validateStringNotEmpty( "reporting.plugins.plugin.version", problems, errOn31, p.getVersion(),
317 p.getKey(), p );
318 }
319 }
320
321 for ( Repository repository : model.getRepositories() )
322 {
323 validateRepository( problems, repository, "repositories.repository", request );
324 }
325
326 for ( Repository repository : model.getPluginRepositories() )
327 {
328 validateRepository( problems, repository, "pluginRepositories.pluginRepository", request );
329 }
330
331 DistributionManagement distMgmt = model.getDistributionManagement();
332 if ( distMgmt != null )
333 {
334 if ( distMgmt.getStatus() != null )
335 {
336 addViolation( problems, Severity.ERROR, "distributionManagement.status", null,
337 "must not be specified.", distMgmt );
338 }
339
340 validateRepository( problems, distMgmt.getRepository(), "distributionManagement.repository", request );
341 validateRepository( problems, distMgmt.getSnapshotRepository(),
342 "distributionManagement.snapshotRepository", request );
343 }
344 }
345 }
346
347 private void validateRawDependencies( ModelProblemCollector problems, List<Dependency> dependencies, String prefix,
348 ModelBuildingRequest request )
349 {
350 Severity errOn30 = getSeverity( request, ModelBuildingRequest.VALIDATION_LEVEL_MAVEN_3_0 );
351 Severity errOn31 = getSeverity( request, ModelBuildingRequest.VALIDATION_LEVEL_MAVEN_3_1 );
352
353 Map<String, Dependency> index = new HashMap<String, Dependency>();
354
355 for ( Dependency dependency : dependencies )
356 {
357 String key = dependency.getManagementKey();
358
359 if ( "import".equals( dependency.getScope() ) )
360 {
361 if ( !"pom".equals( dependency.getType() ) )
362 {
363 addViolation( problems, Severity.WARNING, prefix + ".type", key,
364 "must be 'pom' to import the managed dependencies.", dependency );
365 }
366 else if ( StringUtils.isNotEmpty( dependency.getClassifier() ) )
367 {
368 addViolation( problems, errOn30, prefix + ".classifier", key,
369 "must be empty, imported POM cannot have a classifier.", dependency );
370 }
371 }
372 else if ( "system".equals( dependency.getScope() ) )
373 {
374 String sysPath = dependency.getSystemPath();
375 if ( StringUtils.isNotEmpty( sysPath ) )
376 {
377 if ( !hasExpression( sysPath ) )
378 {
379 addViolation( problems, Severity.WARNING, prefix + ".systemPath", key,
380 "should use a variable instead of a hard-coded path " + sysPath, dependency );
381 }
382 else if ( sysPath.contains( "${basedir}" ) || sysPath.contains( "${project.basedir}" ) )
383 {
384 addViolation( problems, Severity.WARNING, prefix + ".systemPath", key,
385 "should not point at files within the project directory, " + sysPath
386 + " will be unresolvable by dependent projects", dependency );
387 }
388 }
389 }
390
391 Dependency existing = index.get( key );
392
393 if ( existing != null )
394 {
395 String msg;
396 if ( equals( existing.getVersion(), dependency.getVersion() ) )
397 {
398 msg =
399 "duplicate declaration of version "
400 + StringUtils.defaultString( dependency.getVersion(), "(?)" );
401 }
402 else
403 {
404 msg =
405 "version " + StringUtils.defaultString( existing.getVersion(), "(?)" ) + " vs "
406 + StringUtils.defaultString( dependency.getVersion(), "(?)" );
407 }
408
409 addViolation( problems, errOn31, prefix + ".(groupId:artifactId:type:classifier)", null,
410 "must be unique: " + key + " -> " + msg, dependency );
411 }
412 else
413 {
414 index.put( key, dependency );
415 }
416 }
417 }
418
419 private void validateEffectiveDependencies( ModelProblemCollector problems, List<Dependency> dependencies,
420 boolean management, ModelBuildingRequest request )
421 {
422 Severity errOn30 = getSeverity( request, ModelBuildingRequest.VALIDATION_LEVEL_MAVEN_3_0 );
423
424 String prefix = management ? "dependencyManagement.dependencies.dependency." : "dependencies.dependency.";
425
426 for ( Dependency d : dependencies )
427 {
428 validateEffectiveDependency( problems, d, management, prefix, request );
429
430 if ( request.getValidationLevel() >= ModelBuildingRequest.VALIDATION_LEVEL_MAVEN_2_0 )
431 {
432 validateBoolean( prefix + "optional", problems, errOn30, d.getOptional(), d.getManagementKey(), d );
433
434 if ( !management )
435 {
436 validateVersion( prefix + "version", problems, errOn30, d.getVersion(), d.getManagementKey(), d );
437
438
439
440
441
442 validateEnum( prefix + "scope", problems, Severity.WARNING, d.getScope(), d.getManagementKey(), d,
443 "provided", "compile", "runtime", "test", "system" );
444 }
445 }
446 }
447 }
448
449 private void validateEffectivePluginDependencies( ModelProblemCollector problems, Plugin plugin,
450 ModelBuildingRequest request )
451 {
452 List<Dependency> dependencies = plugin.getDependencies();
453
454 if ( !dependencies.isEmpty() )
455 {
456 String prefix = "build.plugins.plugin[" + plugin.getKey() + "].dependencies.dependency.";
457
458 Severity errOn30 = getSeverity( request, ModelBuildingRequest.VALIDATION_LEVEL_MAVEN_3_0 );
459
460 for ( Dependency d : dependencies )
461 {
462 validateEffectiveDependency( problems, d, false, prefix, request );
463
464 validateVersion( prefix + "version", problems, errOn30, d.getVersion(), d.getManagementKey(), d );
465
466 validateEnum( prefix + "scope", problems, errOn30, d.getScope(), d.getManagementKey(), d, "compile",
467 "runtime", "system" );
468 }
469 }
470 }
471
472 private void validateEffectiveDependency( ModelProblemCollector problems, Dependency d, boolean management,
473 String prefix, ModelBuildingRequest request )
474 {
475 validateId( prefix + "artifactId", problems, Severity.ERROR, d.getArtifactId(), d.getManagementKey(), d );
476
477 validateId( prefix + "groupId", problems, Severity.ERROR, d.getGroupId(), d.getManagementKey(), d );
478
479 if ( !management )
480 {
481 validateStringNotEmpty( prefix + "type", problems, Severity.ERROR, d.getType(), d.getManagementKey(), d );
482
483 validateStringNotEmpty( prefix + "version", problems, Severity.ERROR, d.getVersion(), d.getManagementKey(),
484 d );
485 }
486
487 if ( "system".equals( d.getScope() ) )
488 {
489 String systemPath = d.getSystemPath();
490
491 if ( StringUtils.isEmpty( systemPath ) )
492 {
493 addViolation( problems, Severity.ERROR, prefix + "systemPath", d.getManagementKey(), "is missing.",
494 d );
495 }
496 else
497 {
498 File sysFile = new File( systemPath );
499 if ( !sysFile.isAbsolute() )
500 {
501 addViolation( problems, Severity.ERROR, prefix + "systemPath", d.getManagementKey(),
502 "must specify an absolute path but is " + systemPath, d );
503 }
504 else if ( !sysFile.isFile() )
505 {
506 String msg = "refers to a non-existing file " + sysFile.getAbsolutePath();
507 systemPath = systemPath.replace( '/', File.separatorChar ).replace( '\\', File.separatorChar );
508 String jdkHome =
509 request.getSystemProperties().getProperty( "java.home", "" ) + File.separator + "..";
510 if ( systemPath.startsWith( jdkHome ) )
511 {
512 msg += ". Please verify that you run Maven using a JDK and not just a JRE.";
513 }
514 addViolation( problems, Severity.WARNING, prefix + "systemPath", d.getManagementKey(), msg, d );
515 }
516 }
517 }
518 else if ( StringUtils.isNotEmpty( d.getSystemPath() ) )
519 {
520 addViolation( problems, Severity.ERROR, prefix + "systemPath", d.getManagementKey(), "must be omitted."
521 + " This field may only be specified for a dependency with system scope.", d );
522 }
523
524 if ( request.getValidationLevel() >= ModelBuildingRequest.VALIDATION_LEVEL_MAVEN_2_0 )
525 {
526 for ( Exclusion exclusion : d.getExclusions() )
527 {
528 validateId( prefix + "exclusions.exclusion.groupId", problems, Severity.WARNING,
529 exclusion.getGroupId(), d.getManagementKey(), exclusion );
530
531 validateId( prefix + "exclusions.exclusion.artifactId", problems, Severity.WARNING,
532 exclusion.getArtifactId(), d.getManagementKey(), exclusion );
533 }
534 }
535 }
536
537 private void validateRepositories( ModelProblemCollector problems, List<Repository> repositories, String prefix,
538 ModelBuildingRequest request )
539 {
540 Map<String, Repository> index = new HashMap<String, Repository>();
541
542 for ( Repository repository : repositories )
543 {
544 validateStringNotEmpty( prefix + ".id", problems, Severity.ERROR, repository.getId(), repository );
545
546 validateStringNotEmpty( prefix + "[" + repository.getId() + "].url", problems, Severity.ERROR,
547 repository.getUrl(), repository );
548
549 String key = repository.getId();
550
551 Repository existing = index.get( key );
552
553 if ( existing != null )
554 {
555 Severity errOn30 = getSeverity( request, ModelBuildingRequest.VALIDATION_LEVEL_MAVEN_3_0 );
556
557 addViolation( problems, errOn30, prefix + ".id", null, "must be unique: " + repository.getId() + " -> "
558 + existing.getUrl() + " vs " + repository.getUrl(), repository );
559 }
560 else
561 {
562 index.put( key, repository );
563 }
564 }
565 }
566
567 private void validateRepository( ModelProblemCollector problems, Repository repository, String prefix,
568 ModelBuildingRequest request )
569 {
570 if ( repository != null )
571 {
572 Severity errOn31 = getSeverity( request, ModelBuildingRequest.VALIDATION_LEVEL_MAVEN_3_1 );
573
574 validateBannedCharacters( prefix + ".id", problems, errOn31, repository.getId(), null, repository,
575 ILLEGAL_REPO_ID_CHARS );
576
577 if ( "local".equals( repository.getId() ) )
578 {
579 addViolation( problems, errOn31, prefix + ".id", null, "must not be 'local'"
580 + ", this identifier is reserved for the local repository"
581 + ", using it for other repositories will corrupt your repository metadata.", repository );
582 }
583
584 if ( "legacy".equals( repository.getLayout() ) )
585 {
586 addViolation( problems, Severity.WARNING, prefix + ".layout", repository.getId(),
587 "uses the unsupported value 'legacy', artifact resolution might fail.", repository );
588 }
589 }
590 }
591
592 private void validateResources( ModelProblemCollector problems, List<Resource> resources, String prefix,
593 ModelBuildingRequest request )
594 {
595 Severity errOn30 = getSeverity( request, ModelBuildingRequest.VALIDATION_LEVEL_MAVEN_3_0 );
596
597 for ( Resource resource : resources )
598 {
599 validateStringNotEmpty( prefix + ".directory", problems, Severity.ERROR, resource.getDirectory(),
600 resource );
601
602 validateBoolean( prefix + ".filtering", problems, errOn30, resource.getFiltering(),
603 resource.getDirectory(), resource );
604 }
605 }
606
607
608
609
610
611 private boolean validateId( String fieldName, ModelProblemCollector problems, String id,
612 InputLocationTracker tracker )
613 {
614 return validateId( fieldName, problems, Severity.ERROR, id, null, tracker );
615 }
616
617 private boolean validateId( String fieldName, ModelProblemCollector problems, Severity severity, String id,
618 String sourceHint, InputLocationTracker tracker )
619 {
620 if ( !validateStringNotEmpty( fieldName, problems, severity, id, sourceHint, tracker ) )
621 {
622 return false;
623 }
624 else
625 {
626 boolean match = id.matches( ID_REGEX );
627 if ( !match )
628 {
629 addViolation( problems, severity, fieldName, sourceHint, "with value '" + id
630 + "' does not match a valid id pattern.", tracker );
631 }
632 return match;
633 }
634 }
635
636 private boolean validateStringNoExpression( String fieldName, ModelProblemCollector problems, Severity severity,
637 String string, InputLocationTracker tracker )
638 {
639 if ( !hasExpression( string ) )
640 {
641 return true;
642 }
643
644 addViolation( problems, severity, fieldName, null, "contains an expression but should be a constant.",
645 tracker );
646
647 return false;
648 }
649
650 private boolean hasExpression( String value )
651 {
652 return value != null && value.contains( "${" );
653 }
654
655 private boolean validateStringNotEmpty( String fieldName, ModelProblemCollector problems, Severity severity,
656 String string, InputLocationTracker tracker )
657 {
658 return validateStringNotEmpty( fieldName, problems, severity, string, null, tracker );
659 }
660
661
662
663
664
665
666
667
668
669 private boolean validateStringNotEmpty( String fieldName, ModelProblemCollector problems, Severity severity,
670 String string, String sourceHint, InputLocationTracker tracker )
671 {
672 if ( !validateNotNull( fieldName, problems, severity, string, sourceHint, tracker ) )
673 {
674 return false;
675 }
676
677 if ( string.length() > 0 )
678 {
679 return true;
680 }
681
682 addViolation( problems, severity, fieldName, sourceHint, "is missing.", tracker );
683
684 return false;
685 }
686
687
688
689
690
691
692
693
694 private boolean validateNotNull( String fieldName, ModelProblemCollector problems, Severity severity,
695 Object object, String sourceHint, InputLocationTracker tracker )
696 {
697 if ( object != null )
698 {
699 return true;
700 }
701
702 addViolation( problems, severity, fieldName, sourceHint, "is missing.", tracker );
703
704 return false;
705 }
706
707 private boolean validateBoolean( String fieldName, ModelProblemCollector problems, Severity severity,
708 String string, String sourceHint, InputLocationTracker tracker )
709 {
710 if ( string == null || string.length() <= 0 )
711 {
712 return true;
713 }
714
715 if ( "true".equalsIgnoreCase( string ) || "false".equalsIgnoreCase( string ) )
716 {
717 return true;
718 }
719
720 addViolation( problems, severity, fieldName, sourceHint, "must be 'true' or 'false' but is '" + string + "'.",
721 tracker );
722
723 return false;
724 }
725
726 private boolean validateEnum( String fieldName, ModelProblemCollector problems, Severity severity, String string,
727 String sourceHint, InputLocationTracker tracker, String... validValues )
728 {
729 if ( string == null || string.length() <= 0 )
730 {
731 return true;
732 }
733
734 List<String> values = Arrays.asList( validValues );
735
736 if ( values.contains( string ) )
737 {
738 return true;
739 }
740
741 addViolation( problems, severity, fieldName, sourceHint, "must be one of " + values + " but is '" + string
742 + "'.", tracker );
743
744 return false;
745 }
746
747 private boolean validateBannedCharacters( String fieldName, ModelProblemCollector problems, Severity severity,
748 String string, String sourceHint, InputLocationTracker tracker,
749 String banned )
750 {
751 if ( string != null )
752 {
753 for ( int i = string.length() - 1; i >= 0; i-- )
754 {
755 if ( banned.indexOf( string.charAt( i ) ) >= 0 )
756 {
757 addViolation( problems, severity, fieldName, sourceHint,
758 "must not contain any of these characters " + banned + " but found "
759 + string.charAt( i ), tracker );
760 return false;
761 }
762 }
763 }
764
765 return true;
766 }
767
768 private boolean validateVersion( String fieldName, ModelProblemCollector problems, Severity severity,
769 String string, String sourceHint, InputLocationTracker tracker )
770 {
771 if ( string == null || string.length() <= 0 )
772 {
773 return true;
774 }
775
776 if ( hasExpression( string ) )
777 {
778 addViolation( problems, severity, fieldName, sourceHint,
779 "must be a valid version but is '" + string + "'.", tracker );
780 return false;
781 }
782
783 if ( !validateBannedCharacters( fieldName, problems, severity, string, sourceHint, tracker,
784 ILLEGAL_VERSION_CHARS ) )
785 {
786 return false;
787 }
788
789 return true;
790 }
791
792 private boolean validateProperSnapshotVersion( String fieldName, ModelProblemCollector problems, Severity severity,
793 String string, String sourceHint, InputLocationTracker tracker )
794 {
795 if ( string == null || string.length() <= 0 )
796 {
797 return true;
798 }
799
800 if ( string.endsWith( "SNAPSHOT" ) && !string.endsWith( "-SNAPSHOT" ) )
801 {
802 addViolation( problems, severity, fieldName, sourceHint, "uses an unsupported snapshot version format"
803 + ", should be '*-SNAPSHOT' instead.", tracker );
804 return false;
805 }
806
807 return true;
808 }
809
810 private boolean validatePluginVersion( String fieldName, ModelProblemCollector problems, String string,
811 String sourceHint, InputLocationTracker tracker,
812 ModelBuildingRequest request )
813 {
814 if ( string == null )
815 {
816
817 return true;
818 }
819
820 Severity errOn30 = getSeverity( request, ModelBuildingRequest.VALIDATION_LEVEL_MAVEN_3_0 );
821
822 if ( !validateVersion( fieldName, problems, errOn30, string, sourceHint, tracker ) )
823 {
824 return false;
825 }
826
827 if ( string.length() <= 0 || "RELEASE".equals( string ) || "LATEST".equals( string ) )
828 {
829 addViolation( problems, errOn30, fieldName, sourceHint, "must be a valid version but is '" + string + "'.",
830 tracker );
831 return false;
832 }
833
834 return true;
835 }
836
837 private static void addViolation( ModelProblemCollector problems, Severity severity, String fieldName,
838 String sourceHint, String message, InputLocationTracker tracker )
839 {
840 StringBuilder buffer = new StringBuilder( 256 );
841 buffer.append( '\'' ).append( fieldName ).append( '\'' );
842
843 if ( sourceHint != null )
844 {
845 buffer.append( " for " ).append( sourceHint );
846 }
847
848 buffer.append( ' ' ).append( message );
849
850 problems.add( severity, buffer.toString(), getLocation( fieldName, tracker ), null );
851 }
852
853 private static InputLocation getLocation( String fieldName, InputLocationTracker tracker )
854 {
855 InputLocation location = null;
856
857 if ( tracker != null )
858 {
859 if ( fieldName != null )
860 {
861 Object key = fieldName;
862
863 int idx = fieldName.lastIndexOf( '.' );
864 if ( idx >= 0 )
865 {
866 fieldName = fieldName.substring( idx + 1 );
867 key = fieldName;
868 }
869
870 if ( fieldName.endsWith( "]" ) )
871 {
872 key = fieldName.substring( fieldName.lastIndexOf( '[' ) + 1, fieldName.length() - 1 );
873 try
874 {
875 key = Integer.valueOf( key.toString() );
876 }
877 catch ( NumberFormatException e )
878 {
879
880 }
881 }
882
883 location = tracker.getLocation( key );
884 }
885
886 if ( location == null )
887 {
888 location = tracker.getLocation( "" );
889 }
890 }
891
892 return location;
893 }
894
895 private static boolean equals( String s1, String s2 )
896 {
897 return StringUtils.clean( s1 ).equals( StringUtils.clean( s2 ) );
898 }
899
900 private static Severity getSeverity( ModelBuildingRequest request, int errorThreshold )
901 {
902 return getSeverity( request.getValidationLevel(), errorThreshold );
903 }
904
905 private static Severity getSeverity( int validationLevel, int errorThreshold )
906 {
907 if ( validationLevel < errorThreshold )
908 {
909 return Severity.WARNING;
910 }
911 else
912 {
913 return Severity.ERROR;
914 }
915 }
916
917 }