package org.pitest.mutationtest.incremental;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.Reader;
import java.io.StringWriter;
import java.io.Writer;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Logger;
import org.pitest.classinfo.ClassIdentifier;
import org.pitest.classinfo.ClassName;
import org.pitest.classinfo.HierarchicalClassId;
import org.pitest.coverage.CoverageDatabase;
import org.pitest.functional.Option;
import org.pitest.mutationtest.ClassHistory;
import org.pitest.mutationtest.DetectionStatus;
import org.pitest.mutationtest.HistoryStore;
import org.pitest.mutationtest.MutationResult;
import org.pitest.mutationtest.MutationStatusTestPair;
import org.pitest.mutationtest.engine.MutationIdentifier;
import org.pitest.util.Log;
import org.pitest.util.Unchecked;
import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.io.StreamException;
import com.thoughtworks.xstream.io.xml.CompactWriter;
public class XStreamHistoryStore implements HistoryStore {
private static final Logger LOG = Log
.getLogger();
private static final XStream XSTREAM_INSTANCE = configureXStream();
private final WriterFactory outputFactory;
private final BufferedReader input;
private final Map<MutationIdentifier, MutationStatusTestPair> previousResults = new HashMap<MutationIdentifier, MutationStatusTestPair>();
private final Map<ClassName, ClassHistory> previousClassPath = new HashMap<ClassName, ClassHistory>();
public XStreamHistoryStore(final WriterFactory output,
final Option<Reader> input) {
this.outputFactory = output;
this.input = createReader(input);
}
private static XStream configureXStream() {
final XStream xstream = new XStream();
xstream.alias("classHistory", ClassHistory.class);
xstream.alias("fullClassId", HierarchicalClassId.class);
xstream.alias("classId", ClassIdentifier.class);
xstream.alias("name", ClassName.class);
xstream.alias("result", IdResult.class);
xstream.alias("statusTestPair", MutationStatusTestPair.class);
xstream.alias("status", DetectionStatus.class);
xstream.useAttributeFor(MutationStatusTestPair.class, "numberOfTestsRun");
xstream.useAttributeFor(MutationStatusTestPair.class, "status");
xstream.useAttributeFor(MutationStatusTestPair.class, "killingTest");
xstream.useAttributeFor(ClassIdentifier.class, "name");
xstream.useAttributeFor(ClassIdentifier.class, "hash");
xstream.useAttributeFor(HierarchicalClassId.class, "hierarchicalHash");
xstream.useAttributeFor(HierarchicalClassId.class, "classId");
return xstream;
}
private BufferedReader createReader(final Option<Reader> input) {
if (input.hasSome()) {
return new BufferedReader(input.value());
}
return null;
}
@Override
public void recordClassPath(final Collection<HierarchicalClassId> ids,
final CoverageDatabase coverageInfo) {
final PrintWriter output = this.outputFactory.create();
output.println(ids.size());
for (final HierarchicalClassId each : ids) {
final ClassHistory coverage = new ClassHistory(each, coverageInfo
.getCoverageIdForClass(each.getName()).toString(16));
output.println(toXml(coverage));
}
output.flush();
}
@Override
public void recordResult(final MutationResult result) {
final PrintWriter output = this.outputFactory.create();
output.println(toXml(new IdResult(result.getDetails().getId(), result
.getStatusTestPair())));
output.flush();
}
@Override
public Map<MutationIdentifier, MutationStatusTestPair> getHistoricResults() {
return this.previousResults;
}
@Override
public Map<ClassName, ClassHistory> getHistoricClassPath() {
return this.previousClassPath;
}
@Override
public void initialize() {
if (this.input != null) {
restoreClassPath();
restoreResults();
try {
this.input.close();
} catch (final IOException e) {
throw Unchecked.translateCheckedException(e);
}
}
}
private void restoreResults() {
String line;
try {
line = this.input.readLine();
while (line != null) {
final IdResult result = (IdResult) fromXml(line);
this.previousResults.put(result.id, result.status);
line = this.input.readLine();
}
} catch (final IOException e) {
LOG.warning("Could not read previous results");
} catch (final StreamException e) {
LOG.warning("Could not read previous results");
}
}
private void restoreClassPath() {
try {
final long classPathSize = Long.valueOf(this.input.readLine());
for (int i = 0; i != classPathSize; i++) {
final ClassHistory coverage = (ClassHistory) fromXml(this.input
.readLine());
this.previousClassPath.put(coverage.getName(), coverage);
}
} catch (final IOException e) {
LOG.warning("Could not read previous classpath");
} catch (final StreamException e) {
LOG.warning("Could not read previous classpath");
}
}
private static Object fromXml(final String xml) {
return XSTREAM_INSTANCE.fromXML(xml);
}
private static String toXml(final Object o) {
final Writer writer = new StringWriter();
XSTREAM_INSTANCE.marshal(o, new CompactWriter(writer));
return writer.toString().replaceAll("\n", "");
}
private static class IdResult {
final MutationIdentifier id;
final MutationStatusTestPair status;
IdResult(final MutationIdentifier id, final MutationStatusTestPair status) {
this.id = id;
this.status = status;
}
}
}