trace/traceviewer/com.nokia.traceviewer/src/com/nokia/traceviewer/action/OpenTraceLocationAction.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:
 *
 * Open trace location command
 *
 */
package com.nokia.traceviewer.action;

import java.io.File;
import java.net.URI;
import java.net.URL;
import java.util.HashMap;

import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IWorkspace;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.Path;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.ui.IEditorDescriptor;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IWorkbench;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.ide.IDE;
import org.eclipse.ui.part.FileEditorInput;

import com.nokia.carbide.cdt.builder.CarbideBuilderPlugin;
import com.nokia.carbide.cdt.builder.project.ICarbideBuildConfiguration;
import com.nokia.carbide.cdt.builder.project.ICarbideProjectInfo;
import com.nokia.carbide.cpp.sdk.core.ISymbianSDK;
import com.nokia.traceviewer.TraceViewerHelpContextIDs;
import com.nokia.traceviewer.TraceViewerPlugin;
import com.nokia.traceviewer.engine.TraceMetaData;
import com.nokia.traceviewer.engine.TraceViewerGlobals;

/**
 * Handler for open trace location command
 */
public final class OpenTraceLocationAction extends TraceViewerAction {

	/**
	 * Filter names in open file dialog
	 */
	private static final String[] FILTER_NAMES = {
			Messages.getString("OpenTraceLocationAction.SourceCodeFilter"), //$NON-NLS-1$
			Messages.getString("OpenTraceLocationAction.AllFilesFilter") }; //$NON-NLS-1$

	/**
	 * Filter extensions in open file dialog
	 */
	private static final String[] FILTER_EXTS = { "*.cpp", "*.*" }; //$NON-NLS-1$ //$NON-NLS-2$

	/**
	 * Space
	 */
	private static final String SPACE = " "; //$NON-NLS-1$

	/**
	 * Trace MetaData used when opening location
	 */
	private TraceMetaData traceMetadata;

	/**
	 * If only the drive character is changed from the path of the source files,
	 * try to find those files also behind this drive
	 */
	private char changedDriveLetter;

	/**
	 * If this is true, we have already tried to open the source file also with
	 * the changed drive letter
	 */
	private boolean triedWithChangedDrive;

	/**
	 * If source is not found, ask user to browse for it
	 */
	private boolean askToBrowseSource;

	/**
	 * Image for this Action
	 */
	private static ImageDescriptor image;

	static {
		URL url = null;
		url = TraceViewerPlugin.getDefault().getBundle().getEntry(
				"/icons/gotolocation.gif"); //$NON-NLS-1$
		image = ImageDescriptor.createFromURL(url);
	}

	/**
	 * Constructor
	 */
	OpenTraceLocationAction() {
		setText(Messages.getString("OpenTraceLocationAction.Title")); //$NON-NLS-1$
		setToolTipText(Messages.getString("OpenTraceLocationAction.Tooltip")); //$NON-NLS-1$
		setImageDescriptor(image);

		// Set help
		PlatformUI.getWorkbench().getHelpSystem().setHelp(this,
				TraceViewerHelpContextIDs.ACTIONS);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.nokia.traceviewer.action.TraceViewerAction#doRun()
	 */
	@Override
	protected void doRun() {

		// If metadata is null, don't do anything
		if (traceMetadata != null && traceMetadata.getPath() != null
				&& traceMetadata.getLineNumber() != 0) {

			// Open source file to editor
			openSourceFileToEditor(traceMetadata.getPath());
		}
	}

	/**
	 * Opens source file to editor
	 * 
	 * @param filePath
	 *            file path
	 */
	private void openSourceFileToEditor(String filePath) {
		IPath epocRootLocation = null;
		IFile file = null;
		IFile[] fileArr = null;

		// Dictionary location
		IPath dictionaryLocation = new Path(filePath);

		// Try to find EPOC root
		String epocRoot = getEpocRoot();
		if (epocRoot != null && epocRoot.length() > 1) {

			// Get only the device part (e.g. X:)
			epocRoot = epocRoot.substring(0, 2);
			epocRootLocation = dictionaryLocation.setDevice(epocRoot);
		}

		IWorkspace ws = ResourcesPlugin.getWorkspace();
		IWorkspaceRoot root = ws.getRoot();

		// Try to find with EPOC root location
		if (epocRootLocation != null) {
			fileArr = root.findFilesForLocationURI(makeURI(epocRootLocation));
		}

		// If not found, try to find with Dictionary location
		if (fileArr == null || fileArr.length == 0) {
			fileArr = root.findFilesForLocationURI(makeURI(dictionaryLocation));
		}

		// There can be only one file with this url so take the first
		// item
		if (fileArr != null && fileArr.length > 0) {
			file = fileArr[0];
		} else {
			// File not found from Workspace, create External files project
			file = createExternalFilesProject(dictionaryLocation, root);
		}

		// Open the file to the default editor
		if (file != null) {
			openEditor(file);
		}
	}

	/**
	 * Creates URI from given IPath instance.
	 * 
	 * @param path
	 * @return URI from given IPath instance
	 */
	private URI makeURI(IPath path) {
		File file = path.toFile();
		return file.toURI();
	}

	/**
	 * Opens the editor
	 * 
	 * @param file
	 */
	@SuppressWarnings("unchecked")
	private void openEditor(IFile file) {

		// Get workbench page
		IWorkbenchPage page = PlatformUI.getWorkbench()
				.getActiveWorkbenchWindow().getActivePage();

		String defaultEditor = null;
		IEditorDescriptor editorDesc = IDE.getDefaultEditor(file);
		if (editorDesc == null) {
			defaultEditor = "org.eclipse.ui.DefaultTextEditor"; //$NON-NLS-1$
		} else {
			defaultEditor = editorDesc.getId();
		}

		HashMap<String, Comparable> map = new HashMap<String, Comparable>();
		map.put(IMarker.LINE_NUMBER, Integer.valueOf(traceMetadata
				.getLineNumber()));
		map.put(IDE.EDITOR_ID_ATTR, defaultEditor);

		// Try to set markers
		try {
			IMarker marker;
			marker = file.createMarker(IMarker.TEXT);

			marker.setAttributes(map);
			IDE.openEditor(page, marker);
			marker.delete();
		} catch (CoreException e) {
			e.printStackTrace();
		}
	}

	/**
	 * Creates external files project
	 * 
	 * @param location
	 *            location of the file
	 * @param root
	 *            workspace root
	 * @return the file
	 */
	private IFile createExternalFilesProject(IPath location, IWorkspaceRoot root) {
		String externalProject = Messages
				.getString("OpenTraceLocationAction.ExternalFilesProject"); //$NON-NLS-1$

		IFile file = null;
		try {

			IProject project = root.getProject(externalProject);
			if (!project.exists()) {
				project.create(null);
			}
			if (!project.isOpen()) {
				project.open(null);
			}

			// Location must be valid
			if (location.isValidPath(location.toOSString())) {
				file = project.getFile(location.lastSegment());
				if (!file.exists()) {
					file.createLink(location, IResource.NONE, null);

					// Creating the link succeeded
					triedWithChangedDrive = false;
				}
			} else {
				// Location not valid, give an error message

				String locationStr = Messages
						.getString("OpenTraceLocationAction.LocationStr"); //$NON-NLS-1$
				String notValidStr = Messages
						.getString("OpenTraceLocationAction.NotValidStr"); //$NON-NLS-1$
				TraceViewerGlobals.getTraceViewer().getDialogs()
						.showErrorMessage(
								locationStr + SPACE + location.toOSString()
										+ SPACE + notValidStr);
			}

		} catch (CoreException e) {
			file = null;

			// Try with changed drive letter
			if (changedDriveLetter != Character.UNASSIGNED
					&& !triedWithChangedDrive) {

				triedWithChangedDrive = true;
				String newPath = changedDriveLetter
						+ traceMetadata.getPath().substring(1);

				// Try to open with new drive letter
				openSourceFileToEditor(newPath);

			} else {

				triedWithChangedDrive = false;

				if (askToBrowseSource) {

					// Show file was not found dialog
					String notFoundStr = Messages
							.getString("OpenTraceLocationAction.CouldNotFindStr"); //$NON-NLS-1$
					String browseStr = Messages
							.getString("OpenTraceLocationAction.BrowseString"); //$NON-NLS-1$

					boolean browse = TraceViewerGlobals.getTraceViewer()
							.getDialogs().showConfirmationDialog(
									notFoundStr + SPACE + location.toOSString()
											+ browseStr);

					// Browse for a file
					if (browse) {

						// Try to find the original file but change the path to
						// OS specific string first
						IPath loc = new Path(traceMetadata.getPath());
						String osString = loc.toOSString();
						browseForTheFile(osString);
					}
				}
			}

		}
		return file;
	}

	/**
	 * Browse for a file
	 * 
	 * @param path
	 *            initial file path to open
	 */
	private void browseForTheFile(String path) {
		String pathSeparator = System.getProperty("file.separator"); //$NON-NLS-1$
		int indexOfLastSeparator = path.lastIndexOf(pathSeparator);

		// Get variables
		String fileName = path.substring(indexOfLastSeparator + 1);
		String[] names = { fileName, FILTER_NAMES[0], FILTER_NAMES[1] };
		String[] extensions = { fileName, FILTER_EXTS[0], FILTER_EXTS[1] };

		// Open the file dialog
		String[] files = TraceViewerActionUtils.openFileDialog(names,
				extensions, null, fileName, false, true);

		// Open the file to the editor
		if (files != null && files.length > 0) {
			String file = files[0];

			// Check if the only thing that was different in the old and new
			// path is the drive, save the new drive letter also
			String oldFilePath = path.substring(1);
			String newFilePath = file.substring(1);
			if (oldFilePath.equals(newFilePath)) {
				changedDriveLetter = file.charAt(0);
			}

			openSourceFileToEditor(file);
		}
	}

	/**
	 * Sets trace metadata
	 * 
	 * @param metaData
	 *            the metaData to set
	 * @param askToBrowseSource
	 *            if true and source was not found, ask user to browse for the
	 *            source
	 */
	public void setMetaData(TraceMetaData metaData, boolean askToBrowseSource) {
		this.traceMetadata = metaData;
		this.askToBrowseSource = askToBrowseSource;
	}

	/**
	 * Gets EPOC root from Carbide.c++
	 * 
	 * @return Current Epoc root
	 */
	private String getEpocRoot() {
		String epocRoot = null;
		IEditorInput input = null;
		IWorkbench wb = PlatformUI.getWorkbench();

		// Try to get active editor and input for that
		if (wb != null) {
			IWorkbenchWindow wbw = wb.getActiveWorkbenchWindow();
			if (wbw != null) {
				IWorkbenchPage page = wbw.getActivePage();
				if (page != null) {
					IEditorPart editorPart = page.getActiveEditor();
					if (editorPart != null) {
						input = editorPart.getEditorInput();
					}
				}
			}
		}

		// Get the active File
		if (input instanceof FileEditorInput) {
			IFile file = ((FileEditorInput) input).getFile();
			IPath epocRootPath = getEpocRootForProject(file.getProject());

			if (epocRootPath != null) {
				epocRoot = epocRootPath.toOSString();
			}
		}

		return epocRoot;
	}

	/**
	 * Returns the absolute file system path to the EPOCROOT directory of the
	 * SDK for the active build configuration of the project
	 * 
	 * @param project
	 *            the project
	 * @return the absolute path to EPOCROOT, or null if the project is not a
	 *         Carbide project, is closed, or there are no build configurations
	 *         in the project.
	 */
	private IPath getEpocRootForProject(IProject project) {
		IPath epocroot = null;

		if (project != null) {
			if (CarbideBuilderPlugin.getBuildManager()
					.isCarbideProject(project)
					&& project.isAccessible()) {
				ICarbideProjectInfo cpi = CarbideBuilderPlugin
						.getBuildManager().getProjectInfo(project);
				if (cpi != null) {
					ICarbideBuildConfiguration config = cpi
							.getDefaultConfiguration();
					if (config != null) {
						ISymbianSDK sdk = config.getSDK();
						if (sdk != null) {
							epocroot = new Path(sdk.getEPOCROOT());
						}
					}
				}
			}
		}

		return epocroot;
	}
}