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