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