ExecutionDataReader.java revision e69ba4dbb015949c5d84ba7bbb0b53efac28bb23
1/*******************************************************************************
2 * Copyright (c) 2009, 2012 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	 */
53	public void setSessionInfoVisitor(final ISessionInfoVisitor visitor) {
54		this.sessionInfoVisitor = visitor;
55	}
56
57	/**
58	 * Sets an listener for execution data.
59	 *
60	 * @param visitor
61	 */
62	public void setExecutionDataVisitor(final IExecutionDataVisitor visitor) {
63		this.executionDataVisitor = visitor;
64	}
65
66	/**
67	 * Reads all data and reports it to the corresponding visitors. The stream
68	 * is read until its end or a command confirmation has been sent.
69	 *
70	 * @return <code>true</code> if additional data can be expected after a
71	 *         command has been executed. <code>false</code> if the end of the
72	 *         stream has been reached.
73	 * @throws IOException
74	 *             might be thrown by the underlying input stream
75	 */
76	public boolean read() throws IOException {
77		try {
78			byte type;
79			do {
80				type = in.readByte();
81				if (firstBlock && type != ExecutionDataWriter.BLOCK_HEADER) {
82					throw new IOException("Invalid execution data file.");
83				}
84				firstBlock = false;
85			} while (readBlock(type));
86			return true;
87		} catch (final EOFException e) {
88			return false;
89		}
90	}
91
92	/**
93	 * Reads a block of data identified by the given id. Subclasses may
94	 * overwrite this method to support additional block types.
95	 *
96	 * @param blocktype
97	 *            block type
98	 * @return <code>true</code> if there are more blocks to read
99	 * @throws IOException
100	 *             might be thrown by the underlying input stream
101	 */
102	protected boolean readBlock(final byte blocktype) throws IOException {
103		switch (blocktype) {
104		case ExecutionDataWriter.BLOCK_HEADER:
105			readHeader();
106			return true;
107		case ExecutionDataWriter.BLOCK_SESSIONINFO:
108			readSessionInfo();
109			return true;
110		case ExecutionDataWriter.BLOCK_EXECUTIONDATA:
111			readExecutionData();
112			return true;
113		default:
114			throw new IOException(format("Unknown block type %x.",
115					Byte.valueOf(blocktype)));
116		}
117	}
118
119	private void readHeader() throws IOException {
120		if (in.readChar() != ExecutionDataWriter.MAGIC_NUMBER) {
121			throw new IOException("Invalid execution data file.");
122		}
123		final char version = in.readChar();
124		if (version != ExecutionDataWriter.FORMAT_VERSION) {
125			throw new IOException(format("Incompatible version %x.",
126					Integer.valueOf(version)));
127		}
128	}
129
130	private void readSessionInfo() throws IOException {
131		if (sessionInfoVisitor == null) {
132			throw new IOException("No session info visitor.");
133		}
134		final String id = in.readUTF();
135		final long start = in.readLong();
136		final long dump = in.readLong();
137		sessionInfoVisitor.visitSessionInfo(new SessionInfo(id, start, dump));
138	}
139
140	private void readExecutionData() throws IOException {
141		if (executionDataVisitor == null) {
142			throw new IOException("No execution data visitor.");
143		}
144		final long id = in.readLong();
145		final String name = in.readUTF();
146		final boolean[] data = in.readBooleanArray();
147		executionDataVisitor.visitClassExecution(new ExecutionData(id, name,
148				data));
149	}
150
151}
152