/*******************************************************************************
* Copyright (c) 2012, 2014 Wind River Systems, Inc. and others. All rights reserved.
* This program and the accompanying materials are made available under the terms
* of the Eclipse Public License v1.0 which accompanies this distribution, and is
* available at http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Wind River Systems - initial API and implementation
*******************************************************************************/
package org.eclipse.tcf.te.launch.ui.selection;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import org.eclipse.cdt.utils.elf.Elf.Attribute;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.Status;
import org.eclipse.debug.core.ILaunchConfigurationType;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.osgi.util.NLS;
import org.eclipse.tcf.te.core.cdt.elf.ElfUtils;
import org.eclipse.tcf.te.launch.core.selection.LaunchSelection;
import org.eclipse.tcf.te.launch.core.selection.ProjectSelectionContext;
import org.eclipse.tcf.te.launch.core.selection.RemoteSelectionContext;
import org.eclipse.tcf.te.launch.core.selection.interfaces.ILaunchSelection;
import org.eclipse.tcf.te.launch.core.selection.interfaces.ISelectionContext;
import org.eclipse.tcf.te.launch.ui.activator.UIPlugin;
import org.eclipse.tcf.te.launch.ui.model.LaunchNode;
import org.eclipse.tcf.te.launch.ui.nls.Messages;
import org.eclipse.tcf.te.runtime.model.interfaces.IModelNode;
import org.eclipse.tcf.te.runtime.model.interfaces.IModelNodeProvider;
import org.eclipse.tcf.te.runtime.services.ServiceManager;
import org.eclipse.tcf.te.runtime.services.interfaces.IPropertiesAccessService;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.PlatformUI;
/**
* Launch selection manager implementation.
*/
public class LaunchSelectionManager {
/**
* Part id: Target Explorer view
*/
public static final String PART_ID_TE_VIEW = "org.eclipse.tcf.te.ui.views.View"; //$NON-NLS-1$
/**
* Part id: Project Explorer view
*/
public static final String PART_ID_PROJECT_VIEW = "org.eclipse.ui.navigator.ProjectExplorer"; //$NON-NLS-1$
/**
* Part id: Debug view
*/
public static final String PART_ID_DEBUG_VIEW = "org.eclipse.debug.ui.DebugView"; //$NON-NLS-1$
// Remember the last remote context and project selections.
// <p>
// Some operations to determine the corresponding selection contexts may consume
// a lot of time. Avoid to redo them if not really needed.
private IStructuredSelection lastRemoteCtxInputSelection = null;
private Map<IModelNode, Set<IModelNode>> lastRemoteCtxOutputSelections = null;
private IStructuredSelection lastProjectInputSelection = null;
private long lastProjectHash = 0;
private Map<IProject, Set<IPath>> lastProjectOutputSelections = null;
/*
* Thread save singleton instance creation.
*/
private static class LazyInstanceHolder {
public static LaunchSelectionManager instance = new LaunchSelectionManager();
}
/**
* Returns the singleton instance for the manager.
*/
public static LaunchSelectionManager getInstance() {
return LazyInstanceHolder.instance;
}
/**
* Constructor.
*/
LaunchSelectionManager() {
}
/**
* Returns a launch selection for the given launch mode and a preferred part.
*
* @param type The launch configuration type or <code>null</code>, to determine defaults.
* @param mode The launch mode to generate the launch selection for.
* @param preferredPartId The part id whose selections should be preferred. Can be <code>null</code>.
* @return A launch selection.
*/
public ILaunchSelection getLaunchSelection(ILaunchConfigurationType type, String mode, String preferredPartId) {
return new LaunchSelection(mode, getSelectionContexts(type, mode, preferredPartId));
}
private ISelectionContext[] getSelectionContexts(ILaunchConfigurationType type, String mode, String preferredPartId) {
List<ISelectionContext> contexts = new ArrayList<ISelectionContext>();
// Get the selected remote contexts
contexts.addAll(getSelectionContextsFor(PART_ID_TE_VIEW, type, mode, PART_ID_TE_VIEW.equalsIgnoreCase(preferredPartId)));
// Get the selected project contexts
contexts.addAll(getSelectionContextsFor(PART_ID_PROJECT_VIEW, type, mode, PART_ID_PROJECT_VIEW.equalsIgnoreCase(preferredPartId)));
return contexts.toArray(new ISelectionContext[contexts.size()]);
}
public List<ISelectionContext> getSelectionContextsFor(String partId, ILaunchConfigurationType type, String mode, boolean preferedPart) {
List<ISelectionContext> contexts = new ArrayList<ISelectionContext>();
if (PART_ID_TE_VIEW.equalsIgnoreCase(partId)) {
// Get the selected remote contexts
Map<IModelNode, Set<IModelNode>> remoteCtxSelections = getRemoteCtxSelections(getPartSelection(PART_ID_TE_VIEW));
for (Entry<IModelNode, Set<IModelNode>> remoteCtx : remoteCtxSelections.entrySet()) {
contexts.add(new RemoteSelectionContext(remoteCtx.getKey(), remoteCtx.getValue().toArray(), preferedPart));
}
}
else if (PART_ID_PROJECT_VIEW.equalsIgnoreCase(partId)) {
// Get the selected project contexts
Map<IProject, Set<IPath>> projectSelections = getProjectSelections(getPartSelection(PART_ID_PROJECT_VIEW), true);
for (Entry<IProject, Set<IPath>> prj : projectSelections.entrySet()) {
contexts.add(new ProjectSelectionContext(prj.getKey(), prj.getValue().toArray(), preferedPart));
}
}
return contexts;
}
/**
* Analyze the given UI selection and extract the remote context selection from it.
*
* @param structSel The UI selection or <code>null</code>.
* @return The remote context selections or an empty map.
*/
private Map<IModelNode, Set<IModelNode>> getRemoteCtxSelections(IStructuredSelection structSel) {
if (structSel != null && structSel.equals(lastRemoteCtxInputSelection) && lastRemoteCtxOutputSelections != null) {
return lastRemoteCtxOutputSelections;
}
Map<IModelNode, Set<IModelNode>> remoteCtxSelections = new HashMap<IModelNode, Set<IModelNode>>();
if (structSel != null && !structSel.isEmpty()) {
for (Object sel : structSel.toArray()) {
IModelNode remoteCtx = null;
IModelNode node = null;
if (sel instanceof LaunchNode && ((LaunchNode)sel).getModel().getModelRoot() instanceof IModelNode) {
node = (IModelNode)((LaunchNode)sel).getModel().getModelRoot();
}
else if (sel instanceof IModelNodeProvider) {
node = ((IModelNodeProvider)sel).getModelNode();
} else if (sel instanceof IModelNode) {
node = (IModelNode)sel;
}
if (node != null) {
IPropertiesAccessService service = ServiceManager.getInstance().getService(node, IPropertiesAccessService.class);
if (service != null) {
remoteCtx = node;
IModelNode parent = (IModelNode)service.getParent(node);
while (parent != null) {
remoteCtx = parent;
parent = (IModelNode)service.getParent(parent);
}
}
}
Set<IModelNode> nodes;
if (remoteCtx != null) {
if (!remoteCtxSelections.containsKey(remoteCtx)) {
nodes = new HashSet<IModelNode>();
remoteCtxSelections.put(remoteCtx, nodes);
}
else {
nodes = remoteCtxSelections.get(remoteCtx);
}
if (node != null) {
nodes.add(node);
}
}
}
}
lastRemoteCtxInputSelection = structSel;
lastRemoteCtxOutputSelections = remoteCtxSelections;
return lastRemoteCtxOutputSelections;
}
/**
* Calculates a hash code based on the determined projects of
* the given UI selection.
*
* @param structSel The UI selection or <code>null</code>.
* @return The calculated hash code.
*/
private long getProjectHash(IStructuredSelection structSel) {
long hash = 0;
if (structSel != null) {
List<IProject> projects = new ArrayList<IProject>();
for (Object sel : structSel.toArray()) {
IProject prj = null;
IResource resource = null;
if (sel instanceof LaunchNode && ((LaunchNode)sel).getModel().getModelRoot() instanceof IResource) {
sel = ((LaunchNode)sel).getModel().getModelRoot();
}
// If the selection is not an IResource itself, try to adapt to it.
// This will possibly trigger an plugin activation on loadAdapter(...).
if (sel instanceof IProject) {
prj = (IProject)sel;
}
else {
if (sel instanceof IResource) {
resource = (IResource)sel;
}
else if (sel instanceof IAdaptable) {
resource = (IResource)((IAdaptable)sel).getAdapter(IResource.class);
}
else {
resource = (IResource)Platform.getAdapterManager().loadAdapter(sel, IResource.class.getName());
}
// Get the project from the resource
prj = resource != null ? resource.getProject() : null;
}
// If the project could be determined, add the project's
// hash code to the cumulative hash code.
if (prj != null && !projects.contains(prj)) {
projects.add(prj);
hash += prj.hashCode();
}
else {
hash += sel.hashCode();
}
}
projects.clear();
}
return hash;
}
/**
* Analyze the given UI selection and extract the project selection from it.
*
* @param structSel The UI selection or <code>null</code>.
* @param storeToCache If <code>true</code> the project selection will be cached.
*
* @return The project context selection or an empty map.
*/
public Map<IProject, Set<IPath>> getProjectSelections(IStructuredSelection selection, boolean storeToCache) {
long projectHash = 0;
if (selection != null && selection.equals(lastProjectInputSelection) &&
lastProjectOutputSelections != null) {
projectHash = getProjectHash(selection);
if (lastProjectHash == 0 || lastProjectHash == projectHash) {
return lastProjectOutputSelections;
}
}
Map<IProject, Set<IPath>> projectSelections = new HashMap<IProject, Set<IPath>>();
if (selection != null && !selection.isEmpty()) {
for (Object sel : selection.toArray()) {
IProject prj = null;
IPath location = null;
if (sel instanceof LaunchNode && ((LaunchNode)sel).getModel().getModelRoot() instanceof IProject) {
prj = (IProject)((LaunchNode)sel).getModel().getModelRoot();
}
else if (sel instanceof IProject) {
prj = (IProject)sel;
}
else if (sel instanceof IFile) {
IFile file = (IFile)sel;
prj = file.getProject();
if (getLocation(file) != null) {
File filePath = getLocation(file).toFile();
try {
int elfType = ElfUtils.getELFType(filePath);
if (file.exists() &&
(elfType == Attribute.ELF_TYPE_EXE || elfType == Attribute.ELF_TYPE_OBJ)) {
location = file.getLocation();
}
}
catch (IOException e) {
if (Platform.inDebugMode()) {
IStatus status = new Status(IStatus.ERROR, UIPlugin.getUniqueIdentifier(),
NLS.bind(Messages.LaunchSelectionManager_error_failedToDetermineElfType, filePath.getAbsolutePath()), e);
UIPlugin.getDefault().getLog().log(status);
}
}
}
} else {
// Try to adapt the selection to an resource
IResource resource = (IResource)Platform.getAdapterManager().loadAdapter(sel, IResource.class.getName());
if (resource != null) {
prj = resource.getProject();
location = getLocation(resource);
if (location != null) {
try {
int elfType = ElfUtils.getELFType(location.toFile());
if (elfType != Attribute.ELF_TYPE_EXE && elfType != Attribute.ELF_TYPE_OBJ) {
location = null;
}
}
catch (Exception e) {
if (Platform.inDebugMode()) {
IStatus status = new Status(IStatus.ERROR, UIPlugin.getUniqueIdentifier(),
NLS.bind(Messages.LaunchSelectionManager_error_failedToDetermineElfType, location.toFile().getAbsolutePath()), e);
UIPlugin.getDefault().getLog().log(status);
}
location = null;
}
}
}
}
if (prj != null && (location == null || location.toFile().isDirectory())) {
// Try to get it from the build targets
location = getFirstExeLocation(prj);
}
Set<IPath> nodes;
if (prj != null) {
if (!projectSelections.containsKey(prj)) {
nodes = new HashSet<IPath>();
projectSelections.put(prj, nodes);
}
else {
nodes = projectSelections.get(prj);
}
if (location != null) {
nodes.add(location);
}
}
}
}
if (storeToCache) {
lastProjectInputSelection = selection;
lastProjectHash = projectHash != 0 ? projectHash : getProjectHash(selection);
lastProjectOutputSelections = projectSelections;
}
return storeToCache ? lastProjectOutputSelections : projectSelections;
}
/**
* Get the selection of a workbench part.
* <p>
* <b>Note:</b> This method will return null if called from a non-UI thread!
*
* @param partId The part id. Must not be <code>null</code>.
* @return The structured selection if the workbench part or <code>null</code>.
*/
public static IStructuredSelection getPartSelection(String partId) {
IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
if (partId != null && window != null && window.getActivePage() != null) {
ISelection sel = window.getActivePage().getSelection(partId);
if (sel instanceof IStructuredSelection) {
return (IStructuredSelection)sel;
}
}
return null;
}
/**
* Return the location on the file system of the first executable
* build-target found in the given project. Existing build-targets have
* precedence over non-existing build targets.
*
* @param prj The project. Must not be <code>null</code>
* @return IPath The first found executable or <code>null</code>.
*/
public IPath getFirstExeLocation(IProject prj) {
return null;
}
/*
* Get the location of an IResource or null.
*/
private IPath getLocation(IResource resource) {
return (resource != null) ? resource.getLocation() : null;
}
}