/**
* 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.strdump;
import java.util.Iterator;
import java.util.Map;
import java.util.function.Function;
import de.fosd.jdime.artifact.Artifact;
import de.fosd.jdime.config.merge.Revision;
import de.fosd.jdime.matcher.matching.Color;
import de.fosd.jdime.matcher.matching.Matching;
/**
* Dumps the given <code>Artifact</code> tree as indented plaintext.
*/
public class PlaintextTreeDump implements StringDumper {
private static final String LS = System.lineSeparator();
/**
* Appends a plaintext representation of the tree with <code>artifact</code> at its root to the given
* <code>builder</code>.
*
* @param artifact
* the <code>Artifact</code> to dump
* @param getLabel
* the <code>Function</code> to use for producing a label an <code>Artifact</code>
* @param prefix
* the prefix to append before the given <code>artifact</code>
* @param childPrefix
* the prefix to append before all children of the given <code>artifact</code>
* @param builder
* the <code>StringBuilder</code> to append to
* @param <T>
* the type of the <code>Artifact</code>
*/
private <T extends Artifact<T>> void dumpTree(Artifact<T> artifact, Function<Artifact<T>, String> getLabel,
String prefix, String childPrefix, StringBuilder builder) {
if (artifact.isChoice() || artifact.isConflict()) {
String emptyPrefix = replicate(" ", childPrefix.length());
String emptyChildPrefix = emptyPrefix + " ";
builder.append(Color.RED.toShell());
builder.append(prefix); appendArtifact(artifact, getLabel, builder); builder.append(LS);
if (artifact.isChoice()) {
for (Map.Entry<String, T> entry : artifact.getVariants().entrySet()) {
builder.append("#ifdef ").append(entry.getKey()).append(LS);
dumpTree(entry.getValue(), getLabel, emptyPrefix, emptyChildPrefix, builder);
builder.append("#endif").append(LS);
}
} else if (artifact.isConflict()) {
Artifact<T> left = artifact.getLeft();
Artifact<T> right = artifact.getRight();
builder.append("<<<<<<<").append(LS);
dumpTree(left, getLabel, emptyPrefix, emptyChildPrefix, builder);
builder.append("=======").append(LS);
dumpTree(right, getLabel, emptyPrefix, emptyChildPrefix, builder);
builder.append(">>>>>>>").append(LS);
}
builder.append(Color.DEFAULT.toShell());
return;
}
if (artifact.hasMatches()) {
Iterator<Map.Entry<Revision, Matching<T>>> it = artifact.getMatches().entrySet().iterator();
Matching<T> firstEntry = it.next().getValue();
builder.append(firstEntry.getHighlightColor().toShell()).append(prefix);
appendArtifact(artifact, getLabel, builder);
int percentage = (int) (firstEntry.getPercentage() * 100);
builder.append(String.format(" <(%d, %d%%)> ", firstEntry.getScore(), percentage));
appendArtifact(firstEntry.getMatchingArtifact(artifact), getLabel, builder);
it.forEachRemaining(entry -> {
builder.append(Color.DEFAULT.toShell()).append(", ");
builder.append(entry.getValue().getHighlightColor().toShell());
appendArtifact(entry.getValue().getMatchingArtifact(artifact), getLabel, builder);
});
builder.append(Color.DEFAULT.toShell());
} else {
builder.append(prefix);
appendArtifact(artifact, getLabel, builder);
}
builder.append(LS);
for (Iterator<T> it = artifact.getChildren().iterator(); it.hasNext(); ) {
Artifact<T> next = it.next();
builder.append(childPrefix);
char c = next.hasChildren() ? '┬' : '─';
if (it.hasNext()) {
dumpTree(next, getLabel, "├──" + c, childPrefix + "│ ", builder);
} else {
dumpTree(next, getLabel, "└──" + c, childPrefix + " ", builder);
}
}
}
/**
* Appends the representation of the given <code>Artifact</code> to the <code>builder</code>.
*
* @param artifact
* the <code>Artifact</code> to append to the <code>builder</code>
* @param getLabel
* the <code>Function</code> to use for producing a label for the <code>Artifact</code>
* @param builder
* the <code>StringBuilder</code> to append to
* @param <T>
* the type of the <code>Artifact</code>
*/
private <T extends Artifact<T>> void appendArtifact(Artifact<T> artifact, Function<Artifact<T>, String> getLabel,
StringBuilder builder) {
builder.append("(").append(artifact.getId()).append(") ");
builder.append(getLabel.apply(artifact));
}
/**
* Replicates the given <code>String</code> <code>n</code> times and returns the concatenation.
*
* @param s
* the <code>String</code> to replicate
* @param n
* the number of replications
* @return the concatenation
*/
private static String replicate(String s, int n) {
return new String(new char[n]).replace("\0", s).intern();
}
@Override
public <T extends Artifact<T>> String dump(Artifact<T> artifact, Function<Artifact<T>, String> getLabel) {
StringBuilder builder = new StringBuilder();
dumpTree(artifact, getLabel, "", "", builder);
int lastLS = builder.lastIndexOf(LS);
if (lastLS != -1) {
builder.delete(lastLS, lastLS + LS.length());
}
return builder.toString();
}
}