View Javadoc
1   package org.apache.maven.model.building;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *   http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  
23  import org.apache.maven.artifact.versioning.DefaultArtifactVersion;
24  import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException;
25  import org.apache.maven.artifact.versioning.VersionRange;
26  import org.apache.maven.model.Activation;
27  import org.apache.maven.model.Build;
28  import org.apache.maven.model.Dependency;
29  import org.apache.maven.model.DependencyManagement;
30  import org.apache.maven.model.InputLocation;
31  import org.apache.maven.model.InputSource;
32  import org.apache.maven.model.Model;
33  import org.apache.maven.model.Parent;
34  import org.apache.maven.model.Plugin;
35  import org.apache.maven.model.PluginManagement;
36  import org.apache.maven.model.Profile;
37  import org.apache.maven.model.Repository;
38  import org.apache.maven.model.building.ModelProblem.Severity;
39  import org.apache.maven.model.building.ModelProblem.Version;
40  import org.apache.maven.model.composition.DependencyManagementImporter;
41  import org.apache.maven.model.inheritance.InheritanceAssembler;
42  import org.apache.maven.model.interpolation.ModelInterpolator;
43  import org.apache.maven.model.io.ModelParseException;
44  import org.apache.maven.model.management.DependencyManagementInjector;
45  import org.apache.maven.model.management.PluginManagementInjector;
46  import org.apache.maven.model.normalization.ModelNormalizer;
47  import org.apache.maven.model.path.ModelPathTranslator;
48  import org.apache.maven.model.path.ModelUrlNormalizer;
49  import org.apache.maven.model.plugin.LifecycleBindingsInjector;
50  import org.apache.maven.model.plugin.PluginConfigurationExpander;
51  import org.apache.maven.model.plugin.ReportConfigurationExpander;
52  import org.apache.maven.model.plugin.ReportingConverter;
53  import org.apache.maven.model.profile.DefaultProfileActivationContext;
54  import org.apache.maven.model.profile.ProfileInjector;
55  import org.apache.maven.model.profile.ProfileSelector;
56  import org.apache.maven.model.resolution.InvalidRepositoryException;
57  import org.apache.maven.model.resolution.ModelResolver;
58  import org.apache.maven.model.resolution.UnresolvableModelException;
59  import org.apache.maven.model.resolution.WorkspaceModelResolver;
60  import org.apache.maven.model.superpom.SuperPomProvider;
61  import org.apache.maven.model.validation.ModelValidator;
62  import org.codehaus.plexus.interpolation.MapBasedValueSource;
63  import org.codehaus.plexus.interpolation.StringSearchInterpolator;
64  import org.eclipse.sisu.Nullable;
65  
66  import java.io.File;
67  import java.io.IOException;
68  import java.util.ArrayList;
69  import java.util.Collection;
70  import java.util.HashMap;
71  import java.util.Iterator;
72  import java.util.LinkedHashSet;
73  import java.util.List;
74  import java.util.Map;
75  import java.util.Objects;
76  import java.util.Properties;
77  
78  import javax.inject.Inject;
79  import javax.inject.Named;
80  import javax.inject.Singleton;
81  
82  import static org.apache.maven.model.building.Result.error;
83  import static org.apache.maven.model.building.Result.newResult;
84  
85  /**
86   * @author Benjamin Bentmann
87   */
88  @Named
89  @Singleton
90  public class DefaultModelBuilder
91      implements ModelBuilder
92  {
93      @Inject
94      private ModelProcessor modelProcessor;
95  
96      @Inject
97      private ModelValidator modelValidator;
98  
99      @Inject
100     private ModelNormalizer modelNormalizer;
101 
102     @Inject
103     private ModelInterpolator modelInterpolator;
104 
105     @Inject
106     private ModelPathTranslator modelPathTranslator;
107 
108     @Inject
109     private ModelUrlNormalizer modelUrlNormalizer;
110 
111     @Inject
112     private SuperPomProvider superPomProvider;
113 
114     @Inject
115     private InheritanceAssembler inheritanceAssembler;
116 
117     @Inject
118     private ProfileSelector profileSelector;
119 
120     @Inject
121     private ProfileInjector profileInjector;
122 
123     @Inject
124     private PluginManagementInjector pluginManagementInjector;
125 
126     @Inject
127     private DependencyManagementInjector dependencyManagementInjector;
128 
129     @Inject
130     private DependencyManagementImporter dependencyManagementImporter;
131 
132     @Inject
133     @Nullable
134     private LifecycleBindingsInjector lifecycleBindingsInjector;
135 
136     @Inject
137     private PluginConfigurationExpander pluginConfigurationExpander;
138 
139     @Inject
140     private ReportConfigurationExpander reportConfigurationExpander;
141 
142     @Inject
143     private ReportingConverter reportingConverter;
144 
145     public DefaultModelBuilder setModelProcessor( ModelProcessor modelProcessor )
146     {
147         this.modelProcessor = modelProcessor;
148         return this;
149     }
150 
151     public DefaultModelBuilder setModelValidator( ModelValidator modelValidator )
152     {
153         this.modelValidator = modelValidator;
154         return this;
155     }
156 
157     public DefaultModelBuilder setModelNormalizer( ModelNormalizer modelNormalizer )
158     {
159         this.modelNormalizer = modelNormalizer;
160         return this;
161     }
162 
163     public DefaultModelBuilder setModelInterpolator( ModelInterpolator modelInterpolator )
164     {
165         this.modelInterpolator = modelInterpolator;
166         return this;
167     }
168 
169     public DefaultModelBuilder setModelPathTranslator( ModelPathTranslator modelPathTranslator )
170     {
171         this.modelPathTranslator = modelPathTranslator;
172         return this;
173     }
174 
175     public DefaultModelBuilder setModelUrlNormalizer( ModelUrlNormalizer modelUrlNormalizer )
176     {
177         this.modelUrlNormalizer = modelUrlNormalizer;
178         return this;
179     }
180 
181     public DefaultModelBuilder setSuperPomProvider( SuperPomProvider superPomProvider )
182     {
183         this.superPomProvider = superPomProvider;
184         return this;
185     }
186 
187     public DefaultModelBuilder setProfileSelector( ProfileSelector profileSelector )
188     {
189         this.profileSelector = profileSelector;
190         return this;
191     }
192 
193     public DefaultModelBuilder setProfileInjector( ProfileInjector profileInjector )
194     {
195         this.profileInjector = profileInjector;
196         return this;
197     }
198 
199     public DefaultModelBuilder setInheritanceAssembler( InheritanceAssembler inheritanceAssembler )
200     {
201         this.inheritanceAssembler = inheritanceAssembler;
202         return this;
203     }
204 
205     public DefaultModelBuilder setDependencyManagementImporter( DependencyManagementImporter depMgmtImporter )
206     {
207         this.dependencyManagementImporter = depMgmtImporter;
208         return this;
209     }
210 
211     public DefaultModelBuilder setDependencyManagementInjector( DependencyManagementInjector depMgmtInjector )
212     {
213         this.dependencyManagementInjector = depMgmtInjector;
214         return this;
215     }
216 
217     public DefaultModelBuilder setLifecycleBindingsInjector( LifecycleBindingsInjector lifecycleBindingsInjector )
218     {
219         this.lifecycleBindingsInjector = lifecycleBindingsInjector;
220         return this;
221     }
222 
223     public DefaultModelBuilder setPluginConfigurationExpander( PluginConfigurationExpander pluginConfigurationExpander )
224     {
225         this.pluginConfigurationExpander = pluginConfigurationExpander;
226         return this;
227     }
228 
229     public DefaultModelBuilder setPluginManagementInjector( PluginManagementInjector pluginManagementInjector )
230     {
231         this.pluginManagementInjector = pluginManagementInjector;
232         return this;
233     }
234 
235     public DefaultModelBuilder setReportConfigurationExpander( ReportConfigurationExpander reportConfigurationExpander )
236     {
237         this.reportConfigurationExpander = reportConfigurationExpander;
238         return this;
239     }
240 
241     public DefaultModelBuilder setReportingConverter( ReportingConverter reportingConverter )
242     {
243         this.reportingConverter = reportingConverter;
244         return this;
245     }
246 
247     @SuppressWarnings( "checkstyle:methodlength" )
248     @Override
249     public ModelBuildingResult build( ModelBuildingRequest request )
250         throws ModelBuildingException
251     {
252         return build( request, new LinkedHashSet<String>() );
253     }
254 
255     @SuppressWarnings( "checkstyle:methodlength" )
256     protected ModelBuildingResult build( ModelBuildingRequest request, Collection<String> importIds )
257         throws ModelBuildingException
258     {
259         // phase 1
260         DefaultModelBuildingResult result = new DefaultModelBuildingResult();
261 
262         DefaultModelProblemCollector problems = new DefaultModelProblemCollector( result );
263 
264         // profile activation
265         DefaultProfileActivationContext profileActivationContext = getProfileActivationContext( request );
266 
267         problems.setSource( "(external profiles)" );
268         List<Profile> activeExternalProfiles = profileSelector.getActiveProfiles( request.getProfiles(),
269                                                                                   profileActivationContext, problems );
270 
271         result.setActiveExternalProfiles( activeExternalProfiles );
272 
273         if ( !activeExternalProfiles.isEmpty() )
274         {
275             Properties profileProps = new Properties();
276             for ( Profile profile : activeExternalProfiles )
277             {
278                 profileProps.putAll( profile.getProperties() );
279             }
280             profileProps.putAll( profileActivationContext.getUserProperties() );
281             profileActivationContext.setUserProperties( profileProps );
282         }
283 
284         // read and validate raw model
285         Model inputModel = request.getRawModel();
286         if ( inputModel == null )
287         {
288             inputModel = readModel( request.getModelSource(), request.getPomFile(), request, problems );
289         }
290 
291         problems.setRootModel( inputModel );
292 
293         ModelData resultData = new ModelData( request.getModelSource(), inputModel );
294         ModelData superData = new ModelData( null, getSuperModel() );
295 
296         Collection<String> parentIds = new LinkedHashSet<>();
297         List<ModelData> lineage = new ArrayList<>();
298 
299         for ( ModelData currentData = resultData; currentData != null; )
300         {
301             lineage.add( currentData );
302 
303             Model rawModel = currentData.getModel();
304             currentData.setRawModel( rawModel );
305 
306             Model tmpModel = rawModel.clone();
307             currentData.setModel( tmpModel );
308 
309             problems.setSource( tmpModel );
310 
311             // model normalization
312             modelNormalizer.mergeDuplicates( tmpModel, request, problems );
313 
314             profileActivationContext.setProjectProperties( tmpModel.getProperties() );
315 
316             List<Profile> activePomProfiles = profileSelector.getActiveProfiles( rawModel.getProfiles(),
317                                                                                  profileActivationContext, problems );
318             currentData.setActiveProfiles( activePomProfiles );
319 
320             Map<String, Activation> interpolatedActivations = getProfileActivations( rawModel, false );
321             injectProfileActivations( tmpModel, interpolatedActivations );
322 
323             // profile injection
324             for ( Profile activeProfile : activePomProfiles )
325             {
326                 profileInjector.injectProfile( tmpModel, activeProfile, request, problems );
327             }
328 
329             if ( currentData == resultData )
330             {
331                 for ( Profile activeProfile : activeExternalProfiles )
332                 {
333                     profileInjector.injectProfile( tmpModel, activeProfile, request, problems );
334                 }
335             }
336 
337             if ( currentData == superData )
338             {
339                 break;
340             }
341 
342             configureResolver( request.getModelResolver(), tmpModel, problems );
343 
344             ModelData parentData = readParent( tmpModel, currentData.getSource(), request, problems );
345 
346             if ( parentData == null )
347             {
348                 currentData = superData;
349             }
350             else if ( currentData == resultData )
351             { // First iteration - add initial id after version resolution.
352                 currentData.setGroupId( currentData.getRawModel().getGroupId() == null ? parentData.getGroupId()
353                                                                                       : currentData.getRawModel()
354                                                                                           .getGroupId() );
355 
356                 currentData.setVersion( currentData.getRawModel().getVersion() == null ? parentData.getVersion()
357                                                                                       : currentData.getRawModel()
358                                                                                           .getVersion() );
359 
360                 currentData.setArtifactId( currentData.getRawModel().getArtifactId() );
361                 parentIds.add( currentData.getId() );
362                 // Reset - only needed for 'getId'.
363                 currentData.setGroupId( null );
364                 currentData.setArtifactId( null );
365                 currentData.setVersion( null );
366                 currentData = parentData;
367             }
368             else if ( !parentIds.add( parentData.getId() ) )
369             {
370                 String message = "The parents form a cycle: ";
371                 for ( String modelId : parentIds )
372                 {
373                     message += modelId + " -> ";
374                 }
375                 message += parentData.getId();
376 
377                 problems.add( new ModelProblemCollectorRequest( ModelProblem.Severity.FATAL, ModelProblem.Version.BASE )
378                     .setMessage( message ) );
379 
380                 throw problems.newModelBuildingException();
381             }
382             else
383             {
384                 currentData = parentData;
385             }
386         }
387 
388         problems.setSource( inputModel );
389         checkPluginVersions( lineage, request, problems );
390 
391         // inheritance assembly
392         assembleInheritance( lineage, request, problems );
393 
394         Model resultModel = resultData.getModel();
395 
396         problems.setSource( resultModel );
397         problems.setRootModel( resultModel );
398 
399         // model interpolation
400         resultModel = interpolateModel( resultModel, request, problems );
401         resultData.setModel( resultModel );
402 
403         if ( resultModel.getParent() != null )
404         {
405             final ModelData parentData = lineage.get( 1 );
406             if ( parentData.getVersion() == null || parentData.getVersion().contains( "${" ) )
407             {
408                 final Model interpolatedParent = interpolateModel( parentData.getModel(), request, problems );
409                 // parentData.setModel( interpolatedParent );
410                 parentData.setVersion( interpolatedParent.getVersion() );
411             }
412         }
413 
414         // url normalization
415         modelUrlNormalizer.normalize( resultModel, request );
416 
417         // Now the fully interpolated model is available: reconfigure the resolver
418         configureResolver( request.getModelResolver(), resultModel, problems, true );
419 
420         resultData.setGroupId( resultModel.getGroupId() );
421         resultData.setArtifactId( resultModel.getArtifactId() );
422         resultData.setVersion( resultModel.getVersion() );
423 
424         result.setEffectiveModel( resultModel );
425 
426         for ( ModelData currentData : lineage )
427         {
428             String modelId = ( currentData != superData ) ? currentData.getId() : "";
429 
430             result.addModelId( modelId );
431             result.setActivePomProfiles( modelId, currentData.getActiveProfiles() );
432             result.setRawModel( modelId, currentData.getRawModel() );
433         }
434 
435         if ( !request.isTwoPhaseBuilding() )
436         {
437             build( request, result, importIds );
438         }
439 
440         return result;
441     }
442 
443     @Override
444     public ModelBuildingResult build( ModelBuildingRequest request, ModelBuildingResult result )
445         throws ModelBuildingException
446     {
447         return build( request, result, new LinkedHashSet<String>() );
448     }
449 
450     private ModelBuildingResult build( ModelBuildingRequest request, ModelBuildingResult result,
451                                        Collection<String> imports )
452         throws ModelBuildingException
453     {
454         // phase 2
455         Model resultModel = result.getEffectiveModel();
456 
457         DefaultModelProblemCollector problems = new DefaultModelProblemCollector( result );
458         problems.setSource( resultModel );
459         problems.setRootModel( resultModel );
460 
461         // model path translation
462         modelPathTranslator.alignToBaseDirectory( resultModel, resultModel.getProjectDirectory(), request );
463 
464         // plugin management injection
465         pluginManagementInjector.injectManagement( resultModel, request, problems );
466 
467         fireEvent( resultModel, request, problems, ModelBuildingEventCatapult.BUILD_EXTENSIONS_ASSEMBLED );
468 
469         if ( request.isProcessPlugins() )
470         {
471             if ( lifecycleBindingsInjector == null )
472             {
473                 throw new IllegalStateException( "lifecycle bindings injector is missing" );
474             }
475 
476             // lifecycle bindings injection
477             lifecycleBindingsInjector.injectLifecycleBindings( resultModel, request, problems );
478         }
479 
480         // dependency management import
481         importDependencyManagement( resultModel, request, problems, imports );
482 
483         // dependency management injection
484         dependencyManagementInjector.injectManagement( resultModel, request, problems );
485 
486         modelNormalizer.injectDefaultValues( resultModel, request, problems );
487 
488         if ( request.isProcessPlugins() )
489         {
490             // reports configuration
491             reportConfigurationExpander.expandPluginConfiguration( resultModel, request, problems );
492 
493             // reports conversion to decoupled site plugin
494             reportingConverter.convertReporting( resultModel, request, problems );
495 
496             // plugins configuration
497             pluginConfigurationExpander.expandPluginConfiguration( resultModel, request, problems );
498         }
499 
500         // effective model validation
501         modelValidator.validateEffectiveModel( resultModel, request, problems );
502 
503         if ( hasModelErrors( problems ) )
504         {
505             throw problems.newModelBuildingException();
506         }
507 
508         return result;
509     }
510 
511     @Override
512     public Result<? extends Model> buildRawModel( File pomFile, int validationLevel, boolean locationTracking )
513     {
514         final ModelBuildingRequest request = new DefaultModelBuildingRequest().setValidationLevel( validationLevel )
515             .setLocationTracking( locationTracking );
516         final DefaultModelProblemCollector collector =
517             new DefaultModelProblemCollector( new DefaultModelBuildingResult() );
518         try
519         {
520             return newResult( readModel( null, pomFile, request, collector ), collector.getProblems() );
521         }
522         catch ( ModelBuildingException e )
523         {
524             return error( collector.getProblems() );
525         }
526     }
527 
528     private Model readModel( ModelSource modelSource, File pomFile, ModelBuildingRequest request,
529                              DefaultModelProblemCollector problems )
530         throws ModelBuildingException
531     {
532         Model model;
533 
534         if ( modelSource == null )
535         {
536             if ( pomFile != null )
537             {
538                 modelSource = new FileModelSource( pomFile );
539             }
540             else
541             {
542                 throw new NullPointerException( "neither pomFile nor modelSource can be null" );
543             }
544         }
545 
546         problems.setSource( modelSource.getLocation() );
547         try
548         {
549             boolean strict = request.getValidationLevel() >= ModelBuildingRequest.VALIDATION_LEVEL_MAVEN_2_0;
550             InputSource source = request.isLocationTracking() ? new InputSource() : null;
551 
552             Map<String, Object> options = new HashMap<>();
553             options.put( ModelProcessor.IS_STRICT, strict );
554             options.put( ModelProcessor.INPUT_SOURCE, source );
555             options.put( ModelProcessor.SOURCE, modelSource );
556 
557             try
558             {
559                 model = modelProcessor.read( modelSource.getInputStream(), options );
560             }
561             catch ( ModelParseException e )
562             {
563                 if ( !strict )
564                 {
565                     throw e;
566                 }
567 
568                 options.put( ModelProcessor.IS_STRICT, Boolean.FALSE );
569 
570                 try
571                 {
572                     model = modelProcessor.read( modelSource.getInputStream(), options );
573                 }
574                 catch ( ModelParseException ne )
575                 {
576                     // still unreadable even in non-strict mode, rethrow original error
577                     throw e;
578                 }
579 
580                 if ( pomFile != null )
581                 {
582                     problems.add( new ModelProblemCollectorRequest( Severity.ERROR, Version.V20 )
583                         .setMessage( "Malformed POM " + modelSource.getLocation() + ": " + e.getMessage() )
584                         .setException( e ) );
585                 }
586                 else
587                 {
588                     problems.add( new ModelProblemCollectorRequest( Severity.WARNING, Version.V20 )
589                         .setMessage( "Malformed POM " + modelSource.getLocation() + ": " + e.getMessage() )
590                         .setException( e ) );
591                 }
592             }
593 
594             if ( source != null )
595             {
596                 source.setModelId( ModelProblemUtils.toId( model ) );
597                 source.setLocation( modelSource.getLocation() );
598             }
599         }
600         catch ( ModelParseException e )
601         {
602             problems.add( new ModelProblemCollectorRequest( Severity.FATAL, Version.BASE )
603                 .setMessage( "Non-parseable POM " + modelSource.getLocation() + ": " + e.getMessage() )
604                 .setException( e ) );
605             throw problems.newModelBuildingException();
606         }
607         catch ( IOException e )
608         {
609             String msg = e.getMessage();
610             if ( msg == null || msg.length() <= 0 )
611             {
612                 // NOTE: There's java.nio.charset.MalformedInputException and sun.io.MalformedInputException
613                 if ( e.getClass().getName().endsWith( "MalformedInputException" ) )
614                 {
615                     msg = "Some input bytes do not match the file encoding.";
616                 }
617                 else
618                 {
619                     msg = e.getClass().getSimpleName();
620                 }
621             }
622             problems.add( new ModelProblemCollectorRequest( Severity.FATAL, Version.BASE )
623                 .setMessage( "Non-readable POM " + modelSource.getLocation() + ": " + msg ).setException( e ) );
624             throw problems.newModelBuildingException();
625         }
626 
627         if ( pomFile != null )
628         {
629             model.setPomFile( pomFile );
630         }
631         else if ( modelSource instanceof FileModelSource )
632         {
633             model.setPomFile( ( (FileModelSource) modelSource ).getFile() );
634         }
635 
636         problems.setSource( model );
637         modelValidator.validateRawModel( model, request, problems );
638 
639         if ( hasFatalErrors( problems ) )
640         {
641             throw problems.newModelBuildingException();
642         }
643 
644         return model;
645     }
646 
647     private DefaultProfileActivationContext getProfileActivationContext( ModelBuildingRequest request )
648     {
649         DefaultProfileActivationContext context = new DefaultProfileActivationContext();
650 
651         context.setActiveProfileIds( request.getActiveProfileIds() );
652         context.setInactiveProfileIds( request.getInactiveProfileIds() );
653         context.setSystemProperties( request.getSystemProperties() );
654         context.setUserProperties( request.getUserProperties() );
655         context.setProjectDirectory( ( request.getPomFile() != null ) ? request.getPomFile().getParentFile() : null );
656 
657         return context;
658     }
659 
660     private void configureResolver( ModelResolver modelResolver, Model model, DefaultModelProblemCollector problems )
661     {
662         configureResolver( modelResolver, model, problems, false );
663     }
664 
665     private void configureResolver( ModelResolver modelResolver, Model model, DefaultModelProblemCollector problems,
666                                     boolean replaceRepositories )
667     {
668         if ( modelResolver == null )
669         {
670             return;
671         }
672 
673         problems.setSource( model );
674 
675         List<Repository> repositories = model.getRepositories();
676 
677         for ( Repository repository : repositories )
678         {
679             try
680             {
681                 modelResolver.addRepository( repository, replaceRepositories );
682             }
683             catch ( InvalidRepositoryException e )
684             {
685                 problems.add( new ModelProblemCollectorRequest( Severity.ERROR, Version.BASE )
686                     .setMessage( "Invalid repository " + repository.getId() + ": " + e.getMessage() )
687                     .setLocation( repository.getLocation( "" ) ).setException( e ) );
688             }
689         }
690     }
691 
692     private void checkPluginVersions( List<ModelData> lineage, ModelBuildingRequest request,
693                                       ModelProblemCollector problems )
694     {
695         if ( request.getValidationLevel() < ModelBuildingRequest.VALIDATION_LEVEL_MAVEN_2_0 )
696         {
697             return;
698         }
699 
700         Map<String, Plugin> plugins = new HashMap<>();
701         Map<String, String> versions = new HashMap<>();
702         Map<String, String> managedVersions = new HashMap<>();
703 
704         for ( int i = lineage.size() - 1; i >= 0; i-- )
705         {
706             Model model = lineage.get( i ).getModel();
707             Build build = model.getBuild();
708             if ( build != null )
709             {
710                 for ( Plugin plugin : build.getPlugins() )
711                 {
712                     String key = plugin.getKey();
713                     if ( versions.get( key ) == null )
714                     {
715                         versions.put( key, plugin.getVersion() );
716                         plugins.put( key, plugin );
717                     }
718                 }
719                 PluginManagement mgmt = build.getPluginManagement();
720                 if ( mgmt != null )
721                 {
722                     for ( Plugin plugin : mgmt.getPlugins() )
723                     {
724                         String key = plugin.getKey();
725                         if ( managedVersions.get( key ) == null )
726                         {
727                             managedVersions.put( key, plugin.getVersion() );
728                         }
729                     }
730                 }
731             }
732         }
733 
734         for ( String key : versions.keySet() )
735         {
736             if ( versions.get( key ) == null && managedVersions.get( key ) == null )
737             {
738                 InputLocation location = plugins.get( key ).getLocation( "" );
739                 problems
740                     .add( new ModelProblemCollectorRequest( Severity.WARNING, Version.V20 )
741                         .setMessage( "'build.plugins.plugin.version' for " + key + " is missing." )
742                         .setLocation( location ) );
743             }
744         }
745     }
746 
747     private void assembleInheritance( List<ModelData> lineage, ModelBuildingRequest request,
748                                       ModelProblemCollector problems )
749     {
750         for ( int i = lineage.size() - 2; i >= 0; i-- )
751         {
752             Model parent = lineage.get( i + 1 ).getModel();
753             Model child = lineage.get( i ).getModel();
754             inheritanceAssembler.assembleModelInheritance( child, parent, request, problems );
755         }
756     }
757 
758     private Map<String, Activation> getProfileActivations( Model model, boolean clone )
759     {
760         Map<String, Activation> activations = new HashMap<>();
761         for ( Profile profile : model.getProfiles() )
762         {
763             Activation activation = profile.getActivation();
764 
765             if ( activation == null )
766             {
767                 continue;
768             }
769 
770             if ( clone )
771             {
772                 activation = activation.clone();
773             }
774 
775             activations.put( profile.getId(), activation );
776         }
777 
778         return activations;
779     }
780 
781     private void injectProfileActivations( Model model, Map<String, Activation> activations )
782     {
783         for ( Profile profile : model.getProfiles() )
784         {
785             Activation activation = profile.getActivation();
786 
787             if ( activation == null )
788             {
789                 continue;
790             }
791 
792             // restore activation
793             profile.setActivation( activations.get( profile.getId() ) );
794         }
795     }
796 
797     private Model interpolateModel( Model model, ModelBuildingRequest request, ModelProblemCollector problems )
798     {
799         // save profile activations before interpolation, since they are evaluated with limited scope
800         Map<String, Activation> originalActivations = getProfileActivations( model, true );
801 
802         Model interpolatedModel =
803             modelInterpolator.interpolateModel( model, model.getProjectDirectory(), request, problems );
804         if ( interpolatedModel.getParent() != null )
805         {
806             StringSearchInterpolator ssi = new StringSearchInterpolator();
807             ssi.addValueSource( new MapBasedValueSource( request.getUserProperties() ) );
808 
809             ssi.addValueSource( new MapBasedValueSource( model.getProperties() ) );
810 
811             ssi.addValueSource( new MapBasedValueSource( request.getSystemProperties() ) );
812 
813             try
814             {
815                 String interpolated = ssi.interpolate( interpolatedModel.getParent().getVersion() );
816                 interpolatedModel.getParent().setVersion( interpolated );
817             }
818             catch ( Exception e )
819             {
820                 ModelProblemCollectorRequest mpcr =
821                     new ModelProblemCollectorRequest( Severity.ERROR,
822                                                       Version.BASE ).setMessage( "Failed to interpolate field: "
823                                                           + interpolatedModel.getParent().getVersion()
824                                                           + " on class: " ).setException( e );
825                 problems.add( mpcr );
826             }
827 
828             
829         }
830         interpolatedModel.setPomFile( model.getPomFile() );
831 
832         // restore profiles with file activation to their value before full interpolation
833         injectProfileActivations( model, originalActivations );
834 
835         return interpolatedModel;
836     }
837 
838     private ModelData readParent( Model childModel, ModelSource childSource, ModelBuildingRequest request,
839                                   DefaultModelProblemCollector problems )
840         throws ModelBuildingException
841     {
842         ModelData parentData;
843 
844         Parent parent = childModel.getParent();
845 
846         if ( parent != null )
847         {
848             String groupId = parent.getGroupId();
849             String artifactId = parent.getArtifactId();
850             String version = parent.getVersion();
851 
852             parentData = getCache( request.getModelCache(), groupId, artifactId, version, ModelCacheTag.RAW );
853 
854             if ( parentData == null )
855             {
856                 parentData = readParentLocally( childModel, childSource, request, problems );
857 
858                 if ( parentData == null )
859                 {
860                     parentData = readParentExternally( childModel, request, problems );
861                 }
862 
863                 putCache( request.getModelCache(), groupId, artifactId, version, ModelCacheTag.RAW, parentData );
864             }
865             else
866             {
867                 /*
868                  * NOTE: This is a sanity check of the cache hit. If the cached parent POM was locally resolved, the
869                  * child's <relativePath> should point at that parent, too. If it doesn't, we ignore the cache and
870                  * resolve externally, to mimic the behavior if the cache didn't exist in the first place. Otherwise,
871                  * the cache would obscure a bad POM.
872                  */
873 
874                 File pomFile = parentData.getModel().getPomFile();
875                 if ( pomFile != null )
876                 {
877                     FileModelSource pomSource = new FileModelSource( pomFile );
878                     ModelSource expectedParentSource = getParentPomFile( childModel, childSource );
879 
880                     if ( expectedParentSource == null || ( expectedParentSource instanceof ModelSource2
881                         && !pomSource.equals(  expectedParentSource ) ) )
882                     {
883                         parentData = readParentExternally( childModel, request, problems );
884                     }
885                 }
886             }
887 
888             Model parentModel = parentData.getModel();
889 
890             if ( !"pom".equals( parentModel.getPackaging() ) )
891             {
892                 problems.add( new ModelProblemCollectorRequest( Severity.ERROR, Version.BASE )
893                     .setMessage( "Invalid packaging for parent POM " + ModelProblemUtils.toSourceHint( parentModel )
894                                      + ", must be \"pom\" but is \"" + parentModel.getPackaging() + "\"" )
895                     .setLocation( parentModel.getLocation( "packaging" ) ) );
896             }
897         }
898         else
899         {
900             parentData = null;
901         }
902 
903         return parentData;
904     }
905 
906     private ModelData readParentLocally( Model childModel, ModelSource childSource, ModelBuildingRequest request,
907                                          DefaultModelProblemCollector problems )
908         throws ModelBuildingException
909     {
910         final Parent parent = childModel.getParent();
911         final ModelSource candidateSource;
912         final Model candidateModel;
913         final WorkspaceModelResolver resolver = request.getWorkspaceModelResolver();
914         if ( resolver == null )
915         {
916             candidateSource = getParentPomFile( childModel, childSource );
917 
918             if ( candidateSource == null )
919             {
920                 return null;
921             }
922 
923             File pomFile = null;
924             if ( candidateSource instanceof FileModelSource )
925             {
926                 pomFile = ( (FileModelSource) candidateSource ).getPomFile();
927             }
928 
929             candidateModel = readModel( candidateSource, pomFile, request, problems );
930         }
931         else
932         {
933             try
934             {
935                 candidateModel =
936                     resolver.resolveRawModel( parent.getGroupId(), parent.getArtifactId(), parent.getVersion() );
937             }
938             catch ( UnresolvableModelException e )
939             {
940                 problems.add( new ModelProblemCollectorRequest( Severity.FATAL, Version.BASE ) //
941                 .setMessage( e.getMessage().toString() ).setLocation( parent.getLocation( "" ) ).setException( e ) );
942                 throw problems.newModelBuildingException();
943             }
944             if ( candidateModel == null )
945             {
946                 return null;
947             }
948             candidateSource = new FileModelSource( candidateModel.getPomFile() );
949         }
950 
951         //
952         // TODO jvz Why isn't all this checking the job of the duty of the workspace resolver, we know that we
953         // have a model that is suitable, yet more checks are done here and the one for the version is problematic
954         // before because with parents as ranges it will never work in this scenario.
955         //
956 
957         String groupId = candidateModel.getGroupId();
958         if ( groupId == null && candidateModel.getParent() != null )
959         {
960             groupId = candidateModel.getParent().getGroupId();
961         }
962         String artifactId = candidateModel.getArtifactId();
963         String version = candidateModel.getVersion();
964         if ( version == null && candidateModel.getParent() != null )
965         {
966             version = candidateModel.getParent().getVersion();
967         }
968 
969         if ( groupId == null || !groupId.equals( parent.getGroupId() ) || artifactId == null
970             || !artifactId.equals( parent.getArtifactId() ) )
971         {
972             StringBuilder buffer = new StringBuilder( 256 );
973             buffer.append( "'parent.relativePath'" );
974             if ( childModel != problems.getRootModel() )
975             {
976                 buffer.append( " of POM " ).append( ModelProblemUtils.toSourceHint( childModel ) );
977             }
978             buffer.append( " points at " ).append( groupId ).append( ':' ).append( artifactId );
979             buffer.append( " instead of " ).append( parent.getGroupId() ).append( ':' );
980             buffer.append( parent.getArtifactId() ).append( ", please verify your project structure" );
981 
982             problems.setSource( childModel );
983             problems.add( new ModelProblemCollectorRequest( Severity.WARNING, Version.BASE )
984                 .setMessage( buffer.toString() ).setLocation( parent.getLocation( "" ) ) );
985             return null;
986         }
987         if ( version != null && parent.getVersion() != null && !version.equals( parent.getVersion() ) )
988         {
989             try
990             {
991                 VersionRange parentRange = VersionRange.createFromVersionSpec( parent.getVersion() );
992                 if ( !parentRange.hasRestrictions() )
993                 {
994                     // the parent version is not a range, we have version skew, drop back to resolution from repo
995                     return null;
996                 }
997                 if ( !parentRange.containsVersion( new DefaultArtifactVersion( version ) ) )
998                 {
999                     // version skew drop back to resolution from the repository
1000                     return null;
1001                 }
1002 
1003                 // Validate versions aren't inherited when using parent ranges the same way as when read externally.
1004                 if ( childModel.getVersion() == null )
1005                 {
1006                     // Message below is checked for in the MNG-2199 core IT.
1007                     problems.add( new ModelProblemCollectorRequest( Severity.FATAL, Version.V31 )
1008                         .setMessage( "Version must be a constant" ).setLocation( childModel.getLocation( "" ) ) );
1009 
1010                 }
1011                 else
1012                 {
1013                     if ( childModel.getVersion().contains( "${" ) )
1014                     {
1015                         // Message below is checked for in the MNG-2199 core IT.
1016                         problems.add( new ModelProblemCollectorRequest( Severity.FATAL, Version.V31 )
1017                             .setMessage( "Version must be a constant" )
1018                             .setLocation( childModel.getLocation( "version" ) ) );
1019 
1020                     }
1021                 }
1022 
1023                 // MNG-2199: What else to check here ?
1024             }
1025             catch ( InvalidVersionSpecificationException e )
1026             {
1027                 // invalid version range, so drop back to resolution from the repository
1028                 return null;
1029             }
1030         }
1031 
1032         //
1033         // Here we just need to know that a version is fine to use but this validation we can do in our workspace
1034         // resolver.
1035         //
1036 
1037         /*
1038          * if ( version == null || !version.equals( parent.getVersion() ) ) { return null; }
1039          */
1040 
1041         ModelData parentData = new ModelData( candidateSource, candidateModel, groupId, artifactId, version );
1042 
1043         return parentData;
1044     }
1045 
1046     private ModelSource getParentPomFile( Model childModel, ModelSource source )
1047     {
1048         if ( !( source instanceof ModelSource2 ) )
1049         {
1050             return null;
1051         }
1052 
1053         String parentPath = childModel.getParent().getRelativePath();
1054 
1055         if ( parentPath == null || parentPath.length() <= 0 )
1056         {
1057             return null;
1058         }
1059 
1060         return ( (ModelSource2) source ).getRelatedSource( parentPath );
1061     }
1062 
1063     private ModelData readParentExternally( Model childModel, ModelBuildingRequest request,
1064                                             DefaultModelProblemCollector problems )
1065         throws ModelBuildingException
1066     {
1067         problems.setSource( childModel );
1068 
1069         Parent parent = childModel.getParent().clone();
1070 
1071         String groupId = parent.getGroupId();
1072         String artifactId = parent.getArtifactId();
1073         String version = parent.getVersion();
1074 
1075         ModelResolver modelResolver = request.getModelResolver();
1076         Objects.requireNonNull( modelResolver,
1077                                 String.format( "request.modelResolver cannot be null (parent POM %s and POM %s)",
1078                                                ModelProblemUtils.toId( groupId, artifactId, version ),
1079                                                ModelProblemUtils.toSourceHint( childModel ) ) );
1080 
1081         ModelSource modelSource;
1082         try
1083         {
1084             modelSource = modelResolver.resolveModel( parent );
1085         }
1086         catch ( UnresolvableModelException e )
1087         {
1088             // Message below is checked for in the MNG-2199 core IT.
1089             StringBuilder buffer = new StringBuilder( 256 );
1090             buffer.append( "Non-resolvable parent POM" );
1091             if ( !containsCoordinates( e.getMessage(), groupId, artifactId, version ) )
1092             {
1093                 buffer.append( ' ' ).append( ModelProblemUtils.toId( groupId, artifactId, version ) );
1094             }
1095             if ( childModel != problems.getRootModel() )
1096             {
1097                 buffer.append( " for " ).append( ModelProblemUtils.toId( childModel ) );
1098             }
1099             buffer.append( ": " ).append( e.getMessage() );
1100             if ( childModel.getProjectDirectory() != null )
1101             {
1102                 if ( parent.getRelativePath() == null || parent.getRelativePath().length() <= 0 )
1103                 {
1104                     buffer.append( " and 'parent.relativePath' points at no local POM" );
1105                 }
1106                 else
1107                 {
1108                     buffer.append( " and 'parent.relativePath' points at wrong local POM" );
1109                 }
1110             }
1111 
1112             problems.add( new ModelProblemCollectorRequest( Severity.FATAL, Version.BASE )
1113                 .setMessage( buffer.toString() ).setLocation( parent.getLocation( "" ) ).setException( e ) );
1114             throw problems.newModelBuildingException();
1115         }
1116 
1117         ModelBuildingRequest lenientRequest = request;
1118         if ( request.getValidationLevel() > ModelBuildingRequest.VALIDATION_LEVEL_MAVEN_2_0 )
1119         {
1120             lenientRequest = new FilterModelBuildingRequest( request )
1121             {
1122                 @Override
1123                 public int getValidationLevel()
1124                 {
1125                     return ModelBuildingRequest.VALIDATION_LEVEL_MAVEN_2_0;
1126                 }
1127             };
1128         }
1129 
1130         Model parentModel = readModel( modelSource, null, lenientRequest, problems );
1131 
1132         if ( !parent.getVersion().equals( version ) )
1133         {
1134             if ( childModel.getVersion() == null )
1135             {
1136                 // Message below is checked for in the MNG-2199 core IT.
1137                 problems.add( new ModelProblemCollectorRequest( Severity.FATAL, Version.V31 )
1138                     .setMessage( "Version must be a constant" ).setLocation( childModel.getLocation( "" ) ) );
1139 
1140             }
1141             else
1142             {
1143                 if ( childModel.getVersion().contains( "${" ) )
1144                 {
1145                     // Message below is checked for in the MNG-2199 core IT.
1146                     problems.add( new ModelProblemCollectorRequest( Severity.FATAL, Version.V31 )
1147                         .setMessage( "Version must be a constant" )
1148                         .setLocation( childModel.getLocation( "version" ) ) );
1149 
1150                 }
1151             }
1152 
1153             // MNG-2199: What else to check here ?
1154         }
1155 
1156         ModelData parentData = new ModelData( modelSource, parentModel, parent.getGroupId(), parent.getArtifactId(),
1157                                               parent.getVersion() );
1158 
1159         return parentData;
1160     }
1161 
1162     private Model getSuperModel()
1163     {
1164         return superPomProvider.getSuperModel( "4.0.0" ).clone();
1165     }
1166 
1167     @SuppressWarnings( "checkstyle:methodlength" )
1168     private void importDependencyManagement( Model model, ModelBuildingRequest request,
1169                                              DefaultModelProblemCollector problems, Collection<String> importIds )
1170     {
1171         DependencyManagement depMgmt = model.getDependencyManagement();
1172 
1173         if ( depMgmt == null )
1174         {
1175             return;
1176         }
1177 
1178         String importing = model.getGroupId() + ':' + model.getArtifactId() + ':' + model.getVersion();
1179 
1180         importIds.add( importing );
1181 
1182         final WorkspaceModelResolver workspaceResolver = request.getWorkspaceModelResolver();
1183         final ModelResolver modelResolver = request.getModelResolver();
1184 
1185         ModelBuildingRequest importRequest = null;
1186 
1187         List<DependencyManagement> importMgmts = null;
1188 
1189         for ( Iterator<Dependency> it = depMgmt.getDependencies().iterator(); it.hasNext(); )
1190         {
1191             Dependency dependency = it.next();
1192 
1193             if ( !"pom".equals( dependency.getType() ) || !"import".equals( dependency.getScope() ) )
1194             {
1195                 continue;
1196             }
1197 
1198             it.remove();
1199 
1200             String groupId = dependency.getGroupId();
1201             String artifactId = dependency.getArtifactId();
1202             String version = dependency.getVersion();
1203 
1204             if ( groupId == null || groupId.length() <= 0 )
1205             {
1206                 problems.add( new ModelProblemCollectorRequest( Severity.ERROR, Version.BASE )
1207                     .setMessage( "'dependencyManagement.dependencies.dependency.groupId' for "
1208                                      + dependency.getManagementKey() + " is missing." )
1209                     .setLocation( dependency.getLocation( "" ) ) );
1210                 continue;
1211             }
1212             if ( artifactId == null || artifactId.length() <= 0 )
1213             {
1214                 problems.add( new ModelProblemCollectorRequest( Severity.ERROR, Version.BASE )
1215                     .setMessage( "'dependencyManagement.dependencies.dependency.artifactId' for "
1216                                      + dependency.getManagementKey() + " is missing." )
1217                     .setLocation( dependency.getLocation( "" ) ) );
1218                 continue;
1219             }
1220             if ( version == null || version.length() <= 0 )
1221             {
1222                 problems.add( new ModelProblemCollectorRequest( Severity.ERROR, Version.BASE )
1223                     .setMessage( "'dependencyManagement.dependencies.dependency.version' for "
1224                                      + dependency.getManagementKey() + " is missing." )
1225                     .setLocation( dependency.getLocation( "" ) ) );
1226                 continue;
1227             }
1228 
1229             String imported = groupId + ':' + artifactId + ':' + version;
1230 
1231             if ( importIds.contains( imported ) )
1232             {
1233                 String message = "The dependencies of type=pom and with scope=import form a cycle: ";
1234                 for ( String modelId : importIds )
1235                 {
1236                     message += modelId + " -> ";
1237                 }
1238                 message += imported;
1239                 problems.add( new ModelProblemCollectorRequest( Severity.ERROR, Version.BASE ).setMessage( message ) );
1240 
1241                 continue;
1242             }
1243 
1244             DependencyManagement importMgmt = getCache( request.getModelCache(), groupId, artifactId, version,
1245                                                         ModelCacheTag.IMPORT );
1246 
1247             if ( importMgmt == null )
1248             {
1249                 if ( workspaceResolver == null && modelResolver == null )
1250                 {
1251                     throw new NullPointerException( String.format(
1252                         "request.workspaceModelResolver and request.modelResolver cannot be null"
1253                         + " (parent POM %s and POM %s)",
1254                         ModelProblemUtils.toId( groupId, artifactId, version ),
1255                         ModelProblemUtils.toSourceHint( model ) ) );
1256                 }
1257 
1258                 Model importModel = null;
1259                 if ( workspaceResolver != null )
1260                 {
1261                     try
1262                     {
1263                         importModel = workspaceResolver.resolveEffectiveModel( groupId, artifactId, version );
1264                     }
1265                     catch ( UnresolvableModelException e )
1266                     {
1267                         problems.add( new ModelProblemCollectorRequest( Severity.FATAL, Version.BASE )
1268                             .setMessage( e.getMessage().toString() ).setException( e ) );
1269                         continue;
1270                     }
1271                 }
1272 
1273                 // no workspace resolver or workspace resolver returned null (i.e. model not in workspace)
1274                 if ( importModel == null )
1275                 {
1276                     final ModelSource importSource;
1277                     try
1278                     {
1279                         importSource = modelResolver.resolveModel( groupId, artifactId, version );
1280                     }
1281                     catch ( UnresolvableModelException e )
1282                     {
1283                         StringBuilder buffer = new StringBuilder( 256 );
1284                         buffer.append( "Non-resolvable import POM" );
1285                         if ( !containsCoordinates( e.getMessage(), groupId, artifactId, version ) )
1286                         {
1287                             buffer.append( ' ' ).append( ModelProblemUtils.toId( groupId, artifactId, version ) );
1288                         }
1289                         buffer.append( ": " ).append( e.getMessage() );
1290 
1291                         problems.add( new ModelProblemCollectorRequest( Severity.ERROR, Version.BASE )
1292                             .setMessage( buffer.toString() ).setLocation( dependency.getLocation( "" ) )
1293                             .setException( e ) );
1294                         continue;
1295                     }
1296 
1297                     if ( importRequest == null )
1298                     {
1299                         importRequest = new DefaultModelBuildingRequest();
1300                         importRequest.setValidationLevel( ModelBuildingRequest.VALIDATION_LEVEL_MINIMAL );
1301                         importRequest.setModelCache( request.getModelCache() );
1302                         importRequest.setSystemProperties( request.getSystemProperties() );
1303                         importRequest.setUserProperties( request.getUserProperties() );
1304                         importRequest.setLocationTracking( request.isLocationTracking() );
1305                     }
1306 
1307                     importRequest.setModelSource( importSource );
1308                     importRequest.setModelResolver( modelResolver.newCopy() );
1309 
1310                     final ModelBuildingResult importResult;
1311                     try
1312                     {
1313                         importResult = build( importRequest, importIds );
1314                     }
1315                     catch ( ModelBuildingException e )
1316                     {
1317                         problems.addAll( e.getProblems() );
1318                         continue;
1319                     }
1320 
1321                     problems.addAll( importResult.getProblems() );
1322 
1323                     importModel = importResult.getEffectiveModel();
1324                 }
1325 
1326                 importMgmt = importModel.getDependencyManagement();
1327 
1328                 if ( importMgmt == null )
1329                 {
1330                     importMgmt = new DependencyManagement();
1331                 }
1332 
1333                 putCache( request.getModelCache(), groupId, artifactId, version, ModelCacheTag.IMPORT, importMgmt );
1334             }
1335 
1336             if ( importMgmts == null )
1337             {
1338                 importMgmts = new ArrayList<>();
1339             }
1340 
1341             importMgmts.add( importMgmt );
1342         }
1343 
1344         importIds.remove( importing );
1345 
1346         dependencyManagementImporter.importManagement( model, importMgmts, request, problems );
1347     }
1348 
1349     private <T> void putCache( ModelCache modelCache, String groupId, String artifactId, String version,
1350                                ModelCacheTag<T> tag, T data )
1351     {
1352         if ( modelCache != null )
1353         {
1354             modelCache.put( groupId, artifactId, version, tag.getName(), tag.intoCache( data ) );
1355         }
1356     }
1357 
1358     private <T> T getCache( ModelCache modelCache, String groupId, String artifactId, String version,
1359                             ModelCacheTag<T> tag )
1360     {
1361         if ( modelCache != null )
1362         {
1363             Object data = modelCache.get( groupId, artifactId, version, tag.getName() );
1364             if ( data != null )
1365             {
1366                 return tag.fromCache( tag.getType().cast( data ) );
1367             }
1368         }
1369         return null;
1370     }
1371 
1372     private void fireEvent( Model model, ModelBuildingRequest request, ModelProblemCollector problems,
1373                             ModelBuildingEventCatapult catapult )
1374         throws ModelBuildingException
1375     {
1376         ModelBuildingListener listener = request.getModelBuildingListener();
1377 
1378         if ( listener != null )
1379         {
1380             ModelBuildingEvent event = new DefaultModelBuildingEvent( model, request, problems );
1381 
1382             catapult.fire( listener, event );
1383         }
1384     }
1385 
1386     private boolean containsCoordinates( String message, String groupId, String artifactId, String version )
1387     {
1388         return message != null && ( groupId == null || message.contains( groupId ) )
1389             && ( artifactId == null || message.contains( artifactId ) )
1390             && ( version == null || message.contains( version ) );
1391     }
1392 
1393     protected boolean hasModelErrors( ModelProblemCollectorExt problems )
1394     {
1395         if ( problems instanceof DefaultModelProblemCollector )
1396         {
1397             return ( (DefaultModelProblemCollector) problems ).hasErrors();
1398         }
1399         else
1400         {
1401             // the default execution path only knows the DefaultModelProblemCollector,
1402             // only reason it's not in signature is because it's package private
1403             throw new IllegalStateException();
1404         }
1405     }
1406 
1407     protected boolean hasFatalErrors( ModelProblemCollectorExt problems )
1408     {
1409         if ( problems instanceof DefaultModelProblemCollector )
1410         {
1411             return ( (DefaultModelProblemCollector) problems ).hasFatalErrors();
1412         }
1413         else
1414         {
1415             // the default execution path only knows the DefaultModelProblemCollector,
1416             // only reason it's not in signature is because it's package private
1417             throw new IllegalStateException();
1418         }
1419     }
1420 
1421 }