trace/traceviewer/com.nokia.traceviewer/src/com/nokia/traceviewer/engine/PlainTextScrollReader.java
author Matti Laitinen <matti.t.laitinen@nokia.com>
Wed, 23 Jun 2010 14:49:59 +0300
changeset 11 5b9d4d8641ce
permissions -rw-r--r--
TraceViewer 2.6.0

/*
 * Copyright (c) 2007-2010 Nokia Corporation and/or its subsidiary(-ies). 
 * All rights reserved.
 * This component and the accompanying materials are made available
 * under the terms of "Eclipse Public License v1.0"
 * which accompanies this distribution, and is available
 * at the URL "http://www.eclipse.org/legal/epl-v10.html".
 *
 * Initial Contributors:
 * Nokia Corporation - initial contribution.
 *
 * Contributors:
 *
 * Description:
 *
 * PlainTextScrollReader class
 *
 */
package com.nokia.traceviewer.engine;

import java.io.IOException;
import java.nio.ByteBuffer;

/**
 * PlainText Scroll Reader class
 * 
 */
public class PlainTextScrollReader extends BaseDataReader implements
		DataScrollReader {

	/**
	 * Running boolean
	 */
	private boolean running;

	/**
	 * Number of blocks to process when reader is block-reader
	 */
	private int numberOfBlocks = 1;

	/**
	 * Starting offset when reader is block-reader
	 */
	private int startReadingTracesFrom;

	/**
	 * Number of traces read when reader is block-reader
	 */
	private int numberOfTracesRead;

	/**
	 * Indicates that not all traces can be fit in one buffer
	 */
	private boolean tracesDontFitInBuffer;

	/**
	 * Is this reader blocking
	 */
	private boolean blocking;

	/**
	 * Possible middle of line break
	 */
	private boolean middleOfLineBreak;

	/**
	 * Constructor
	 * 
	 * @param mediaCallback
	 *            callback
	 * @param configuration
	 *            trace configuration used in this reader
	 */
	public PlainTextScrollReader(MediaCallback mediaCallback,
			TraceConfiguration configuration) {
		filePath = TraceViewerGlobals.getTraceViewer().getDataReaderAccess()
				.getCurrentDataReader().getFilePath();
		createFileChannel();
		this.mediaCallback = mediaCallback;
		trace = new TraceProperties(configuration);
		traceConfiguration = configuration;

		receiveBuffer = ByteBuffer.allocateDirect(RECEIVE_BUFFER_SIZE);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see java.lang.Thread#run()
	 */
	@Override
	public synchronized void run() {
		try {
			running = true;
			receiveBuffer.position(0);
			while (running) {

				// Set buffer limit
				if (!tracesDontFitInBuffer) {
					tracesDontFitInBuffer = setBufferLimit();
				}

				// Reads data to buffer. ReceiveBuffer position is always zero
				// here so readBytes will read until the limit or to the end of
				// file
				int readBytes = readFile.getChannel().read(receiveBuffer);
				if (readBytes <= 0) {
					putToSleep();
					// If data is found, process buffer
				} else {
					// If file is at end, set limit according
					if (readBytes < receiveBuffer.limit()) {
						receiveBuffer.limit(readBytes);
					}
					// Start processing
					processBuffer();
				}
			}
		} catch (Throwable t) {
			t.printStackTrace();
		}
	}

	/**
	 * Sets buffer limit
	 * 
	 * @return true if traces fitted into the buffer
	 * @throws IOException
	 */
	private boolean setBufferLimit() throws IOException {
		boolean tracesDontFitInBuffer = false;
		FileMap fileMap = TraceViewerGlobals.getTraceViewer()
				.getDataReaderAccess().getCurrentDataReader().getFileMap();
		int index = startReadingTracesFrom / TraceViewerGlobals.blockSize;

		// Filemap must have the offset for end trace
		if (fileMap.size() > index + numberOfBlocks) {

			// Get positions and the difference
			long pos = readFile.getFilePointer();
			long pos2 = fileMap.getItem(index + numberOfBlocks).longValue();
			long limit = pos2 - pos;

			// If difference is smaller than capacity, set as limit
			if (limit < receiveBuffer.capacity()) {
				receiveBuffer.limit((int) limit);
			} else {
				receiveBuffer.limit(receiveBuffer.capacity());
				tracesDontFitInBuffer = true;
			}
		} else {
			receiveBuffer.limit(receiveBuffer.capacity());
			tracesDontFitInBuffer = true;
		}
		return tracesDontFitInBuffer;
	}

	/**
	 * Process character buffer
	 */
	private void processBuffer() {
		receiveBuffer.position(0);
		StringBuffer buf = new StringBuffer();
		boolean traceReady = false;

		// Loop until there is characters left
		while (receiveBuffer.hasRemaining()) {
			byte c = receiveBuffer.get();

			// If in the middle of a line break from previous buffer, skip first
			// character
			if (middleOfLineBreak && c == '\n' && receiveBuffer.hasRemaining()) {
				c = receiveBuffer.get();
			}
			middleOfLineBreak = false;

			// Check the character
			switch ((char) c) {
			case '\n':
				traceReady = true;
				break;
			case '\r':
				if (!receiveBuffer.hasRemaining()) {
					middleOfLineBreak = true;
				} else if (((char) receiveBuffer.get()) == '\n') {
				} else {
					receiveBuffer.position(receiveBuffer.position() - 1);
				}
				traceReady = true;
				break;
			default:
				buf.append((char) c);
				traceReady = false;
			}

			// Trace is ready, insert it
			if (traceReady) {
				String traceString = buf.toString();
				buf.setLength(0);
				processTrace(traceString);
				traceReady = false;
			}
		}

		// File at end, process last trace
		try {
			if (readFile.getFilePointer() >= readFile.length()) {
				String traceString = buf.toString();
				buf.setLength(0);
				processTrace(traceString);
				traceReady = false;
			}

			// If data left in buffer, move file backwards the same amount
			else if (buf.length() > 0) {
				setFilePosition(readFile.getFilePointer() - buf.length());

			}
		} catch (IOException e) {
			e.printStackTrace();
		}
		receiveBuffer.position(0);
	}

	/**
	 * Process plain text trace line
	 * 
	 * @param traceString
	 *            trace string
	 */
	private void processTrace(String traceString) {
		numberOfTracesRead++;
		trace = new TraceProperties(traceConfiguration);
		trace.traceString = traceString;
		trace.traceNumber = startReadingTracesFrom + numberOfTracesRead;

		// Not the last trace yet
		if (!lastTrace()) {
			trace.lastTrace = false;
			mediaCallback.processTrace(trace);

			// Last trace
		} else {
			tracesDontFitInBuffer = false;
			trace.lastTrace = true;
			blocking = true;
			numberOfTracesRead = 0;

			mediaCallback.processTrace(trace);

			putToSleep();
		}
	}

	/**
	 * Tells if this is the last trace
	 * 
	 * @return indication of if this is the last trace or not
	 */
	public boolean lastTrace() {
		boolean lastTrace = false;

		// 1 or 2 blocks read
		if (numberOfTracesRead >= TraceViewerGlobals.blockSize * numberOfBlocks) {
			lastTrace = true;
			// Last block
		} else if (startReadingTracesFrom + numberOfTracesRead >= TraceViewerGlobals
				.getTraceViewer().getDataReaderAccess().getCurrentDataReader()
				.getTraceCount()) {
			lastTrace = true;
		}
		return lastTrace;
	}

	/**
	 * Puts this thread to sleep
	 */
	public void putToSleep() {
		try {
			// Null variables if this reader is only for blocks
			if (TraceViewerGlobals.getTraceViewer().getStateHolder()
					.isScrolling()) {
				numberOfTracesRead = 0;
				receiveBuffer.limit(receiveBuffer.capacity());
				receiveBuffer.position(receiveBuffer.capacity());
			}
			if (blocking) {
				this.notifyAll();
				this.wait();
			}

		} catch (InterruptedException e) {
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.nokia.traceviewer.engine.DataReader#shutdown()
	 */
	public void shutdown() {
		running = false;
		try {
			sourceChannel.close();
			readFile.close();
		} catch (IOException e) {
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.nokia.traceviewer.engine.DataScrollReader#setBlockReader(int,
	 * int, boolean)
	 */
	public void setBlockReader(int numberOfBlocks, int startingTrace,
			boolean blocking) {
		this.numberOfBlocks = numberOfBlocks;
		this.startReadingTracesFrom = startingTrace;
		this.blocking = blocking;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.nokia.traceviewer.engine.DataScrollReader#setFilePosition(long)
	 */
	@Override
	public void setFilePosition(long filePos) {
		try {
			readFile.seek(filePos);

			if (filePos == fileStartOffset) {
				receiveBuffer.limit(receiveBuffer.capacity());
				receiveBuffer.position(receiveBuffer.capacity());
			}

		} catch (IOException e) {
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.nokia.traceviewer.engine.DataReader#pause(boolean)
	 */
	public void pause(boolean pause) {
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.nokia.traceviewer.engine.DataReader#isPaused()
	 */
	public boolean isPaused() {
		return false;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.nokia.traceviewer.engine.DataReader#getTracePositionInFile()
	 */
	public long getTracePositionInFile() {
		return 0;
	}

	@Override
	protected byte[] setProtocolSpecificStuffToMultiPartTrace(byte[] byteArr,
			TraceProperties trace) {
		// There isn't any multipart traces in plain text
		return byteArr;
	}
}