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