| /******************************************************************************* |
| * Copyright (c) 2009, 2018 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 <code>true</code> if additional data can be expected after a |
| * command has been executed. <code>false</code> 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 <code>true</code> 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)); |
| } |
| |
| } |