/*
 * 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.
 */
package org.apache.maven.model.merge;

import java.io.ObjectStreamException;
import java.util.AbstractList;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Properties;

import org.apache.maven.model.Activation;
import org.apache.maven.model.Build;
import org.apache.maven.model.BuildBase;
import org.apache.maven.model.CiManagement;
import org.apache.maven.model.ConfigurationContainer;
import org.apache.maven.model.Contributor;
import org.apache.maven.model.Dependency;
import org.apache.maven.model.DependencyManagement;
import org.apache.maven.model.DeploymentRepository;
import org.apache.maven.model.Developer;
import org.apache.maven.model.DistributionManagement;
import org.apache.maven.model.Exclusion;
import org.apache.maven.model.Extension;
import org.apache.maven.model.FileSet;
import org.apache.maven.model.InputLocation;
import org.apache.maven.model.IssueManagement;
import org.apache.maven.model.License;
import org.apache.maven.model.MailingList;
import org.apache.maven.model.Model;
import org.apache.maven.model.ModelBase;
import org.apache.maven.model.Notifier;
import org.apache.maven.model.Organization;
import org.apache.maven.model.Parent;
import org.apache.maven.model.PatternSet;
import org.apache.maven.model.Plugin;
import org.apache.maven.model.PluginConfiguration;
import org.apache.maven.model.PluginContainer;
import org.apache.maven.model.PluginExecution;
import org.apache.maven.model.PluginManagement;
import org.apache.maven.model.Prerequisites;
import org.apache.maven.model.Profile;
import org.apache.maven.model.Relocation;
import org.apache.maven.model.ReportPlugin;
import org.apache.maven.model.ReportSet;
import org.apache.maven.model.Reporting;
import org.apache.maven.model.Repository;
import org.apache.maven.model.RepositoryBase;
import org.apache.maven.model.RepositoryPolicy;
import org.apache.maven.model.Resource;
import org.apache.maven.model.Scm;
import org.apache.maven.model.Site;
import org.codehaus.plexus.util.xml.Xpp3Dom;

/**
 * This is a hand-crafted prototype of the default model merger that should eventually be generated by Modello by a new
 * Java plugin. Code structure to merge source (read-only) object into the target object is:<ul>
 * <li><code>merge<i>Classname</i>( <i>Classname</i> target, <i>Classname</i> source, boolean sourceDominant,
 * Map&lt;Object, Object&gt; context )</code> for each model class</li>
 * <li><code>merge<i>Classname</i>_<i>FieldName</i>( <i>Classname</i> target, <i>Classname</i> source, boolean
 * sourceDominant, Map&lt;Object, Object&gt; context )</code> for each field of each model class</li>
 * <li><code>Object get<i>Classname</i>Key( <i>Classname</i> <i>classname</i> )</code>
 * for each class that is used in a list</li>
 * </ul>
 * Code is written like it could be generated, with default behaviour to be overridden when necessary.
 * This is particularly the case for <code>Object get<i>Classname</i>Key( <i>Classname</i> <i>classname</i> )</code>
 * method, which by default return the object itself and is expected to be overridden to calculate better suited key
 * value.
 *
 * @author Benjamin Bentmann
 */
@SuppressWarnings({"checkstyle:methodname"})
public class ModelMerger {

    /**
     * Merges the specified source object into the given target object.
     *
     * @param target The target object whose existing contents should be merged with the source, must not be
     *            <code>null</code>.
     * @param source The (read-only) source object that should be merged into the target object, may be
     *            <code>null</code>.
     * @param sourceDominant A flag indicating whether either the target object or the source object provides the
     *            dominant data.
     * @param hints A set of key-value pairs that customized merger implementations can use to carry domain-specific
     *            information along, may be <code>null</code>.
     */
    public void merge(Model target, Model source, boolean sourceDominant, Map<?, ?> hints) {
        Objects.requireNonNull(target, "target cannot be null");

        if (source == null) {
            return;
        }

        Map<Object, Object> context = new HashMap<>();
        if (hints != null) {
            context.putAll(hints);
        }

        mergeModel(target, source, sourceDominant, context);
    }

    protected void mergeModel(Model target, Model source, boolean sourceDominant, Map<Object, Object> context) {
        mergeModelBase(target, source, sourceDominant, context);

        mergeModel_ChildProjectUrlInheritAppendPath(target, source, sourceDominant, context);
        mergeModel_ModelVersion(target, source, sourceDominant, context);
        mergeModel_Parent(target, source, sourceDominant, context);
        mergeModel_GroupId(target, source, sourceDominant, context);
        mergeModel_ArtifactId(target, source, sourceDominant, context);
        mergeModel_Version(target, source, sourceDominant, context);
        mergeModel_Packaging(target, source, sourceDominant, context);
        mergeModel_Name(target, source, sourceDominant, context);
        mergeModel_Description(target, source, sourceDominant, context);
        mergeModel_Url(target, source, sourceDominant, context);
        mergeModel_InceptionYear(target, source, sourceDominant, context);
        mergeModel_Organization(target, source, sourceDominant, context);
        mergeModel_Licenses(target, source, sourceDominant, context);
        mergeModel_MailingLists(target, source, sourceDominant, context);
        mergeModel_Developers(target, source, sourceDominant, context);
        mergeModel_Contributors(target, source, sourceDominant, context);
        mergeModel_IssueManagement(target, source, sourceDominant, context);
        mergeModel_Scm(target, source, sourceDominant, context);
        mergeModel_CiManagement(target, source, sourceDominant, context);
        mergeModel_Prerequisites(target, source, sourceDominant, context);
        mergeModel_Build(target, source, sourceDominant, context);
        mergeModel_Profiles(target, source, sourceDominant, context);
    }

    protected void mergeModel_ModelVersion(
            Model target, Model source, boolean sourceDominant, Map<Object, Object> context) {
        String src = source.getModelVersion();
        if (src != null) {
            if (sourceDominant || target.getModelVersion() == null) {
                target.setModelVersion(src);
                target.setLocation("modelVersion", source.getLocation("modelVersion"));
            }
        }
    }

    protected void mergeModel_Parent(Model target, Model source, boolean sourceDominant, Map<Object, Object> context) {
        Parent src = source.getParent();
        if (src != null) {
            Parent tgt = target.getParent();
            if (tgt == null) {
                tgt = new Parent();
                tgt.setRelativePath(null);
                target.setParent(tgt);
            }
            mergeParent(tgt, src, sourceDominant, context);
        }
    }

    protected void mergeModel_GroupId(Model target, Model source, boolean sourceDominant, Map<Object, Object> context) {
        String src = source.getGroupId();
        if (src != null) {
            if (sourceDominant || target.getGroupId() == null) {
                target.setGroupId(src);
                target.setLocation("groupId", source.getLocation("groupId"));
            }
        }
    }

    protected void mergeModel_ArtifactId(
            Model target, Model source, boolean sourceDominant, Map<Object, Object> context) {
        String src = source.getArtifactId();
        if (src != null) {
            if (sourceDominant || target.getArtifactId() == null) {
                target.setArtifactId(src);
                target.setLocation("artifactId", source.getLocation("artifactId"));
            }
        }
    }

    protected void mergeModel_ChildProjectUrlInheritAppendPath(
            Model target, Model source, boolean sourceDominant, Map<Object, Object> context) {
        String src = source.getChildProjectUrlInheritAppendPath();
        if (src != null) {
            if (sourceDominant || target.getChildProjectUrlInheritAppendPath() == null) {
                target.setChildProjectUrlInheritAppendPath(src);
                target.setLocation(
                        "child.project.url.inherit.append.path",
                        source.getLocation("child.project.url.inherit.append.path"));
            }
        }
    }

    protected void mergeModel_Version(Model target, Model source, boolean sourceDominant, Map<Object, Object> context) {
        String src = source.getVersion();
        if (src != null) {
            if (sourceDominant || target.getVersion() == null) {
                target.setVersion(src);
                target.setLocation("version", source.getLocation("version"));
            }
        }
    }

    protected void mergeModel_Packaging(
            Model target, Model source, boolean sourceDominant, Map<Object, Object> context) {
        String src = source.getPackaging();
        if (src != null) {
            if (sourceDominant || target.getPackaging() == null) {
                target.setPackaging(src);
                target.setLocation("packaging", source.getLocation("packaging"));
            }
        }
    }

    protected void mergeModel_Name(Model target, Model source, boolean sourceDominant, Map<Object, Object> context) {
        String src = source.getName();
        if (src != null) {
            if (sourceDominant || target.getName() == null) {
                target.setName(src);
                target.setLocation("name", source.getLocation("name"));
            }
        }
    }

    protected void mergeModel_Description(
            Model target, Model source, boolean sourceDominant, Map<Object, Object> context) {
        String src = source.getDescription();
        if (src != null) {
            if (sourceDominant || target.getDescription() == null) {
                target.setDescription(src);
                target.setLocation("description", source.getLocation("description"));
            }
        }
    }

    protected void mergeModel_Url(Model target, Model source, boolean sourceDominant, Map<Object, Object> context) {
        String src = source.getUrl();
        if (src != null) {
            if (sourceDominant || target.getUrl() == null) {
                target.setUrl(src);
                target.setLocation("url", source.getLocation("url"));
            }
        }
    }

    protected void mergeModel_InceptionYear(
            Model target, Model source, boolean sourceDominant, Map<Object, Object> context) {
        String src = source.getInceptionYear();
        if (src != null) {
            if (sourceDominant || target.getInceptionYear() == null) {
                target.setInceptionYear(src);
                target.setLocation("inceptionYear", source.getLocation("inceptionYear"));
            }
        }
    }

    protected void mergeModel_Organization(
            Model target, Model source, boolean sourceDominant, Map<Object, Object> context) {
        Organization src = source.getOrganization();
        if (src != null) {
            Organization tgt = target.getOrganization();
            if (tgt == null) {
                tgt = new Organization();
                target.setOrganization(tgt);
            }
            mergeOrganization(tgt, src, sourceDominant, context);
        }
    }

    protected void mergeModel_Licenses(
            Model target, Model source, boolean sourceDominant, Map<Object, Object> context) {
        target.setLicenses(merge(target.getLicenses(), source.getLicenses(), sourceDominant, new LicenseKeyComputer()));
    }

    protected void mergeModel_MailingLists(
            Model target, Model source, boolean sourceDominant, Map<Object, Object> context) {
        target.setMailingLists(merge(
                target.getMailingLists(), source.getMailingLists(), sourceDominant, new MailingListKeyComputer()));
    }

    protected void mergeModel_Developers(
            Model target, Model source, boolean sourceDominant, Map<Object, Object> context) {
        target.setDevelopers(
                merge(target.getDevelopers(), source.getDevelopers(), sourceDominant, new DeveloperKeyComputer()));
    }

    protected void mergeModel_Contributors(
            Model target, Model source, boolean sourceDominant, Map<Object, Object> context) {
        target.setContributors(merge(
                target.getContributors(), source.getContributors(), sourceDominant, new ContributorKeyComputer()));
    }

    protected void mergeModel_IssueManagement(
            Model target, Model source, boolean sourceDominant, Map<Object, Object> context) {
        IssueManagement src = source.getIssueManagement();
        if (src != null) {
            IssueManagement tgt = target.getIssueManagement();
            if (tgt == null) {
                tgt = new IssueManagement();
                target.setIssueManagement(tgt);
            }
            mergeIssueManagement(tgt, src, sourceDominant, context);
        }
    }

    protected void mergeModel_Scm(Model target, Model source, boolean sourceDominant, Map<Object, Object> context) {
        Scm src = source.getScm();
        if (src != null) {
            Scm tgt = target.getScm();
            if (tgt == null) {
                tgt = new Scm();
                tgt.setTag(null);
                target.setScm(tgt);
            }
            mergeScm(tgt, src, sourceDominant, context);
        }
    }

    protected void mergeModel_CiManagement(
            Model target, Model source, boolean sourceDominant, Map<Object, Object> context) {
        CiManagement src = source.getCiManagement();
        if (src != null) {
            CiManagement tgt = target.getCiManagement();
            if (tgt == null) {
                tgt = new CiManagement();
                target.setCiManagement(tgt);
            }
            mergeCiManagement(tgt, src, sourceDominant, context);
        }
    }

    protected void mergeModel_Prerequisites(
            Model target, Model source, boolean sourceDominant, Map<Object, Object> context) {
        Prerequisites src = source.getPrerequisites();
        if (src != null) {
            Prerequisites tgt = target.getPrerequisites();
            if (tgt == null) {
                tgt = new Prerequisites();
                tgt.setMaven(null);
                target.setPrerequisites(tgt);
            }
            mergePrerequisites(tgt, src, sourceDominant, context);
        }
    }

    protected void mergeModel_Build(Model target, Model source, boolean sourceDominant, Map<Object, Object> context) {
        Build src = source.getBuild();
        if (src != null) {
            Build tgt = target.getBuild();
            if (tgt == null) {
                tgt = new Build();
                target.setBuild(tgt);
            }
            mergeBuild(tgt, src, sourceDominant, context);
        }
    }

    protected void mergeModel_Profiles(
            Model target, Model source, boolean sourceDominant, Map<Object, Object> context) {
        target.setProfiles(merge(target.getProfiles(), source.getProfiles(), sourceDominant, new ProfileKeyComputer()));
    }

    protected void mergeModelBase(
            ModelBase target, ModelBase source, boolean sourceDominant, Map<Object, Object> context) {
        mergeModelBase_DistributionManagement(target, source, sourceDominant, context);
        mergeModelBase_Modules(target, source, sourceDominant, context);
        mergeModelBase_Repositories(target, source, sourceDominant, context);
        mergeModelBase_PluginRepositories(target, source, sourceDominant, context);
        mergeModelBase_Dependencies(target, source, sourceDominant, context);
        mergeModelBase_Reporting(target, source, sourceDominant, context);
        mergeModelBase_DependencyManagement(target, source, sourceDominant, context);
        mergeModelBase_Properties(target, source, sourceDominant, context);
    }

    protected void mergeModelBase_Modules(
            ModelBase target, ModelBase source, boolean sourceDominant, Map<Object, Object> context) {
        List<String> src = source.getModules();
        if (!src.isEmpty()) {
            List<String> tgt = target.getModules();
            List<String> merged = new ArrayList<>(tgt.size() + src.size());
            merged.addAll(tgt);
            merged.addAll(src);
            target.setModules(merged);
        }
    }

    protected void mergeModelBase_Dependencies(
            ModelBase target, ModelBase source, boolean sourceDominant, Map<Object, Object> context) {
        target.setDependencies(
                merge(target.getDependencies(), source.getDependencies(), sourceDominant, new DependencyKeyComputer()));
    }

    protected void mergeModelBase_Repositories(
            ModelBase target, ModelBase source, boolean sourceDominant, Map<Object, Object> context) {
        target.setRepositories(
                merge(target.getRepositories(), source.getRepositories(), sourceDominant, new RepositoryKeyComputer()));
    }

    protected void mergeModelBase_PluginRepositories(
            ModelBase target, ModelBase source, boolean sourceDominant, Map<Object, Object> context) {
        target.setPluginRepositories(merge(
                target.getPluginRepositories(),
                source.getPluginRepositories(),
                sourceDominant,
                new RepositoryKeyComputer()));
    }

    protected void mergeModelBase_DistributionManagement(
            ModelBase target, ModelBase source, boolean sourceDominant, Map<Object, Object> context) {
        DistributionManagement src = source.getDistributionManagement();
        if (src != null) {
            DistributionManagement tgt = target.getDistributionManagement();
            if (tgt == null) {
                tgt = new DistributionManagement();
                target.setDistributionManagement(tgt);
            }
            mergeDistributionManagement(tgt, src, sourceDominant, context);
        }
    }

    protected void mergeModelBase_Reporting(
            ModelBase target, ModelBase source, boolean sourceDominant, Map<Object, Object> context) {
        Reporting src = source.getReporting();
        if (src != null) {
            Reporting tgt = target.getReporting();
            if (tgt == null) {
                tgt = new Reporting();
                target.setReporting(tgt);
            }
            mergeReporting(tgt, src, sourceDominant, context);
        }
    }

    protected void mergeModelBase_DependencyManagement(
            ModelBase target, ModelBase source, boolean sourceDominant, Map<Object, Object> context) {
        DependencyManagement src = source.getDependencyManagement();
        if (src != null) {
            DependencyManagement tgt = target.getDependencyManagement();
            if (tgt == null) {
                tgt = new DependencyManagement();
                target.setDependencyManagement(tgt);
            }
            mergeDependencyManagement(tgt, src, sourceDominant, context);
        }
    }

    protected void mergeModelBase_Properties(
            ModelBase target, ModelBase source, boolean sourceDominant, Map<Object, Object> context) {
        Properties merged = new Properties();
        if (sourceDominant) {
            merged.putAll(target.getProperties());
            merged.putAll(source.getProperties());
        } else {
            merged.putAll(source.getProperties());
            merged.putAll(target.getProperties());
        }
        target.setProperties(merged);
        target.setLocation(
                "properties",
                InputLocation.merge(
                        target.getLocation("properties"), source.getLocation("properties"), sourceDominant));
    }

    protected void mergeDistributionManagement(
            DistributionManagement target,
            DistributionManagement source,
            boolean sourceDominant,
            Map<Object, Object> context) {
        mergeDistributionManagement_Repository(target, source, sourceDominant, context);
        mergeDistributionManagement_SnapshotRepository(target, source, sourceDominant, context);
        mergeDistributionManagement_Site(target, source, sourceDominant, context);
        mergeDistributionManagement_Status(target, source, sourceDominant, context);
        mergeDistributionManagement_DownloadUrl(target, source, sourceDominant, context);
    }

    protected void mergeDistributionManagement_Repository(
            DistributionManagement target,
            DistributionManagement source,
            boolean sourceDominant,
            Map<Object, Object> context) {
        DeploymentRepository src = source.getRepository();
        if (src != null) {
            DeploymentRepository tgt = target.getRepository();
            if (tgt == null) {
                tgt = new DeploymentRepository();
                target.setRepository(tgt);
            }
            mergeDeploymentRepository(tgt, src, sourceDominant, context);
        }
    }

    protected void mergeDistributionManagement_SnapshotRepository(
            DistributionManagement target,
            DistributionManagement source,
            boolean sourceDominant,
            Map<Object, Object> context) {
        DeploymentRepository src = source.getSnapshotRepository();
        if (src != null) {
            DeploymentRepository tgt = target.getSnapshotRepository();
            if (tgt == null) {
                tgt = new DeploymentRepository();
                target.setSnapshotRepository(tgt);
            }
            mergeDeploymentRepository(tgt, src, sourceDominant, context);
        }
    }

    protected void mergeDistributionManagement_Site(
            DistributionManagement target,
            DistributionManagement source,
            boolean sourceDominant,
            Map<Object, Object> context) {
        Site src = source.getSite();
        if (src != null) {
            Site tgt = target.getSite();
            if (tgt == null) {
                tgt = new Site();
                target.setSite(tgt);
            }
            mergeSite(tgt, src, sourceDominant, context);
        }
    }

    protected void mergeDistributionManagement_Status(
            DistributionManagement target,
            DistributionManagement source,
            boolean sourceDominant,
            Map<Object, Object> context) {
        String src = source.getStatus();
        if (src != null) {
            if (sourceDominant || target.getStatus() == null) {
                target.setStatus(src);
                target.setLocation("status", source.getLocation("status"));
            }
        }
    }

    protected void mergeDistributionManagement_DownloadUrl(
            DistributionManagement target,
            DistributionManagement source,
            boolean sourceDominant,
            Map<Object, Object> context) {
        String src = source.getDownloadUrl();
        if (src != null) {
            if (sourceDominant || target.getDownloadUrl() == null) {
                target.setDownloadUrl(src);
                target.setLocation("downloadUrl", source.getLocation("downloadUrl"));
            }
        }
    }

    protected void mergeRelocation(
            Relocation target, Relocation source, boolean sourceDominant, Map<Object, Object> context) {
        mergeRelocation_GroupId(target, source, sourceDominant, context);
        mergeRelocation_ArtifactId(target, source, sourceDominant, context);
        mergeRelocation_Version(target, source, sourceDominant, context);
        mergeRelocation_Message(target, source, sourceDominant, context);
    }

    protected void mergeRelocation_GroupId(
            Relocation target, Relocation source, boolean sourceDominant, Map<Object, Object> context) {
        String src = source.getGroupId();
        if (src != null) {
            if (sourceDominant || target.getGroupId() == null) {
                target.setGroupId(src);
                target.setLocation("groupId", source.getLocation("groupId"));
            }
        }
    }

    protected void mergeRelocation_ArtifactId(
            Relocation target, Relocation source, boolean sourceDominant, Map<Object, Object> context) {
        String src = source.getArtifactId();
        if (src != null) {
            if (sourceDominant || target.getArtifactId() == null) {
                target.setArtifactId(src);
                target.setLocation("artifactId", source.getLocation("artifactId"));
            }
        }
    }

    protected void mergeRelocation_Version(
            Relocation target, Relocation source, boolean sourceDominant, Map<Object, Object> context) {
        String src = source.getVersion();
        if (src != null) {
            if (sourceDominant || target.getVersion() == null) {
                target.setVersion(src);
                target.setLocation("version", source.getLocation("version"));
            }
        }
    }

    protected void mergeRelocation_Message(
            Relocation target, Relocation source, boolean sourceDominant, Map<Object, Object> context) {
        String src = source.getMessage();
        if (src != null) {
            if (sourceDominant || target.getMessage() == null) {
                target.setMessage(src);
                target.setLocation("message", source.getLocation("message"));
            }
        }
    }

    protected void mergeDeploymentRepository(
            DeploymentRepository target,
            DeploymentRepository source,
            boolean sourceDominant,
            Map<Object, Object> context) {
        mergeRepository(target, source, sourceDominant, context);
        mergeDeploymentRepository_UniqueVersion(target, source, sourceDominant, context);
    }

    protected void mergeDeploymentRepository_UniqueVersion(
            DeploymentRepository target,
            DeploymentRepository source,
            boolean sourceDominant,
            Map<Object, Object> context) {
        if (sourceDominant) {
            target.setUniqueVersion(source.isUniqueVersion());
            target.setLocation("uniqueVersion", source.getLocation("uniqueVersion"));
        }
    }

    protected void mergeSite(Site target, Site source, boolean sourceDominant, Map<Object, Object> context) {
        mergeSite_ChildSiteUrlInheritAppendPath(target, source, sourceDominant, context);
        mergeSite_Id(target, source, sourceDominant, context);
        mergeSite_Name(target, source, sourceDominant, context);
        mergeSite_Url(target, source, sourceDominant, context);
    }

    protected void mergeSite_ChildSiteUrlInheritAppendPath(
            Site target, Site source, boolean sourceDominant, Map<Object, Object> context) {
        String src = source.getChildSiteUrlInheritAppendPath();
        if (src != null) {
            if (sourceDominant || target.getChildSiteUrlInheritAppendPath() == null) {
                target.setChildSiteUrlInheritAppendPath(src);
                target.setLocation(
                        "child.site.url.inherit.append.path", source.getLocation("child.site.url.inherit.append.path"));
            }
        }
    }

    protected void mergeSite_Id(Site target, Site source, boolean sourceDominant, Map<Object, Object> context) {
        String src = source.getId();
        if (src != null) {
            if (sourceDominant || target.getId() == null) {
                target.setId(src);
                target.setLocation("id", source.getLocation("id"));
            }
        }
    }

    protected void mergeSite_Name(Site target, Site source, boolean sourceDominant, Map<Object, Object> context) {
        String src = source.getName();
        if (src != null) {
            if (sourceDominant || target.getName() == null) {
                target.setName(src);
                target.setLocation("name", source.getLocation("name"));
            }
        }
    }

    protected void mergeSite_Url(Site target, Site source, boolean sourceDominant, Map<Object, Object> context) {
        String src = source.getUrl();
        if (src != null) {
            if (sourceDominant || target.getUrl() == null) {
                target.setUrl(src);
                target.setLocation("url", source.getLocation("url"));
            }
        }
    }

    protected void mergeRepository(
            Repository target, Repository source, boolean sourceDominant, Map<Object, Object> context) {
        mergeRepositoryBase(target, source, sourceDominant, context);
        mergeRepository_Releases(target, source, sourceDominant, context);
        mergeRepository_Snapshots(target, source, sourceDominant, context);
    }

    protected void mergeRepository_Releases(
            Repository target, Repository source, boolean sourceDominant, Map<Object, Object> context) {
        RepositoryPolicy src = source.getReleases();
        if (src != null) {
            RepositoryPolicy tgt = target.getReleases();
            if (tgt == null) {
                tgt = new RepositoryPolicy();
                target.setReleases(tgt);
            }
            mergeRepositoryPolicy(tgt, src, sourceDominant, context);
        }
    }

    protected void mergeRepository_Snapshots(
            Repository target, Repository source, boolean sourceDominant, Map<Object, Object> context) {
        RepositoryPolicy src = source.getSnapshots();
        if (src != null) {
            RepositoryPolicy tgt = target.getSnapshots();
            if (tgt == null) {
                tgt = new RepositoryPolicy();
                target.setSnapshots(tgt);
            }
            mergeRepositoryPolicy(tgt, src, sourceDominant, context);
        }
    }

    protected void mergeRepositoryBase(
            RepositoryBase target, RepositoryBase source, boolean sourceDominant, Map<Object, Object> context) {
        mergeRepositoryBase_Id(target, source, sourceDominant, context);
        mergeRepositoryBase_Name(target, source, sourceDominant, context);
        mergeRepositoryBase_Url(target, source, sourceDominant, context);
        mergeRepositoryBase_Layout(target, source, sourceDominant, context);
    }

    protected void mergeRepositoryBase_Id(
            RepositoryBase target, RepositoryBase source, boolean sourceDominant, Map<Object, Object> context) {
        String src = source.getId();
        if (src != null) {
            if (sourceDominant || target.getId() == null) {
                target.setId(src);
                target.setLocation("id", source.getLocation("id"));
            }
        }
    }

    protected void mergeRepositoryBase_Url(
            RepositoryBase target, RepositoryBase source, boolean sourceDominant, Map<Object, Object> context) {
        String src = source.getUrl();
        if (src != null) {
            if (sourceDominant || target.getUrl() == null) {
                target.setUrl(src);
                target.setLocation("url", source.getLocation("url"));
            }
        }
    }

    protected void mergeRepositoryBase_Name(
            RepositoryBase target, RepositoryBase source, boolean sourceDominant, Map<Object, Object> context) {
        String src = source.getName();
        if (src != null) {
            if (sourceDominant || target.getName() == null) {
                target.setName(src);
                target.setLocation("name", source.getLocation("name"));
            }
        }
    }

    protected void mergeRepositoryBase_Layout(
            RepositoryBase target, RepositoryBase source, boolean sourceDominant, Map<Object, Object> context) {
        String src = source.getLayout();
        if (src != null) {
            if (sourceDominant || target.getLayout() == null) {
                target.setLayout(src);
                target.setLocation("layout", source.getLocation("layout"));
            }
        }
    }

    protected void mergeRepositoryPolicy(
            RepositoryPolicy target, RepositoryPolicy source, boolean sourceDominant, Map<Object, Object> context) {
        mergeRepositoryPolicy_Enabled(target, source, sourceDominant, context);
        mergeRepositoryPolicy_UpdatePolicy(target, source, sourceDominant, context);
        mergeRepositoryPolicy_ChecksumPolicy(target, source, sourceDominant, context);
    }

    protected void mergeRepositoryPolicy_Enabled(
            RepositoryPolicy target, RepositoryPolicy source, boolean sourceDominant, Map<Object, Object> context) {
        String src = source.getEnabled();
        if (src != null) {
            if (sourceDominant || target.getEnabled() == null) {
                target.setEnabled(src);
                target.setLocation("enabled", source.getLocation("enabled"));
            }
        }
    }

    protected void mergeRepositoryPolicy_UpdatePolicy(
            RepositoryPolicy target, RepositoryPolicy source, boolean sourceDominant, Map<Object, Object> context) {
        String src = source.getUpdatePolicy();
        if (src != null) {
            if (sourceDominant || target.getUpdatePolicy() == null) {
                target.setUpdatePolicy(src);
                target.setLocation("updatePolicy", source.getLocation("updatePolicy"));
            }
        }
    }

    protected void mergeRepositoryPolicy_ChecksumPolicy(
            RepositoryPolicy target, RepositoryPolicy source, boolean sourceDominant, Map<Object, Object> context) {
        String src = source.getChecksumPolicy();
        if (src != null) {
            if (sourceDominant || target.getChecksumPolicy() == null) {
                target.setChecksumPolicy(src);
                target.setLocation("checksumPolicy", source.getLocation("checksumPolicy"));
            }
        }
    }

    protected void mergeDependency(
            Dependency target, Dependency source, boolean sourceDominant, Map<Object, Object> context) {
        mergeDependency_GroupId(target, source, sourceDominant, context);
        mergeDependency_ArtifactId(target, source, sourceDominant, context);
        mergeDependency_Version(target, source, sourceDominant, context);
        mergeDependency_Type(target, source, sourceDominant, context);
        mergeDependency_Classifier(target, source, sourceDominant, context);
        mergeDependency_Scope(target, source, sourceDominant, context);
        mergeDependency_SystemPath(target, source, sourceDominant, context);
        mergeDependency_Optional(target, source, sourceDominant, context);
        mergeDependency_Exclusions(target, source, sourceDominant, context);
    }

    protected void mergeDependency_GroupId(
            Dependency target, Dependency source, boolean sourceDominant, Map<Object, Object> context) {
        String src = source.getGroupId();
        if (src != null) {
            if (sourceDominant || target.getGroupId() == null) {
                target.setGroupId(src);
                target.setLocation("groupId", source.getLocation("groupId"));
            }
        }
    }

    protected void mergeDependency_ArtifactId(
            Dependency target, Dependency source, boolean sourceDominant, Map<Object, Object> context) {
        String src = source.getArtifactId();
        if (src != null) {
            if (sourceDominant || target.getArtifactId() == null) {
                target.setArtifactId(src);
                target.setLocation("artifactId", source.getLocation("artifactId"));
            }
        }
    }

    protected void mergeDependency_Version(
            Dependency target, Dependency source, boolean sourceDominant, Map<Object, Object> context) {
        String src = source.getVersion();
        if (src != null) {
            if (sourceDominant || target.getVersion() == null) {
                target.setVersion(src);
                target.setLocation("version", source.getLocation("version"));
            }
        }
    }

    protected void mergeDependency_Type(
            Dependency target, Dependency source, boolean sourceDominant, Map<Object, Object> context) {
        String src = source.getType();
        if (src != null) {
            if (sourceDominant || target.getType() == null) {
                target.setType(src);
                target.setLocation("type", source.getLocation("type"));
            }
        }
    }

    protected void mergeDependency_Classifier(
            Dependency target, Dependency source, boolean sourceDominant, Map<Object, Object> context) {
        String src = source.getClassifier();
        if (src != null) {
            if (sourceDominant || target.getClassifier() == null) {
                target.setClassifier(src);
                target.setLocation("classifier", source.getLocation("classifier"));
            }
        }
    }

    protected void mergeDependency_Scope(
            Dependency target, Dependency source, boolean sourceDominant, Map<Object, Object> context) {
        String src = source.getScope();
        if (src != null) {
            if (sourceDominant || target.getScope() == null) {
                target.setScope(src);
                target.setLocation("scope", source.getLocation("scope"));
            }
        }
    }

    protected void mergeDependency_SystemPath(
            Dependency target, Dependency source, boolean sourceDominant, Map<Object, Object> context) {
        String src = source.getSystemPath();
        if (src != null) {
            if (sourceDominant || target.getSystemPath() == null) {
                target.setSystemPath(src);
                target.setLocation("systemPath", source.getLocation("systemPath"));
            }
        }
    }

    protected void mergeDependency_Optional(
            Dependency target, Dependency source, boolean sourceDominant, Map<Object, Object> context) {
        String src = source.getOptional();
        if (src != null) {
            if (sourceDominant || target.getOptional() == null) {
                target.setOptional(src);
                target.setLocation("optional", source.getLocation("optional"));
            }
        }
    }

    protected void mergeDependency_Exclusions(
            Dependency target, Dependency source, boolean sourceDominant, Map<Object, Object> context) {
        target.setExclusions(
                merge(target.getExclusions(), source.getExclusions(), sourceDominant, new ExclusionKeyComputer()));
    }

    protected void mergeExclusion(
            Exclusion target, Exclusion source, boolean sourceDominant, Map<Object, Object> context) {
        mergeExclusion_GroupId(target, source, sourceDominant, context);
        mergeExclusion_ArtifactId(target, source, sourceDominant, context);
    }

    protected void mergeExclusion_GroupId(
            Exclusion target, Exclusion source, boolean sourceDominant, Map<Object, Object> context) {
        String src = source.getGroupId();
        if (src != null) {
            if (sourceDominant || target.getGroupId() == null) {
                target.setGroupId(src);
                target.setLocation("groupId", source.getLocation("groupId"));
            }
        }
    }

    protected void mergeExclusion_ArtifactId(
            Exclusion target, Exclusion source, boolean sourceDominant, Map<Object, Object> context) {
        String src = source.getArtifactId();
        if (src != null) {
            if (sourceDominant || target.getArtifactId() == null) {
                target.setArtifactId(src);
                target.setLocation("artifactId", source.getLocation("artifactId"));
            }
        }
    }

    protected void mergeReporting(
            Reporting target, Reporting source, boolean sourceDominant, Map<Object, Object> context) {
        mergeReporting_OutputDirectory(target, source, sourceDominant, context);
        mergeReporting_ExcludeDefaults(target, source, sourceDominant, context);
        mergeReporting_Plugins(target, source, sourceDominant, context);
    }

    protected void mergeReporting_OutputDirectory(
            Reporting target, Reporting source, boolean sourceDominant, Map<Object, Object> context) {
        String src = source.getOutputDirectory();
        if (src != null) {
            if (sourceDominant || target.getOutputDirectory() == null) {
                target.setOutputDirectory(src);
                target.setLocation("outputDirectory", source.getLocation("outputDirectory"));
            }
        }
    }

    protected void mergeReporting_ExcludeDefaults(
            Reporting target, Reporting source, boolean sourceDominant, Map<Object, Object> context) {
        String src = source.getExcludeDefaults();
        if (src != null) {
            if (sourceDominant || target.getExcludeDefaults() == null) {
                target.setExcludeDefaults(src);
                target.setLocation("excludeDefaults", source.getLocation("excludeDefaults"));
            }
        }
    }

    protected void mergeReporting_Plugins(
            Reporting target, Reporting source, boolean sourceDominant, Map<Object, Object> context) {
        target.setPlugins(
                merge(target.getPlugins(), source.getPlugins(), sourceDominant, new ReportPluginKeyComputer()));
    }

    protected void mergeReportPlugin(
            ReportPlugin target, ReportPlugin source, boolean sourceDominant, Map<Object, Object> context) {
        mergeConfigurationContainer(target, source, sourceDominant, context);
        mergeReportPlugin_GroupId(target, source, sourceDominant, context);
        mergeReportPlugin_ArtifactId(target, source, sourceDominant, context);
        mergeReportPlugin_Version(target, source, sourceDominant, context);
        mergeReportPlugin_ReportSets(target, source, sourceDominant, context);
    }

    protected void mergeReportPlugin_GroupId(
            ReportPlugin target, ReportPlugin source, boolean sourceDominant, Map<Object, Object> context) {
        String src = source.getGroupId();
        if (src != null) {
            if (sourceDominant || target.getGroupId() == null) {
                target.setGroupId(src);
                target.setLocation("groupId", source.getLocation("groupId"));
            }
        }
    }

    protected void mergeReportPlugin_ArtifactId(
            ReportPlugin target, ReportPlugin source, boolean sourceDominant, Map<Object, Object> context) {
        String src = source.getArtifactId();
        if (src != null) {
            if (sourceDominant || target.getArtifactId() == null) {
                target.setArtifactId(src);
                target.setLocation("artifactId", source.getLocation("artifactId"));
            }
        }
    }

    protected void mergeReportPlugin_Version(
            ReportPlugin target, ReportPlugin source, boolean sourceDominant, Map<Object, Object> context) {
        String src = source.getVersion();
        if (src != null) {
            if (sourceDominant || target.getVersion() == null) {
                target.setVersion(src);
                target.setLocation("version", source.getLocation("version"));
            }
        }
    }

    protected void mergeReportPlugin_ReportSets(
            ReportPlugin target, ReportPlugin source, boolean sourceDominant, Map<Object, Object> context) {
        target.setReportSets(
                merge(target.getReportSets(), source.getReportSets(), sourceDominant, new ReportSetKeyComputer()));
    }

    protected void mergeReportSet(
            ReportSet target, ReportSet source, boolean sourceDominant, Map<Object, Object> context) {
        mergeConfigurationContainer(target, source, sourceDominant, context);
        mergeReportSet_Id(target, source, sourceDominant, context);
        mergeReportSet_Reports(target, source, sourceDominant, context);
    }

    protected void mergeReportSet_Id(
            ReportSet target, ReportSet source, boolean sourceDominant, Map<Object, Object> context) {
        String src = source.getId();
        if (src != null) {
            if (sourceDominant || target.getId() == null) {
                target.setId(src);
                target.setLocation("id", source.getLocation("id"));
            }
        }
    }

    protected void mergeReportSet_Reports(
            ReportSet target, ReportSet source, boolean sourceDominant, Map<Object, Object> context) {
        List<String> src = source.getReports();
        if (!src.isEmpty()) {
            List<String> tgt = target.getReports();
            List<String> merged = new ArrayList<>(tgt.size() + src.size());
            merged.addAll(tgt);
            merged.addAll(src);
            target.setReports(merged);

            InputLocation sourceLocation = source.getLocation("reports");
            if (sourceLocation != null) {
                InputLocation targetLocation = target.getLocation("reports");
                if (targetLocation == null) {
                    target.setLocation("reports", sourceLocation);
                } else {
                    for (int i = 0; i < src.size(); i++) {
                        targetLocation.setLocation(
                                Integer.valueOf(tgt.size() + i), sourceLocation.getLocation(Integer.valueOf(i)));
                    }
                }
            }
        }
    }

    protected void mergeDependencyManagement(
            DependencyManagement target,
            DependencyManagement source,
            boolean sourceDominant,
            Map<Object, Object> context) {
        mergeDependencyManagement_Dependencies(target, source, sourceDominant, context);
    }

    protected void mergeDependencyManagement_Dependencies(
            DependencyManagement target,
            DependencyManagement source,
            boolean sourceDominant,
            Map<Object, Object> context) {
        target.setDependencies(
                merge(target.getDependencies(), source.getDependencies(), sourceDominant, new DependencyKeyComputer()));
    }

    protected void mergeParent(Parent target, Parent source, boolean sourceDominant, Map<Object, Object> context) {
        mergeParent_GroupId(target, source, sourceDominant, context);
        mergeParent_ArtifactId(target, source, sourceDominant, context);
        mergeParent_Version(target, source, sourceDominant, context);
        mergeParent_RelativePath(target, source, sourceDominant, context);
    }

    protected void mergeParent_GroupId(
            Parent target, Parent source, boolean sourceDominant, Map<Object, Object> context) {
        String src = source.getGroupId();
        if (src != null) {
            if (sourceDominant || target.getGroupId() == null) {
                target.setGroupId(src);
                target.setLocation("groupId", source.getLocation("groupId"));
            }
        }
    }

    protected void mergeParent_ArtifactId(
            Parent target, Parent source, boolean sourceDominant, Map<Object, Object> context) {
        String src = source.getArtifactId();
        if (src != null) {
            if (sourceDominant || target.getArtifactId() == null) {
                target.setArtifactId(src);
                target.setLocation("artifactId", source.getLocation("artifactId"));
            }
        }
    }

    protected void mergeParent_Version(
            Parent target, Parent source, boolean sourceDominant, Map<Object, Object> context) {
        String src = source.getVersion();
        if (src != null) {
            if (sourceDominant || target.getVersion() == null) {
                target.setVersion(src);
                target.setLocation("version", source.getLocation("version"));
            }
        }
    }

    protected void mergeParent_RelativePath(
            Parent target, Parent source, boolean sourceDominant, Map<Object, Object> context) {
        String src = source.getRelativePath();
        if (src != null) {
            if (sourceDominant || target.getRelativePath() == null) {
                target.setRelativePath(src);
                target.setLocation("relativePath", source.getLocation("relativePath"));
            }
        }
    }

    protected void mergeOrganization(
            Organization target, Organization source, boolean sourceDominant, Map<Object, Object> context) {
        mergeOrganization_Name(target, source, sourceDominant, context);
        mergeOrganization_Url(target, source, sourceDominant, context);
    }

    protected void mergeOrganization_Name(
            Organization target, Organization source, boolean sourceDominant, Map<Object, Object> context) {
        String src = source.getName();
        if (src != null) {
            if (sourceDominant || target.getName() == null) {
                target.setName(src);
                target.setLocation("name", source.getLocation("name"));
            }
        }
    }

    protected void mergeOrganization_Url(
            Organization target, Organization source, boolean sourceDominant, Map<Object, Object> context) {
        String src = source.getUrl();
        if (src != null) {
            if (sourceDominant || target.getUrl() == null) {
                target.setUrl(src);
                target.setLocation("url", source.getLocation("url"));
            }
        }
    }

    protected void mergeLicense(License target, License source, boolean sourceDominant, Map<Object, Object> context) {
        mergeLicense_Name(target, source, sourceDominant, context);
        mergeLicense_Url(target, source, sourceDominant, context);
        mergeLicense_Distribution(target, source, sourceDominant, context);
        mergeLicense_Comments(target, source, sourceDominant, context);
    }

    protected void mergeLicense_Name(
            License target, License source, boolean sourceDominant, Map<Object, Object> context) {
        String src = source.getName();
        if (src != null) {
            if (sourceDominant || target.getName() == null) {
                target.setName(src);
                target.setLocation("name", source.getLocation("name"));
            }
        }
    }

    protected void mergeLicense_Url(
            License target, License source, boolean sourceDominant, Map<Object, Object> context) {
        String src = source.getUrl();
        if (src != null) {
            if (sourceDominant || target.getUrl() == null) {
                target.setUrl(src);
                target.setLocation("url", source.getLocation("url"));
            }
        }
    }

    protected void mergeLicense_Distribution(
            License target, License source, boolean sourceDominant, Map<Object, Object> context) {
        String src = source.getDistribution();
        if (src != null) {
            if (sourceDominant || target.getDistribution() == null) {
                target.setDistribution(src);
                target.setLocation("distribution", source.getLocation("distribution"));
            }
        }
    }

    protected void mergeLicense_Comments(
            License target, License source, boolean sourceDominant, Map<Object, Object> context) {
        String src = source.getComments();
        if (src != null) {
            if (sourceDominant || target.getComments() == null) {
                target.setComments(src);
                target.setLocation("comments", source.getLocation("comments"));
            }
        }
    }

    protected void mergeMailingList(
            MailingList target, MailingList source, boolean sourceDominant, Map<Object, Object> context) {
        mergeMailingList_Name(target, source, sourceDominant, context);
        mergeMailingList_Subscribe(target, source, sourceDominant, context);
        mergeMailingList_Unsubscribe(target, source, sourceDominant, context);
        mergeMailingList_Post(target, source, sourceDominant, context);
        mergeMailingList_OtherArchives(target, source, sourceDominant, context);
    }

    protected void mergeMailingList_Name(
            MailingList target, MailingList source, boolean sourceDominant, Map<Object, Object> context) {
        String src = source.getName();
        if (src != null) {
            if (sourceDominant || target.getName() == null) {
                target.setName(src);
                target.setLocation("name", source.getLocation("name"));
            }
        }
    }

    protected void mergeMailingList_Subscribe(
            MailingList target, MailingList source, boolean sourceDominant, Map<Object, Object> context) {
        String src = source.getSubscribe();
        if (src != null) {
            if (sourceDominant || target.getSubscribe() == null) {
                target.setSubscribe(src);
                target.setLocation("subscribe", source.getLocation("subscribe"));
            }
        }
    }

    protected void mergeMailingList_Unsubscribe(
            MailingList target, MailingList source, boolean sourceDominant, Map<Object, Object> context) {
        String src = source.getUnsubscribe();
        if (src != null) {
            if (sourceDominant || target.getUnsubscribe() == null) {
                target.setUnsubscribe(src);
                target.setLocation("unsubscribe", source.getLocation("unsubscribe"));
            }
        }
    }

    protected void mergeMailingList_Post(
            MailingList target, MailingList source, boolean sourceDominant, Map<Object, Object> context) {
        String src = source.getPost();
        if (src != null) {
            if (sourceDominant || target.getPost() == null) {
                target.setPost(src);
                target.setLocation("post", source.getLocation("post"));
            }
        }
    }

    protected void mergeMailingList_Archive(
            MailingList target, MailingList source, boolean sourceDominant, Map<Object, Object> context) {
        String src = source.getArchive();
        if (src != null) {
            if (sourceDominant || target.getArchive() == null) {
                target.setArchive(src);
                target.setLocation("archive", source.getLocation("archive"));
            }
        }
    }

    protected void mergeMailingList_OtherArchives(
            MailingList target, MailingList source, boolean sourceDominant, Map<Object, Object> context) {
        List<String> src = source.getOtherArchives();
        if (!src.isEmpty()) {
            List<String> tgt = target.getOtherArchives();
            List<String> merged = new ArrayList<>(tgt.size() + src.size());
            merged.addAll(tgt);
            merged.addAll(src);
            target.setOtherArchives(merged);
        }
    }

    protected void mergeDeveloper(
            Developer target, Developer source, boolean sourceDominant, Map<Object, Object> context) {
        mergeContributor(target, source, sourceDominant, context);
        mergeDeveloper_Id(target, source, sourceDominant, context);
    }

    protected void mergeDeveloper_Id(
            Developer target, Developer source, boolean sourceDominant, Map<Object, Object> context) {
        String src = source.getId();
        if (src != null) {
            if (sourceDominant || target.getId() == null) {
                target.setId(src);
                target.setLocation("id", source.getLocation("id"));
            }
        }
    }

    protected void mergeContributor(
            Contributor target, Contributor source, boolean sourceDominant, Map<Object, Object> context) {
        mergeContributor_Name(target, source, sourceDominant, context);
        mergeContributor_Email(target, source, sourceDominant, context);
        mergeContributor_Url(target, source, sourceDominant, context);
        mergeContributor_Organization(target, source, sourceDominant, context);
        mergeContributor_OrganizationUrl(target, source, sourceDominant, context);
        mergeContributor_Timezone(target, source, sourceDominant, context);
        mergeContributor_Roles(target, source, sourceDominant, context);
        mergeContributor_Properties(target, source, sourceDominant, context);
    }

    protected void mergeContributor_Name(
            Contributor target, Contributor source, boolean sourceDominant, Map<Object, Object> context) {
        String src = source.getName();
        if (src != null) {
            if (sourceDominant || target.getName() == null) {
                target.setName(src);
                target.setLocation("name", source.getLocation("name"));
            }
        }
    }

    protected void mergeContributor_Email(
            Contributor target, Contributor source, boolean sourceDominant, Map<Object, Object> context) {
        String src = source.getEmail();
        if (src != null) {
            if (sourceDominant || target.getEmail() == null) {
                target.setEmail(src);
                target.setLocation("email", source.getLocation("email"));
            }
        }
    }

    protected void mergeContributor_Url(
            Contributor target, Contributor source, boolean sourceDominant, Map<Object, Object> context) {
        String src = source.getUrl();
        if (src != null) {
            if (sourceDominant || target.getUrl() == null) {
                target.setUrl(src);
                target.setLocation("url", source.getLocation("url"));
            }
        }
    }

    protected void mergeContributor_Organization(
            Contributor target, Contributor source, boolean sourceDominant, Map<Object, Object> context) {
        String src = source.getOrganization();
        if (src != null) {
            if (sourceDominant || target.getOrganization() == null) {
                target.setOrganization(src);
                target.setLocation("organization", source.getLocation("organization"));
            }
        }
    }

    protected void mergeContributor_OrganizationUrl(
            Contributor target, Contributor source, boolean sourceDominant, Map<Object, Object> context) {
        String src = source.getOrganizationUrl();
        if (src != null) {
            if (sourceDominant || target.getOrganizationUrl() == null) {
                target.setOrganizationUrl(src);
                target.setLocation("organizationUrl", source.getLocation("organizationUrl"));
            }
        }
    }

    protected void mergeContributor_Timezone(
            Contributor target, Contributor source, boolean sourceDominant, Map<Object, Object> context) {
        String src = source.getTimezone();
        if (src != null) {
            if (sourceDominant || target.getTimezone() == null) {
                target.setTimezone(src);
                target.setLocation("timezone", source.getLocation("timezone"));
            }
        }
    }

    protected void mergeContributor_Roles(
            Contributor target, Contributor source, boolean sourceDominant, Map<Object, Object> context) {
        List<String> src = source.getRoles();
        if (!src.isEmpty()) {
            List<String> tgt = target.getRoles();
            List<String> merged = new ArrayList<>(tgt.size() + src.size());
            merged.addAll(tgt);
            merged.addAll(src);
            target.setRoles(merged);
        }
    }

    protected void mergeContributor_Properties(
            Contributor target, Contributor source, boolean sourceDominant, Map<Object, Object> context) {
        Properties merged = new Properties();
        if (sourceDominant) {
            merged.putAll(target.getProperties());
            merged.putAll(source.getProperties());
        } else {
            merged.putAll(source.getProperties());
            merged.putAll(target.getProperties());
        }
        target.setProperties(merged);
        target.setLocation(
                "properties",
                InputLocation.merge(
                        target.getLocation("properties"), source.getLocation("properties"), sourceDominant));
    }

    protected void mergeIssueManagement(
            IssueManagement target, IssueManagement source, boolean sourceDominant, Map<Object, Object> context) {
        mergeIssueManagement_Url(target, source, sourceDominant, context);
        mergeIssueManagement_System(target, source, sourceDominant, context);
    }

    protected void mergeIssueManagement_System(
            IssueManagement target, IssueManagement source, boolean sourceDominant, Map<Object, Object> context) {
        String src = source.getSystem();
        if (src != null) {
            if (sourceDominant || target.getSystem() == null) {
                target.setSystem(src);
                target.setLocation("system", source.getLocation("system"));
            }
        }
    }

    protected void mergeIssueManagement_Url(
            IssueManagement target, IssueManagement source, boolean sourceDominant, Map<Object, Object> context) {
        String src = source.getUrl();
        if (src != null) {
            if (sourceDominant || target.getUrl() == null) {
                target.setUrl(src);
                target.setLocation("url", source.getLocation("url"));
            }
        }
    }

    protected void mergeScm(Scm target, Scm source, boolean sourceDominant, Map<Object, Object> context) {
        mergeScm_ChildScmConnectionInheritAppendPath(target, source, sourceDominant, context);
        mergeScm_ChildScmDeveloperConnectionInheritAppendPath(target, source, sourceDominant, context);
        mergeScm_ChildScmUrlInheritAppendPath(target, source, sourceDominant, context);
        mergeScm_Url(target, source, sourceDominant, context);
        mergeScm_Connection(target, source, sourceDominant, context);
        mergeScm_DeveloperConnection(target, source, sourceDominant, context);
        mergeScm_Tag(target, source, sourceDominant, context);
    }

    protected void mergeScm_ChildScmConnectionInheritAppendPath(
            Scm target, Scm source, boolean sourceDominant, Map<Object, Object> context) {
        String src = source.getChildScmConnectionInheritAppendPath();
        if (src != null) {
            if (sourceDominant || target.getChildScmConnectionInheritAppendPath() == null) {
                target.setChildScmConnectionInheritAppendPath(src);
                target.setLocation(
                        "child.scm.connection.inherit.append.path",
                        source.getLocation("child.scm.connection.inherit.append.path"));
            }
        }
    }

    protected void mergeScm_ChildScmDeveloperConnectionInheritAppendPath(
            Scm target, Scm source, boolean sourceDominant, Map<Object, Object> context) {
        String src = source.getChildScmDeveloperConnectionInheritAppendPath();
        if (src != null) {
            if (sourceDominant || target.getChildScmDeveloperConnectionInheritAppendPath() == null) {
                target.setChildScmDeveloperConnectionInheritAppendPath(src);
                target.setLocation(
                        "child.scm.developerConnection.inherit.append.path",
                        source.getLocation("child.scm.developerConnection.inherit.append.path"));
            }
        }
    }

    protected void mergeScm_ChildScmUrlInheritAppendPath(
            Scm target, Scm source, boolean sourceDominant, Map<Object, Object> context) {
        String src = source.getChildScmUrlInheritAppendPath();
        if (src != null) {
            if (sourceDominant || target.getChildScmUrlInheritAppendPath() == null) {
                target.setChildScmUrlInheritAppendPath(src);
                target.setLocation(
                        "child.scm.url.inherit.append.path", source.getLocation("child.scm.url.inherit.append.path"));
            }
        }
    }

    protected void mergeScm_Url(Scm target, Scm source, boolean sourceDominant, Map<Object, Object> context) {
        String src = source.getUrl();
        if (src != null) {
            if (sourceDominant || target.getUrl() == null) {
                target.setUrl(src);
                target.setLocation("url", source.getLocation("url"));
            }
        }
    }

    protected void mergeScm_Connection(Scm target, Scm source, boolean sourceDominant, Map<Object, Object> context) {
        String src = source.getConnection();
        if (src != null) {
            if (sourceDominant || target.getConnection() == null) {
                target.setConnection(src);
                target.setLocation("connection", source.getLocation("connection"));
            }
        }
    }

    protected void mergeScm_DeveloperConnection(
            Scm target, Scm source, boolean sourceDominant, Map<Object, Object> context) {
        String src = source.getDeveloperConnection();
        if (src != null) {
            if (sourceDominant || target.getDeveloperConnection() == null) {
                target.setDeveloperConnection(src);
                target.setLocation("developerConnection", source.getLocation("developerConnection"));
            }
        }
    }

    protected void mergeScm_Tag(Scm target, Scm source, boolean sourceDominant, Map<Object, Object> context) {
        String src = source.getTag();
        if (src != null) {
            if (sourceDominant || target.getTag() == null) {
                target.setTag(src);
                target.setLocation("tag", source.getLocation("tag"));
            }
        }
    }

    protected void mergeCiManagement(
            CiManagement target, CiManagement source, boolean sourceDominant, Map<Object, Object> context) {
        mergeCiManagement_System(target, source, sourceDominant, context);
        mergeCiManagement_Url(target, source, sourceDominant, context);
        mergeCiManagement_Notifiers(target, source, sourceDominant, context);
    }

    protected void mergeCiManagement_System(
            CiManagement target, CiManagement source, boolean sourceDominant, Map<Object, Object> context) {
        String src = source.getSystem();
        if (src != null) {
            if (sourceDominant || target.getSystem() == null) {
                target.setSystem(src);
                target.setLocation("system", source.getLocation("system"));
            }
        }
    }

    protected void mergeCiManagement_Url(
            CiManagement target, CiManagement source, boolean sourceDominant, Map<Object, Object> context) {
        String src = source.getUrl();
        if (src != null) {
            if (sourceDominant || target.getUrl() == null) {
                target.setUrl(src);
                target.setLocation("url", source.getLocation("url"));
            }
        }
    }

    protected void mergeCiManagement_Notifiers(
            CiManagement target, CiManagement source, boolean sourceDominant, Map<Object, Object> context) {
        target.setNotifiers(
                merge(target.getNotifiers(), source.getNotifiers(), sourceDominant, new NotifierKeyComputer()));
    }

    protected void mergeNotifier(
            Notifier target, Notifier source, boolean sourceDominant, Map<Object, Object> context) {
        mergeNotifier_Type(target, source, sourceDominant, context);
        mergeNotifier_Address(target, source, sourceDominant, context);
        mergeNotifier_Configuration(target, source, sourceDominant, context);
        mergeNotifier_SendOnError(target, source, sourceDominant, context);
        mergeNotifier_SendOnFailure(target, source, sourceDominant, context);
        mergeNotifier_SendOnSuccess(target, source, sourceDominant, context);
        mergeNotifier_SendOnWarning(target, source, sourceDominant, context);
    }

    protected void mergeNotifier_Type(
            Notifier target, Notifier source, boolean sourceDominant, Map<Object, Object> context) {
        String src = source.getType();
        if (src != null) {
            if (sourceDominant || target.getType() == null) {
                target.setType(src);
            }
        }
    }

    protected void mergeNotifier_Address(
            Notifier target, Notifier source, boolean sourceDominant, Map<Object, Object> context) {
        String src = source.getAddress();
        if (src != null) {
            if (sourceDominant || target.getAddress() == null) {
                target.setAddress(src);
            }
        }
    }

    protected void mergeNotifier_Configuration(
            Notifier target, Notifier source, boolean sourceDominant, Map<Object, Object> context) {
        Properties merged = new Properties();
        if (sourceDominant) {
            merged.putAll(target.getConfiguration());
            merged.putAll(source.getConfiguration());
        } else {
            merged.putAll(source.getConfiguration());
            merged.putAll(target.getConfiguration());
        }
        target.setConfiguration(merged);
    }

    protected void mergeNotifier_SendOnError(
            Notifier target, Notifier source, boolean sourceDominant, Map<Object, Object> context) {
        if (sourceDominant) {
            target.setSendOnError(source.isSendOnError());
        }
    }

    protected void mergeNotifier_SendOnFailure(
            Notifier target, Notifier source, boolean sourceDominant, Map<Object, Object> context) {
        if (sourceDominant) {
            target.setSendOnFailure(source.isSendOnFailure());
        }
    }

    protected void mergeNotifier_SendOnSuccess(
            Notifier target, Notifier source, boolean sourceDominant, Map<Object, Object> context) {
        if (sourceDominant) {
            target.setSendOnSuccess(source.isSendOnSuccess());
        }
    }

    protected void mergeNotifier_SendOnWarning(
            Notifier target, Notifier source, boolean sourceDominant, Map<Object, Object> context) {
        if (sourceDominant) {
            target.setSendOnWarning(source.isSendOnWarning());
        }
    }

    protected void mergePrerequisites(
            Prerequisites target, Prerequisites source, boolean sourceDominant, Map<Object, Object> context) {
        mergePrerequisites_Maven(target, source, sourceDominant, context);
    }

    protected void mergePrerequisites_Maven(
            Prerequisites target, Prerequisites source, boolean sourceDominant, Map<Object, Object> context) {
        String src = source.getMaven();
        if (src != null) {
            if (sourceDominant || target.getMaven() == null) {
                target.setMaven(src);
                target.setLocation("maven", source.getLocation("maven"));
            }
        }
    }

    protected void mergeBuild(Build target, Build source, boolean sourceDominant, Map<Object, Object> context) {
        mergeBuildBase(target, source, sourceDominant, context);
        mergeBuild_SourceDirectory(target, source, sourceDominant, context);
        mergeBuild_ScriptSourceDirectory(target, source, sourceDominant, context);
        mergeBuild_TestSourceDirectory(target, source, sourceDominant, context);
        mergeBuild_OutputDirectory(target, source, sourceDominant, context);
        mergeBuild_TestOutputDirectory(target, source, sourceDominant, context);
        mergeBuild_Extensions(target, source, sourceDominant, context);
    }

    protected void mergeBuild_SourceDirectory(
            Build target, Build source, boolean sourceDominant, Map<Object, Object> context) {
        String src = source.getSourceDirectory();
        if (src != null) {
            if (sourceDominant || target.getSourceDirectory() == null) {
                target.setSourceDirectory(src);
                target.setLocation("sourceDirectory", source.getLocation("sourceDirectory"));
            }
        }
    }

    protected void mergeBuild_ScriptSourceDirectory(
            Build target, Build source, boolean sourceDominant, Map<Object, Object> context) {
        String src = source.getScriptSourceDirectory();
        if (src != null) {
            if (sourceDominant || target.getScriptSourceDirectory() == null) {
                target.setScriptSourceDirectory(src);
                target.setLocation("scriptSourceDirectory", source.getLocation("scriptSourceDirectory"));
            }
        }
    }

    protected void mergeBuild_TestSourceDirectory(
            Build target, Build source, boolean sourceDominant, Map<Object, Object> context) {
        String src = source.getTestSourceDirectory();
        if (src != null) {
            if (sourceDominant || target.getTestSourceDirectory() == null) {
                target.setTestSourceDirectory(src);
                target.setLocation("testSourceDirectory", source.getLocation("testSourceDirectory"));
            }
        }
    }

    protected void mergeBuild_OutputDirectory(
            Build target, Build source, boolean sourceDominant, Map<Object, Object> context) {
        String src = source.getOutputDirectory();
        if (src != null) {
            if (sourceDominant || target.getOutputDirectory() == null) {
                target.setOutputDirectory(src);
                target.setLocation("outputDirectory", source.getLocation("outputDirectory"));
            }
        }
    }

    protected void mergeBuild_TestOutputDirectory(
            Build target, Build source, boolean sourceDominant, Map<Object, Object> context) {
        String src = source.getTestOutputDirectory();
        if (src != null) {
            if (sourceDominant || target.getTestOutputDirectory() == null) {
                target.setTestOutputDirectory(src);
                target.setLocation("testOutputDirectory", source.getLocation("testOutputDirectory"));
            }
        }
    }

    protected void mergeBuild_Extensions(
            Build target, Build source, boolean sourceDominant, Map<Object, Object> context) {
        target.setExtensions(
                merge(target.getExtensions(), source.getExtensions(), sourceDominant, new ExtensionKeyComputer()));
    }

    protected void mergeExtension(
            Extension target, Extension source, boolean sourceDominant, Map<Object, Object> context) {
        mergeExtension_GroupId(target, source, sourceDominant, context);
        mergeExtension_ArtifactId(target, source, sourceDominant, context);
        mergeExtension_Version(target, source, sourceDominant, context);
    }

    protected void mergeExtension_GroupId(
            Extension target, Extension source, boolean sourceDominant, Map<Object, Object> context) {
        String src = source.getGroupId();
        if (src != null) {
            if (sourceDominant || target.getGroupId() == null) {
                target.setGroupId(src);
                target.setLocation("groupId", source.getLocation("groupId"));
            }
        }
    }

    protected void mergeExtension_ArtifactId(
            Extension target, Extension source, boolean sourceDominant, Map<Object, Object> context) {
        String src = source.getArtifactId();
        if (src != null) {
            if (sourceDominant || target.getArtifactId() == null) {
                target.setArtifactId(src);
                target.setLocation("artifactId", source.getLocation("artifactId"));
            }
        }
    }

    protected void mergeExtension_Version(
            Extension target, Extension source, boolean sourceDominant, Map<Object, Object> context) {
        String src = source.getVersion();
        if (src != null) {
            if (sourceDominant || target.getVersion() == null) {
                target.setVersion(src);
                target.setLocation("version", source.getLocation("version"));
            }
        }
    }

    protected void mergeBuildBase(
            BuildBase target, BuildBase source, boolean sourceDominant, Map<Object, Object> context) {
        mergePluginConfiguration(target, source, sourceDominant, context);
        mergeBuildBase_DefaultGoal(target, source, sourceDominant, context);
        mergeBuildBase_FinalName(target, source, sourceDominant, context);
        mergeBuildBase_Directory(target, source, sourceDominant, context);
        mergeBuildBase_Resources(target, source, sourceDominant, context);
        mergeBuildBase_TestResources(target, source, sourceDominant, context);
        mergeBuildBase_Filters(target, source, sourceDominant, context);
    }

    protected void mergeBuildBase_DefaultGoal(
            BuildBase target, BuildBase source, boolean sourceDominant, Map<Object, Object> context) {
        String src = source.getDefaultGoal();
        if (src != null) {
            if (sourceDominant || target.getDefaultGoal() == null) {
                target.setDefaultGoal(src);
                target.setLocation("defaultGoal", source.getLocation("defaultGoal"));
            }
        }
    }

    protected void mergeBuildBase_Directory(
            BuildBase target, BuildBase source, boolean sourceDominant, Map<Object, Object> context) {
        String src = source.getDirectory();
        if (src != null) {
            if (sourceDominant || target.getDirectory() == null) {
                target.setDirectory(src);
                target.setLocation("directory", source.getLocation("directory"));
            }
        }
    }

    protected void mergeBuildBase_FinalName(
            BuildBase target, BuildBase source, boolean sourceDominant, Map<Object, Object> context) {
        String src = source.getFinalName();
        if (src != null) {
            if (sourceDominant || target.getFinalName() == null) {
                target.setFinalName(src);
                target.setLocation("finalName", source.getLocation("finalName"));
            }
        }
    }

    protected void mergeBuildBase_Filters(
            BuildBase target, BuildBase source, boolean sourceDominant, Map<Object, Object> context) {
        List<String> src = source.getFilters();
        if (!src.isEmpty()) {
            List<String> tgt = target.getFilters();
            List<String> merged = new ArrayList<>(tgt.size() + src.size());
            merged.addAll(tgt);
            merged.addAll(src);
            target.setFilters(merged);
        }
    }

    protected void mergeBuildBase_Resources(
            BuildBase target, BuildBase source, boolean sourceDominant, Map<Object, Object> context) {
        target.setResources(
                merge(target.getResources(), source.getResources(), sourceDominant, new ResourceKeyComputer()));
    }

    protected void mergeBuildBase_TestResources(
            BuildBase target, BuildBase source, boolean sourceDominant, Map<Object, Object> context) {
        target.setTestResources(
                merge(target.getTestResources(), source.getTestResources(), sourceDominant, new ResourceKeyComputer()));
    }

    protected void mergePluginConfiguration(
            PluginConfiguration target,
            PluginConfiguration source,
            boolean sourceDominant,
            Map<Object, Object> context) {
        mergePluginContainer(target, source, sourceDominant, context);
        mergePluginConfiguration_PluginManagement(target, source, sourceDominant, context);
    }

    protected void mergePluginConfiguration_PluginManagement(
            PluginConfiguration target,
            PluginConfiguration source,
            boolean sourceDominant,
            Map<Object, Object> context) {
        PluginManagement src = source.getPluginManagement();
        if (src != null) {
            PluginManagement tgt = target.getPluginManagement();
            if (tgt == null) {
                tgt = new PluginManagement();
                target.setPluginManagement(tgt);
            }
            mergePluginManagement(tgt, src, sourceDominant, context);
        }
    }

    protected void mergePluginContainer(
            PluginContainer target, PluginContainer source, boolean sourceDominant, Map<Object, Object> context) {
        mergePluginContainer_Plugins(target, source, sourceDominant, context);
    }

    protected void mergePluginContainer_Plugins(
            PluginContainer target, PluginContainer source, boolean sourceDominant, Map<Object, Object> context) {
        target.setPlugins(merge(target.getPlugins(), source.getPlugins(), sourceDominant, new PluginKeyComputer()));
    }

    protected void mergePluginManagement(
            PluginManagement target, PluginManagement source, boolean sourceDominant, Map<Object, Object> context) {
        mergePluginContainer(target, source, sourceDominant, context);
    }

    protected void mergePlugin(Plugin target, Plugin source, boolean sourceDominant, Map<Object, Object> context) {
        mergeConfigurationContainer(target, source, sourceDominant, context);
        mergePlugin_GroupId(target, source, sourceDominant, context);
        mergePlugin_ArtifactId(target, source, sourceDominant, context);
        mergePlugin_Version(target, source, sourceDominant, context);
        mergePlugin_Extensions(target, source, sourceDominant, context);
        mergePlugin_Dependencies(target, source, sourceDominant, context);
        mergePlugin_Executions(target, source, sourceDominant, context);
    }

    protected void mergePlugin_GroupId(
            Plugin target, Plugin source, boolean sourceDominant, Map<Object, Object> context) {
        String src = source.getGroupId();
        if (src != null) {
            if (sourceDominant || target.getGroupId() == null) {
                target.setGroupId(src);
                target.setLocation("groupId", source.getLocation("groupId"));
            }
        }
    }

    protected void mergePlugin_ArtifactId(
            Plugin target, Plugin source, boolean sourceDominant, Map<Object, Object> context) {
        String src = source.getArtifactId();
        if (src != null) {
            if (sourceDominant || target.getArtifactId() == null) {
                target.setArtifactId(src);
                target.setLocation("artifactId", source.getLocation("artifactId"));
            }
        }
    }

    protected void mergePlugin_Version(
            Plugin target, Plugin source, boolean sourceDominant, Map<Object, Object> context) {
        String src = source.getVersion();
        if (src != null) {
            if (sourceDominant || target.getVersion() == null) {
                target.setVersion(src);
                target.setLocation("version", source.getLocation("version"));
            }
        }
    }

    protected void mergePlugin_Extensions(
            Plugin target, Plugin source, boolean sourceDominant, Map<Object, Object> context) {
        String src = source.getExtensions();
        if (src != null) {
            if (sourceDominant || target.getExtensions() == null) {
                target.setExtensions(src);
                target.setLocation("extensions", source.getLocation("extensions"));
            }
        }
    }

    protected void mergePlugin_Dependencies(
            Plugin target, Plugin source, boolean sourceDominant, Map<Object, Object> context) {
        target.setDependencies(
                merge(target.getDependencies(), source.getDependencies(), sourceDominant, new DependencyKeyComputer()));
    }

    protected void mergePlugin_Executions(
            Plugin target, Plugin source, boolean sourceDominant, Map<Object, Object> context) {
        target.setExecutions(
                merge(target.getExecutions(), source.getExecutions(), sourceDominant, new ExecutionKeyComputer()));
    }

    protected void mergeConfigurationContainer(
            ConfigurationContainer target,
            ConfigurationContainer source,
            boolean sourceDominant,
            Map<Object, Object> context) {
        mergeConfigurationContainer_Inherited(target, source, sourceDominant, context);
        mergeConfigurationContainer_Configuration(target, source, sourceDominant, context);
    }

    protected void mergeConfigurationContainer_Inherited(
            ConfigurationContainer target,
            ConfigurationContainer source,
            boolean sourceDominant,
            Map<Object, Object> context) {
        String src = source.getInherited();
        if (src != null) {
            if (sourceDominant || target.getInherited() == null) {
                target.setInherited(src);
                target.setLocation("inherited", source.getLocation("inherited"));
            }
        }
    }

    protected void mergeConfigurationContainer_Configuration(
            ConfigurationContainer target,
            ConfigurationContainer source,
            boolean sourceDominant,
            Map<Object, Object> context) {
        Xpp3Dom src = (Xpp3Dom) source.getConfiguration();
        if (src != null) {
            Xpp3Dom tgt = (Xpp3Dom) target.getConfiguration();
            if (sourceDominant || tgt == null) {
                tgt = Xpp3Dom.mergeXpp3Dom(new Xpp3Dom(src), tgt);
            } else {
                tgt = Xpp3Dom.mergeXpp3Dom(tgt, src);
            }
            target.setConfiguration(tgt);
        }
    }

    protected void mergePluginExecution(
            PluginExecution target, PluginExecution source, boolean sourceDominant, Map<Object, Object> context) {
        mergeConfigurationContainer(target, source, sourceDominant, context);
        mergePluginExecution_Id(target, source, sourceDominant, context);
        mergePluginExecution_Phase(target, source, sourceDominant, context);
        mergePluginExecution_Goals(target, source, sourceDominant, context);
    }

    protected void mergePluginExecution_Id(
            PluginExecution target, PluginExecution source, boolean sourceDominant, Map<Object, Object> context) {
        String src = source.getId();
        if (src != null) {
            if (sourceDominant || target.getId() == null) {
                target.setId(src);
                target.setLocation("id", source.getLocation("id"));
            }
        }
    }

    protected void mergePluginExecution_Phase(
            PluginExecution target, PluginExecution source, boolean sourceDominant, Map<Object, Object> context) {
        String src = source.getPhase();
        if (src != null) {
            if (sourceDominant || target.getPhase() == null) {
                target.setPhase(src);
                target.setLocation("phase", source.getLocation("phase"));
            }
        }
    }

    protected void mergePluginExecution_Goals(
            PluginExecution target, PluginExecution source, boolean sourceDominant, Map<Object, Object> context) {
        List<String> src = source.getGoals();
        if (!src.isEmpty()) {
            List<String> tgt = target.getGoals();
            List<String> merged = new ArrayList<>(tgt.size() + src.size());
            merged.addAll(tgt);
            merged.addAll(src);
            target.setGoals(merged);
        }
    }

    protected void mergeResource(
            Resource target, Resource source, boolean sourceDominant, Map<Object, Object> context) {
        mergeFileSet(target, source, sourceDominant, context);
        mergeResource_TargetPath(target, source, sourceDominant, context);
        mergeResource_Filtering(target, source, sourceDominant, context);
        mergeResource_MergeId(target, source, sourceDominant, context);
    }

    protected void mergeResource_TargetPath(
            Resource target, Resource source, boolean sourceDominant, Map<Object, Object> context) {
        String src = source.getTargetPath();
        if (src != null) {
            if (sourceDominant || target.getTargetPath() == null) {
                target.setTargetPath(src);
                target.setLocation("targetPath", source.getLocation("targetPath"));
            }
        }
    }

    protected void mergeResource_Filtering(
            Resource target, Resource source, boolean sourceDominant, Map<Object, Object> context) {
        String src = source.getFiltering();
        if (src != null) {
            if (sourceDominant || target.getFiltering() == null) {
                target.setFiltering(src);
                target.setLocation("filtering", source.getLocation("filtering"));
            }
        }
    }

    protected void mergeResource_MergeId(
            Resource target, Resource source, boolean sourceDominant, Map<Object, Object> context) {
        String src = source.getMergeId();
        if (src != null) {
            if (sourceDominant || target.getMergeId() == null) {
                target.setMergeId(src);
            }
        }
    }

    protected void mergeFileSet(FileSet target, FileSet source, boolean sourceDominant, Map<Object, Object> context) {
        mergePatternSet(target, source, sourceDominant, context);
        mergeFileSet_Directory(target, source, sourceDominant, context);
    }

    protected void mergeFileSet_Directory(
            FileSet target, FileSet source, boolean sourceDominant, Map<Object, Object> context) {
        String src = source.getDirectory();
        if (src != null) {
            if (sourceDominant || target.getDirectory() == null) {
                target.setDirectory(src);
                target.setLocation("directory", source.getLocation("directory"));
            }
        }
    }

    protected void mergePatternSet(
            PatternSet target, PatternSet source, boolean sourceDominant, Map<Object, Object> context) {
        mergePatternSet_Includes(target, source, sourceDominant, context);
        mergePatternSet_Excludes(target, source, sourceDominant, context);
    }

    protected void mergePatternSet_Includes(
            PatternSet target, PatternSet source, boolean sourceDominant, Map<Object, Object> context) {
        List<String> src = source.getIncludes();
        if (!src.isEmpty()) {
            List<String> tgt = target.getIncludes();
            List<String> merged = new ArrayList<>(tgt.size() + src.size());
            merged.addAll(tgt);
            merged.addAll(src);
            target.setIncludes(merged);
        }
    }

    protected void mergePatternSet_Excludes(
            PatternSet target, PatternSet source, boolean sourceDominant, Map<Object, Object> context) {
        List<String> src = source.getExcludes();
        if (!src.isEmpty()) {
            List<String> tgt = target.getExcludes();
            List<String> merged = new ArrayList<>(tgt.size() + src.size());
            merged.addAll(tgt);
            merged.addAll(src);
            target.setExcludes(merged);
        }
    }

    protected void mergeProfile(Profile target, Profile source, boolean sourceDominant, Map<Object, Object> context) {
        mergeModelBase(target, source, sourceDominant, context);
        // TODO
    }

    protected void mergeActivation(
            Activation target, Activation source, boolean sourceDominant, Map<Object, Object> context) {
        // TODO
    }

    protected Object getDependencyKey(Dependency dependency) {
        return dependency;
    }

    protected Object getPluginKey(Plugin plugin) {
        return plugin;
    }

    protected Object getPluginExecutionKey(PluginExecution pluginExecution) {
        return pluginExecution;
    }

    protected Object getReportPluginKey(ReportPlugin reportPlugin) {
        return reportPlugin;
    }

    protected Object getReportSetKey(ReportSet reportSet) {
        return reportSet;
    }

    protected Object getLicenseKey(License license) {
        return license;
    }

    protected Object getMailingListKey(MailingList mailingList) {
        return mailingList;
    }

    protected Object getDeveloperKey(Developer developer) {
        return developer;
    }

    protected Object getContributorKey(Contributor contributor) {
        return contributor;
    }

    protected Object getProfileKey(Profile profile) {
        return profile;
    }

    protected Object getRepositoryKey(Repository repository) {
        return getRepositoryBaseKey(repository);
    }

    protected Object getRepositoryBaseKey(RepositoryBase repositoryBase) {
        return repositoryBase;
    }

    protected Object getNotifierKey(Notifier notifier) {
        return notifier;
    }

    protected Object getResourceKey(Resource resource) {
        return resource;
    }

    protected Object getExtensionKey(Extension extension) {
        return extension;
    }

    protected Object getExclusionKey(Exclusion exclusion) {
        return exclusion;
    }

    /**
     * Use to compute keys for data structures
     * @param <T>
     */
    private interface KeyComputer<T> {
        Object key(T t);
    }

    /**
     * Remapping function
     * @param <T>
     */
    private interface Remapping<T> {
        T merge(T u, T v);
    }

    /**
     * KeyComputer for Dependency
     */
    private final class DependencyKeyComputer implements KeyComputer<Dependency> {
        @Override
        public Object key(Dependency dependency) {
            return getDependencyKey(dependency);
        }
    }

    /**
     * KeyComputer for License
     */
    private class LicenseKeyComputer implements KeyComputer<License> {
        @Override
        public Object key(License license) {
            return getLicenseKey(license);
        }
    }

    /**
     * KeyComputer for MailingList
     */
    private class MailingListKeyComputer implements KeyComputer<MailingList> {
        @Override
        public Object key(MailingList mailingList) {
            return getMailingListKey(mailingList);
        }
    }

    /**
     * KeyComputer for Developer
     */
    private class DeveloperKeyComputer implements KeyComputer<Developer> {
        @Override
        public Object key(Developer developer) {
            return getDeveloperKey(developer);
        }
    }

    /**
     * KeyComputer for Contributor
     */
    private class ContributorKeyComputer implements KeyComputer<Contributor> {
        @Override
        public Object key(Contributor contributor) {
            return getContributorKey(contributor);
        }
    }

    /**
     * KeyComputer for Profile
     */
    private class ProfileKeyComputer implements KeyComputer<Profile> {
        @Override
        public Object key(Profile profile) {
            return getProfileKey(profile);
        }
    }

    /**
     * KeyComputer for Repository
     */
    private class RepositoryKeyComputer implements KeyComputer<Repository> {
        @Override
        public Object key(Repository repository) {
            return getRepositoryKey(repository);
        }
    }

    /**
     * KeyComputer for ReportPlugin
     */
    private class ReportPluginKeyComputer implements KeyComputer<ReportPlugin> {
        @Override
        public Object key(ReportPlugin plugin) {
            return getReportPluginKey(plugin);
        }
    }

    /**
     * KeyComputer for Plugin
     */
    private class PluginKeyComputer implements KeyComputer<Plugin> {
        @Override
        public Object key(Plugin plugin) {
            return getPluginKey(plugin);
        }
    }

    /**
     * KeyComputer for ReportSet
     */
    private class ReportSetKeyComputer implements KeyComputer<ReportSet> {
        @Override
        public Object key(ReportSet reportSet) {
            return getReportSetKey(reportSet);
        }
    }

    /**
     * KeyComputer for Notifier
     */
    private class NotifierKeyComputer implements KeyComputer<Notifier> {
        @Override
        public Object key(Notifier notifier) {
            return getNotifierKey(notifier);
        }
    }

    /**
     * KeyComputer for Extension
     */
    private class ExtensionKeyComputer implements KeyComputer<Extension> {
        @Override
        public Object key(Extension extension) {
            return getExtensionKey(extension);
        }
    }

    /**
     * KeyComputer for Resource
     */
    private class ResourceKeyComputer implements KeyComputer<Resource> {
        @Override
        public Object key(Resource resource) {
            return getResourceKey(resource);
        }
    }

    /**
     * KeyComputer for PluginExecution
     */
    private class ExecutionKeyComputer implements KeyComputer<PluginExecution> {
        @Override
        public Object key(PluginExecution pluginExecution) {
            return getPluginExecutionKey(pluginExecution);
        }
    }

    /**
     * KeyComputer for Exclusion
     */
    private class ExclusionKeyComputer implements KeyComputer<Exclusion> {
        @Override
        public Object key(Exclusion exclusion) {
            return getExclusionKey(exclusion);
        }
    }

    /**
     * Return the second value if <code>sourceDominant</code> is true, the first one otherwise.
     * @param <T>
     */
    private static class SourceDominant<T> implements Remapping<T> {
        private final boolean sourceDominant;

        SourceDominant(boolean sourceDominant) {
            this.sourceDominant = sourceDominant;
        }

        @Override
        public T merge(T u, T v) {
            return sourceDominant ? v : u;
        }
    }

    /**
     * Merge two lists
     */
    private static <T> List<T> merge(List<T> tgt, List<T> src, boolean sourceDominant, KeyComputer<T> computer) {
        return merge(tgt, src, computer, new SourceDominant<T>(sourceDominant));
    }

    private static <T> List<T> merge(List<T> tgt, List<T> src, KeyComputer<T> computer, Remapping<T> remapping) {
        if (src.isEmpty()) {
            return tgt;
        }

        MergingList<T> list;
        if (tgt instanceof MergingList) {
            list = (MergingList<T>) tgt;
        } else {
            list = new MergingList<>(computer, src.size() + tgt.size());
            list.mergeAll(tgt, new SourceDominant<T>(true));
        }

        list.mergeAll(src, remapping);
        return list;
    }

    /**
     * Merging list
     * @param <V>
     */
    private static class MergingList<V> extends AbstractList<V> implements java.io.Serializable {

        private final KeyComputer<V> keyComputer;
        private Map<Object, V> map;
        private List<V> list;

        MergingList(KeyComputer<V> keyComputer, int initialCapacity) {
            this.map = new LinkedHashMap<>(initialCapacity);
            this.keyComputer = keyComputer;
        }

        Object writeReplace() throws ObjectStreamException {
            return new ArrayList<>(this);
        }

        @Override
        public Iterator<V> iterator() {
            if (map != null) {
                return map.values().iterator();
            } else {
                return list.iterator();
            }
        }

        void mergeAll(Collection<V> vs, Remapping<V> remapping) {
            if (map == null) {
                map = new LinkedHashMap<>(list.size() + vs.size());
                for (V v : list) {
                    map.put(keyComputer.key(v), v);
                }
                list = null;
            }
            if (vs instanceof MergingList && ((MergingList) vs).map != null) {
                for (Map.Entry<Object, V> e : ((MergingList<V>) vs).map.entrySet()) {
                    Object key = e.getKey();
                    V oldValue = map.get(key);
                    // JDK8: this should be a call to map.merge( key, v, remapping )
                    V newValue = (oldValue == null) ? e.getValue() : remapping.merge(oldValue, e.getValue());
                    if (newValue == null) {
                        remove(key);
                    } else if (newValue != oldValue) {
                        map.put(key, newValue);
                    }
                }
            } else {
                for (V v : vs) {
                    Object key = keyComputer.key(v);
                    // JDK8: this should be a call to map.merge( key, v, remapping )
                    V oldValue = map.get(key);
                    V newValue = (oldValue == null) ? v : remapping.merge(oldValue, v);
                    if (newValue == null) {
                        remove(key);
                    } else {
                        map.put(key, newValue);
                    }
                }
            }
        }

        @Override
        public boolean contains(Object o) {
            if (map != null) {
                return map.containsValue(o);
            } else {
                return list.contains(o);
            }
        }

        private List<V> asList() {
            if (list == null) {
                list = new ArrayList<>(map.values());
                map = null;
            }
            return list;
        }

        @Override
        public void add(int index, V element) {
            asList().add(index, element);
        }

        @Override
        public V remove(int index) {
            return asList().remove(index);
        }

        @Override
        public V get(int index) {
            return asList().get(index);
        }

        @Override
        public int size() {
            if (map != null) {
                return map.size();
            } else {
                return list.size();
            }
        }
    }
}
