/******************************************************************************* * Copyright (c) 2009, 2019 Mountainminds GmbH & Co. KG and Contributors * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Marc R. Hoffmann - initial API and implementation * *******************************************************************************/ package org.jacoco.core.data; import static java.lang.String.format; import java.io.IOException; import java.io.InputStream; import org.jacoco.core.internal.data.CompactDataInput; /** * Deserialization of execution data from binary streams. */ public class ExecutionDataReader { /** Underlying data input */ protected final CompactDataInput in; private ISessionInfoVisitor sessionInfoVisitor = null; private IExecutionDataVisitor executionDataVisitor = null; private boolean firstBlock = true; /** * Creates a new reader based on the given input stream input. Depending on * the nature of the underlying stream input should be buffered as most data * is read in single bytes. * * @param input * input stream to read execution data from */ public ExecutionDataReader(final InputStream input) { this.in = new CompactDataInput(input); } /** * Sets an listener for session information. * * @param visitor * visitor to retrieve session info events */ public void setSessionInfoVisitor(final ISessionInfoVisitor visitor) { this.sessionInfoVisitor = visitor; } /** * Sets an listener for execution data. * * @param visitor * visitor to retrieve execution data events */ public void setExecutionDataVisitor(final IExecutionDataVisitor visitor) { this.executionDataVisitor = visitor; } /** * Reads all data and reports it to the corresponding visitors. The stream * is read until its end or a command confirmation has been sent. * * @return true if additional data can be expected after a * command has been executed. false if the end of the * stream has been reached. * @throws IOException * might be thrown by the underlying input stream * @throws IncompatibleExecDataVersionException * incompatible data version from different JaCoCo release */ public boolean read() throws IOException, IncompatibleExecDataVersionException { byte type; do { int i = in.read(); if (i == -1) { return false; // EOF } type = (byte) i; if (firstBlock && type != ExecutionDataWriter.BLOCK_HEADER) { throw new IOException("Invalid execution data file."); } firstBlock = false; } while (readBlock(type)); return true; } /** * Reads a block of data identified by the given id. Subclasses may * overwrite this method to support additional block types. * * @param blocktype * block type * @return true if there are more blocks to read * @throws IOException * might be thrown by the underlying input stream */ protected boolean readBlock(final byte blocktype) throws IOException { switch (blocktype) { case ExecutionDataWriter.BLOCK_HEADER: readHeader(); return true; case ExecutionDataWriter.BLOCK_SESSIONINFO: readSessionInfo(); return true; case ExecutionDataWriter.BLOCK_EXECUTIONDATA: readExecutionData(); return true; default: throw new IOException(format("Unknown block type %x.", Byte.valueOf(blocktype))); } } private void readHeader() throws IOException { if (in.readChar() != ExecutionDataWriter.MAGIC_NUMBER) { throw new IOException("Invalid execution data file."); } final char version = in.readChar(); if (version != ExecutionDataWriter.FORMAT_VERSION) { throw new IncompatibleExecDataVersionException(version); } } private void readSessionInfo() throws IOException { if (sessionInfoVisitor == null) { throw new IOException("No session info visitor."); } final String id = in.readUTF(); final long start = in.readLong(); final long dump = in.readLong(); sessionInfoVisitor.visitSessionInfo(new SessionInfo(id, start, dump)); } private void readExecutionData() throws IOException { if (executionDataVisitor == null) { throw new IOException("No execution data visitor."); } final long id = in.readLong(); final String name = in.readUTF(); final boolean[] probes = in.readBooleanArray(); executionDataVisitor.visitClassExecution(new ExecutionData(id, name, probes)); } }