/** * 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.matcher.ordered; import java.util.ArrayList; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Optional; import java.util.Set; import java.util.stream.Collector; import java.util.stream.Collectors; import de.fosd.jdime.artifact.Artifact; import de.fosd.jdime.config.merge.MergeContext; import de.fosd.jdime.matcher.MatcherInterface; import de.fosd.jdime.matcher.matching.Matching; import de.fosd.jdime.matcher.matching.Matchings; import de.fosd.jdime.util.UnorderedTuple; /** * The <code>EqualityMatcher</code> can be used to compute <code>Matchings</code> for identical trees. * It traverses two ordered trees in post order and produces respective <code>Matchings</code>. * <p> * <code>EqualityMatcher</code> does not use its parent matcher to dispatch match() calls, and uses its own * implementation instead. * <p> * Usage:<br> * To check whether the trees are equal, extract the <code>Matching</code> with the highest score and compare it * with the size of the trees. * * @param <T> type of <code>Artifact</code> * @author Olaf Lessenich */ public class EqualityMatcher<T extends Artifact<T>> extends OrderedMatcher<T> { private static final String ID = EqualityMatcher.class.getSimpleName(); private static final Collector<Integer, ?, Integer> SUM_IDENTITY = Collectors.summingInt(i -> i); private Set<UnorderedTuple<T, T>> didNotMatch; /** * Constructs a new <code>EqualityMatcher</code>.<br> * This matcher does not use the parent matcher to dispatch further calls. * * @param matcher the parent <code>MatcherInterface</code> */ public EqualityMatcher(MatcherInterface<T> matcher) { super(matcher); this.didNotMatch = new HashSet<>(); } @Override public Matchings<T> match(MergeContext context, T left, T right) { Matchings<T> matchings = new Matchings<>(); List<Matching<T>> directChildMatchings = new ArrayList<>(); Iterator<T> lIt = left.getChildren().iterator(); Iterator<T> rIt = right.getChildren().iterator(); boolean allMatched = true; while (lIt.hasNext() && rIt.hasNext()) { T l = lIt.next(); T r = rIt.next(); Matchings<T> childMatchings = match(context, l, r); Optional<Matching<T>> directChildMatching = childMatchings.get(l, r); if (directChildMatching.isPresent()) { directChildMatchings.add(directChildMatching.get()); } else { allMatched = false; } matchings.addAll(childMatchings); } if (allMatched && left.getNumChildren() == right.getNumChildren() && left.matches(right)) { Integer sumScore = directChildMatchings.stream().map(Matching::getScore).collect(SUM_IDENTITY); matchings.add(new Matching<>(left, right, sumScore + 1)); LOG.finer(() -> { String format = "%s - Trees are equal: (%s, %s)"; return String.format(format, ID, left.getId(), right.getId()); }); } else { didNotMatch.add(UnorderedTuple.of(left, right)); LOG.finer(() -> { String format = "%s - Trees are NOT equal: (%s, %s)"; return String.format(format, ID, left.getId(), right.getId()); }); } matchings.stream().forEach(m -> m.setAlgorithm(ID)); return matchings; } /** * Returns whether this <code>EqualityMatcher</code> determined (in a previous run of * {@link #match(MergeContext, Artifact, Artifact)}) that the given pair of <code>Artifact</code>s do not * exactly match. * * @param artifacts * the pair of <code>Artifact</code>s to check * @return true if the given <code>Artifact</code>s were checked by the <code>EqualityMatcher</code> and did not * match */ public boolean didNotMatch(UnorderedTuple<T, T> artifacts) { return didNotMatch.contains(artifacts); } }