• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*******************************************************************************
2  * Copyright (c) 2009, 2021 Mountainminds GmbH & Co. KG and Contributors
3  * This program and the accompanying materials are made available under
4  * the terms of the Eclipse Public License 2.0 which is available at
5  * http://www.eclipse.org/legal/epl-2.0
6  *
7  * SPDX-License-Identifier: EPL-2.0
8  *
9  * Contributors:
10  *    Marc R. Hoffmann - initial API and implementation
11  *
12  *******************************************************************************/
13 package org.jacoco.core.data;
14 
15 import static java.lang.String.format;
16 
17 import java.io.IOException;
18 import java.io.InputStream;
19 
20 import org.jacoco.core.internal.data.CompactDataInput;
21 
22 /**
23  * Deserialization of execution data from binary streams.
24  */
25 public class ExecutionDataReader {
26 
27 	/** Underlying data input */
28 	protected final CompactDataInput in;
29 
30 	private ISessionInfoVisitor sessionInfoVisitor = null;
31 
32 	private IExecutionDataVisitor executionDataVisitor = null;
33 
34 	private boolean firstBlock = true;
35 
36 	/**
37 	 * Creates a new reader based on the given input stream input. Depending on
38 	 * the nature of the underlying stream input should be buffered as most data
39 	 * is read in single bytes.
40 	 *
41 	 * @param input
42 	 *            input stream to read execution data from
43 	 */
ExecutionDataReader(final InputStream input)44 	public ExecutionDataReader(final InputStream input) {
45 		this.in = new CompactDataInput(input);
46 	}
47 
48 	/**
49 	 * Sets an listener for session information.
50 	 *
51 	 * @param visitor
52 	 *            visitor to retrieve session info events
53 	 */
setSessionInfoVisitor(final ISessionInfoVisitor visitor)54 	public void setSessionInfoVisitor(final ISessionInfoVisitor visitor) {
55 		this.sessionInfoVisitor = visitor;
56 	}
57 
58 	/**
59 	 * Sets an listener for execution data.
60 	 *
61 	 * @param visitor
62 	 *            visitor to retrieve execution data events
63 	 */
setExecutionDataVisitor(final IExecutionDataVisitor visitor)64 	public void setExecutionDataVisitor(final IExecutionDataVisitor visitor) {
65 		this.executionDataVisitor = visitor;
66 	}
67 
68 	/**
69 	 * Reads all data and reports it to the corresponding visitors. The stream
70 	 * is read until its end or a command confirmation has been sent.
71 	 *
72 	 * @return <code>true</code> if additional data can be expected after a
73 	 *         command has been executed. <code>false</code> if the end of the
74 	 *         stream has been reached.
75 	 * @throws IOException
76 	 *             might be thrown by the underlying input stream
77 	 * @throws IncompatibleExecDataVersionException
78 	 *             incompatible data version from different JaCoCo release
79 	 */
read()80 	public boolean read()
81 			throws IOException, IncompatibleExecDataVersionException {
82 		byte type;
83 		do {
84 			int i = in.read();
85 			if (i == -1) {
86 				return false; // EOF
87 			}
88 			type = (byte) i;
89 			if (firstBlock && type != ExecutionDataWriter.BLOCK_HEADER) {
90 				throw new IOException("Invalid execution data file.");
91 			}
92 			firstBlock = false;
93 		} while (readBlock(type));
94 		return true;
95 	}
96 
97 	/**
98 	 * Reads a block of data identified by the given id. Subclasses may
99 	 * overwrite this method to support additional block types.
100 	 *
101 	 * @param blocktype
102 	 *            block type
103 	 * @return <code>true</code> if there are more blocks to read
104 	 * @throws IOException
105 	 *             might be thrown by the underlying input stream
106 	 */
readBlock(final byte blocktype)107 	protected boolean readBlock(final byte blocktype) throws IOException {
108 		switch (blocktype) {
109 		case ExecutionDataWriter.BLOCK_HEADER:
110 			readHeader();
111 			return true;
112 		case ExecutionDataWriter.BLOCK_SESSIONINFO:
113 			readSessionInfo();
114 			return true;
115 		case ExecutionDataWriter.BLOCK_EXECUTIONDATA:
116 			readExecutionData();
117 			return true;
118 		default:
119 			throw new IOException(
120 					format("Unknown block type %x.", Byte.valueOf(blocktype)));
121 		}
122 	}
123 
readHeader()124 	private void readHeader() throws IOException {
125 		if (in.readChar() != ExecutionDataWriter.MAGIC_NUMBER) {
126 			throw new IOException("Invalid execution data file.");
127 		}
128 		final char version = in.readChar();
129 		if (version != ExecutionDataWriter.FORMAT_VERSION) {
130 			throw new IncompatibleExecDataVersionException(version);
131 		}
132 	}
133 
readSessionInfo()134 	private void readSessionInfo() throws IOException {
135 		if (sessionInfoVisitor == null) {
136 			throw new IOException("No session info visitor.");
137 		}
138 		final String id = in.readUTF();
139 		final long start = in.readLong();
140 		final long dump = in.readLong();
141 		sessionInfoVisitor.visitSessionInfo(new SessionInfo(id, start, dump));
142 	}
143 
readExecutionData()144 	private void readExecutionData() throws IOException {
145 		if (executionDataVisitor == null) {
146 			throw new IOException("No execution data visitor.");
147 		}
148 		final long id = in.readLong();
149 		final String name = in.readUTF();
150 		final boolean[] probes = in.readBooleanArray();
151 		executionDataVisitor
152 				.visitClassExecution(new ExecutionData(id, name, probes));
153 	}
154 
155 }
156