ExecutionDataReader.java revision 398ee59bebad6835dab57b60157eff16d511709e
1/*******************************************************************************
2 * Copyright (c) 2009, 2015 Mountainminds GmbH & Co. KG and Contributors
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Eclipse Public License v1.0
5 * which accompanies this distribution, and is available at
6 * http://www.eclipse.org/legal/epl-v10.html
7 *
8 * Contributors:
9 *    Marc R. Hoffmann - initial API and implementation
10 *
11 *******************************************************************************/
12package org.jacoco.core.data;
13
14import static java.lang.String.format;
15
16import java.io.EOFException;
17import java.io.IOException;
18import java.io.InputStream;
19
20import org.jacoco.core.internal.data.CompactDataInput;
21
22/**
23 * Deserialization of execution data from binary streams.
24 */
25public 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	 */
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	 */
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	 */
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	 */
78	public boolean read() throws IOException {
79		try {
80			byte type;
81			do {
82				type = in.readByte();
83				if (firstBlock && type != ExecutionDataWriter.BLOCK_HEADER) {
84					throw new IOException("Invalid execution data file.");
85				}
86				firstBlock = false;
87			} while (readBlock(type));
88			return true;
89		} catch (final EOFException e) {
90			return false;
91		}
92	}
93
94	/**
95	 * Reads a block of data identified by the given id. Subclasses may
96	 * overwrite this method to support additional block types.
97	 *
98	 * @param blocktype
99	 *            block type
100	 * @return <code>true</code> if there are more blocks to read
101	 * @throws IOException
102	 *             might be thrown by the underlying input stream
103	 */
104	protected boolean readBlock(final byte blocktype) throws IOException {
105		switch (blocktype) {
106		case ExecutionDataWriter.BLOCK_HEADER:
107			readHeader();
108			return true;
109		case ExecutionDataWriter.BLOCK_SESSIONINFO:
110			readSessionInfo();
111			return true;
112		case ExecutionDataWriter.BLOCK_EXECUTIONDATA:
113			readExecutionData();
114			return true;
115		default:
116			throw new IOException(format("Unknown block type %x.",
117					Byte.valueOf(blocktype)));
118		}
119	}
120
121	private void readHeader() throws IOException {
122		if (in.readChar() != ExecutionDataWriter.MAGIC_NUMBER) {
123			throw new IOException("Invalid execution data file.");
124		}
125		final char version = in.readChar();
126		if (version != ExecutionDataWriter.FORMAT_VERSION) {
127			throw new IOException(format("Incompatible version %x.",
128					Integer.valueOf(version)));
129		}
130	}
131
132	private void readSessionInfo() throws IOException {
133		if (sessionInfoVisitor == null) {
134			throw new IOException("No session info visitor.");
135		}
136		final String id = in.readUTF();
137		final long start = in.readLong();
138		final long dump = in.readLong();
139		sessionInfoVisitor.visitSessionInfo(new SessionInfo(id, start, dump));
140	}
141
142	private void readExecutionData() throws IOException {
143		if (executionDataVisitor == null) {
144			throw new IOException("No execution data visitor.");
145		}
146		final long id = in.readLong();
147		final String name = in.readUTF();
148		final boolean[] probes = in.readBooleanArray();
149		executionDataVisitor.visitClassExecution(new ExecutionData(id, name,
150				probes));
151	}
152
153}
154