trace/traceviewer/com.nokia.traceviewer/src/com/nokia/traceviewer/engine/dataprocessor/FilterProcessor.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:
 *
 * FilterProcessor DataProcessor
 *
 */
package com.nokia.traceviewer.engine.dataprocessor;

import java.io.FileNotFoundException;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.ByteChannel;
import java.util.Iterator;

import com.nokia.traceviewer.TraceViewerPlugin;
import com.nokia.traceviewer.dialog.FilterDialog;
import com.nokia.traceviewer.dialog.FilterAdvancedDialog.ExitStatus;
import com.nokia.traceviewer.dialog.treeitem.FilterTreeBaseItem;
import com.nokia.traceviewer.dialog.treeitem.FilterTreeComponentItem;
import com.nokia.traceviewer.dialog.treeitem.FilterTreeItem;
import com.nokia.traceviewer.dialog.treeitem.FilterTreeTextItem;
import com.nokia.traceviewer.dialog.treeitem.TreeItem;
import com.nokia.traceviewer.dialog.treeitem.TreeItemContentProvider;
import com.nokia.traceviewer.dialog.treeitem.TreeItemListener;
import com.nokia.traceviewer.engine.DataWriter;
import com.nokia.traceviewer.engine.PlainTextReader;
import com.nokia.traceviewer.engine.TraceProperties;
import com.nokia.traceviewer.engine.TraceViewerGlobals;
import com.nokia.traceviewer.engine.TraceViewerDialogInterface.Dialog;
import com.nokia.traceviewer.engine.dataprocessor.FilterRuleSet.LogicalOperator;
import com.nokia.traceviewer.engine.preferences.PreferenceConstants;
import com.nokia.traceviewer.engine.preferences.XMLFilterConfigurationImporter;

/**
 * Filter DataProcessor
 * 
 */
public final class FilterProcessor implements DataProcessor {

	/**
	 * Interval how often to update progressbar
	 */
	private static final int PROGRESSBAR_UPDATE_INTERVAL = 100;

	/**
	 * Name of Filtered file
	 */
	private static final String DEFAULT_FILTER_FILE_NAME = Messages
			.getString("FilterProcessor.DefaultFilterFileName"); //$NON-NLS-1$

	/**
	 * Path of Filtered file
	 */
	public static final String DEFAULT_FILTER_FILE_PATH = TraceViewerPlugin
			.getDefault().getStateLocation().append(DEFAULT_FILTER_FILE_NAME)
			.toOSString();

	/**
	 * Filter dialog used in setting filters
	 */
	private FilterDialog filterDialog;

	/**
	 * Content provider for the filter dialog
	 */
	private TreeItemContentProvider contentProvider;

	/**
	 * First visible object in the filter dialog tree
	 */
	private TreeItem root;

	/**
	 * Filter rule sets
	 */
	private final FilterRuleSet filterRuleSet;

	/**
	 * DataWriter used to write binary filtered file
	 */
	private DataWriter dataWriter;

	/**
	 * Writer to be used for plain text writing
	 */
	private PrintWriter plainOutput;

	/**
	 * External Filter Processor
	 */
	private ExternalFilterProcessor externalFilterProcessor;

	/**
	 * Indicates that external filter program is in use
	 */
	private boolean usingExternalFilter;

	/**
	 * Indicates that stream must be flushed
	 */
	private boolean needsFlushing;

	/**
	 * Indicates to show traces containing the rule. Otherwise they're hidden
	 */
	private boolean showTracesContainingRule = true;

	/**
	 * Indicates that logical OR is in use. Otherwise AND is in use.
	 */
	private boolean logicalOrInUse = true;

	/**
	 * Advanced filter string
	 */
	private String advancedFilterString = ""; //$NON-NLS-1$

	/**
	 * Constructor
	 */
	public FilterProcessor() {
		createInitialFilterTree();
		filterRuleSet = new FilterRuleSet();
		filterRuleSet.setOperator(LogicalOperator.OR);
	}

	/**
	 * Creates initial filter tree
	 */
	public void createInitialFilterTree() {
		contentProvider = new TreeItemContentProvider();
		// Create root node
		TreeItem treeRoot = new FilterTreeBaseItem(contentProvider, null,
				"root", //$NON-NLS-1$
				FilterTreeItem.Rule.GROUP);
		root = new FilterTreeBaseItem(contentProvider, treeRoot,
				TraceViewerPlugin.getDefault().getPreferenceStore().getString(
						PreferenceConstants.CONFIGURATION_FILE),
				FilterTreeItem.Rule.GROUP);
		treeRoot.addChild(root);
	}

	/**
	 * Imports filter rules from configuration file
	 */
	public void importFilterRules() {
		// Import Filter rules
		XMLFilterConfigurationImporter importer = new XMLFilterConfigurationImporter(
				root, TraceViewerPlugin.getDefault().getPreferenceStore()
						.getString(PreferenceConstants.CONFIGURATION_FILE),
				true);
		importer.importData();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * com.nokia.traceviewer.engine.DataProcessor#processData(com.nokia.traceviewer
	 * .engine.TraceProperties)
	 */
	public void processData(TraceProperties properties) {
		TraceViewerGlobals.debug("processData in Filter", //$NON-NLS-1$
				TraceViewerGlobals.DebugLevel.TEST);
		if (isFiltering()) {
			// Read from filter file or triggered out, don't try to filter again
			if (properties.traceConfiguration.isReadFromFilterFile()
					|| properties.traceConfiguration.isTriggeredOut()) {
				// Don't' do anything
			} else {
				// Filter everything out from view that isn't from filterFile
				properties.traceConfiguration.setFilteredOut(true);

				// External filter processor
				if (isUsingExternalFilter()) {
					externalFilterProcessor.writeTraceToProcess(properties);
				} else {

					// Process filter rules
					boolean filterHits = processFilterRules(properties,
							filterRuleSet);

					// Write trace
					writeTrace(properties, filterHits);
				}
			}
		} else {
			properties.traceConfiguration.setFilteredOut(false);
		}

		// Update progressBar if needed
		updateProgressBar();
	}

	/**
	 * Writes trace if conditions ok
	 * 
	 * @param properties
	 *            trace to write
	 * @param filterHits
	 *            filter hits property
	 */
	private void writeTrace(TraceProperties properties, boolean filterHits) {
		// Write the trace
		if (filterHits && showTracesContainingRule) {
			writeTraceToFile(properties);
		} else if (!filterHits && !showTracesContainingRule) {
			writeTraceToFile(properties);
		}
	}

	/**
	 * Process Filter Rules
	 * 
	 * @param properties
	 *            trace properties
	 * @param ruleObject
	 *            filter rule set
	 * @return true if the rule object should be written to filter file
	 */
	private boolean processFilterRules(TraceProperties properties,
			FilterRuleObject ruleObject) {
		boolean filterHits = true;

		// Process the rule
		if (ruleObject != null) {
			filterHits = ruleObject.processRule(properties);

			// Rule set is null, abort
		} else {
			filterHits = false;
		}

		return filterHits;
	}

	/**
	 * Writes trace to file
	 * 
	 * @param properties
	 *            trace properties
	 */
	private void writeTraceToFile(TraceProperties properties) {

		// Write the trace to filterFile
		if (dataWriter != null) {

			// Write all parts of multipart trace
			if (properties.bTraceInformation.getMultiPartTraceParts() != null) {
				Iterator<byte[]> i = properties.bTraceInformation
						.getMultiPartTraceParts().getTraceParts().iterator();

				while (i.hasNext()) {
					byte[] byteArr = i.next();
					dataWriter.writeMessage(ByteBuffer.wrap(byteArr), 0,
							byteArr.length);
				}
			} else {
				dataWriter.writeMessage(properties.byteBuffer,
						properties.messageStart, properties.messageLength);
			}
		} else if (plainOutput != null) {
			writeStringToPlainTextFilterFile(properties.traceString);
		}
	}

	/**
	 * Writes a string to plain text filter file. Adds line break after the
	 * string
	 * 
	 * @param string
	 *            string to write
	 */
	private void writeStringToPlainTextFilterFile(String string) {
		StringBuffer buf = new StringBuffer(string.length() + 1);
		buf.append(string);
		buf.append('\n');
		plainOutput.write(buf.toString());
		needsFlushing = true;
	}

	/**
	 * Updates progressBar if needed
	 */
	private void updateProgressBar() {

		// Check that we are processing filter and update interval is reached
		if (isProcessingFilter()
				&& TraceViewerGlobals.getTraceViewer().getDataReaderAccess()
						.getMainDataReader().getTraceCount()
						% PROGRESSBAR_UPDATE_INTERVAL == 0) {
			filterDialog.getProgressBar().updateProgressBar(
					TraceViewerGlobals.getTraceViewer().getDataReaderAccess()
							.getMainDataReader().getTraceCount());

		}
	}

	/**
	 * Tells if filters are on
	 * 
	 * @return filtering status
	 */
	public boolean isFiltering() {
		boolean filtering = false;
		if (hasRules()) {
			filtering = true;
		} else if (isUsingExternalFilter()) {
			filtering = true;
		}
		return filtering;
	}

	/**
	 * Creates filtered file
	 */
	public void createFilteredFile() {
		// Build the file writer to filter file
		buildFilterFileWriter();

		// Shut down possible old filter reader
		TraceViewerGlobals.getTraceViewer().getDataReaderAccess()
				.deleteFilterReader();

		// Start reading the data file from the beginning
		TraceViewerGlobals.getTraceViewer().readDataFileFromBeginning();

		// Create filter datareader
		TraceViewerGlobals.getTraceViewer().getDataReaderAccess()
				.createFilterDataReader();

	}

	/**
	 * Builds the filter file writer
	 * 
	 * @return status of building filter file
	 */
	public boolean buildFilterFileWriter() {
		boolean success = false;
		try {
			// Binary writer
			if (!isUsingExternalFilter()
					&& !(TraceViewerGlobals.getTraceViewer()
							.getDataReaderAccess().getMainDataReader() instanceof PlainTextReader)) {

				// If plain output exists, close it
				if (plainOutput != null) {
					plainOutput.close();
					plainOutput = null;
				}

				// Close possible old binary writer
				if (dataWriter != null) {
					dataWriter.closeChannel();
					dataWriter = null;
				}

				RandomAccessFile filterFile = null;
				filterFile = new RandomAccessFile(DEFAULT_FILTER_FILE_PATH,
						"rw"); //$NON-NLS-1$
				filterFile.setLength(0);

				ByteChannel writeChannel = filterFile.getChannel();

				dataWriter = TraceViewerGlobals.getTraceProvider()
						.createDataWriter(writeChannel);

				success = true;
				// Plain text writer
			} else {
				// Close possible binary writer
				if (dataWriter != null) {
					dataWriter.closeChannel();
					dataWriter = null;
				}

				// Close possible previous plainOutput writer
				if (plainOutput != null) {
					plainOutput.close();
				}

				plainOutput = new PrintWriter(new FileWriter(
						DEFAULT_FILTER_FILE_PATH));
				success = true;
			}

		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return success;
	}

	/**
	 * Gets filter dialog
	 * 
	 * @return filter dialog
	 */
	public FilterDialog getFilterDialog() {
		if (filterDialog == null) {
			filterDialog = (FilterDialog) TraceViewerGlobals.getTraceViewer()
					.getDialogs().createDialog(Dialog.FILTER);
		}
		return filterDialog;
	}

	/**
	 * Tells if initial filtering is in process
	 * 
	 * @return true if initial filtering is in progress
	 */
	public boolean isProcessingFilter() {
		boolean isProcessing = false;
		if (filterDialog != null && filterDialog.getProgressBar() != null
				&& filterDialog.getProgressBar().getShell() != null
				&& !filterDialog.getProgressBar().getShell().isDisposed()) {
			isProcessing = true;
		}
		return isProcessing;
	}

	/**
	 * Gets filter rule set
	 * 
	 * @return filter rule set
	 */
	public FilterRuleSet getFilterRules() {
		return filterRuleSet;
	}

	/**
	 * Indicates that some filtering rules are applied
	 * 
	 * @return true if some filtering rules are applied
	 */
	public boolean hasRules() {
		boolean hasRules = false;
		if (filterRuleSet != null && !filterRuleSet.getFilterRules().isEmpty()) {
			hasRules = true;
		}
		return hasRules;
	}

	/**
	 * Gets the visible root of the tree
	 * 
	 * @return the root root of the tree
	 */
	public TreeItem getRoot() {
		return root;
	}

	/**
	 * Gets Filter item listener
	 * 
	 * @return the contentProvider
	 */
	public TreeItemListener getTreeItemListener() {
		return contentProvider;
	}

	/**
	 * Gets data writer
	 * 
	 * @return the dataWriter
	 */
	public DataWriter getDataWriter() {
		return dataWriter;
	}

	/**
	 * Sets using of external filter boolean
	 * 
	 * @param using
	 *            if true, starts using external filter program
	 */
	public void setUsingExternalFilter(boolean using) {
		// Previously true, shut down possible Processes
		if (usingExternalFilter && !using) {
			externalFilterProcessor.stopExternalApplication();
		} else if (using) {
			// Create the processor when first needed
			if (externalFilterProcessor == null) {
				externalFilterProcessor = new ExternalFilterProcessor();
			}
		}
		this.usingExternalFilter = using;
	}

	/**
	 * Flushes plain text writing streams
	 */
	public void flush() {
		// Flush the filter file writer
		if (plainOutput != null && needsFlushing) {
			plainOutput.flush();
			needsFlushing = false;
		}
	}

	/**
	 * Gets External Filter Processor
	 * 
	 * @return the externalFilterProcessor
	 */
	public ExternalFilterProcessor getExternalFilterProcessor() {
		return externalFilterProcessor;
	}

	/**
	 * Indicates that we are using external filter file
	 * 
	 * @return the usingExternalFilter
	 */
	public boolean isUsingExternalFilter() {
		boolean usingFilter = false;
		if (usingExternalFilter && externalFilterProcessor != null
				&& externalFilterProcessor.getProcess() != null) {
			usingFilter = true;
		}
		return usingFilter;
	}

	/**
	 * Checks if logical or is in use. Otherwise it's AND
	 * 
	 * @return the logicalOrInUse
	 */
	public boolean isLogicalOrInUse() {
		return logicalOrInUse;
	}

	/**
	 * Sets logical or to use
	 * 
	 * @param logicalOrInUse
	 *            the logicalOrInUse to set
	 */
	public void setLogicalOrInUse(boolean logicalOrInUse) {
		this.logicalOrInUse = logicalOrInUse;
	}

	/**
	 * Checks if show traces is on. Otherwise hide traces is on.
	 * 
	 * @return the showTracesContainingRule
	 */
	public boolean isShowTracesContainingRule() {
		return showTracesContainingRule;
	}

	/**
	 * Sets the show / hide traces option
	 * 
	 * @param showTracesContainingRule
	 *            the showTracesContainingRule to set
	 */
	public void setShowTracesContainingRule(boolean showTracesContainingRule) {
		this.showTracesContainingRule = showTracesContainingRule;
	}

	/**
	 * Filters string from external process and if it passes, writes it to
	 * filter file
	 * 
	 * @param properties
	 *            trace properties from the external process
	 */
	public void filterStringFromExternalProcess(TraceProperties properties) {
		// If there are filter rules
		if (hasRules()) {

			// Line must hit a filter to be written to the file
			processFilterRules(properties, filterRuleSet);

		} else {
			// Write the trace
			writeStringToPlainTextFilterFile(properties.traceString);
		}
	}

	/**
	 * Enable filter rule
	 * 
	 * @param item
	 *            the rule item
	 * @param op
	 *            logical operator
	 */
	public void enableRule(FilterTreeItem item, LogicalOperator op) {
		// Get or create the set where new items are inserted
		FilterRuleSet set;
		if (filterRuleSet.getFilterRules().isEmpty()) {
			set = new FilterRuleSet();
			set.setOperator(op);
			filterRuleSet.addObject(set);
		} else {
			set = (FilterRuleSet) filterRuleSet.getFilterRules().get(0);
		}

		if (item instanceof FilterTreeTextItem) {
			set.addObject(item);
		} else if (item instanceof FilterTreeComponentItem) {
			set.addObject(0, item);
		}

		// First rule to enable
		if (set.getFilterRules().size() == 1) {
			enableRulesOnStartup();
		}
	}

	/**
	 * Enable advanced filter string
	 * 
	 * @param filterString
	 *            the filter string
	 */
	public void enableAdvancedFilter(String filterString) {
		advancedFilterString = filterString;

		// Create needed shell items to advanced dialog
		getFilterDialog().getAdvancedDialog().create();

		// Check that rules are ok and then enable them
		if (getFilterDialog().getAdvancedDialog().checkWrittenRules(
				filterString)) {
			getFilterDialog().getAdvancedDialog().exitStatus = ExitStatus.APPLYBUTTON;

			// Generate the filter rule set
			FilterRuleObject set = getFilterDialog().getAdvancedDialog()
					.createRuleSet(filterString, false);
			filterRuleSet.addObject(set);

			enableRulesOnStartup();
		}
	}

	/**
	 * Enables rules on startup
	 */
	private void enableRulesOnStartup() {
		createFilteredFile();

		// Update view name
		TraceViewerGlobals.getTraceViewer().getView().updateViewName();
	}

	/**
	 * Gets advanced filter string
	 * 
	 * @return the advancedFilterString
	 */
	public String getAdvancedFilterString() {
		return advancedFilterString.trim();
	}

	/**
	 * Sets advanced filter string
	 * 
	 * @param advancedFilterString
	 *            the advancedFilterString to set
	 */
	public void setAdvancedFilterString(String advancedFilterString) {
		this.advancedFilterString = advancedFilterString;
	}
}