de.fosd.jdime.merge.Merge.java Source code

Java tutorial

Introduction

Here is the source code for de.fosd.jdime.merge.Merge.java

Source

/*
 * Copyright (C) 2013-2014 Olaf Lessenich
 * Copyright (C) 2014-2015 University of Passau, Germany
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 * MA 02110-1301  USA
 *
 * Contributors:
 *     Olaf Lessenich <lessenic@fim.uni-passau.de>
 *     Georg Seibt <seibt@fim.uni-passau.de>
 */
package de.fosd.jdime.merge;

import java.io.IOException;
import java.util.List;
import java.util.Objects;

import de.fosd.jdime.common.ASTNodeArtifact;
import de.fosd.jdime.common.Artifact;
import de.fosd.jdime.common.MergeContext;
import de.fosd.jdime.common.MergeTriple;
import de.fosd.jdime.common.operations.AddOperation;
import de.fosd.jdime.common.operations.ConflictOperation;
import de.fosd.jdime.common.operations.DeleteOperation;
import de.fosd.jdime.common.operations.MergeOperation;
import de.fosd.jdime.matcher.Color;
import de.fosd.jdime.matcher.Matching;
import org.apache.commons.lang3.ClassUtils;
import org.apache.log4j.Logger;

/**
 * @author Olaf Lessenich
 *
 * @param <T>
 *            type of artifact
 */
public class Merge<T extends Artifact<T>> implements MergeInterface<T> {

    private static final Logger LOG = Logger.getLogger(ClassUtils.getShortClassName(Merge.class));
    private UnorderedMerge<T> unorderedMerge = null;
    private OrderedMerge<T> orderedMerge = null;
    private String logprefix;

    /**
     * TODO: this needs high-level explanation.
     *
     * @param operation
     * @param context
     *
     * @throws IOException
     * @throws InterruptedException
     */
    @Override
    public final void merge(final MergeOperation<T> operation, final MergeContext context)
            throws IOException, InterruptedException {
        logprefix = operation.getId() + " - ";
        MergeTriple<T> triple = operation.getMergeTriple();
        T left = triple.getLeft();
        T base = triple.getBase();
        T right = triple.getRight();
        T target = operation.getTarget();

        if (!context.isDiffOnly() && context.isQuiet()) {
            Objects.requireNonNull(target, "target must not be null!");
        }

        Diff<T> diff = new Diff<>();

        Matching<T> m;
        if (!left.matchingComputed() && !right.matchingComputed()) {
            if (!base.isEmptyDummy()) {
                // 3-way merge

                // diff base left
                m = diff.compare(base, left, Color.GREEN);
                if (LOG.isDebugEnabled()) {
                    if (m.getScore() == 0) {
                        LOG.debug(base.getId() + " and " + left.getId() + " have no matches.");
                    }
                }

                // diff base right
                m = diff.compare(base, right, Color.GREEN);
                if (LOG.isDebugEnabled()) {
                    if (m.getScore() == 0) {
                        LOG.debug(base.getId() + " and " + right.getId() + " have no matches.");
                    }
                }
            }

            // diff left right
            m = diff.compare(left, right, Color.BLUE);

            // TODO: compute and write diff stats
            if (context.isDiffOnly() && left.isRoot() && left instanceof ASTNodeArtifact) {
                assert (right.isRoot());
                return;
            }

            if (m.getScore() == 0) {
                if (LOG.isDebugEnabled()) {
                    LOG.debug(left.getId() + " and " + right.getId() + " have no matches.");
                }
                return;
            }
        }

        assert (left.hasMatching(right) && right.hasMatching(left));

        if (target != null && target.isRoot() && !target.hasMatches()) {
            // hack to fix the matches for the merged root node
            target.cloneMatches(left);
        }

        // check if one or both the nodes have no children
        List<T> leftChildren = left.getChildren();
        List<T> rightChildren = right.getChildren();

        if (LOG.isTraceEnabled()) {
            LOG.trace(prefix() + "Children that need to be merged:");
            LOG.trace(prefix(left) + "-> (" + leftChildren + ")");
            LOG.trace(prefix(right) + "-> (" + rightChildren + ")");
        }

        if ((base.isEmptyDummy() || base.hasChildren()) && (leftChildren.isEmpty() || rightChildren.isEmpty())) {
            if (leftChildren.isEmpty() && rightChildren.isEmpty()) {
                if (LOG.isTraceEnabled()) {
                    LOG.trace(prefix(left) + "and [" + right.getId() + "] have no children.");
                }
                return;
            } else if (leftChildren.isEmpty()) {
                if (LOG.isTraceEnabled()) {
                    LOG.trace(prefix(left) + "has no children.");
                    LOG.trace(prefix(right) + "was deleted by left");
                }
                if (right.hasChanges()) {
                    if (LOG.isTraceEnabled()) {
                        LOG.trace(prefix(right) + "has changes in subtree");
                    }
                    for (T rightChild : right.getChildren()) {
                        //                  ConflictOperation<T> conflictOp = new ConflictOperation<>(
                        //                        rightChild, null, rightChild, target);
                        //                  conflictOp.apply(context);
                        AddOperation<T> addOp = new AddOperation<>(rightChild, target, false);
                        addOp.apply(context);
                    }
                    return;
                } else {
                    for (T rightChild : rightChildren) {

                        DeleteOperation<T> delOp = new DeleteOperation<>(rightChild, null);
                        delOp.apply(context);
                    }
                    return;
                }
            } else if (rightChildren.isEmpty()) {
                if (LOG.isTraceEnabled()) {
                    LOG.trace(prefix(right) + "has no children.");
                    LOG.trace(prefix(left) + " was deleted by right");
                }
                if (left.hasChanges()) {
                    if (LOG.isTraceEnabled()) {
                        LOG.trace(prefix(left) + " has changes in subtree");
                    }
                    for (T leftChild : left.getChildren()) {
                        //                  ConflictOperation<T> conflictOp = new ConflictOperation<>(
                        //                        leftChild, leftChild, null, target);
                        //                  conflictOp.apply(context);
                        DeleteOperation<T> delOp = new DeleteOperation<>(leftChild, target);
                        delOp.apply(context);

                    }
                    return;
                } else {
                    for (T leftChild : leftChildren) {
                        DeleteOperation<T> delOp = new DeleteOperation<>(leftChild, null);
                        delOp.apply(context);
                    }
                    return;
                }
            } else {
                throw new RuntimeException("Something is very broken.");
            }
        }

        // determine whether we have to respect the order of children
        boolean isOrdered = false;
        for (int i = 0; !isOrdered && i < left.getNumChildren(); i++) {
            if (left.getChild(i).isOrdered()) {
                isOrdered = true;
            }
        }
        for (int i = 0; !isOrdered && i < right.getNumChildren(); i++) {
            if (right.getChild(i).isOrdered()) {
                isOrdered = true;
            }
        }

        if (LOG.isTraceEnabled() && target != null) {
            LOG.trace(logprefix + "target.dumpTree() before merge:");
            System.out.println(target.dumpRootTree());
        }
        if (isOrdered) {
            if (orderedMerge == null) {
                orderedMerge = new OrderedMerge<>();
            }
            orderedMerge.merge(operation, context);
        } else {
            if (unorderedMerge == null) {
                unorderedMerge = new UnorderedMerge<>();
            }
            unorderedMerge.merge(operation, context);
        }
        return;
    }

    /**
     * Returns the logging prefix.
     *
     * @return logging prefix
     */
    private String prefix() {
        return logprefix;
    }

    /**
     * Returns the logging prefix.
     *
     * @param artifact
     *            artifact that is subject of the logging
     * @return logging prefix
     */
    private String prefix(final T artifact) {
        return logprefix + "[" + artifact.getId() + "] ";
    }
}