content: /*******************************************************************************
   * Copyright (c) 2009, 2019 Mountainminds GmbH & Co. KG and Contributors
   * All rights reserved. This program and the accompanying materials
   * are made available under the terms of the Eclipse Public License v1.0
   * which accompanies this distribution, and is available at
   * http://www.eclipse.org/legal/epl-v10.html
   *
   * Contributors:
   *    Marc R. Hoffmann - initial API and implementation
   *    
   *******************************************************************************/
  package org.jacoco.core.data;
  
  import java.io.ByteArrayOutputStream;
  import java.io.IOException;
  import java.io.OutputStream;
  
  import org.jacoco.core.internal.data.CompactDataOutput;
  
  /**
   * Serialization of execution data into binary streams.
   */
  public class ExecutionDataWriter implements ISessionInfoVisitor,
  		IExecutionDataVisitor {
  
  	/** File format version, will be incremented for each incompatible change. */
  	public static final char FORMAT_VERSION;
  
  	static {
  		// Runtime initialize to ensure javac does not inline the value.
  		FORMAT_VERSION = 0x1007;
  	}
  
  	/** Magic number in header for file format identification. */
  	public static final char MAGIC_NUMBER = 0xC0C0;
  
  	/** Block identifier for file headers. */
  	public static final byte BLOCK_HEADER = 0x01;
  
  	/** Block identifier for session information. */
  	public static final byte BLOCK_SESSIONINFO = 0x10;
  
  	/** Block identifier for execution data of a single class. */
  	public static final byte BLOCK_EXECUTIONDATA = 0x11;
  
  	/** Underlying data output */
  	protected final CompactDataOutput out;
  
  	/**
  	 * Creates a new writer based on the given output stream. Depending on the
  	 * nature of the underlying stream output should be buffered as most data is
  	 * written in single bytes.
  	 * 
  	 * @param output
  	 *            binary stream to write execution data to
  	 * @throws IOException
  	 *             if the header can't be written
  	 */
  	public ExecutionDataWriter(final OutputStream output) throws IOException {
  		this.out = new CompactDataOutput(output);
  		writeHeader();
  	}
  
  	/**
  	 * Writes an file header to identify the stream and its protocol version.
  	 * 
  	 * @throws IOException
  	 *             if the header can't be written
  	 */
  	private void writeHeader() throws IOException {
  		out.writeByte(BLOCK_HEADER);
  		out.writeChar(MAGIC_NUMBER);
  		out.writeChar(FORMAT_VERSION);
  	}
  
  	/**
  	 * Flushes the underlying stream.
  	 * 
  	 * @throws IOException
  	 *             if the underlying stream can't be flushed
  	 */
  	public void flush() throws IOException {
  		out.flush();
  	}
  
  	public void visitSessionInfo(final SessionInfo info) {
  		try {
  			out.writeByte(BLOCK_SESSIONINFO);
  			out.writeUTF(info.getId());
  			out.writeLong(info.getStartTimeStamp());
  			out.writeLong(info.getDumpTimeStamp());
  		} catch (final IOException e) {
  			throw new RuntimeException(e);
  		}
  	}
  
  	public void visitClassExecution(final ExecutionData data) {
  		if (data.hasHits()) {
  			try {
  				out.writeByte(BLOCK_EXECUTIONDATA);
  				out.writeLong(data.getId());
  				out.writeUTF(data.getName());
  				out.writeBooleanArray(data.getProbes());
  			} catch (final IOException e) {
  				throw new RuntimeException(e);
  			}
  		}
  	}
  
  	/**
  	 * Returns the first bytes of a file that represents a valid execution data
  	 * file. In any case every execution data file starts with the three bytes
  	 * <code>0x01 0xC0 0xC0</code>.
  	 * 
  	 * @return first bytes of a execution data file
  	 */
  	public static final byte[] getFileHeader() {
  		final ByteArrayOutputStream buffer = new ByteArrayOutputStream();
  		try {
  			new ExecutionDataWriter(buffer);
  		} catch (final IOException e) {
  			// Must not happen with ByteArrayOutputStream
  			throw new AssertionError(e);
  		}
  		return buffer.toByteArray();
  	}
  
  }
  