org.apache.maven.model.building.DefaultModelBuilder.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.maven.model.building.DefaultModelBuilder.java

Source

package org.apache.maven.model.building;

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

import org.apache.commons.lang3.Validate;
import org.apache.maven.artifact.versioning.DefaultArtifactVersion;
import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException;
import org.apache.maven.artifact.versioning.VersionRange;
import org.apache.maven.model.Activation;
import org.apache.maven.model.Build;
import org.apache.maven.model.Dependency;
import org.apache.maven.model.DependencyManagement;
import org.apache.maven.model.InputLocation;
import org.apache.maven.model.InputSource;
import org.apache.maven.model.Model;
import org.apache.maven.model.Parent;
import org.apache.maven.model.Plugin;
import org.apache.maven.model.PluginManagement;
import org.apache.maven.model.Profile;
import org.apache.maven.model.Repository;
import org.apache.maven.model.building.ModelProblem.Severity;
import org.apache.maven.model.building.ModelProblem.Version;
import org.apache.maven.model.composition.DependencyManagementImporter;
import org.apache.maven.model.inheritance.InheritanceAssembler;
import org.apache.maven.model.interpolation.ModelInterpolator;
import org.apache.maven.model.io.ModelParseException;
import org.apache.maven.model.management.DependencyManagementInjector;
import org.apache.maven.model.management.PluginManagementInjector;
import org.apache.maven.model.normalization.ModelNormalizer;
import org.apache.maven.model.path.ModelPathTranslator;
import org.apache.maven.model.path.ModelUrlNormalizer;
import org.apache.maven.model.plugin.LifecycleBindingsInjector;
import org.apache.maven.model.plugin.PluginConfigurationExpander;
import org.apache.maven.model.plugin.ReportConfigurationExpander;
import org.apache.maven.model.plugin.ReportingConverter;
import org.apache.maven.model.profile.DefaultProfileActivationContext;
import org.apache.maven.model.profile.ProfileInjector;
import org.apache.maven.model.profile.ProfileSelector;
import org.apache.maven.model.resolution.InvalidRepositoryException;
import org.apache.maven.model.resolution.ModelResolver;
import org.apache.maven.model.resolution.UnresolvableModelException;
import org.apache.maven.model.resolution.WorkspaceModelResolver;
import org.apache.maven.model.superpom.SuperPomProvider;
import org.apache.maven.model.validation.ModelValidator;
import org.codehaus.plexus.component.annotations.Component;
import org.codehaus.plexus.component.annotations.Requirement;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Properties;

import static org.apache.maven.model.building.Result.error;
import static org.apache.maven.model.building.Result.newResult;

/**
 * @author Benjamin Bentmann
 */
@Component(role = ModelBuilder.class)
public class DefaultModelBuilder implements ModelBuilder {
    @Requirement
    private ModelProcessor modelProcessor;

    @Requirement
    private ModelValidator modelValidator;

    @Requirement
    private ModelNormalizer modelNormalizer;

    @Requirement
    private ModelInterpolator modelInterpolator;

    @Requirement
    private ModelPathTranslator modelPathTranslator;

    @Requirement
    private ModelUrlNormalizer modelUrlNormalizer;

    @Requirement
    private SuperPomProvider superPomProvider;

    @Requirement
    private InheritanceAssembler inheritanceAssembler;

    @Requirement
    private ProfileSelector profileSelector;

    @Requirement
    private ProfileInjector profileInjector;

    @Requirement
    private PluginManagementInjector pluginManagementInjector;

    @Requirement
    private DependencyManagementInjector dependencyManagementInjector;

    @Requirement
    private DependencyManagementImporter dependencyManagementImporter;

    @Requirement(optional = true)
    private LifecycleBindingsInjector lifecycleBindingsInjector;

    @Requirement
    private PluginConfigurationExpander pluginConfigurationExpander;

    @Requirement
    private ReportConfigurationExpander reportConfigurationExpander;

    @Requirement
    private ReportingConverter reportingConverter;

    public DefaultModelBuilder setModelProcessor(ModelProcessor modelProcessor) {
        this.modelProcessor = modelProcessor;
        return this;
    }

    public DefaultModelBuilder setModelValidator(ModelValidator modelValidator) {
        this.modelValidator = modelValidator;
        return this;
    }

    public DefaultModelBuilder setModelNormalizer(ModelNormalizer modelNormalizer) {
        this.modelNormalizer = modelNormalizer;
        return this;
    }

    public DefaultModelBuilder setModelInterpolator(ModelInterpolator modelInterpolator) {
        this.modelInterpolator = modelInterpolator;
        return this;
    }

    public DefaultModelBuilder setModelPathTranslator(ModelPathTranslator modelPathTranslator) {
        this.modelPathTranslator = modelPathTranslator;
        return this;
    }

    public DefaultModelBuilder setModelUrlNormalizer(ModelUrlNormalizer modelUrlNormalizer) {
        this.modelUrlNormalizer = modelUrlNormalizer;
        return this;
    }

    public DefaultModelBuilder setSuperPomProvider(SuperPomProvider superPomProvider) {
        this.superPomProvider = superPomProvider;
        return this;
    }

    public DefaultModelBuilder setProfileSelector(ProfileSelector profileSelector) {
        this.profileSelector = profileSelector;
        return this;
    }

    public DefaultModelBuilder setProfileInjector(ProfileInjector profileInjector) {
        this.profileInjector = profileInjector;
        return this;
    }

    public DefaultModelBuilder setInheritanceAssembler(InheritanceAssembler inheritanceAssembler) {
        this.inheritanceAssembler = inheritanceAssembler;
        return this;
    }

    public DefaultModelBuilder setDependencyManagementImporter(DependencyManagementImporter depMngmntImporter) {
        this.dependencyManagementImporter = depMngmntImporter;
        return this;
    }

    public DefaultModelBuilder setDependencyManagementInjector(DependencyManagementInjector depMngmntInjector) {
        this.dependencyManagementInjector = depMngmntInjector;
        return this;
    }

    public DefaultModelBuilder setLifecycleBindingsInjector(LifecycleBindingsInjector lifecycleBindingsInjector) {
        this.lifecycleBindingsInjector = lifecycleBindingsInjector;
        return this;
    }

    public DefaultModelBuilder setPluginConfigurationExpander(
            PluginConfigurationExpander pluginConfigurationExpander) {
        this.pluginConfigurationExpander = pluginConfigurationExpander;
        return this;
    }

    public DefaultModelBuilder setPluginManagementInjector(PluginManagementInjector pluginManagementInjector) {
        this.pluginManagementInjector = pluginManagementInjector;
        return this;
    }

    public DefaultModelBuilder setReportConfigurationExpander(
            ReportConfigurationExpander reportConfigurationExpander) {
        this.reportConfigurationExpander = reportConfigurationExpander;
        return this;
    }

    public DefaultModelBuilder setReportingConverter(ReportingConverter reportingConverter) {
        this.reportingConverter = reportingConverter;
        return this;
    }

    @Override
    public ModelBuildingResult build(ModelBuildingRequest request) throws ModelBuildingException {
        // phase 1
        DefaultModelBuildingResult result = new DefaultModelBuildingResult();

        DefaultModelProblemCollector problems = new DefaultModelProblemCollector(result);

        // profile activation
        DefaultProfileActivationContext profileActivationContext = getProfileActivationContext(request);

        problems.setSource("(external profiles)");
        List<Profile> activeExternalProfiles = profileSelector.getActiveProfiles(request.getProfiles(),
                profileActivationContext, problems);

        result.setActiveExternalProfiles(activeExternalProfiles);

        if (!activeExternalProfiles.isEmpty()) {
            Properties profileProps = new Properties();
            for (Profile profile : activeExternalProfiles) {
                profileProps.putAll(profile.getProperties());
            }
            profileProps.putAll(profileActivationContext.getUserProperties());
            profileActivationContext.setUserProperties(profileProps);
        }

        // read and validate raw model
        Model inputModel = request.getRawModel();
        if (inputModel == null) {
            inputModel = readModel(request.getModelSource(), request.getPomFile(), request, problems);
        }

        problems.setRootModel(inputModel);

        ModelData resultData = new ModelData(request.getModelSource(), inputModel);
        ModelData superData = new ModelData(null, getSuperModel());

        Collection<String> parentIds = new LinkedHashSet<>();
        List<ModelData> lineage = new ArrayList<>();

        for (ModelData currentData = resultData; currentData != null;) {
            lineage.add(currentData);

            Model rawModel = currentData.getModel();
            currentData.setRawModel(rawModel);

            Model tmpModel = rawModel.clone();
            currentData.setModel(tmpModel);

            problems.setSource(tmpModel);

            // model normalization
            modelNormalizer.mergeDuplicates(tmpModel, request, problems);

            profileActivationContext.setProjectProperties(tmpModel.getProperties());

            List<Profile> activePomProfiles = profileSelector.getActiveProfiles(rawModel.getProfiles(),
                    profileActivationContext, problems);
            currentData.setActiveProfiles(activePomProfiles);

            Map<String, Activation> interpolatedActivations = getProfileActivations(rawModel, false);
            injectProfileActivations(tmpModel, interpolatedActivations);

            // profile injection
            for (Profile activeProfile : activePomProfiles) {
                profileInjector.injectProfile(tmpModel, activeProfile, request, problems);
            }

            if (currentData == resultData) {
                for (Profile activeProfile : activeExternalProfiles) {
                    profileInjector.injectProfile(tmpModel, activeProfile, request, problems);
                }
            }

            if (currentData == superData) {
                break;
            }

            configureResolver(request.getModelResolver(), tmpModel, problems);

            ModelData parentData = readParent(tmpModel, currentData.getSource(), request, problems);

            if (parentData == null) {
                currentData = superData;
            } else if (currentData == resultData) { // First iteration - add initial id after version resolution.
                currentData.setGroupId(currentData.getRawModel().getGroupId() == null ? parentData.getGroupId()
                        : currentData.getRawModel().getGroupId());

                currentData.setVersion(currentData.getRawModel().getVersion() == null ? parentData.getVersion()
                        : currentData.getRawModel().getVersion());

                currentData.setArtifactId(currentData.getRawModel().getArtifactId());
                parentIds.add(currentData.getId());
                // Reset - only needed for 'getId'.
                currentData.setGroupId(null);
                currentData.setArtifactId(null);
                currentData.setVersion(null);
                currentData = parentData;
            } else if (!parentIds.add(parentData.getId())) {
                String message = "The parents form a cycle: ";
                for (String modelId : parentIds) {
                    message += modelId + " -> ";
                }
                message += parentData.getId();

                problems.add(
                        new ModelProblemCollectorRequest(ModelProblem.Severity.FATAL, ModelProblem.Version.BASE)
                                .setMessage(message));

                throw problems.newModelBuildingException();
            } else {
                currentData = parentData;
            }
        }

        problems.setSource(inputModel);
        checkPluginVersions(lineage, request, problems);

        // [MNG-5971] Imported dependencies should be available to inheritance processing
        processImports(lineage, request, problems);

        // inheritance assembly
        assembleInheritance(lineage, request, problems);

        Model resultModel = resultData.getModel();

        problems.setSource(resultModel);
        problems.setRootModel(resultModel);

        // model interpolation
        resultModel = interpolateModel(resultModel, request, problems);
        resultData.setModel(resultModel);

        // url normalization
        modelUrlNormalizer.normalize(resultModel, request);

        // Now the fully interpolated model is available: reconfigure the resolver
        configureResolver(request.getModelResolver(), resultModel, problems, true);

        resultData.setGroupId(resultModel.getGroupId());
        resultData.setArtifactId(resultModel.getArtifactId());
        resultData.setVersion(resultModel.getVersion());

        result.setEffectiveModel(resultModel);

        for (ModelData currentData : lineage) {
            String modelId = (currentData != superData) ? currentData.getId() : "";

            result.addModelId(modelId);
            result.setActivePomProfiles(modelId, currentData.getActiveProfiles());
            result.setRawModel(modelId, currentData.getRawModel());
        }

        if (!request.isTwoPhaseBuilding()) {
            build(request, result);
        }

        return result;
    }

    @Override
    public ModelBuildingResult build(ModelBuildingRequest request, ModelBuildingResult result)
            throws ModelBuildingException {
        return build(request, result, new LinkedHashSet<String>(), new LinkedHashSet<String>());
    }

    private ModelBuildingResult build(ModelBuildingRequest request, ModelBuildingResult result,
            Collection<String> managementImports, Collection<String> dependencyImports)
            throws ModelBuildingException {
        // phase 2
        Model resultModel = result.getEffectiveModel();

        DefaultModelProblemCollector problems = new DefaultModelProblemCollector(result);
        problems.setSource(resultModel);
        problems.setRootModel(resultModel);

        // model path translation
        modelPathTranslator.alignToBaseDirectory(resultModel, resultModel.getProjectDirectory(), request);

        // plugin management injection
        pluginManagementInjector.injectManagement(resultModel, request, problems);

        fireEvent(resultModel, request, problems, ModelBuildingEventCatapult.BUILD_EXTENSIONS_ASSEMBLED);

        if (request.isProcessPlugins()) {
            if (lifecycleBindingsInjector == null) {
                throw new IllegalStateException("lifecycle bindings injector is missing");
            }

            // lifecycle bindings injection
            lifecycleBindingsInjector.injectLifecycleBindings(resultModel, request, problems);
        }

        // dependency management injection
        dependencyManagementInjector.injectManagement(resultModel, request, problems);

        modelNormalizer.injectDefaultValues(resultModel, request, problems);

        if (request.isProcessPlugins()) {
            // reports configuration
            reportConfigurationExpander.expandPluginConfiguration(resultModel, request, problems);

            // reports conversion to decoupled site plugin
            reportingConverter.convertReporting(resultModel, request, problems);

            // plugins configuration
            pluginConfigurationExpander.expandPluginConfiguration(resultModel, request, problems);
        }

        // effective model validation
        modelValidator.validateEffectiveModel(resultModel, request, problems);

        if (hasModelErrors(problems)) {
            throw problems.newModelBuildingException();
        }

        return result;
    }

    @Override
    public Result<? extends Model> buildRawModel(File pomFile, int validationLevel, boolean locationTracking) {
        final ModelBuildingRequest request = new DefaultModelBuildingRequest().setValidationLevel(validationLevel)
                .setLocationTracking(locationTracking);
        final DefaultModelProblemCollector collector = new DefaultModelProblemCollector(
                new DefaultModelBuildingResult());
        try {
            return newResult(readModel(null, pomFile, request, collector), collector.getProblems());
        } catch (ModelBuildingException e) {
            return error(collector.getProblems());
        }
    }

    private Model readModel(ModelSource modelSource, File pomFile, ModelBuildingRequest request,
            DefaultModelProblemCollector problems) throws ModelBuildingException {
        Model model;

        if (modelSource == null) {
            if (pomFile != null) {
                modelSource = new FileModelSource(pomFile);
            } else {
                throw new NullPointerException("neither pomFile nor modelSource can be null");
            }
        }

        problems.setSource(modelSource.getLocation());
        try {
            boolean strict = request.getValidationLevel() >= ModelBuildingRequest.VALIDATION_LEVEL_MAVEN_2_0;
            InputSource source = request.isLocationTracking() ? new InputSource() : null;

            Map<String, Object> options = new HashMap<>();
            options.put(ModelProcessor.IS_STRICT, strict);
            options.put(ModelProcessor.INPUT_SOURCE, source);
            options.put(ModelProcessor.SOURCE, modelSource);

            try {
                model = modelProcessor.read(modelSource.getInputStream(), options);
            } catch (ModelParseException e) {
                if (!strict) {
                    throw e;
                }

                options.put(ModelProcessor.IS_STRICT, Boolean.FALSE);

                try {
                    model = modelProcessor.read(modelSource.getInputStream(), options);
                } catch (ModelParseException ne) {
                    // still unreadable even in non-strict mode, rethrow original error
                    throw e;
                }

                if (pomFile != null) {
                    problems.add(new ModelProblemCollectorRequest(Severity.ERROR, Version.V20)
                            .setMessage("Malformed POM " + modelSource.getLocation() + ": " + e.getMessage())
                            .setException(e));
                } else {
                    problems.add(new ModelProblemCollectorRequest(Severity.WARNING, Version.V20)
                            .setMessage("Malformed POM " + modelSource.getLocation() + ": " + e.getMessage())
                            .setException(e));
                }
            }

            if (source != null) {
                source.setModelId(ModelProblemUtils.toId(model));
                source.setLocation(modelSource.getLocation());
            }
        } catch (ModelParseException e) {
            problems.add(new ModelProblemCollectorRequest(Severity.FATAL, Version.BASE)
                    .setMessage("Non-parseable POM " + modelSource.getLocation() + ": " + e.getMessage())
                    .setException(e));
            throw problems.newModelBuildingException();
        } catch (IOException e) {
            String msg = e.getMessage();
            if (msg == null || msg.length() <= 0) {
                // NOTE: There's java.nio.charset.MalformedInputException and sun.io.MalformedInputException
                if (e.getClass().getName().endsWith("MalformedInputException")) {
                    msg = "Some input bytes do not match the file encoding.";
                } else {
                    msg = e.getClass().getSimpleName();
                }
            }
            problems.add(new ModelProblemCollectorRequest(Severity.FATAL, Version.BASE)
                    .setMessage("Non-readable POM " + modelSource.getLocation() + ": " + msg).setException(e));
            throw problems.newModelBuildingException();
        }

        model.setPomFile(pomFile);

        problems.setSource(model);

        modelValidator.validateRawModel(model, request, problems);

        if (hasFatalErrors(problems)) {
            throw problems.newModelBuildingException();
        }

        return model;
    }

    private DefaultProfileActivationContext getProfileActivationContext(ModelBuildingRequest request) {
        DefaultProfileActivationContext context = new DefaultProfileActivationContext();

        context.setActiveProfileIds(request.getActiveProfileIds());
        context.setInactiveProfileIds(request.getInactiveProfileIds());
        context.setSystemProperties(request.getSystemProperties());
        context.setUserProperties(request.getUserProperties());
        context.setProjectDirectory((request.getPomFile() != null) ? request.getPomFile().getParentFile() : null);

        return context;
    }

    private void configureResolver(ModelResolver modelResolver, Model model,
            DefaultModelProblemCollector problems) {
        configureResolver(modelResolver, model, problems, false);
    }

    private void configureResolver(ModelResolver modelResolver, Model model, DefaultModelProblemCollector problems,
            boolean replaceRepositories) {
        if (modelResolver == null) {
            return;
        }

        problems.setSource(model);

        List<Repository> repositories = model.getRepositories();

        for (Repository repository : repositories) {
            try {
                modelResolver.addRepository(repository, replaceRepositories);
            } catch (InvalidRepositoryException e) {
                problems.add(new ModelProblemCollectorRequest(Severity.ERROR, Version.BASE)
                        .setMessage("Invalid repository " + repository.getId() + ": " + e.getMessage())
                        .setLocation(repository.getLocation("")).setException(e));
            }
        }
    }

    private void checkPluginVersions(List<ModelData> lineage, ModelBuildingRequest request,
            ModelProblemCollector problems) {
        if (request.getValidationLevel() < ModelBuildingRequest.VALIDATION_LEVEL_MAVEN_2_0) {
            return;
        }

        Map<String, Plugin> plugins = new HashMap<>();
        Map<String, String> versions = new HashMap<>();
        Map<String, String> managedVersions = new HashMap<>();

        for (int i = lineage.size() - 1; i >= 0; i--) {
            Model model = lineage.get(i).getModel();
            Build build = model.getBuild();
            if (build != null) {
                for (Plugin plugin : build.getPlugins()) {
                    String key = plugin.getKey();
                    if (versions.get(key) == null) {
                        versions.put(key, plugin.getVersion());
                        plugins.put(key, plugin);
                    }
                }
                PluginManagement mngt = build.getPluginManagement();
                if (mngt != null) {
                    for (Plugin plugin : mngt.getPlugins()) {
                        String key = plugin.getKey();
                        if (managedVersions.get(key) == null) {
                            managedVersions.put(key, plugin.getVersion());
                        }
                    }
                }
            }
        }

        for (String key : versions.keySet()) {
            if (versions.get(key) == null && managedVersions.get(key) == null) {
                InputLocation location = plugins.get(key).getLocation("");
                problems.add(new ModelProblemCollectorRequest(Severity.WARNING, Version.V20)
                        .setMessage("'build.plugins.plugin.version' for " + key + " is missing.")
                        .setLocation(location));
            }
        }
    }

    private void processImports(final List<ModelData> lineage, final ModelBuildingRequest request,
            final DefaultModelProblemCollector problems) {
        // [MNG-5971] Imported dependencies should be available to inheritance processing

        // Creates an intermediate model with property and repository inheritance.
        final List<Model> intermediateLineage = new ArrayList<>(lineage.size());

        for (int i = 0, s0 = lineage.size(); i < s0; i++) {
            final Model model = lineage.get(i).getModel();
            intermediateLineage.add(model.clone());
        }

        for (int i = intermediateLineage.size() - 2; i >= 0; i--) {
            final Model parent = intermediateLineage.get(i + 1);
            final Model child = intermediateLineage.get(i);

            final Properties properties = new Properties();
            properties.putAll(parent.getProperties());
            properties.putAll(child.getProperties());
            child.setProperties(properties);

            final List<Repository> repositories = new ArrayList<>();
            repositories.addAll(child.getRepositories());

            for (final Repository parentRepository : parent.getRepositories()) {
                if (!repositories.contains(parentRepository)) {
                    repositories.add(parentRepository);
                }
            }

            child.setRepositories(repositories);
        }

        // Interpolates the intermediate model.
        for (int i = 0, s0 = intermediateLineage.size(); i < s0; i++) {
            final Model model = intermediateLineage.get(i);
            problems.setSource(model);
            this.interpolateModel(model, request, problems);
        }

        // Exchanges 'import' scope dependencies in the original lineage with possibly interpolated values.
        for (int i = 0, s0 = lineage.size(); i < s0; i++) {
            final Model model = lineage.get(i).getModel();

            if (model.getDependencyManagement() != null) {
                for (int j = 0, s1 = model.getDependencyManagement().getDependencies().size(); j < s1; j++) {
                    final Dependency dependency = model.getDependencyManagement().getDependencies().get(j);

                    if ("import".equals(dependency.getScope()) && "pom".equals(dependency.getType())) {
                        final Dependency interpolated = intermediateLineage.get(i).getDependencyManagement()
                                .getDependencies().get(j);

                        model.getDependencyManagement().getDependencies().set(j, interpolated);
                    }
                }
            }
        }

        // [MNG-4488] [regression] Parent POMs resolved from repository are validated in strict mode
        ModelBuildingRequest lenientRequest = request;
        if (request.getValidationLevel() > ModelBuildingRequest.VALIDATION_LEVEL_MAVEN_2_0) {
            lenientRequest = new FilterModelBuildingRequest(request) {

                @Override
                public int getValidationLevel() {
                    return ModelBuildingRequest.VALIDATION_LEVEL_MAVEN_2_0;
                }

            };
        }

        // Imports dependencies into the original model using the repositories of the intermediate model.
        for (int i = 0, s0 = lineage.size(), superModelIdx = lineage.size() - 1; i < s0; i++) {
            final Model model = lineage.get(i).getModel();
            this.configureResolver(lenientRequest.getModelResolver(), intermediateLineage.get(i), problems, true);
            this.importDependencyManagement(model, "import", lenientRequest, problems, new HashSet<String>());

            if (i != superModelIdx) {
                problems.setSource(model);
                modelValidator.validateRawModel(model, lenientRequest, problems);
            }
        }
    }

    private void assembleInheritance(List<ModelData> lineage, ModelBuildingRequest request,
            ModelProblemCollector problems) {
        for (int i = lineage.size() - 2; i >= 0; i--) {
            Model parent = lineage.get(i + 1).getModel();
            Model child = lineage.get(i).getModel();
            inheritanceAssembler.assembleModelInheritance(child, parent, request, problems);
        }
    }

    private Map<String, Activation> getProfileActivations(Model model, boolean clone) {
        Map<String, Activation> activations = new HashMap<>();
        for (Profile profile : model.getProfiles()) {
            Activation activation = profile.getActivation();

            if (activation == null) {
                continue;
            }

            if (clone) {
                activation = activation.clone();
            }

            activations.put(profile.getId(), activation);
        }

        return activations;
    }

    private void injectProfileActivations(Model model, Map<String, Activation> activations) {
        for (Profile profile : model.getProfiles()) {
            Activation activation = profile.getActivation();

            if (activation == null) {
                continue;
            }

            // restore activation
            profile.setActivation(activations.get(profile.getId()));
        }
    }

    private Model interpolateModel(Model model, ModelBuildingRequest request, ModelProblemCollector problems) {
        // save profile activations before interpolation, since they are evaluated with limited scope
        Map<String, Activation> originalActivations = getProfileActivations(model, true);

        Model result = modelInterpolator.interpolateModel(model, model.getProjectDirectory(), request, problems);
        result.setPomFile(model.getPomFile());

        // restore profiles with file activation to their value before full interpolation
        injectProfileActivations(model, originalActivations);

        return result;
    }

    private ModelData readParent(Model childModel, ModelSource childSource, ModelBuildingRequest request,
            DefaultModelProblemCollector problems) throws ModelBuildingException {
        ModelData parentData;

        Parent parent = childModel.getParent();

        if (parent != null) {
            String groupId = parent.getGroupId();
            String artifactId = parent.getArtifactId();
            String version = parent.getVersion();

            parentData = getCache(request.getModelCache(), groupId, artifactId, version, ModelCacheTag.RAW);

            if (parentData == null) {
                parentData = readParentLocally(childModel, childSource, request, problems);

                if (parentData == null) {
                    parentData = readParentExternally(childModel, request, problems);
                }

                putCache(request.getModelCache(), groupId, artifactId, version, ModelCacheTag.RAW, parentData);
            } else {
                /*
                 * NOTE: This is a sanity check of the cache hit. If the cached parent POM was locally resolved, the
                 * child's <relativePath> should point at that parent, too. If it doesn't, we ignore the cache and
                 * resolve externally, to mimic the behavior if the cache didn't exist in the first place. Otherwise,
                 * the cache would obscure a bad POM.
                 */

                File pomFile = parentData.getModel().getPomFile();
                if (pomFile != null) {
                    ModelSource expectedParentSource = getParentPomFile(childModel, childSource);

                    if (expectedParentSource instanceof ModelSource2
                            && !pomFile.toURI().equals(((ModelSource2) expectedParentSource).getLocationURI())) {
                        parentData = readParentExternally(childModel, request, problems);
                    }
                }
            }

            Model parentModel = parentData.getModel();

            if (!"pom".equals(parentModel.getPackaging())) {
                problems.add(new ModelProblemCollectorRequest(Severity.ERROR, Version.BASE)
                        .setMessage(
                                "Invalid packaging for parent POM " + ModelProblemUtils.toSourceHint(parentModel)
                                        + ", must be \"pom\" but is \"" + parentModel.getPackaging() + "\"")
                        .setLocation(parentModel.getLocation("packaging")));
            }
        } else {
            parentData = null;
        }

        return parentData;
    }

    private ModelData readParentLocally(Model childModel, ModelSource childSource, ModelBuildingRequest request,
            DefaultModelProblemCollector problems) throws ModelBuildingException {
        final Parent parent = childModel.getParent();
        final ModelSource candidateSource;
        final Model candidateModel;
        final WorkspaceModelResolver resolver = request.getWorkspaceModelResolver();
        if (resolver == null) {
            candidateSource = getParentPomFile(childModel, childSource);

            if (candidateSource == null) {
                return null;
            }

            File pomFile = null;
            if (candidateSource instanceof FileModelSource) {
                pomFile = ((FileModelSource) candidateSource).getPomFile();
            }

            candidateModel = readModel(candidateSource, pomFile, request, problems);
        } else {
            try {
                candidateModel = resolver.resolveRawModel(parent.getGroupId(), parent.getArtifactId(),
                        parent.getVersion());
            } catch (UnresolvableModelException e) {
                problems.add(new ModelProblemCollectorRequest(Severity.FATAL, Version.BASE) //
                        .setMessage(e.getMessage().toString()).setLocation(parent.getLocation("")).setException(e));
                throw problems.newModelBuildingException();
            }
            if (candidateModel == null) {
                return null;
            }
            candidateSource = new FileModelSource(candidateModel.getPomFile());
        }

        //
        // TODO:jvz Why isn't all this checking the job of the duty of the workspace resolver, we know that we
        // have a model that is suitable, yet more checks are done here and the one for the version is problematic
        // before because with parents as ranges it will never work in this scenario.
        //

        String groupId = candidateModel.getGroupId();
        if (groupId == null && candidateModel.getParent() != null) {
            groupId = candidateModel.getParent().getGroupId();
        }
        String artifactId = candidateModel.getArtifactId();
        String version = candidateModel.getVersion();
        if (version == null && candidateModel.getParent() != null) {
            version = candidateModel.getParent().getVersion();
        }

        if (groupId == null || !groupId.equals(parent.getGroupId()) || artifactId == null
                || !artifactId.equals(parent.getArtifactId())) {
            StringBuilder buffer = new StringBuilder(256);
            buffer.append("'parent.relativePath'");
            if (childModel != problems.getRootModel()) {
                buffer.append(" of POM ").append(ModelProblemUtils.toSourceHint(childModel));
            }
            buffer.append(" points at ").append(groupId).append(':').append(artifactId);
            buffer.append(" instead of ").append(parent.getGroupId()).append(':');
            buffer.append(parent.getArtifactId()).append(", please verify your project structure");

            problems.setSource(childModel);
            problems.add(new ModelProblemCollectorRequest(Severity.WARNING, Version.BASE)
                    .setMessage(buffer.toString()).setLocation(parent.getLocation("")));
            return null;
        }
        if (version != null && parent.getVersion() != null && !version.equals(parent.getVersion())) {
            try {
                VersionRange parentRange = VersionRange.createFromVersionSpec(parent.getVersion());
                if (!parentRange.hasRestrictions()) {
                    // the parent version is not a range, we have version skew, drop back to resolution from repo
                    return null;
                }
                if (!parentRange.containsVersion(new DefaultArtifactVersion(version))) {
                    // version skew drop back to resolution from the repository
                    return null;
                }

                // Validate versions aren't inherited when using parent ranges the same way as when read externally.
                if (childModel.getVersion() == null) {
                    problems.add(new ModelProblemCollectorRequest(Severity.FATAL, Version.V31)
                            .setMessage("Version must be a constant").setLocation(childModel.getLocation("")));

                } else {
                    if (childModel.getVersion().contains("${")) {
                        problems.add(new ModelProblemCollectorRequest(Severity.FATAL, Version.V31)
                                .setMessage("Version must be a constant")
                                .setLocation(childModel.getLocation("version")));

                    }
                }

                // MNG-2199: What else to check here ?
            } catch (InvalidVersionSpecificationException e) {
                // invalid version range, so drop back to resolution from the repository
                return null;
            }
        }

        //
        // Here we just need to know that a version is fine to use but this validation we can do in our workspace
        // resolver.
        //

        /*
         * if ( version == null || !version.equals( parent.getVersion() ) ) { return null; }
         */

        ModelData parentData = new ModelData(candidateSource, candidateModel, groupId, artifactId, version);

        return parentData;
    }

    private ModelSource getParentPomFile(Model childModel, ModelSource source) {
        if (!(source instanceof ModelSource2)) {
            return null;
        }

        String parentPath = childModel.getParent().getRelativePath();

        if (parentPath == null || parentPath.length() <= 0) {
            return null;
        }

        return ((ModelSource2) source).getRelatedSource(parentPath);
    }

    private ModelData readParentExternally(Model childModel, ModelBuildingRequest request,
            DefaultModelProblemCollector problems) throws ModelBuildingException {
        problems.setSource(childModel);

        Parent parent = childModel.getParent().clone();

        String groupId = parent.getGroupId();
        String artifactId = parent.getArtifactId();
        String version = parent.getVersion();

        ModelResolver modelResolver = request.getModelResolver();

        Validate.notNull(modelResolver, "request.modelResolver cannot be null (parent POM %s and POM %s)",
                ModelProblemUtils.toId(groupId, artifactId, version), ModelProblemUtils.toSourceHint(childModel));

        ModelSource modelSource;
        try {
            modelSource = modelResolver.resolveModel(parent);
        } catch (UnresolvableModelException e) {
            StringBuilder buffer = new StringBuilder(256);
            buffer.append("Non-resolvable parent POM");
            if (!containsCoordinates(e.getMessage(), groupId, artifactId, version)) {
                buffer.append(' ').append(ModelProblemUtils.toId(groupId, artifactId, version));
            }
            if (childModel != problems.getRootModel()) {
                buffer.append(" for ").append(ModelProblemUtils.toId(childModel));
            }
            buffer.append(": ").append(e.getMessage());
            if (childModel.getProjectDirectory() != null) {
                if (parent.getRelativePath() == null || parent.getRelativePath().length() <= 0) {
                    buffer.append(" and 'parent.relativePath' points at no local POM");
                } else {
                    buffer.append(" and 'parent.relativePath' points at wrong local POM");
                }
            }

            problems.add(new ModelProblemCollectorRequest(Severity.FATAL, Version.BASE)
                    .setMessage(buffer.toString()).setLocation(parent.getLocation("")).setException(e));
            throw problems.newModelBuildingException();
        }

        ModelBuildingRequest lenientRequest = request;
        if (request.getValidationLevel() > ModelBuildingRequest.VALIDATION_LEVEL_MAVEN_2_0) {
            lenientRequest = new FilterModelBuildingRequest(request) {
                @Override
                public int getValidationLevel() {
                    return ModelBuildingRequest.VALIDATION_LEVEL_MAVEN_2_0;
                }
            };
        }

        Model parentModel = readModel(modelSource, null, lenientRequest, problems);

        if (!parent.getVersion().equals(version)) {
            if (childModel.getVersion() == null) {
                problems.add(new ModelProblemCollectorRequest(Severity.FATAL, Version.V31)
                        .setMessage("Version must be a constant").setLocation(childModel.getLocation("")));

            } else {
                if (childModel.getVersion().contains("${")) {
                    problems.add(new ModelProblemCollectorRequest(Severity.FATAL, Version.V31)
                            .setMessage("Version must be a constant")
                            .setLocation(childModel.getLocation("version")));

                }
            }

            // MNG-2199: What else to check here ?
        }

        ModelData parentData = new ModelData(modelSource, parentModel, parent.getGroupId(), parent.getArtifactId(),
                parent.getVersion());

        return parentData;
    }

    private Model getSuperModel() {
        return superPomProvider.getSuperModel("4.0.0").clone();
    }

    private void importDependencyManagement(Model model, String scope, ModelBuildingRequest request,
            DefaultModelProblemCollector problems, Collection<String> importIds) {
        DependencyManagement depMngt = model.getDependencyManagement();

        if (depMngt == null) {
            return;
        }

        problems.setSource(model);

        String importing = model.getGroupId() + ':' + model.getArtifactId() + ':' + model.getVersion();

        importIds.add(importing);

        final WorkspaceModelResolver workspaceResolver = request.getWorkspaceModelResolver();
        final ModelResolver modelResolver = request.getModelResolver();

        ModelBuildingRequest importRequest = null;

        List<DependencyManagement> importMngts = null;

        for (Iterator<Dependency> it = depMngt.getDependencies().iterator(); it.hasNext();) {
            Dependency dependency = it.next();

            if (!"pom".equals(dependency.getType()) || !scope.equals(dependency.getScope())) {
                continue;
            }

            it.remove();

            String groupId = dependency.getGroupId();
            String artifactId = dependency.getArtifactId();
            String version = dependency.getVersion();

            if (groupId == null || groupId.length() <= 0) {
                problems.add(new ModelProblemCollectorRequest(Severity.ERROR, Version.BASE)
                        .setMessage("'dependencyManagement.dependencies.dependency.groupId' for "
                                + dependency.getManagementKey() + " is missing.")
                        .setLocation(dependency.getLocation("")));
                continue;
            }
            if (artifactId == null || artifactId.length() <= 0) {
                problems.add(new ModelProblemCollectorRequest(Severity.ERROR, Version.BASE)
                        .setMessage("'dependencyManagement.dependencies.dependency.artifactId' for "
                                + dependency.getManagementKey() + " is missing.")
                        .setLocation(dependency.getLocation("")));
                continue;
            }
            if (version == null || version.length() <= 0) {
                problems.add(new ModelProblemCollectorRequest(Severity.ERROR, Version.BASE)
                        .setMessage("'dependencyManagement.dependencies.dependency.version' for "
                                + dependency.getManagementKey() + " is missing.")
                        .setLocation(dependency.getLocation("")));
                continue;
            }

            String imported = groupId + ':' + artifactId + ':' + version;

            if (importIds.contains(imported)) {
                String message = "The dependencies of type=pom and scope=" + scope + " form a cycle: ";
                for (String modelId : importIds) {
                    message += modelId + " -> ";
                }
                message += imported;
                problems.add(new ModelProblemCollectorRequest(Severity.ERROR, Version.BASE).setMessage(message));
                continue;
            }

            DependencyManagement importMngt = getCache(request.getModelCache(), groupId, artifactId, version,
                    ModelCacheTag.IMPORT);

            if (importMngt == null) {
                if (workspaceResolver == null && modelResolver == null) {
                    throw new NullPointerException(String.format(
                            "request.workspaceModelResolver and request.modelResolver cannot be null"
                                    + " (parent POM %s and POM %s)",
                            ModelProblemUtils.toId(groupId, artifactId, version),
                            ModelProblemUtils.toSourceHint(model)));
                }

                Model importModel = null;
                if (workspaceResolver != null) {
                    try {
                        importModel = workspaceResolver.resolveEffectiveModel(groupId, artifactId, version);
                    } catch (UnresolvableModelException e) {
                        problems.add(new ModelProblemCollectorRequest(Severity.FATAL, Version.BASE)
                                .setMessage(e.getMessage().toString()).setException(e));
                        continue;
                    }
                }

                // no workspace resolver or workspace resolver returned null (i.e. model not in workspace)
                if (importModel == null) {
                    final ModelSource importSource;
                    try {
                        dependency = dependency.clone();
                        importSource = modelResolver.resolveModel(dependency);
                        final String resolvedId = dependency.getGroupId() + ':' + dependency.getArtifactId() + ':'
                                + dependency.getVersion();

                        if (!imported.equals(resolvedId) && importIds.contains(resolvedId)) {
                            // A version range has been resolved to a cycle.
                            String message = "The dependencies of type=pom and scope=" + scope + " form a cycle: ";
                            for (String modelId : importIds) {
                                message += modelId + " -> ";
                            }
                            message += resolvedId;
                            problems.add(new ModelProblemCollectorRequest(Severity.ERROR, Version.BASE)
                                    .setMessage(message));

                            continue;
                        }
                    } catch (UnresolvableModelException e) {
                        StringBuilder buffer = new StringBuilder(256);
                        buffer.append("Non-resolvable " + scope + " POM");
                        if (!containsCoordinates(e.getMessage(), groupId, artifactId, version)) {
                            buffer.append(' ').append(ModelProblemUtils.toId(groupId, artifactId, version));
                        }
                        buffer.append(": ").append(e.getMessage());

                        problems.add(new ModelProblemCollectorRequest(Severity.ERROR, Version.BASE)
                                .setMessage(buffer.toString()).setLocation(dependency.getLocation(""))
                                .setException(e));
                        continue;
                    }

                    if (importRequest == null) {
                        importRequest = new DefaultModelBuildingRequest();
                        importRequest.setValidationLevel(ModelBuildingRequest.VALIDATION_LEVEL_MINIMAL);
                        importRequest.setModelCache(request.getModelCache());
                        importRequest.setSystemProperties(request.getSystemProperties());
                        importRequest.setUserProperties(request.getUserProperties());
                        importRequest.setLocationTracking(request.isLocationTracking());
                    }

                    importRequest.setModelSource(importSource);
                    importRequest.setModelResolver(modelResolver.newCopy());

                    final ModelBuildingResult importResult;
                    try {
                        importResult = build(importRequest);
                    } catch (ModelBuildingException e) {
                        problems.addAll(e.getProblems());
                        continue;
                    }

                    problems.addAll(importResult.getProblems());

                    importModel = importResult.getEffectiveModel();
                }

                importMngt = importModel.getDependencyManagement();

                if (importMngt == null) {
                    importMngt = new DependencyManagement();
                }

                putCache(request.getModelCache(), groupId, artifactId, version, ModelCacheTag.IMPORT, importMngt);
            }

            if (importMngts == null) {
                importMngts = new ArrayList<>();
            }

            importMngts.add(importMngt);
        }

        importIds.remove(importing);

        dependencyManagementImporter.importManagement(model, importMngts, request, problems);
    }

    private <T> void putCache(ModelCache modelCache, String groupId, String artifactId, String version,
            ModelCacheTag<T> tag, T data) {
        if (modelCache != null) {
            modelCache.put(groupId, artifactId, version, tag.getName(), tag.intoCache(data));
        }
    }

    private <T> T getCache(ModelCache modelCache, String groupId, String artifactId, String version,
            ModelCacheTag<T> tag) {
        if (modelCache != null) {
            Object data = modelCache.get(groupId, artifactId, version, tag.getName());
            if (data != null) {
                return tag.fromCache(tag.getType().cast(data));
            }
        }
        return null;
    }

    private void fireEvent(Model model, ModelBuildingRequest request, ModelProblemCollector problems,
            ModelBuildingEventCatapult catapult) throws ModelBuildingException {
        ModelBuildingListener listener = request.getModelBuildingListener();

        if (listener != null) {
            ModelBuildingEvent event = new DefaultModelBuildingEvent(model, request, problems);

            catapult.fire(listener, event);
        }
    }

    private boolean containsCoordinates(String message, String groupId, String artifactId, String version) {
        return message != null && (groupId == null || message.contains(groupId))
                && (artifactId == null || message.contains(artifactId))
                && (version == null || message.contains(version));
    }

    protected boolean hasModelErrors(ModelProblemCollectorExt problems) {
        if (problems instanceof DefaultModelProblemCollector) {
            return ((DefaultModelProblemCollector) problems).hasErrors();
        } else {
            // the default execution path only knows the DefaultModelProblemCollector,
            // only reason it's not in signature is because it's package private
            throw new IllegalStateException();
        }
    }

    protected boolean hasFatalErrors(ModelProblemCollectorExt problems) {
        if (problems instanceof DefaultModelProblemCollector) {
            return ((DefaultModelProblemCollector) problems).hasFatalErrors();
        } else {
            // the default execution path only knows the DefaultModelProblemCollector,
            // only reason it's not in signature is because it's package private
            throw new IllegalStateException();
        }
    }

}