trace/traceviewer/com.nokia.traceviewer/src/com/nokia/traceviewer/engine/dataprocessor/SearchProcessor.java
changeset 11 5b9d4d8641ce
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/trace/traceviewer/com.nokia.traceviewer/src/com/nokia/traceviewer/engine/dataprocessor/SearchProcessor.java	Wed Jun 23 14:49:59 2010 +0300
@@ -0,0 +1,789 @@
+/*
+ * 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();
+		}
+	}
+}
\ No newline at end of file