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