blob: 7f36f9749be3c68777f646dfcf5cd2fec6f45d9a [file] [log] [blame]
/**********************************************************************
* Copyright (c) 2005, 2010 IBM Corporation.
* 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:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.ptp.pldt.common.actions;
import java.lang.reflect.InvocationTargetException;
import java.net.URI;
import java.util.Iterator;
import java.util.List;
import org.eclipse.cdt.core.CCorePlugin;
import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit;
import org.eclipse.cdt.core.index.IIndex;
import org.eclipse.cdt.core.model.ICContainer;
import org.eclipse.cdt.core.model.ICElement;
import org.eclipse.cdt.core.model.ICProject;
import org.eclipse.cdt.core.model.ITranslationUnit;
import org.eclipse.core.commands.ExecutionEvent;
import org.eclipse.core.commands.ExecutionException;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IProjectNature;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Preferences;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.dialogs.MessageDialogWithToggle;
import org.eclipse.jface.dialogs.ProgressMonitorDialog;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.ptp.pldt.common.Artifact;
import org.eclipse.ptp.pldt.common.ArtifactMarkingVisitor;
import org.eclipse.ptp.pldt.common.CommonPlugin;
import org.eclipse.ptp.pldt.common.IDs;
import org.eclipse.ptp.pldt.common.ScanReturn;
import org.eclipse.ptp.pldt.common.messages.Messages;
import org.eclipse.ptp.pldt.common.util.AnalysisUtil;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.IViewPart;
import org.eclipse.ui.IViewReference;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.IWorkbenchWindowActionDelegate;
import org.eclipse.ui.actions.WorkspaceModifyOperation;
/**
*
* RunAnalyseBase - run analysis to create generic artifact markers. <br>
* The analysis is done in the doArtifactAnalysis() method
*
* @author Beth Tibbitts
*
* IObjectActionDelegate enables popup menu selection <br>
* IWindowActionDelegate enables toolbar(or menu) selection
*/
public abstract class RunAnalyseHandlerBase extends RunAnalyseHandler {
protected static /*final*/ boolean traceOn = false;
/**
* indent amount for each level of nesting; useful when printing debug
* statements
*/
private static final int INDENT_INCR = 2;
protected boolean forceEcho = false;
protected IWorkbenchWindow window;
protected boolean cancelledByUser = false;
protected int cumulativeArtifacts = 0;
/** the type of artifact e.g. "MPI" or "OpenMP" */
protected String name;// = "MPI";
protected ArtifactMarkingVisitor visitor;
protected String markerID;
private boolean err = false;
protected Shell shell;
/**
* Constructor for the "Run Analysis" action
*
* @param name
* the type of artifact e.g. "MPI" or "OpenMP"
* @param visitor
* the visitor that will put the markers on the source files
* @param markerID
* marker ID
*/
public RunAnalyseHandlerBase(String name, ArtifactMarkingVisitor visitor,
String markerID) {
this.name = name;
this.visitor = visitor;
this.markerID = markerID;
traceOn=CommonPlugin.getTraceOn();
if(traceOn)System.out.println("RunAnalyseBase.ctor: traceOn="+traceOn); //$NON-NLS-1$
// get the navigator, project explorer, c/C++ projects view, etc
//need to get selectionChanged on that, to cache the most recent selection there,
// otherwise HanderUtil will tell us latest selection including ones we don't want
}
public void setActivePart(IAction action, IWorkbenchPart targetPart) {
if (traceOn)
System.out.println("RunAnalyseBase.setActivePart()..."); //$NON-NLS-1$
shell = targetPart.getSite().getShell();
}
/**
* Do the "Run Analysis" on a resource (project, folder, or file).
* Descends to all child nodes, collecting artifacts on each.
*
*/
public void run() {
if (traceOn)
System.out.println("RunAnalyseHandlerBase.run()..."); //$NON-NLS-1$
cancelledByUser = false;
err = false;
cumulativeArtifacts = 0;
readPreferences();
final int indent = 0;
if ((selection == null) || selection.isEmpty()) {
MessageDialog
.openWarning(null, Messages.RunAnalyseHandlerBase_no_files_selected,
Messages.RunAnalyseHandlerBase_please_select);
return;
} else {
// get preference for include paths
final List<String> includes = getIncludePath();
if (areIncludePathsNeeded() && includes.isEmpty()) {
//System.out.println("RunAnalyseHandlerBase.run(), no include paths found.");
MessageDialog.openWarning(shell, name
+ Messages.RunAnalyseHandlerBase_include_paths_not_found,
Messages.RunAnalyseHandlerBase_please_first_specify + name
+ Messages.RunAnalyseHandlerBase_incl_paths_in_pref_page);
} else {
// batch ws modifications *and* report progress
WorkspaceModifyOperation wmo = new WorkspaceModifyOperation() {
@Override
protected void execute(IProgressMonitor monitor)
throws CoreException, InvocationTargetException,
InterruptedException {
err = runResources(monitor, indent, includes);
}
};
ProgressMonitorDialog pmdialog = new ProgressMonitorDialog(
shell);
try {
pmdialog.run(true, true, wmo); // fork=true; if false, not
// cancelable
} catch (InvocationTargetException e) {
err = true;
Throwable cause = e.getCause();
System.out.println("Error running analysis: ITE: " //$NON-NLS-1$
+ e.getMessage());
System.out.println(" cause: " + cause + " - " //$NON-NLS-1$ //$NON-NLS-2$
+ cause.getMessage());
cause.printStackTrace();
} catch (InterruptedException e) {
cancelledByUser = true;
}
}// end else
}
if (traceOn)
System.out.println("RunAnalyseBase: retd from run iterator, err=" //$NON-NLS-1$
+ err);
String artsFound = "\nNumber of " + name + " Artifacts found: " //$NON-NLS-1$ //$NON-NLS-2$
+ cumulativeArtifacts;
if (cancelledByUser) {
MessageDialog.openInformation(null, Messages.RunAnalyseHandlerBase_partial_analysis_complete,
Messages.RunAnalyseHandlerBase_15
+ artsFound);
} else {
String msg = Messages.RunAnalyseHandlerBase_cancelled_by_user;
if (!err) {
String key = IDs.SHOW_ANALYSIS_CONFIRMATION;
IPreferenceStore pf = CommonPlugin.getDefault()
.getPreferenceStore();
boolean showDialog = pf
.getBoolean(IDs.SHOW_ANALYSIS_CONFIRMATION);
if (showDialog) {
String title = Messages.RunAnalyseHandlerBase_analysis_complete;
StringBuffer sMsg = new StringBuffer(cumulativeArtifacts + " " + name //$NON-NLS-1$
+ Messages.RunAnalyseHandlerBase_artifacts_found);
// provide some explanation of why perhaps no artifacts were found.
// Note: should this perhaps be in a "Details" section of the dialog?
if(cumulativeArtifacts==0) {
sMsg.append(Messages.RunAnalyseHandlerBase_20).append(name).append(Messages.RunAnalyseHandlerBase_21);
sMsg.append(name).append(Messages.RunAnalyseHandlerBase_22);
sMsg.append(Messages.RunAnalyseHandlerBase_23);
}
String togMsg = Messages.RunAnalyseHandlerBase_dont_show_this_again;
MessageDialogWithToggle.openInformation(shell, title, sMsg.toString(),
togMsg, false, pf, key);
showStatusMessage(sMsg.toString(), "RunAnalyseBase.run()"); //$NON-NLS-1$
}
activateProblemsView();
activateArtifactView();
} else { // error occurred
showStatusMessage(msg, "RunAnalyseBase.run() error"); //$NON-NLS-1$
msg = Messages.RunAnalyseHandlerBase_27;
MessageDialog.openError(null, Messages.RunAnalyseHandlerBase_28,
msg + artsFound);
}
}
}
/**
* Run the analysis on the current selection (file, container, or
* multiple-selection)
*
* @param monitor
* progress monitor on which to report progress.
* @param indent
* indent amount, in number of spaces, used only for debug
* printing.
* @param includes
* @return true if any errors were found.
* @throws InterruptedException
*/
@SuppressWarnings("unchecked") // on Iterator
protected boolean runResources(IProgressMonitor monitor, int indent,
List<String> includes) throws InterruptedException {
boolean foundError = false;
// First, count files so we know how much work to do.
// note this is number of files of any type, not necessarily number of
// files that will be analyzed.
int count = countFilesSelected();
monitor.beginTask(Messages.RunAnalyseHandlerBase_29, count);
if(traceOn)System.out.println("RAHB.runResources(): using selection: "+selection);
// Get elements of a possible multiple selection
IStructuredSelection lastSel=AnalysisDropdownHandler.getInstance().getLastSelection();
Iterator<IStructuredSelection> iter=lastSel.iterator();// fix analysis selection bug 327122
while (iter.hasNext()) {
if (monitor.isCanceled()) {
// this is usually caught here while processing
// multiple-selection of files
throw new InterruptedException();
}
Object obj = (Object) iter.next();// piece of selection
// It can be a Project, Folder, File, etc...
if (obj instanceof IAdaptable) {
// ICElement covers folders and translationunits
// If fortran file (*.f*) and Photran not installed, ce is null and we ignore it (first) here.
final ICElement ce = (ICElement) ((IAdaptable) obj)
.getAdapter(ICElement.class);// cdt40
if (ce != null) {
// cdt40
// IASTTranslationUnit atu = tu.getAST(); not yet
boolean err = runResource(monitor, ce, indent, includes);
if(traceOn)System.out.println("Error (err="+err+")running analysis on "+ce.getResource().getName()); //$NON-NLS-1$ //$NON-NLS-2$
}
}
}
monitor.done();
return foundError;
}
abstract protected void activateArtifactView();
/**
* If the analysis has an additional view to bring up, override this
*/
protected void activateProblemsView(){}
/**
* Get the include path. Subclass should override this method.
*
* @return
*/
abstract protected List<String> getIncludePath();
/**
* Show something in the status line; this is used when we don't have easy
* access to the view for getting the StatusLineManager.
*
* @param message
* @param debugMessage
*/
private void showStatusMessage(String message, String debugMessage) {
if (false) {
message += " - "; //$NON-NLS-1$
message += debugMessage;
}
IWorkbenchWindow ww = CommonPlugin.getDefault().getWorkbench()
.getActiveWorkbenchWindow();
IWorkbenchPage page = ww.getActivePage();
IViewReference[] viewRefs = page.getViewReferences();
for (int j = 0; j < viewRefs.length; j++) {
IViewReference reference = viewRefs[j];
IViewPart vp = reference.getView(false);
if (vp != null)
vp.getViewSite().getActionBars().getStatusLineManager()
.setMessage(message);
}
}
/**
* Read preferences
*
*/
protected void readPreferences() {
Preferences pref = CommonPlugin.getDefault().getPluginPreferences();
forceEcho = pref.getBoolean(IDs.P_ECHO_FORCE);
}
/**
* Run analysis on a resource (e.g. File or Folder) Will descend to members
* of folder
*
* @param atu
* the resource
* @param indent
* number of levels of nesting/recursion for prettyprinting
* @param includes
* contains header files include paths from the Preference page
* @return true if an error was encountered
* @throws InterruptedException
*/
public boolean runResource(IProgressMonitor monitor, ICElement ce,
int indent, List<String> includes) throws InterruptedException {
indent += INDENT_INCR;
ScanReturn results;
boolean foundError = false;
if (!monitor.isCanceled()) {
if (ce instanceof ITranslationUnit) {
IResource res = ce.getResource(); // null if not part of C
// project in ws
// cdt40: eventually shd be able to deal with just tu;
// tu.getResource() can always work later...
if (res instanceof IFile) {//shd always be true (but might be null)
IFile file = (IFile) res;
String filename = file.getName();
//String fn2 = ce.getElementName();// shd be filename too
// cdt40
boolean cpp = isCPPproject(ce);
//if (AnalysisUtil.validForAnalysis(filename,cpp)) {
if (validForAnalysis(filename,cpp)) {
if (traceOn)
println(getSpaces(indent) + "file: " + filename); //$NON-NLS-1$
results = analyse(monitor, (ITranslationUnit) ce,
includes);
foundError = foundError || results == null
|| results.wasError();
if (foundError) {
int stopHere = 0;
System.out.println("found error on " //$NON-NLS-1$
+ file.getName() + " " + stopHere); //$NON-NLS-1$
}
if (traceOn)
println("******** RunAnalyseBase, analysis complete; ScanReturn=" //$NON-NLS-1$
+ results);
if (results != null) {
// apply markers to the file
processResults(results, file);
}
} else {
if (traceOn)
println(getSpaces(indent)
+ "---omit: not valid file: " + filename); //$NON-NLS-1$
}
return foundError;
}
}
else if (ce instanceof ICContainer) {
ICContainer container = (ICContainer) ce;
try {
ICElement[] mems = container.getChildren();
for (int i = 0; i < mems.length; i++) {
if (monitor.isCanceled()) {
// this is usually hit while processing normal
// analysis of e.g. container
throw new InterruptedException();
}
boolean err = runResource(monitor, mems[i], indent,
includes);
foundError = foundError || err;
}
} catch (CoreException e) {
e.printStackTrace();
}
}
else if (ce instanceof ICProject) {
ICProject proj = (ICProject) ce;
try {
ICElement[] mems = proj.getChildren();
for (int i = 0; i < mems.length; i++) {
if (monitor.isCanceled()) {
// this is usually hit while processing normal
// analysis of e.g. container
throw new InterruptedException();
}
boolean err = runResource(monitor, mems[i], indent,
includes);
foundError = foundError || err;
}
} catch (CoreException e) {
e.printStackTrace();
}
}
// container could be project or folder
} // end if !monitor.isCanceled()
else {
String name = ""; //$NON-NLS-1$
//cdt40
name = ce.getElementName();
//String p=ce.getPath().toString();
System.out.println("Cancelled by User, aborting analysis on subsequent files... " //$NON-NLS-1$
+ name);
throw new InterruptedException();
}
return foundError;
}
/**
* Determine if the project is a C++ project
* @param ce the ICElement representing a file
* @return
*/
protected boolean isCPPproject(ICElement ce) {
IProject p = ce.getCProject().getProject();
try {
IProjectNature nature = p.getNature("org.eclipse.cdt.core.ccnature"); //$NON-NLS-1$
if(nature!=null) {
return true;
}
} catch (CoreException e) {
// TODO Auto-generated catch block
//e.printStackTrace();
}
return false;
}
protected void processResults(ScanReturn results, IResource resource) {
List<Artifact> artifacts = results.getArtifactList();
visitor.visitFile(resource, artifacts);
}
public ScanReturn analyse(IProgressMonitor monitor, ITranslationUnit tu,
List<String> includes) {
if (traceOn)
println("RunAnalyseBase.analyse()..."); //$NON-NLS-1$
//ScanReturn nr = null;
String errMsg = null;
monitor.subTask(Messages.RunAnalyseHandlerBase_42);
String rawPath = tu.getLocationURI().toString();
if (traceOn)
println("RunAnalyseBase: file = " + rawPath); //$NON-NLS-1$
monitor.subTask(Messages.RunAnalyseHandlerBase_on + rawPath);
ScanReturn scanReturn = doArtifactAnalysis(tu, includes);
monitor.worked(1);
if (traceOn)
println("Artifact analysis complete..."); //$NON-NLS-1$
int numArtifacts = scanReturn.getArtifactList().size();
cumulativeArtifacts = cumulativeArtifacts + numArtifacts;
if (traceOn)
System.out.println("Artifacts found for " //$NON-NLS-1$
+ tu.getResource().getProjectRelativePath() + ": " + numArtifacts); //$NON-NLS-1$
if (traceOn)
System.out.println(" Total # found: " + cumulativeArtifacts); //$NON-NLS-1$
if (scanReturn == null) {
System.out.println("ScanReturn result is NULL. No results for " //$NON-NLS-1$
+ tu.getResource().getProjectRelativePath());
errMsg = "Error: No results were returned from analysis of " //$NON-NLS-1$
+ tu.getResource().getProjectRelativePath();
MessageDialog.openError(shell, "Error in Analysis", errMsg); //$NON-NLS-1$
} else {
if (traceOn)
System.out.println("RunAnalyzeBase: ScanReturn received for " //$NON-NLS-1$
+ tu.getElementName());
}
if (scanReturn != null) {
boolean wasError = scanReturn.wasError();
if (traceOn)
System.out.println("error occurred =" + wasError); //$NON-NLS-1$
if (wasError) {
System.out.println("RunAnalyseBase.analyse...Error..."); //$NON-NLS-1$
}
}
return scanReturn;
}
/**
* return a string of spaces of a certain length
*
* @param indent
* the number of spaces to return (used for successively
* indenting debug statements based on depth of nesting)
*/
private static final String SPACES = " "; //$NON-NLS-1$
private String getSpaces(int indent) {
String indentSpace = ""; //$NON-NLS-1$
try {
indentSpace = SPACES.substring(0, indent);
} catch (StringIndexOutOfBoundsException e) {
println("RunAnalyseBase: Nesting level " + indent //$NON-NLS-1$
+ " exceeds print indent; INCR at each level is " //$NON-NLS-1$
+ INDENT_INCR);
// e.printStackTrace();
}
return indentSpace;
}
/**
* print to log
*
* @param str
*/
void println(String str) {
System.out.println(str);
}
/**
* We can use this method to dispose of any system resources we previously
* allocated.
*
* @see IWorkbenchWindowActionDelegate#dispose implemented for toolbar
* enablement of this action
*/
public void dispose() {
}
/**
* Cache the window object to be able to provide parent shell for the
* message dialog.
*
* @see IWorkbenchWindowActionDelegate#init implemented for toolbar
* enablement of this action NOTE: window object will thus be null for
* context menu use!! so...we are not using this, using
* Display.getCurrent() and Display.getCurrent().getActiveShell();
*/
public void init(IWorkbenchWindow window) {
this.window = window;
}
/**
* Provide a human-readable version of what will be analyzed.
*
* @param obj
* the file, folder, or project
* @return a string indicating what it is
*/
public String getPrefacedName(Object obj) {
String preface = ""; //$NON-NLS-1$
if (obj instanceof IFolder)
preface = Messages.RunAnalyseHandlerBase_60;
else if (obj instanceof IProject)
preface = Messages.RunAnalyseHandlerBase_61;
else if (obj instanceof IFile)
preface = Messages.RunAnalyseHandlerBase_62;
String res = preface + ((IResource) obj).getName();
return res;
}
/**
* Returns artifact analysis for file. <br>
* Derived class should override this method.
*
* @param tu
* @param includes
* header files include paths
* @return
*/
public abstract ScanReturn doArtifactAnalysis(final ITranslationUnit tu,
final List<String> includes);
/**
* returns true if include paths must be set for this implementation. For
* example, C needs include paths, but Fortran doesn't.
*/
public boolean areIncludePathsNeeded() {
return true;
}
/**
* Implemented for Handler; this replaces run() which is for actions.
*/
public Object execute(ExecutionEvent event) throws ExecutionException {
if(traceOn)System.out.println("RunAnalyseHandlerBase.execute()..."); //$NON-NLS-1$
getSelection(event);
if(traceOn)System.out.println("selection: "+selection); //$NON-NLS-1$
run();
AnalysisDropdownHandler.setLastHandledAnalysis(this, selection);
return null;
}
/**
* Default determination of if a given filename is valid for our artifact analysis
* @param filename
* @param isCPP is the project a C++ project or not
* @return
*/
protected boolean validForAnalysis(String filename, boolean isCPP) {
return AnalysisUtil.validForAnalysis(filename,isCPP);
}
/**
* Get AST from index, not full tu
* @param tu translation unit from which to get the AST
* @return
*/
protected IASTTranslationUnit getAST(ITranslationUnit tu) {
IIndex index;
try {
index = CCorePlugin.getIndexManager().getIndex(tu.getCProject());
IASTTranslationUnit ast = tu.getAST(index, ITranslationUnit.AST_SKIP_ALL_HEADERS);
//IASTTranslationUnit ast = tu.getAST(index, 0);
if(traceOn)System.out.println(" getAST(index,AST_SKIP_ALL_HEADERS)"); //$NON-NLS-1$
return ast;
} catch (CoreException e) {
CommonPlugin.log(IStatus.ERROR,"RunAnalyseMPICommandHandler.getAST():Error getting AST (from index) for project "+tu.getCProject()); //$NON-NLS-1$
return null;
}
}
}