1 /******************************************************************************* 2 * Copyright (c) 2009, 2015 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.EOFException; 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 */ read()78 public boolean read() throws IOException { 79 try { 80 byte type; 81 do { 82 type = in.readByte(); 83 if (firstBlock && type != ExecutionDataWriter.BLOCK_HEADER) { 84 throw new IOException("Invalid execution data file."); 85 } 86 firstBlock = false; 87 } while (readBlock(type)); 88 return true; 89 } catch (final EOFException e) { 90 return false; 91 } 92 } 93 94 /** 95 * Reads a block of data identified by the given id. Subclasses may 96 * overwrite this method to support additional block types. 97 * 98 * @param blocktype 99 * block type 100 * @return <code>true</code> if there are more blocks to read 101 * @throws IOException 102 * might be thrown by the underlying input stream 103 */ readBlock(final byte blocktype)104 protected boolean readBlock(final byte blocktype) throws IOException { 105 switch (blocktype) { 106 case ExecutionDataWriter.BLOCK_HEADER: 107 readHeader(); 108 return true; 109 case ExecutionDataWriter.BLOCK_SESSIONINFO: 110 readSessionInfo(); 111 return true; 112 case ExecutionDataWriter.BLOCK_EXECUTIONDATA: 113 readExecutionData(); 114 return true; 115 default: 116 throw new IOException(format("Unknown block type %x.", 117 Byte.valueOf(blocktype))); 118 } 119 } 120 readHeader()121 private void readHeader() throws IOException { 122 if (in.readChar() != ExecutionDataWriter.MAGIC_NUMBER) { 123 throw new IOException("Invalid execution data file."); 124 } 125 final char version = in.readChar(); 126 if (version != ExecutionDataWriter.FORMAT_VERSION) { 127 throw new IOException(format("Incompatible version %x.", 128 Integer.valueOf(version))); 129 } 130 } 131 readSessionInfo()132 private void readSessionInfo() throws IOException { 133 if (sessionInfoVisitor == null) { 134 throw new IOException("No session info visitor."); 135 } 136 final String id = in.readUTF(); 137 final long start = in.readLong(); 138 final long dump = in.readLong(); 139 sessionInfoVisitor.visitSessionInfo(new SessionInfo(id, start, dump)); 140 } 141 readExecutionData()142 private void readExecutionData() throws IOException { 143 if (executionDataVisitor == null) { 144 throw new IOException("No execution data visitor."); 145 } 146 final long id = in.readLong(); 147 final String name = in.readUTF(); 148 final boolean[] probes = in.readBooleanArray(); 149 executionDataVisitor.visitClassExecution(new ExecutionData(id, name, 150 probes)); 151 } 152 153 } 154