1 /******************************************************************************* 2 * Copyright (c) 2009, 2018 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