sysperfana/analyzetoolext/com.nokia.s60tools.analyzetool/src/com/nokia/s60tools/analyzetool/ui/TreeHelper.java
author Matti Laitinen <matti.t.laitinen@nokia.com>
Wed, 21 Apr 2010 19:42:48 +0300
changeset 6 f65f740e69f9
parent 1 1050670c6980
permissions -rw-r--r--
AnalyzeTool Carbide Extension v1.9.0

/*
 * Copyright (c) 2008-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:  Definitions for the class TreeHelper
 *
 */

package com.nokia.s60tools.analyzetool.ui;

import java.util.AbstractList;
import java.util.Hashtable;
import java.util.Iterator;

import org.eclipse.jface.preference.IPreferenceStore;

import com.nokia.s60tools.analyzetool.engine.AnalysisItem;
import com.nokia.s60tools.analyzetool.engine.CallstackItem;
import com.nokia.s60tools.analyzetool.engine.MMPInfo;
import com.nokia.s60tools.analyzetool.engine.RunResults;
import com.nokia.s60tools.analyzetool.engine.Subtest;
import com.nokia.s60tools.analyzetool.global.Constants;
import com.nokia.s60tools.analyzetool.global.Util;

/**
 * Class to create Tree model for the AnalyzeTool view.
 *
 * @author kihe
 *
 */
public class TreeHelper {


	/** Last active memory leak tree item. */
	private TreeObject activeTreeItem;

	/** Last selected tree item. */
	private final Object lastSelectedObject;

	/** Contains module leak info. */
	private final java.util.Hashtable<String, Integer> moduleLeaks;

	/** Used AnalyzeTool preference store */
	private final IPreferenceStore store;

	/** Contains already checked modules states */
	private final Hashtable<String, Boolean> checkedModules;

	/** Contains information which modules are part of the project */
	private final Hashtable<String, Boolean> projectModules;

	/**
	 * Constructor.
	 *
	 * @param selectedObject
	 *            Last selected tree item
	 *
	 * @param storeRef AnalyzeTool preference store reference
	 */
	public TreeHelper(final Object selectedObject,
			final IPreferenceStore storeRef) {
		lastSelectedObject = selectedObject;
		moduleLeaks = new java.util.Hashtable<String, Integer>();
		store = storeRef;
		checkedModules = new Hashtable<String, Boolean>();
		projectModules = new Hashtable<String, Boolean>();
	}

	/**
	 * Compares two different TreeObject.
	 *
	 * @param obj1
	 *            Actual
	 * @param obj2
	 *            New object
	 * @return True if objects equals otherwise false
	 */
	public final boolean compareItems(final Object obj1, final Object obj2) {
		boolean found = false;

		if (obj1 == null || obj2 == null) {
			return false;
		}

		TreeObject tree1 = (TreeObject) obj1;
		TreeObject tree2 = (TreeObject) obj2;

		if (tree1.getMemLeakID() == tree2.getMemLeakID()
				&& tree1.getRunID() == tree2.getRunID()
				&& tree1.getName().equals(tree2.getName())
				&& tree1.getSubtestID() == tree2.getSubtestID()) {
			activeTreeItem = (TreeObject) obj1;
			found = true;
		}
		return found;
	}

	/**
	 * Creates tree node for analysis items.
	 *
	 * @param oneRunTree
	 *            Current item parent tree node
	 * @param items
	 *            Analysis items
	 * @param runID
	 *            Current run id
	 * @param isSubtest
	 *            Is this element subtest
	 * @param subTestID
	 *            Subtest ID
	 * @param modules Selected project target modules
	 * @return How many items created
	 */
	public final int createItemResults(final TreeParent oneRunTree,
			final AbstractList<AnalysisItem> items, final int runID,
			final boolean isSubtest, final int subTestID, AbstractList<MMPInfo> modules) {
		boolean thereAreItemsDisplayed = false;
		moduleLeaks.clear();

		int displayed = 0;
		// thru actual memory leaks
		Iterator<AnalysisItem> iterItem = items.iterator();
		while (iterItem.hasNext()) {
			// get one analysis file
			AnalysisItem oneItem = iterItem.next();

			if (getActiveDetailLevel().equals(Constants.REPORT_EVERY)
					|| oneItem.containValidCallstack()) {
				displayed++;
				char space = ' ';
				StringBuffer leakString = new StringBuffer(64);
				leakString.append(Constants.ITEM_TREE_MEM_LEAKS);
				leakString.append(oneItem.getID());
				leakString.append(" (");
				leakString.append(oneItem.getLeakSize());
				leakString.append(" bytes) (");
				leakString.append(oneItem.getMemoryAddress());
				leakString.append(") ");
				leakString.append(oneItem.getMemoryLeakTime());
				leakString.append(space);
				leakString.append(oneItem.getModuleName());

				TreeObject oneLeak = new TreeObject();
				if (!modules.isEmpty()) {
					oneLeak.setBuild(checkItemBuildState(modules, oneItem.getModuleName()));
					oneLeak.setBelongs(checkProjectModules(modules, oneItem.getModuleName()));
				}

				if (isSubtest) {
					oneLeak.setSubtest(true);
					oneLeak.setSubtestID(subTestID);
				}
				oneLeak.setRunID(runID);
				oneLeak.setMemLeakID(oneItem.getID());
				oneLeak.setName(leakString.toString());
				oneLeak.setModuleName(oneItem.getModuleName());
				oneLeak.setMemAddress(oneItem.getMemoryAddress());

				oneRunTree.addChild(oneLeak);
				thereAreItemsDisplayed = true;
				updateModuleLeaksInfo(oneItem.getModuleName());
				compareItems(oneLeak, lastSelectedObject);
			}

		}

		// if no leaks added to list
		if (!thereAreItemsDisplayed && !items.isEmpty()) {
			TreeObject infoObject = new TreeObject();
			infoObject.setName(Constants.NO_MEM_LEAKS_CURRENT_LEVEL);
			oneRunTree.addChild(infoObject);
		}
		return displayed;
	}

	/**
	 * Check is module built with AnalyzeTool
	 * @param modules Project modules
	 * @param moduleName Module name
	 * @return True if module is built with AnalyzeTool otherwise false
	 */
	private final boolean checkItemBuildState( AbstractList<MMPInfo> modules, String moduleName)
	{
		if( checkedModules.containsKey(moduleName)) {
			return checkedModules.get(moduleName);
		}
		else if (!modules.isEmpty()) {
			boolean build = Util.checkModuleBuildState(modules, moduleName);
			if( !build ) {
				checkedModules.put(moduleName, build);
			}
			return build;
		}
		return false;
	}

	/**
	 * Checks is module part of the project
	 * @param modules Project modules
	 * @param moduleName Module name
	 * @return True if module is part of the project otherwise false
	 */
	private final boolean checkProjectModules(AbstractList<MMPInfo> modules, String moduleName)
	{
		if( projectModules.containsKey(moduleName) ) {
			return projectModules.get(moduleName);
		}
		else if( !modules.isEmpty() ) {
			boolean partOfTheProject = Util.isModulePartOfProject(modules,moduleName);
			projectModules.put(moduleName, partOfTheProject);
			return partOfTheProject;
		}
		return false;
	}

	/**
	 * Creates tree item for run results.
	 *
	 * @param oneRunResults
	 *            One run results
	 * @param modules Selected project target modules
	 * @return Tree item which contains run results
	 */
	public final TreeParent createRunResults(final RunResults oneRunResults, AbstractList<MMPInfo> modules) {

		// create new run tree
		TreeParent oneRunTree = new TreeParent("");
		oneRunTree.setRunID(oneRunResults.getItemID());

		// create new handle leak tree
		TreeParent handleSummary = getHandleLeakInfo(oneRunResults
				.getHandleLeaks(), oneRunResults.getItemID(), modules);
		if (handleSummary.hasChildren()) {
			oneRunTree.addChild(handleSummary);
		}

		// if no memory leaks set "No memory leaks" info
		AbstractList<AnalysisItem> items = oneRunResults.getAnalysisItems();
		if (items.isEmpty()) {
			TreeObject noLeaks = new TreeObject();
			noLeaks.setName(Constants.RUN_NO_LEAKS);
			oneRunTree.addChild(noLeaks);
		}

		// create item tree and calculate how many of memory leaks are displayed
		int howManyDisplayed = createItemResults(oneRunTree, items,
				oneRunResults.getItemID(), false, 0, modules);
		oneRunTree.setName(getRunTreeTitle(oneRunResults, howManyDisplayed));

		// module leaks
		TreeParent moduleSummary = getModuleLeakInfo(getCalcModuleLeaksInfo(),
				oneRunResults.getItemID(), modules);
		if (moduleSummary.hasChildren()) {
			oneRunTree.addChild(0, moduleSummary);
		}

		// get subtests
		AbstractList<Subtest> subtest = oneRunResults.getSubtest();
		createSubtestResults(oneRunTree, subtest, oneRunResults.getItemID(), modules);

		return oneRunTree;
	}

	/**
	 * Creates tree node for subtests.
	 *
	 * @param oneRunTree
	 *            Parent tree node
	 * @param subtest
	 *            Subtest information
	 * @param runID
	 *            Current run id
	 * @param modules Selected project target modules
	 */
	public final void createSubtestResults(final TreeParent oneRunTree,
			final AbstractList<Subtest> subtest, final int runID, AbstractList<MMPInfo> modules) {
		// thru subtest
		Iterator<Subtest> iterSubtest = subtest.iterator();

		// thru subtest
		while (iterSubtest.hasNext()) {
			// get subtest
			Subtest oneSubtest = iterSubtest.next();

			// create tree item for the subtest
			TreeParent oneSubtestParent = new TreeParent("");

			// get and calculate how many memory leaks subtest contains
			int howManyDisplayed = createItemResults(oneSubtestParent,
					oneSubtest.getAnalysisItems(), runID, true, oneSubtest
							.getItemID(), modules);

			// udpate subtest tree title
			oneSubtestParent.setName(getSubtestTreeTitle(oneSubtest,
					howManyDisplayed));

			if (oneSubtest.getAnalysisItems().isEmpty()) {
				TreeObject noLeaks = new TreeObject();
				noLeaks.setName(Constants.RUN_NO_LEAKS);
				oneSubtestParent.addChild(noLeaks);
			}

			// if subtest contains handle leaks
			TreeParent handleLeaksInfo = getHandleLeakInfo(oneSubtest
					.getHandleLeaks(), oneSubtest.getItemID(), modules);
			if (handleLeaksInfo.hasChildren()) {
				oneSubtestParent.addChild(handleLeaksInfo);
			}

			// set subtest module leak info
			TreeParent moduleLeaksInfo = getModuleLeakInfo(
					getCalcModuleLeaksInfo(), oneSubtest.getItemID(), modules);
			if (moduleLeaksInfo.hasChildren()) {
				oneSubtestParent.addChild(0, moduleLeaksInfo);
			}

			oneRunTree.addChild(oneSubtestParent);
		}
	}

	/**
	 * Gets active detail level.
	 *
	 * @return Active detail level
	 */
	public final String getActiveDetailLevel() {
		// get active report level
		return store.getString(Constants.REPORT_LEVEL);
	}

	/**
	 * Returns active item of tree.
	 *
	 * @return Active itemf
	 */
	public final TreeObject getActiveItem() {
		return activeTreeItem;
	}

	/**
	 * Returns tree item which contains callstack information.
	 *
	 * @param item
	 *            AnalysisItem
	 * @param modules Selected project target modules
	 * @return Tree item
	 */
	public final TreeParent getCallstack(final AnalysisItem item, AbstractList<MMPInfo> modules) {

		// get active logging mode
		String reportLevel = store.getString(Constants.REPORT_LEVEL);
		
		// create tree parent for test runs
		String title = null;
		if( item.getCallstackItems() != null ) {
			title = Constants.ITEM_TREE_MEM_LEAKS
			+ item.getID() + " - " + item.getMemoryLeakTime() +" - Callstack size: " +item.getCallstackItems().size();
		}
		else {
			title = Constants.ITEM_TREE_MEM_LEAKS + item.getID() + " - " + item.getMemoryLeakTime();
		}
		TreeParent testRuns = new TreeParent(title);
		
		// get callstack items
		Iterator<CallstackItem> stackItems = item.getCallstackItems().iterator();

		// is one know line printed
		boolean printed = false;

		// thru call stack items
		while (stackItems.hasNext()) {
			// get one callstack item
			CallstackItem oneStackItem = stackItems.next();

			TreeObject to = new TreeObject();

			// get function name, memory leak line number and file name
			String functionName = oneStackItem.getFunctionName();
			int leakLineNumber = oneStackItem.getLeakLineNumber();
			String fileName = oneStackItem.getFileName();
			String memAddress = oneStackItem.getMemoryAddress();
			String moduleName = oneStackItem.getModuleName();

			if (!modules.isEmpty()) {
				to.setBuild(checkItemBuildState(modules, moduleName));
				to.setBelongs(checkProjectModules(modules, moduleName));
			}

			// report all
			if (Constants.REPORT_EVERY.equals(reportLevel)) {

				if (leakLineNumber > 0) {
					// if build is urel
					// => change info order
					if (oneStackItem.isUrelBuild()) {
						to.setName(memAddress + " " + moduleName + " "
								+ functionName + " " + leakLineNumber + " "
								+ fileName);
					} else {
						to.setName(memAddress + " " + moduleName + " "
								+ functionName + " " + fileName + " "
								+ leakLineNumber);
					}

				} else {
					to.setName(memAddress + " " + moduleName + " "
							+ functionName + " " + fileName);
				}

				to.setCallstackItem(oneStackItem);
				testRuns.addChild(to);
			}
			// display only known lines
			else if (Constants.REPORT_KNOWN.equals(reportLevel)
					&& (functionName != null && leakLineNumber > 0 && fileName != null)) {
				// if build is urel
				// => change info order
				if (oneStackItem.isUrelBuild()) {
					to.setName(memAddress + " " + moduleName + " "
							+ functionName + " " + leakLineNumber + " "
							+ fileName);
				} else {
					to.setName(memAddress + " " + moduleName + " "
							+ functionName + " " + fileName + " "
							+ leakLineNumber);
				}
				to.setCallstackItem(oneStackItem);
				testRuns.addChild(to);
			}
			// display only topmost
			else if (Constants.REPORT_TOPMOST.equals(reportLevel)
					&& !printed
					&& (functionName != null && leakLineNumber > 0 && fileName != null)) {
				// if build is urel
				// => change info order
				if (oneStackItem.isUrelBuild()) {
					to.setName(memAddress + " " + moduleName + " "
							+ functionName + " " + leakLineNumber + " "
							+ fileName);
				} else {
					to.setName(memAddress + " " + moduleName + " "
							+ functionName + " " + fileName + " "
							+ leakLineNumber);
				}
				to.setCallstackItem(oneStackItem);
				testRuns.addChild(to);
				printed = true;
			}

		}
		
		Object[] childs = testRuns.getChildren();
		int childCount = 0;
		if( childs != null ) {
			childCount = childs.length;
		}
		int callstackCount = item.getCallstackItems().size();
		
		if( callstackCount != childCount ) {
			title = Constants.ITEM_TREE_MEM_LEAKS
			+ item.getID() + " - " + item.getMemoryLeakTime() +" - Callstack size: " +callstackCount +" ("+(callstackCount - childCount)+" filtered)";
			testRuns.setName(title);
		}
		TreeParent incRoot;
		incRoot = new TreeParent(Constants.ANALYZE_TOOL_TITLE);
		incRoot.addChild(testRuns);
		return incRoot;
	}

	/**
	 * Gets handle leaks tree item.
	 *
	 * @param handleLeaks
	 *            Handle leaks information
	 * @param runID
	 *            Run id
	 * @param modules Selected project target modules
	 * @return Handle leak tree item
	 */
	public final TreeParent getHandleLeakInfo(
			final Hashtable<String, Integer> handleLeaks, final int runID, AbstractList<MMPInfo> modules) {
		// get handle leak information
		TreeParent handleSummary = new TreeParent(
				Constants.HANDLE_LEAK_MODULES_TITLE);
		handleSummary.setRunID(runID);
		for (java.util.Enumeration<String> e1 = handleLeaks.keys(); e1
				.hasMoreElements();) {
			String moduleName = e1.nextElement();
			int handleLeakCount = handleLeaks.get(moduleName);
			TreeObject object = new TreeObject();
			object.setRunID(runID);
			object.setName(moduleName + " (" + handleLeakCount
					+ Constants.MODULE_TREE_HANDLE_LEAKS + ")");
			if (!modules.isEmpty()) {
				object.setBuild(checkItemBuildState(modules, moduleName));
				object.setBelongs(checkProjectModules(modules, moduleName));
			}

			handleSummary.addChild(object);
		}
		return handleSummary;
	}

	/**
	 * Gets module leaks tree item.
	 *
	 * @param moduleLeaksInfo
	 *            Module leaks information
	 * @param runID
	 *            Run id
	 * @param modules Selected project target modules
	 * @return Module leak tree item
	 */
	public final TreeParent getModuleLeakInfo(
			final Hashtable<String, Integer> moduleLeaksInfo, final int runID, AbstractList<MMPInfo> modules) {
		TreeParent moduleSummary = new TreeParent(
				Constants.MEMORY_LEAK_MODULES_TITLE);

		moduleSummary.setRunID(runID);
		for (java.util.Enumeration<String> e = moduleLeaksInfo.keys(); e
				.hasMoreElements();) {
			String moduleName = e.nextElement();
			int moduleLeakCount = moduleLeaksInfo.get(moduleName);
			TreeObject object = new TreeObject();
			object.setRunID(runID);
			object.setName(moduleName + " (" + moduleLeakCount
					+ Constants.MODULE_TREE_MEM_LEAKS + ")");
			if (!modules.isEmpty()) {
				object.setBuild(checkItemBuildState(modules, moduleName));
				object.setBelongs(checkProjectModules(modules, moduleName));
			}

			moduleSummary.addChild(object);
		}
		return moduleSummary;
	}

	/**
	 * Return moduleaks info.
	 *
	 * @return Module leaks info
	 */
	public final Hashtable<String, Integer> getCalcModuleLeaksInfo() {
		return moduleLeaks;
	}

	/**
	 * Gets run tree title.
	 *
	 * @param oneRunResults
	 *            Run results
	 * @param howManyDisplayed
	 *            How many of memory leak items are displayed
	 * @return Run tree title
	 */
	public final String getRunTreeTitle(final RunResults oneRunResults,
			final int howManyDisplayed) {
		StringBuffer runTreeTitle = new StringBuffer(64);
		runTreeTitle.append(Constants.RUN_TREE_RUN);
		runTreeTitle.append(Integer.toString(oneRunResults.getItemID()));
		runTreeTitle.append(Constants.RUN_TREE_RUN_MEM_LEAKS);
		runTreeTitle.append(oneRunResults.getAnalysisItems().size());
		if (howManyDisplayed > 0
				&& oneRunResults.getAnalysisItems().size() != howManyDisplayed) {
			int diff = oneRunResults.getAnalysisItems().size()
					- howManyDisplayed;
			runTreeTitle.append(" (");
			runTreeTitle.append(Integer.toString(diff));
			runTreeTitle.append(Constants.RUN_TREE_FILTERED);

		}
		runTreeTitle.append(Constants.RUN_TREE_RUN_HANDLE_LEAKS);
		runTreeTitle.append(oneRunResults.getHandleLeakCount());
		runTreeTitle.append(Constants.RUN_TREE_START_TIME);
		runTreeTitle.append(oneRunResults.getStartTime());
		runTreeTitle.append(Constants.RUN_TREE_PROCESS_NAME);
		runTreeTitle.append(oneRunResults.getProcessName());
		runTreeTitle.append(Constants.RUN_TREE_BUILD_TARGET);
		runTreeTitle.append(oneRunResults.getBuildTarget());

		if (oneRunResults.getEndTime() == null
				|| ("").equals(oneRunResults.getEndTime())) {
			runTreeTitle.append(Constants.RUN_FAILED);
		} else if ((Constants.RUN_ABNORMAL).equals(oneRunResults.getEndTime())) {
			runTreeTitle.append(' ');
			runTreeTitle.append(Constants.RUN_ABNORMAL);
			runTreeTitle.append(' ');
		}

		return runTreeTitle.toString();
	}

	/**
	 * Gets subtest title.
	 *
	 * @param oneSubtest
	 *            Subtest info
	 * @param howManyDisplayed
	 *            How many of subtest items are displayed
	 * @return Subtest title
	 */
	public final String getSubtestTreeTitle(final Subtest oneSubtest,
			final int howManyDisplayed) {
		StringBuffer subtestTitle = new StringBuffer(64);
		String startTime = oneSubtest.getStartTime();
		subtestTitle.append(Constants.SUBTEST_TREE_TITLE);
		subtestTitle.append(oneSubtest.getName());
		subtestTitle.append(Constants.RUN_TREE_RUN_MEM_LEAKS);
		subtestTitle.append(oneSubtest.getAnalysisItems().size());

		if (howManyDisplayed > 0
				&& oneSubtest.getAnalysisItems().size() != howManyDisplayed) {
			int diff = oneSubtest.getAnalysisItems().size() - howManyDisplayed;

			subtestTitle.append(" (");
			subtestTitle.append(diff);
			subtestTitle.append(Constants.RUN_TREE_FILTERED);
		}
		subtestTitle.append(Constants.RUN_TREE_RUN_HANDLE_LEAKS);
		subtestTitle.append(oneSubtest.getHandleLeakCount());
		if (startTime.length() != 0) {
			subtestTitle.append(Constants.RUN_TREE_START_TIME);
			subtestTitle.append(oneSubtest.getStartTime());
		}
		return subtestTitle.toString();
	}

	/**
	 * Updates module leak count for current module.
	 *
	 * @param moduleName
	 *            Module name
	 */
	public final void updateModuleLeaksInfo(final String moduleName) {

		//check module name
		if( moduleName == null || ("").equals(moduleName) ) {
			return;
		}
		//if same module name is already added => update count value
		else if (moduleLeaks.containsKey(moduleName)) {
			int currentCount = moduleLeaks.get(moduleName);
			currentCount++;
			moduleLeaks.put(moduleName, currentCount);
		} else {
			moduleLeaks.put(moduleName, 1);
		}
	}
}