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