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}