trace/traceviewer/com.nokia.traceviewer/src/com/nokia/traceviewer/engine/dataprocessor/SearchProcessor.java
author Jussi Ryoma <ext-jussi.s.ryoma@nokia.com>
Tue, 24 Aug 2010 14:01:48 +0300
changeset 16 72f198be1c1d
parent 11 5b9d4d8641ce
permissions -rw-r--r--
Crash Analyser Carbide Extension 1.4.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:
 *
 * SearchProcessor DataProcessor
 *
 */
package com.nokia.traceviewer.engine.dataprocessor;

import java.util.ArrayList;
import java.util.List;

import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.Document;
import org.eclipse.jface.text.FindReplaceDocumentAdapter;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IRegion;
import org.eclipse.ui.PlatformUI;

import com.nokia.traceviewer.action.TraceViewerActionUtils;
import com.nokia.traceviewer.dialog.SearchDialog;
import com.nokia.traceviewer.engine.DataProcessorAccess;
import com.nokia.traceviewer.engine.DataReader;
import com.nokia.traceviewer.engine.DataScrollReader;
import com.nokia.traceviewer.engine.MediaCallback;
import com.nokia.traceviewer.engine.StateHolder;
import com.nokia.traceviewer.engine.TraceInformation;
import com.nokia.traceviewer.engine.TraceProperties;
import com.nokia.traceviewer.engine.TraceViewerGlobals;
import com.nokia.traceviewer.engine.TraceViewerDialogInterface.Dialog;

/**
 * SearchProcessor DataProcessor
 */
public final class SearchProcessor implements DataProcessor, MediaCallback {

	/**
	 * Enum indicating the error codes of TraceViewer API operations
	 */
	public enum TimestampSearchPhase {

		/**
		 * Timestamp search is not ongoing
		 */
		NONE,

		/**
		 * Finding end timestamp
		 */
		FINDING_END,

		/**
		 * End found
		 */
		END_FOUND;

	}

	/**
	 * Search dialog used in searching
	 */
	private SearchDialog searchDialog;

	/**
	 * Temporary document used in searching
	 */
	private final IDocument document;

	/**
	 * Buffer to hold data before inserting all to document
	 */
	private final StringBuffer data;

	/**
	 * Finder adapter
	 */
	private final FindReplaceDocumentAdapter finder;

	/**
	 * Search properties
	 */
	private SearchProperties searchProperties;

	/**
	 * Timestamp range end trace number
	 */
	private int timestampEndTrace;

	/**
	 * Tells if we have hit EOF already
	 */
	private boolean hitEOFAlready;

	/**
	 * Tells that we should stop searching
	 */
	private boolean stopSearching;

	/**
	 * Tells that search is ongoing
	 */
	private boolean searchOngoing;

	/**
	 * Array containing trace informations (cid, gid and tid)
	 */
	private final List<TraceInformation> traceInformations;

	/**
	 * Array containing timestamp strings
	 */
	private final List<String> timestampStrings;

	/**
	 * Search data reader
	 */
	private DataScrollReader searchDataReader;

	/**
	 * Timestamp search phase
	 */
	private TimestampSearchPhase timestampSearchPhase = TimestampSearchPhase.NONE;

	/**
	 * Constructor
	 */
	public SearchProcessor() {
		// Create needed elements and arrays
		data = new StringBuffer();
		document = new Document();
		finder = new FindReplaceDocumentAdapter(document);
		searchProperties = new SearchProperties();
		traceInformations = new ArrayList<TraceInformation>();
		timestampStrings = new ArrayList<String>();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * com.nokia.traceviewer.engine.DataProcessor#processData(com.nokia.traceviewer
	 * .engine.TraceProperties)
	 */
	public void processData(TraceProperties properties) {
		DataProcessorAccess dpa = TraceViewerGlobals.getTraceViewer()
				.getDataProcessorAccess();

		// If binary trace, decode it
		if (properties.binaryTrace) {
			dpa.getDecoder().processData(properties);
		}

		// Parse timestamp
		dpa.getTimestampParser().processData(properties);

		// Traces missing
		if (properties.bTraceInformation.isTraceMissing()) {
			data.append(TraceViewerActionUtils.TRACES_DROPPED_MSG);
		}

		// Timestamp string
		if (properties.timestampString != null) {
			data.append(properties.timestampString);
			data.append(dpa.getTimestampParser().getTimeFromPreviousString(
					properties.timeFromPreviousTrace));
			data.append('\t');

			// Add to the timestampstrings array
			timestampStrings.add(properties.timestampString);

		}

		// Trace string
		data.append(properties.traceString);

		// Trace comment
		if (properties.traceComment != null) {
			data.append(TraceViewerActionUtils.COMMENT_PREFIX);
			data.append(properties.traceComment);
		}
		data.append('\n');

		traceInformations.add(properties.information);

		// Last trace, start search
		if (properties.lastTrace) {
			document.set(data.toString());
			data.setLength(0);

			// Data is received, start search
			startSearch();
			traceInformations.clear();
			timestampStrings.clear();
		}
	}

	/**
	 * Starts search
	 */
	private void startSearch() {
		if (!stopSearching) {
			try {
				int tracesTillNow = searchProperties
						.getCurrentSearchStartLine()
						+ TraceViewerGlobals.blockSize * 2;

				// Get the search start offset
				int searchStartOffset = getSearchStartOffset();

				// Find the trace and get the offset of it
				int offset = findTrace(searchStartOffset);

				// Update progressBar
				searchDialog.updateSearchProgressBar(searchProperties
						.getCurrentSearchStartLine() + 1);

				if (searchProperties.isSearchingForward()) {
					processSearchingForward(tracesTillNow, offset);
				} else {
					processSearchingBackward(offset);
				}

			} catch (BadLocationException e) {
				e.printStackTrace();
				searchOngoing = false;
			}
		} else {
			TraceViewerGlobals.getTraceViewer().getStateHolder().setState(
					StateHolder.State.NORMAL);
			stopSearching = false;
			searchOngoing = false;
		}

	}

	/**
	 * Gets the correct search start offset
	 * 
	 * @return search start offset
	 */
	private int getSearchStartOffset() {
		int searchStartOffset;

		// Set proper offset to start searching from
		if (searchProperties.isSearchingForward()) {
			searchStartOffset = 0;

			// Searching backward
		} else {
			searchStartOffset = document.getLength() - 1;
		}

		return searchStartOffset;
	}

	/**
	 * Finds the trace using search properties
	 * 
	 * @param searchStartOffset
	 *            start offset where to start the search
	 * @return offset of the trace found or -1 if not found
	 * @throws BadLocationException
	 */
	private int findTrace(int searchStartOffset) throws BadLocationException {
		int offset = SearchUtils.NOT_FOUND;

		// Search using component, group and trace ID's
		if (SearchUtils.containsIdQuery(searchProperties.getSearchString())) {
			offset = findTraceIDSearch(searchStartOffset);

			// Timestamp search
		} else if (SearchUtils.containsTimestampQuery(searchProperties
				.getSearchString())) {
			offset = findTraceTimestampSearch(searchStartOffset);

			// Create a normal text search
		} else {
			timestampSearchPhase = TimestampSearchPhase.NONE;
			IRegion region = finder.find(searchStartOffset, searchProperties
					.getSearchString(), searchProperties.isSearchingForward(),
					searchProperties.isCaseSensitive(), searchProperties
							.isWholeWord(), searchProperties.isRegExp());

			if (region != null) {
				offset = region.getOffset();
			}
		}

		return offset;
	}

	/**
	 * Finds the trace using ID search
	 * 
	 * @param searchStartOffset
	 *            search start offset
	 * @return found offset
	 * @throws BadLocationException
	 */
	private int findTraceIDSearch(int searchStartOffset)
			throws BadLocationException {
		int offset;
		timestampSearchPhase = TimestampSearchPhase.NONE;

		// Parse ID's from the search string
		TraceInformation inf = SearchUtils.parseIDsFromString(searchProperties
				.getSearchString());
		int startTrace = document.getLineOfOffset(searchStartOffset);

		// Get the trace number that matches the ID's
		int foundTrace = SearchUtils.findTraceOffsetFromInformations(
				startTrace, inf, traceInformations, searchProperties
						.isSearchingForward());

		if (foundTrace == SearchUtils.NOT_FOUND) {
			offset = SearchUtils.NOT_FOUND;
		} else {
			offset = document.getLineOffset(foundTrace);
		}
		return offset;
	}

	/**
	 * Finds the trace using timestamp search
	 * 
	 * @param searchStartOffset
	 *            search start offset
	 * @return found offset
	 * @throws BadLocationException
	 */
	private int findTraceTimestampSearch(int searchStartOffset)
			throws BadLocationException {
		int offset;
		boolean getFirstTimestamp = true;
		boolean timestampSearchOnGoing = false;
		if (timestampSearchPhase == TimestampSearchPhase.NONE
				|| timestampSearchPhase == TimestampSearchPhase.FINDING_END) {
			timestampSearchOnGoing = true;
		}
		if (SearchUtils.containsTimestampRangeQuery(searchProperties
				.getSearchString())
				&& timestampSearchOnGoing) {
			getFirstTimestamp = false;
			timestampSearchPhase = TimestampSearchPhase.FINDING_END;
		}

		// Parse timestamp from the search string
		String timestamp = SearchUtils.parseTimestampFromString(
				searchProperties.getSearchString(), getFirstTimestamp);

		int startTrace = document.getLineOfOffset(searchStartOffset);

		// Get the trace which matches the timestamp
		int foundTrace = SearchUtils.findTraceOffsetFromTimestamps(startTrace,
				timestamp, timestampStrings, searchProperties
						.isSearchingForward());

		if (foundTrace == SearchUtils.NOT_FOUND) {
			offset = SearchUtils.NOT_FOUND;
		} else {
			offset = document.getLineOffset(foundTrace);
		}
		return offset;
	}

	/**
	 * Processes situation when searching forward
	 * 
	 * @param tracesTillNow
	 *            Tells how many traces is read from file
	 * @param offset
	 *            offset to found item
	 * @throws BadLocationException
	 */
	private void processSearchingForward(int tracesTillNow, int offset)
			throws BadLocationException {

		// String found
		if (offset != SearchUtils.NOT_FOUND) {
			processStringFound(offset);

			// Full round processed
		} else if (tracesTillNow > searchProperties
				.getOriginalSearchStartLine()
				&& hitEOFAlready) {
			fullRoundProcessed();
			// We didn't find anything from these 2 blocks, go to next one
		} else {
			// Not End Of File
			if (tracesTillNow < TraceViewerGlobals.getTraceViewer()
					.getDataReaderAccess().getCurrentDataReader()
					.getTraceCount()) {
				searchProperties.setCurrentSearchStartLine(tracesTillNow + 1);
				processSearch(searchProperties, true);
				// End Of File
			} else {
				hitEOFAlready = true;
				searchDialog.updateSearchProgressBar(TraceViewerGlobals
						.getTraceViewer().getDataReaderAccess()
						.getCurrentDataReader().getTraceCount());
				searchDialog.searchHitEOF();
				searchOngoing = false;
			}
		}
	}

	/**
	 * Full round processed
	 */
	private void fullRoundProcessed() {
		hitEOFAlready = false;
		searchDialog.updateSearchProgressBar(searchProperties
				.getOriginalSearchStartLine());
		searchDialog.searchHitFullRound();
		searchOngoing = false;
	}

	/**
	 * Processes situation where result was found
	 * 
	 * @param offset
	 *            offset of the result
	 * @throws BadLocationException
	 */
	private void processStringFound(int offset) throws BadLocationException {
		hitEOFAlready = false;
		int line = document.getLineOfOffset(offset);

		int foundTraceLine = searchProperties.getCurrentSearchStartLine()
				+ line;

		// Update progressBar
		searchDialog.updateSearchProgressBar(foundTraceLine);

		// Set the search line
		if (timestampSearchPhase == TimestampSearchPhase.NONE) {
			searchOngoing = false;
			TraceViewerGlobals.getTraceViewer().getView().highlightLines(
					foundTraceLine, 0, false);

			// Timestamp range search
		} else {
			if (timestampSearchPhase == TimestampSearchPhase.FINDING_END) {
				timestampSearchPhase = TimestampSearchPhase.END_FOUND;
				timestampEndTrace = foundTraceLine;

				// Start new search
				searchProperties.setCurrentSearchStartLine(foundTraceLine);
				processSearch(searchProperties, true);

				// End and start both found
			} else if (timestampSearchPhase == TimestampSearchPhase.END_FOUND) {
				searchOngoing = false;
				TraceViewerGlobals.getTraceViewer().getView().highlightLines(
						foundTraceLine, timestampEndTrace, true);
				timestampSearchPhase = TimestampSearchPhase.NONE;
				timestampEndTrace = 0;
			} else {
				searchOngoing = false;
			}
		}

	}

	/**
	 * Processes situation when searching backwards
	 * 
	 * @param offset
	 *            offset to found item
	 * @throws BadLocationException
	 */
	private void processSearchingBackward(int offset)
			throws BadLocationException {

		// String found
		if (offset != SearchUtils.NOT_FOUND) {
			processStringFound(offset);
			// Full round processed
		} else if (searchProperties.getCurrentSearchStartLine() <= searchProperties
				.getOriginalSearchStartLine()
				&& hitEOFAlready) {
			fullRoundProcessed();
		} else {
			// Not End Of File
			if (searchProperties.getCurrentSearchStartLine() > 0) {
				searchProperties.setCurrentSearchStartLine(searchProperties
						.getCurrentSearchStartLine()
						- (TraceViewerGlobals.blockSize / 2));
				processSearch(searchProperties, true);
				// End Of File
			} else {
				hitEOFAlready = true;
				searchDialog.updateSearchProgressBar(0);

				// Timestamp search
				if (timestampSearchPhase == TimestampSearchPhase.END_FOUND
						|| timestampSearchPhase == TimestampSearchPhase.FINDING_END) {
					if (timestampEndTrace == 0) {
						timestampEndTrace--;
					}
					TraceViewerGlobals.getTraceViewer().getView()
							.highlightLines(0, timestampEndTrace + 1, false);
					timestampSearchPhase = TimestampSearchPhase.NONE;
					timestampEndTrace = 0;

					// Normal search
				} else {
					searchDialog.searchHitEOF();
				}
				searchOngoing = false;
			}
		}
	}

	/**
	 * Sets hitEOFAlready variable
	 * 
	 * @param hitEOF
	 *            boolean to set varible to
	 */
	public void setHitEOFAlready(boolean hitEOF) {
		this.hitEOFAlready = hitEOF;
	}

	/**
	 * Gets search dialog
	 * 
	 * @return search dialog
	 */
	public SearchDialog getSearchDialog() {
		if (searchDialog == null) {
			searchDialog = (SearchDialog) TraceViewerGlobals.getTraceViewer()
					.getDialogs().createDialog(Dialog.SEARCH);
		}
		return searchDialog;
	}

	/**
	 * Sets stop boolean as true or false. If true, searching will stop when
	 * next block has been fetched from the file
	 * 
	 * @param stop
	 *            indicates if the search should be stopped
	 */
	public void stopSearch(boolean stop) {
		stopSearching = stop;
	}

	/**
	 * Process search with given searchProperties
	 * 
	 * @param searchProperties
	 *            searchproperties
	 * @param internal
	 *            if true, the search request is coming from TraceViewer
	 *            internally
	 */
	public void processSearch(SearchProperties searchProperties,
			boolean internal) {

		// If search is already ongoing, stop it
		if (!internal && searchOngoing) {
			stopSearch(true);
			while (searchOngoing) {
				try {
					Thread.sleep(50);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}
		searchOngoing = true;

		int traceCount = TraceViewerGlobals.getTraceViewer()
				.getDataReaderAccess().getCurrentDataReader().getTraceCount();

		int numberOfBlocks = 2;
		int traceToStartReading = searchProperties.getCurrentSearchStartLine();

		// Searching forward
		if (searchProperties.isSearchingForward()) {

			// Check now many blocks are needed
			if (traceToStartReading + TraceViewerGlobals.blockSize > traceCount) {
				numberOfBlocks = 1;
			}

			// Searching backward
		} else {
			// Check now many blocks are needed
			if (traceToStartReading < TraceViewerGlobals.blockSize) {
				numberOfBlocks = 1;
			} else {
				// If searching backwards, read traces from one block before
				traceToStartReading = traceToStartReading
						- TraceViewerGlobals.blockSize;
			}
		}

		// Get the file position having given offset
		int index = TraceViewerGlobals.getTraceViewer().getDataReaderAccess()
				.getCurrentDataReader().getFileMap().getIndexFromOffset(
						traceToStartReading);

		this.searchProperties = searchProperties;

		long pos = TraceViewerGlobals.getTraceViewer().getDataReaderAccess()
				.getCurrentDataReader().getFileMap().getItem(index).longValue();
		int startTrace = index * TraceViewerGlobals.blockSize;

		this.searchProperties.setCurrentSearchStartLine(startTrace);

		// Create searchDataReader if it doesn't exist and start it
		searchDataReader = TraceViewerGlobals.getTraceViewer()
				.getDataReaderAccess().startOwnDataReader(searchDataReader,
						this, numberOfBlocks, pos, startTrace, false);
	}

	/**
	 * Performs timestamp range search. Must be called from UI thread!
	 * 
	 * @param findStr
	 *            find str
	 */
	public void doTimestampRangeSearch(String findStr) {
		int traceCount = TraceViewerGlobals.getTraceViewer()
				.getDataReaderAccess().getCurrentDataReader().getTraceCount();
		searchProperties.setCurrentSearchStartLine(traceCount);
		searchProperties.setOriginalSearchStartLine(traceCount);
		searchProperties.setSearchString(findStr);
		searchProperties.setSearchingForward(false);
		getSearchDialog().setSearchText(findStr);
		processSearch(searchProperties, false);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * com.nokia.traceviewer.engine.MediaCallback#endOfFile(com.nokia.traceviewer
	 * .engine.DataReader)
	 */
	public void endOfFile(DataReader reader) {
		// Do nothing
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.nokia.traceviewer.engine.MediaCallback#dataHandleChanged()
	 */
	public void dataHandleChanged() {
		shutdownSearchReader();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * com.nokia.traceviewer.engine.MediaCallback#processTrace(com.nokia.traceviewer
	 * .engine.TraceProperties)
	 */
	public void processTrace(TraceProperties properties) {
		processData(properties);
	}

	/**
	 * Disposes search dialog. Also kill the search data reader.
	 * 
	 * @return true if search dialog was closed. False if it wasn't open.
	 */
	public boolean disposeSearchDialog() {
		boolean closed = false;
		if (searchDialog != null) {
			closed = searchDialog.forceClose();
			searchDialog = null;
			shutdownSearchReader();
		}
		return closed;
	}

	/**
	 * Shuts down search data reader
	 */
	public void shutdownSearchReader() {
		if (searchDataReader != null) {
			searchDataReader.shutdown();
			searchDataReader = null;
		}
	}

	/**
	 * Searches the next trace with ID. Must be called from UI thread!
	 * 
	 * @param cid
	 *            Component ID
	 * @param gid
	 *            Group ID
	 * @param tid
	 *            Trace ID
	 */
	public void searchTraceWithID(int cid, int gid, int tid) {

		// Create search string
		final String findStr = SearchUtils.createStringFromIDs(cid, gid, tid);
		searchTraceWithString(findStr);
	}

	/**
	 * Searches trace with timestamp. Must be called from UI thread and
	 * TraceViewer view must exists!
	 * 
	 * @param startTimestamp
	 *            start timestamp string
	 * @param endTimestamp
	 *            end timestamp string
	 */
	public void searchTraceWithTimestamp(final String startTimestamp,
			final String endTimestamp) {

		// Create search string
		final String findStr = SearchUtils.createStringFromTimestamp(
				startTimestamp, endTimestamp);
		searchTraceWithString(findStr);
	}

	/**
	 * Searches the next trace with text. Must be called from UI thread!
	 * 
	 * @param text
	 *            text to search for
	 */
	public void searchTraceWithString(final String text) {
		final SearchDialog dialog = getSearchDialog();
		if (!dialog.isOpen()) {
			dialog.create();
		}

		// Create a thread that will start the search because this thread will
		// stop to message event loop when opening the search dialog
		Thread a = new Thread() {

			/*
			 * (non-Javadoc)
			 * 
			 * @see java.lang.Thread#run()
			 */
			@Override
			public void run() {
				// Stop previous search
				if (searchOngoing) {
					stopSearch(true);
				}

				// Must be synced with the UI thread to be able to update the
				// search dialog
				PlatformUI.getWorkbench().getDisplay().asyncExec(
						new Runnable() {

							/*
							 * (non-Javadoc)
							 * 
							 * @see java.lang.Runnable#run()
							 */
							public void run() {
								dialog.startSearch(text);
							}
						});

			}
		};
		a.start();

		if (!dialog.isVisible()) {
			dialog.openDialog();
		}
	}
}