package lapsePlus.views; /* * LapseView.java,version 2.8, 2010 */ import java.io.BufferedReader; import java.io.IOException; import java.io.PrintWriter; import java.io.StringReader; import java.io.StringWriter; import java.lang.reflect.InvocationTargetException; import java.util.Collection; import java.util.Iterator; import java.util.LinkedList; import lapsePlus.CallerFinder; import lapsePlus.DefinitionLocation; import lapsePlus.HistoryDefinitionLocation; import lapsePlus.LapsePlugin; import lapsePlus.NodeFinder; import lapsePlus.Utils; import lapsePlus.views.SinkView; import org.eclipse.core.filebuffers.FileBuffers; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IResource; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.jobs.Job; /**JAVA MODEL**/ import org.eclipse.jdt.core.IBuffer; import org.eclipse.jdt.core.IClassFile; import org.eclipse.jdt.core.ICompilationUnit; import org.eclipse.jdt.core.IJavaElement; import org.eclipse.jdt.core.IMethod; import org.eclipse.jdt.core.IOpenable; import org.eclipse.jdt.core.JavaModelException; import org.eclipse.jdt.core.dom.AST; import org.eclipse.jdt.core.dom.ASTNode; import org.eclipse.jdt.core.dom.ASTParser; import org.eclipse.jdt.core.dom.ArrayAccess; import org.eclipse.jdt.core.dom.CastExpression; import org.eclipse.jdt.core.dom.ClassInstanceCreation; import org.eclipse.jdt.core.dom.CompilationUnit; import org.eclipse.jdt.core.dom.Expression; import org.eclipse.jdt.core.dom.FieldDeclaration; //import org.eclipse.jdt.core.dom.IMethodBinding; import org.eclipse.jdt.core.dom.InfixExpression; import org.eclipse.jdt.core.dom.MethodDeclaration; import org.eclipse.jdt.core.dom.MethodInvocation; import org.eclipse.jdt.core.dom.NullLiteral; import org.eclipse.jdt.core.dom.ParenthesizedExpression; import org.eclipse.jdt.core.dom.ReturnStatement; import org.eclipse.jdt.core.dom.SimpleName; import org.eclipse.jdt.core.dom.StringLiteral; import org.eclipse.jdt.core.dom.VariableDeclaration; import org.eclipse.jdt.core.search.IJavaSearchScope; import org.eclipse.jdt.internal.ui.JavaPlugin; import org.eclipse.jdt.internal.ui.JavaPluginImages; import org.eclipse.jdt.internal.ui.javaeditor.EditorUtility; import org.eclipse.jface.action.Action; import org.eclipse.jface.action.IMenuListener; import org.eclipse.jface.action.IMenuManager; import org.eclipse.jface.action.IStatusLineManager; import org.eclipse.jface.action.IToolBarManager; import org.eclipse.jface.action.MenuManager; import org.eclipse.jface.action.Separator; import org.eclipse.jface.dialogs.MessageDialog; import org.eclipse.jface.operation.IRunnableWithProgress; import org.eclipse.jface.resource.ImageDescriptor; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.ITextSelection; import org.eclipse.jface.util.Assert; import org.eclipse.jface.viewers.DoubleClickEvent; import org.eclipse.jface.viewers.IBaseLabelProvider; import org.eclipse.jface.viewers.IDoubleClickListener; import org.eclipse.jface.viewers.ISelection; import org.eclipse.jface.viewers.ISelectionChangedListener; import org.eclipse.jface.viewers.ISelectionProvider; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.viewers.SelectionChangedEvent; import org.eclipse.jface.viewers.StructuredSelection; import org.eclipse.jface.viewers.TreeViewer; import org.eclipse.swt.SWT; import org.eclipse.swt.SWTError; import org.eclipse.swt.dnd.Clipboard; import org.eclipse.swt.dnd.DND; import org.eclipse.swt.dnd.TextTransfer; import org.eclipse.swt.dnd.Transfer; import org.eclipse.swt.events.KeyEvent; import org.eclipse.swt.events.KeyListener; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Menu; import org.eclipse.swt.widgets.Tree; import org.eclipse.swt.widgets.TreeItem; import org.eclipse.ui.IActionBars; import org.eclipse.ui.IEditorInput; import org.eclipse.ui.IEditorPart; import org.eclipse.ui.ISelectionService; import org.eclipse.ui.ISharedImages; import org.eclipse.ui.IViewSite; import org.eclipse.ui.IWorkbenchActionConstants; import org.eclipse.ui.IWorkbenchPartSite; import org.eclipse.ui.PartInitException; import org.eclipse.ui.part.ViewPart; import org.eclipse.ui.progress.IProgressConstants; import org.eclipse.ui.texteditor.IEditorStatusLine; import org.eclipse.ui.texteditor.ITextEditor; /** * Main view class that calls most of the computational logic * behind the slicer. * * It extends from ViewPart: an abstract base implementation of all workbench views. * * */ public class LapseView extends ViewPart{ /********************************* Members *********************************/ LocationViewer fViewer; //Tree Viewer ProvenanceContentProvider fContentProvider; IJavaSearchScope fSearchScope; ITextEditor fEditor; IOpenable fOpenable; CompilationUnit fRoot; IDocument fCurrentDocument; SuperListener fSuperListener; ASTParser fParser; Clipboard fClipboard; LapseLayoutActionGroup fPullDownActions; SlicingJob fSlicingJob; SlicingFromSinkJob fSlicingFromSinkJob; /********************************* Actions *********************************/ private Action updateAction; private Action doubleClickAction; private Action prefAction; private Action historyAction; private Action selectAllAction; private LapseCopyAction copyAction; // Expansion/collapsing private Action collapseAction; private Action expandAction; private Action expandAllAction; private Action collapseAllAction; protected int fExpansionLevel; protected static final boolean TRACE = true; protected static final int MIN_EXPANSION_LEVEL = 0; public LapseView() { LapsePlugin.getDefault().setLapseView(this); } /** * Initializes this view */ public void init(IViewSite site) throws PartInitException { log("In init(...)"); super.setSite(site); if (fSuperListener == null) { fSuperListener = new SuperListener(this); //showMessage("Registering the plugin"); //To track if the plugin is selected ISelectionService service = site.getWorkbenchWindow().getSelectionService(); //We add a listener to notify if the selection has changed service.addPostSelectionListener(fSuperListener); //A file that can be edited by more than one client FileBuffers.getTextFileBufferManager().addFileBufferListener(fSuperListener); } //We create the parser for the Abstract Syntax Tree fParser = ASTParser.newParser(AST.JLS3);//Java Language Specifitacion 3 fParser.setResolveBindings(true);//The compiler have to provide binding information for the AST nodes //Backward slicer from sinks fSlicingJob = new SlicingJob("Backward data slicer"); fSlicingFromSinkJob = new SlicingFromSinkJob("Backward slicer from a sink", this); } public void createPartControl(Composite parent) { log("In createPartControl(...)"); //viewer = new TableViewer(parent, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL); fViewer = new LocationViewer(parent); fContentProvider = new ProvenanceContentProvider.HierarchicalViewContentProvider(); fViewer.setContentProvider(fContentProvider); fViewer.setLabelProvider((IBaseLabelProvider) new DLTreeLabelProvider()); //viewer.setSorter(new NameSorter()); fViewer.setInput(getViewSite()); fClipboard = new Clipboard(parent.getDisplay()); makeActions(); hookContextMenu(); hookViewerEvents(); fViewer.getControl().addKeyListener(new KeyListener() { public void keyPressed(KeyEvent event) { if ((event.stateMask & SWT.CTRL) != 0) { switch (event.keyCode) { case '+' : if(expandAction.isEnabled()) { expandAction.run(); } return; case '-' : if(collapseAction.isEnabled()) { collapseAction.run(); } return; case 'L' : if((event.stateMask & SWT.SHIFT) != 0 && updateAction.isEnabled()) { updateAction.run(); } return; } } } public void keyReleased(KeyEvent e) {} }); // obtain focus setFocus(); } private void hookContextMenu() { MenuManager menuMgr = new MenuManager("#PopupMenu"); menuMgr.setRemoveAllWhenShown(true); menuMgr.addMenuListener(new IMenuListener() { public void menuAboutToShow(IMenuManager manager) { fillContextMenu(manager); } }); Menu menu = menuMgr.createContextMenu(fViewer.getControl()); fViewer.getControl().setMenu(menu); getSite().registerContextMenu(menuMgr, fViewer); } private void contributeToActionBars() { IActionBars bars = getViewSite().getActionBars(); //fillLocalPullDown(bars.getMenuManager()); fillLocalToolBar(bars.getToolBarManager()); fPullDownActions = new LapseLayoutActionGroup(this); fPullDownActions.fillActionBars(bars); } protected void fillContextMenu(IMenuManager manager) { //super.fillContextMenu(manager); //manager.add(updateAction); manager.add(selectAllAction); manager.add(copyAction); // Other plug-ins can contribute there actions here manager.add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS)); } private void fillLocalToolBar(IToolBarManager manager) { manager.add(updateAction); //manager.add(expandAction); manager.add(expandAllAction); //manager.add(collapseAction); manager.add(collapseAllAction); manager.add(prefAction); manager.add(historyAction); manager.add(copyAction); } class SlicingJob extends Job { public SlicingJob(String name) { super(name); // setProperty(IProgressConstants.ACTION_PROPERTY, // new Action("Pop up a dialog") { // public void run() { // MessageDialog.openInformation(getSite().getShell(), // "Goto Action", // "The job can have an action associated with it" // ); // } // }); } //Receives the monitor of the progress of the activity protected IStatus run(IProgressMonitor monitor) { IRunnableWithProgress runnable = new IRunnableWithProgress() { public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException{ try { if(fRoot == null || fOpenable == null) { logError("Uninitialized inputs"); throw new RuntimeException(); } updateSlice(fRoot, fOpenable.getBuffer().getUnderlyingResource(), monitor); } catch (InvalidSlicingSelectionException e) { throw new RuntimeException(e); } catch (JavaModelException e) { throw new RuntimeException(e); } } }; try { runnable.run(monitor); if(fContentProvider.getTotalElementCount() == 0) { Display.getDefault().syncExec(new Runnable() { public void run() { setContentDescription("Error creating a slice"); } }); return Status.CANCEL_STATUS; } else { Display.getDefault().syncExec(new Runnable() { public void run() { // now enable the actions collapseAction.setEnabled(true); expandAction.setEnabled(true); collapseAllAction.setEnabled(true); expandAllAction.setEnabled(true); //System.err.println("Setting enabled to true"); // enable actions in the menu fPullDownActions.setEnabled(true); refresh(); } }); } return Status.OK_STATUS; } catch (InvocationTargetException e) { log(e.getMessage(), e); } catch (InterruptedException e) { log(e.getMessage(), e); } catch (RuntimeException e) { log(e.getMessage(), e); } return Status.CANCEL_STATUS; } } class SlicingFromSinkJob extends Job { ASTNode sink; CompilationUnit unit; IResource resource; private LapseView view; public SlicingFromSinkJob(String name, LapseView view) { super(name); this.view = view; setProperty(IProgressConstants.ACTION_PROPERTY, new Action("Pop up a dialog") { public void run() { MessageDialog.openInformation(getSite().getShell(), "Goto Action", "The job can have an action associated with it"); } }); } public void setSink(ASTNode sink) { this.sink = sink; } public ASTNode getSink() { return sink; } public IResource getResource() { return resource; } public void setResource(IResource resource) { if(resource == null) { throw new RuntimeException("Setting the resource to NULL"); } this.resource = resource; } public CompilationUnit getUnit() { return unit; } public void setUnit(CompilationUnit unit) { if(unit == null) { throw new RuntimeException("Setting the unit to NULL"); } this.unit = unit; } protected IStatus run(IProgressMonitor monitor) { IRunnableWithProgress runnable = new IRunnableWithProgress() { public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException { try { updateSlice(sink, unit, resource, monitor); } catch (InvalidSlicingSelectionException e) { throw new RuntimeException(e); } } }; try { try { runnable.run(monitor); } catch ( RuntimeException e) { return Status.CANCEL_STATUS; } if(fContentProvider.getTotalElementCount() == 0) { Display.getDefault().syncExec(new Runnable() { public void run() { setContentDescription("Error creating a slice"); } }); return Status.CANCEL_STATUS; } else { Display.getDefault().syncExec(new Runnable() { public void run() { // now enable the actions collapseAction.setEnabled(true); expandAction.setEnabled(true); collapseAllAction.setEnabled(true); expandAllAction.setEnabled(true); //System.err.println("Setting enabled to true"); // enable actions in the menu fPullDownActions.setEnabled(true); view.getViewSite().getWorkbenchWindow().getActivePage().activate(view); }}); } return Status.OK_STATUS; } catch (InvocationTargetException e) { log(e.getMessage(), e); } catch (InterruptedException e) { log(e.getMessage(), e); } catch (RuntimeException e) { log(e.getMessage(), e); //showMessage("Invalid input state: " + e.getMessage()); } return Status.CANCEL_STATUS; } } private void makeActions() { { updateAction = new Action() { public void run() { log("Pressed the update slice button"); fSlicingJob.schedule(); } }; updateAction.setText("Compute backward slice"); updateAction.setToolTipText("Compute backward slice"); ImageDescriptor desc = JavaPluginImages.DESC_OBJS_SEARCH_READACCESS; updateAction.setImageDescriptor(desc); updateAction.setHoverImageDescriptor(desc); updateAction.setAccelerator(SWT.CTRL | SWT.ALT | 'L'); } { prefAction = new Action() { public void run() { LapseConfigurationDialog dialog = new LapseConfigurationDialog(fViewer.getControl().getShell()); //dialog.create(); dialog.open(); dialog.getReturnCode(); } }; prefAction.setText("Set slicing preferences"); prefAction.setToolTipText("Set slicing preferences"); ImageDescriptor desc = JavaPluginImages.DESC_ELCL_FILTER; prefAction.setImageDescriptor(desc); prefAction.setHoverImageDescriptor(desc); } { historyAction = new HistoryDropDownAction(this); historyAction.setText("Slicing history"); historyAction.setToolTipText("Slicing history"); ImageDescriptor desc = JavaPluginImages.DESC_OBJS_SEARCH_REF; historyAction.setImageDescriptor(desc); historyAction.setHoverImageDescriptor(desc); } { collapseAction = new Action() { public void run() { if(fExpansionLevel == MIN_EXPANSION_LEVEL) { beep(); }else { fExpansionLevel--; fViewer.collapseAll(); fViewer.expandToLevel(fExpansionLevel); fViewer.getTree().setRedraw(true); fViewer.refresh(); //System.out.println("Expansion level: " + fExpansionLevel); } } }; collapseAction.setText("Collapse one level"); collapseAction.setToolTipText("Collapse one level (Ctrl -)"); //updateAction.setAccelerator(SWT.CTRL | '-'); collapseAction.setEnabled(false); JavaPluginImages.setLocalImageDescriptors(collapseAction, "collapseall.gif"); } { expandAction = new Action() { public void run() { if(fExpansionLevel == fContentProvider.getDepth()) { beep(); }else { fExpansionLevel++; fViewer.expandToLevel(fExpansionLevel); fViewer.getTree().setRedraw(true); fViewer.refresh(); //System.out.println("Expansion level: " + fExpansionLevel); } } }; expandAction.setText("Expand one level"); expandAction.setToolTipText("Expand one level (Ctrl +)"); //updateAction.setAccelerator(SWT.CTRL | '+'); expandAction.setEnabled(false); JavaPluginImages.setLocalImageDescriptors(expandAction, "pack_empty_co.gif"); } { collapseAllAction = new Action() { public void run() { if(fExpansionLevel != MIN_EXPANSION_LEVEL) { fExpansionLevel = MIN_EXPANSION_LEVEL; fViewer.collapseAll(); fViewer.getTree().setRedraw(true); fViewer.refresh(); //System.out.println("Expansion level: " + fExpansionLevel); } else { beep(); } } }; collapseAllAction.setText("Collapse all"); collapseAllAction.setToolTipText("Collapse all"); collapseAllAction.setEnabled(false); JavaPluginImages.setLocalImageDescriptors(collapseAllAction, "collapseall.gif"); } { expandAllAction = new Action() { public void run() { if(fExpansionLevel != fContentProvider.getDepth()) { fExpansionLevel = fContentProvider.getDepth(); fViewer.expandAll(); fViewer.getTree().setRedraw(true); fViewer.refresh(); //System.out.println("Expansion level: " + fExpansionLevel); } else { beep(); } } }; expandAllAction.setText("Expand all"); expandAllAction.setToolTipText("Expand all"); expandAllAction.setEnabled(false); JavaPluginImages.setLocalImageDescriptors(expandAllAction, "pack_empty_co.gif"); } { copyAction = new LapseCopyAction(this, fClipboard, fViewer); copyAction.setText("Copy to clipboard"); copyAction.setToolTipText("Copy to clipboard"); copyAction.setEnabled(true); copyAction.setImageDescriptor(JavaPluginImages.DESC_DLCL_COPY_QUALIFIED_NAME); } { selectAllAction = new Action() { public void run() { fViewer.setSelection( new StructuredSelection( fContentProvider.getAllElements())); } }; selectAllAction.setText("Select all"); selectAllAction.setToolTipText("Select all"); selectAllAction.setEnabled(true); ISharedImages workbenchImages = JavaPlugin.getDefault().getWorkbench().getSharedImages(); selectAllAction.setImageDescriptor(workbenchImages.getImageDescriptor(ISharedImages.IMG_TOOL_PASTE)); } log("Done making actions. Adding them to the bars."); contributeToActionBars(); } private void setStatusBarMessage(String message, boolean isError) { IEditorStatusLine statusLine= (IEditorStatusLine) fEditor.getAdapter(IEditorStatusLine.class); if (statusLine != null) { statusLine.setMessage(false, message, JavaPluginImages.get(JavaPluginImages.IMG_MISC_PRIVATE)); }else { logError("No status line!"); } if(isError) { beep(); } } private void setStatusBarMessage(String message) { setStatusBarMessage(message, false); } private void beep() { if(fEditor != null) { fEditor.getSite().getShell().getDisplay().beep(); } } private void hookViewerEvents() { // double-click fViewer.addDoubleClickListener(new IDoubleClickListener() { public void doubleClick(DoubleClickEvent event) { // handle double-click in the pane if(doubleClickAction == null) { doubleClickAction = new Action() { public void run() { //System.err.println("Detected a double-click"); ISelection selection = fViewer.getSelection(); HistoryDefinitionLocation dl = (HistoryDefinitionLocation) ((IStructuredSelection) selection).getFirstElement(); if(dl.getLineNumber() == DefinitionLocation.INVALID_SOURCE_LINE) { setStatusBarMessage("Invalid location", true); // invalid location -- get out of here return; } ITextEditor editor = null; try { editor = (ITextEditor) EditorUtility.openInEditor(dl.getResource()); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } if(editor == null) { JavaPlugin.logErrorMessage("Can't open an editor for " + dl.getASTNode()); return; } editor.selectAndReveal( dl.getASTNode().getStartPosition(), dl.getASTNode().getLength()); // activate the new window if necessary if(editor != fEditor) { try { // reset the input setInput(editor); // reactivate the editor editor.getSite().getPage().activate(editor); log("Activated " + editor.getTitle()); } catch (CoreException e) { log(e.getMessage(), e); return; } } setStatusBarMessage("Opened " + dl.getResource().getName() + ":" + dl.getLineNumber() + " -- " + dl.toString()); } }; } doubleClickAction.run(); } }); fViewer.addSelectionChangedListener(new ISelectionChangedListener() { public void selectionChanged(SelectionChangedEvent event) { IStructuredSelection sel = (IStructuredSelection) event.getSelection(); HistoryDefinitionLocation match = (HistoryDefinitionLocation) sel.getFirstElement(); if(match != null) { IStatusLineManager slManager = getViewSite().getActionBars().getStatusLineManager(); slManager.setMessage(match.toString()); } } }); } private void showMessage(String message) { MessageDialog.openInformation(fViewer.getControl().getShell(), "Provenance Tracker", message); } public void setFocus() { fViewer.getControl().forceFocus(); } public static IOpenable getJavaInput(IEditorPart part) { IEditorInput editorInput= part.getEditorInput(); if (editorInput != null) { IJavaElement input= (IJavaElement)editorInput.getAdapter(IJavaElement.class); if (input instanceof IOpenable) { return (IOpenable) input; } } return null; } static VariableDeclaration name2decl(SimpleName sn, CompilationUnit cu, IResource resource){ DeclarationInfoManager.DeclarationInfo info = DeclarationInfoManager.retrieveDeclarationInfo(cu); //System.out.println(info); VariableDeclaration decl = info.getVariableDeclaration(sn); if(decl == null) { logError("decl == null for " + sn); } return decl; } static boolean isFinal(SimpleName sn, CompilationUnit cu, IResource resource){ DeclarationInfoManager.DeclarationInfo info = DeclarationInfoManager.retrieveDeclarationInfo(cu); return info.isFinal(sn); } /** * Tests whether a given expression is a String contant. * */ public static boolean isStringContant(Expression arg, CompilationUnit cu, IResource resource) { if(arg instanceof StringLiteral) { return true; } else // if(arg instanceof SimpleName) { // // find out if the name is final... // } else if(arg instanceof InfixExpression) { InfixExpression infixExpr = (InfixExpression) arg; if(!isStringContant(infixExpr.getLeftOperand(), cu, resource)) return false; if(!isStringContant(infixExpr.getRightOperand(), cu, resource)) return false; for(Iterator iter2 = infixExpr.extendedOperands().iterator(); iter2.hasNext(); ) { if(!isStringContant((Expression) iter2.next(), cu, resource)) { return false; } } return true; } // TODO: add final/const return false; } void processNodeSelection(SimpleName sn, HistoryDefinitionLocation defl, CompilationUnit cu, IResource resource, IProgressMonitor monitor){ //_cp.addElement("Visited node " + sn.toString()); if(name2decl(sn, cu, resource) == null) { // only deal with variables logError("No definition for " + sn + " is found"); return; } //Name otherName = decl.getName(); //String var = Name.getFullyQualifiedName(); //fContentProvider.clearElements(); // make the root of the slicing tree HistoryDefinitionLocation dl = new HistoryDefinitionLocation( sn.toString(), (IFile)resource, cu.getLineNumber(sn.getStartPosition()), sn, defl, HistoryDefinitionLocation.INITIAL); if(defl==null){ setCurrentInput(dl); addHistoryEntry(dl); fContentProvider.addElement(dl); } processDecl(sn, cu, resource, dl, new LinkedList<MethodInvocation>(), monitor); //showMessage("Got " + _cp.getElementsCount() + " in the list"); //fViewer.setSelection(new StructuredSelection(covering)); } private void processDecl(SimpleName name, CompilationUnit cu, IResource resource, HistoryDefinitionLocation parent, LinkedList<MethodInvocation> stack, IProgressMonitor monitor) { VariableDeclaration decl = name2decl(name, cu, resource); // if(TRACE) System.out.print('.'); if(monitor.isCanceled()) { // check if the search has been cancelled return; } //monitor.setTaskName("Processing " + name + " in " + resource.getName/*getFullPath*/()); monitor.subTask("Processing " + name + " in " + resource.getName/*getFullPath*/()); if(decl == null) { logError( "decl: " + decl + " on line " + (decl == null ? DefinitionLocation.INVALID_SOURCE_LINE : fRoot.getLineNumber(decl.getStartPosition())) + " and name: " + name + "(" + name.hashCode() + ") on line " + fRoot.getLineNumber(name.getStartPosition())); // the case of no declaration found -- add a question mark /*HistoryDefinitionLocation dl = */new HistoryDefinitionLocation( name.toString(), null, // this will be ignored DefinitionLocation.INVALID_SOURCE_LINE, name, parent, HistoryDefinitionLocation.UNDEFINED); //System.out.println("Case 1"); } else if(decl.getParent() instanceof MethodDeclaration){ // the case of a parameter -- add the actuals and recurse //showMessage(decl.toString() + " is a parameter of " + parent.getClass()); MethodDeclaration methodDecl = (MethodDeclaration) decl.getParent(); IMethod method = new MethodFinder((IFile) resource).convertMethodDecl2IMethod(methodDecl); if(method == null) { JavaPlugin.logErrorMessage("Internal error: No method found for " + methodDecl); return; } HistoryDefinitionLocation paramDL = new HistoryDefinitionLocation( decl.toString(), resource, cu.getLineNumber(decl.getStartPosition()), decl, parent, HistoryDefinitionLocation.FORMAL_PARAMETER); if(!registerExpansion(paramDL)) { // recursion detected here return; } Expression onlyCall = (Expression) (stack.isEmpty() ? null : stack.getLast()); log("Looking for calls from " + onlyCall); Collection/*<ExpressionUnitPair>*/ c = CallerFinder.getActualsForFormal(method, name, onlyCall, monitor, null); if(c.isEmpty()){ logError( "No suitable actual arguments for formal argument " + name + " of " + method.getElementName() + " at " + resource.getName() + " found"); } else for (Iterator iter = c.iterator(); iter.hasNext();) { Utils.ExpressionUnitPair eup = (Utils.ExpressionUnitPair) iter.next(); Expression e = eup.getExpression(); CompilationUnit nextCU = eup.getCompilationUnit(); IResource nextResource = eup.getResource(); processExpression(paramDL, e, nextCU, nextResource, stack, monitor, HistoryDefinitionLocation.CALL_ARG,false); } //System.out.println("Case 2"); } else { /** * The case of a declaration -- look at the right hand side. * */ Object obj = (decl.getParent() instanceof VariableDeclaration) ? decl.getParent() : decl; String desc = obj.toString(); int type = (decl.getParent() instanceof FieldDeclaration) ? HistoryDefinitionLocation.FIELD : HistoryDefinitionLocation.DECLARATION; // the case of a regilar declaration being found HistoryDefinitionLocation dl = new HistoryDefinitionLocation( desc, resource, cu.getLineNumber(decl.getStartPosition()), decl, parent, type); Expression e = decl.getInitializer(); if(e != null) { // TODO: add processing of the RHS of the declaration processExpression(dl, e, cu, resource, stack, monitor, HistoryDefinitionLocation.COPY,false); } //System.out.println("Case 3"); } } protected void processExpression(HistoryDefinitionLocation parent, Expression e, CompilationUnit cu, IResource resource, LinkedList<MethodInvocation> stack, IProgressMonitor monitor, int defaultType, boolean first) { log("In processExpression with expr=" + e); monitor.subTask("Processing " + e.toString()); SimpleName nextName = CallerFinder.SlicingUtils.getVariable(e); if(nextName != null) { /** * Follow a simple assignment. * */ HistoryDefinitionLocation actualDL = new HistoryDefinitionLocation( nextName/*.getParent()*/.toString(), resource, cu.getLineNumber(nextName.getStartPosition()), nextName, parent, defaultType); //showMessage("Type " + nextName.getParent().getClass()); // marks actualDL as recursive if necessary if(registerExpansion(actualDL)) { processDecl(nextName, cu, resource, actualDL, stack, monitor); } } else if(e instanceof MethodInvocation) { /** * Go back through a method invocation. * */ MethodInvocation mi = (MethodInvocation) e; SimpleName methodName = mi.getName(); log("Going back through callee " + methodName.toString()); HistoryDefinitionLocation callDL; if(!first){ callDL= new HistoryDefinitionLocation( mi.toString(), resource, cu.getLineNumber(mi.getStartPosition()), mi, parent, HistoryDefinitionLocation.CALL); } else{ callDL=parent; } if(registerExpansion(callDL)) { Collection/*<MethodDeclarationUnitPair>*/ callees = CallerFinder.findCallees(monitor, methodName.toString(), null, false); if(callees.isEmpty()) { if(SinkView.isDerivationName(methodName.getFullyQualifiedName())) { HistoryDefinitionLocation dl = new HistoryDefinitionLocation( mi.getExpression().toString(), resource, cu.getLineNumber(mi.getStartPosition()), mi.getExpression(), callDL, HistoryDefinitionLocation.DERIVATION); if(registerExpansion(dl)) { Expression expr = mi.getExpression(); if(expr != null) { // Recurse on the returned expression processExpression(dl, expr, cu, resource, stack, monitor, HistoryDefinitionLocation.DERIVATION,false); } } } else{ logError("No suitable callees for " + methodName + " in " + resource + " found"); System.out.println("No suitable callees for " + methodName + " in " + resource + " found"); } } else for (Iterator iter = callees.iterator(); iter.hasNext();) { Utils.MethodDeclarationUnitPair element = (Utils.MethodDeclarationUnitPair) iter.next(); MethodDeclaration methodDeclaration = element.getMethod(); CompilationUnit nextCU = element.getCompilationUnit(); IResource nextResource = element.getResource(); if(methodDeclaration != null){ if(!LapsePlugin.FOLLOW_INTO_FUNCTIONS) { /*HistoryDefinitionLocation dl = */new HistoryDefinitionLocation( "Method " + methodDeclaration.getName().getFullyQualifiedName(), nextResource, nextCU.getLineNumber(e.getStartPosition()), methodDeclaration, callDL, HistoryDefinitionLocation.RETURN); } else { Collection/*<ReturnStatement>*/ returns = CallerFinder.findReturns(monitor, methodDeclaration, null); for (Iterator iter2 = returns.iterator(); iter2.hasNext();) { ReturnStatement returnStmt = (ReturnStatement) iter2.next(); HistoryDefinitionLocation dl = new HistoryDefinitionLocation( returnStmt.toString(), nextResource, nextCU.getLineNumber(returnStmt.getStartPosition()), returnStmt, callDL, HistoryDefinitionLocation.RETURN); if(registerExpansion(dl)) { Expression expr = returnStmt.getExpression(); if(expr != null) { // Recurse on the returned expression stack.addLast(mi); processExpression(dl, expr, nextCU, nextResource, stack, monitor, HistoryDefinitionLocation.COPY,false); stack.removeLast(); } } } } } else{ //check if it is a derivtion expression if(SinkView.isDerivationName(methodName.getFullyQualifiedName())) { processExpression(callDL, mi.getExpression(), cu, resource, stack, monitor, HistoryDefinitionLocation.DERIVATION,false); break; } } } } } else if ( e instanceof InfixExpression && ( ((InfixExpression)e).getOperator() == InfixExpression.Operator.PLUS ) ) { /** * Follow arguments of a string concatenation. * */ InfixExpression ie = (InfixExpression) e; Expression leftExpr = ie.getLeftOperand(); Expression rightExpr = ie.getRightOperand(); HistoryDefinitionLocation concatDL; if(!first){ concatDL = new HistoryDefinitionLocation( e.toString(), resource, cu.getLineNumber(e.getStartPosition()), e, parent, HistoryDefinitionLocation.STRING_CONCAT); } else concatDL=parent; if(registerExpansion(concatDL)) { processExpression(concatDL, leftExpr, cu, resource, stack, monitor, HistoryDefinitionLocation.COPY,false); processExpression(concatDL, rightExpr, cu, resource, stack, monitor, HistoryDefinitionLocation.COPY,false); if(ie.extendedOperands() != null) { for(Iterator iter = ie.extendedOperands().iterator(); iter.hasNext(); ) { Expression ext_e = (Expression) iter.next(); processExpression(concatDL, ext_e, cu, resource, stack, monitor, HistoryDefinitionLocation.COPY,false); } } } } else if(e instanceof ParenthesizedExpression){ ParenthesizedExpression ex=(ParenthesizedExpression)e; processExpression(parent, ex.getExpression(), cu, resource, stack, monitor, HistoryDefinitionLocation.UNDEFINED,false); } else if(e instanceof CastExpression){ CastExpression cex=(CastExpression)e; processExpression(parent, cex.getExpression(), cu, resource, stack, monitor, HistoryDefinitionLocation.UNDEFINED,false); } else if(e instanceof ArrayAccess) { ArrayAccess ae=(ArrayAccess)e; HistoryDefinitionLocation arrAccess; if(!first){ arrAccess= new HistoryDefinitionLocation( e.toString(), resource, cu.getLineNumber(e.getStartPosition()), e, parent, HistoryDefinitionLocation.ARRAYACCESS); } else arrAccess=parent; if(registerExpansion(arrAccess)) processExpression(arrAccess, ae.getArray(), cu, resource, stack, monitor, HistoryDefinitionLocation.ARRAYACCESS,false); } else if(e instanceof ClassInstanceCreation) { ClassInstanceCreation c=(ClassInstanceCreation)e; HistoryDefinitionLocation cc; if(!first){ cc= new HistoryDefinitionLocation( e.toString(), resource, cu.getLineNumber(e.getStartPosition()), e, parent, HistoryDefinitionLocation.CLASS_INSTANCE_CREATION); }else cc=parent; String aux=(c.getType()).toString(); if(SinkView.isDerivationName(aux)){ for(Object arg:c.arguments()){ if(registerExpansion(cc)) processExpression(cc,(Expression)arg,cu,resource,stack,monitor,HistoryDefinitionLocation.DERIVATION,false); } } } else { /** * Some other expression. */ /*HistoryDefinitionLocation dl = */new HistoryDefinitionLocation( e.toString(), resource, cu.getLineNumber(e.getStartPosition()), e, parent, getExpressionType(e, cu, resource)); } } private int getExpressionType(Expression expr, CompilationUnit cu, IResource resource) { if(isStringContant(expr, cu, resource)) { return HistoryDefinitionLocation.STRING_CONSTANT; }else if(expr instanceof NullLiteral){ return HistoryDefinitionLocation.NULL; } else { return HistoryDefinitionLocation.UNDEFINED; } } /** * @return false means that propagation should be stopped. * */ private boolean registerExpansion(HistoryDefinitionLocation dl) { /** * Recursion point is here. Before we recurse, we want to make sure we are not entering a * potentially infinite loop, so, here goes an occurs check. * */ if(dl.containsAncestor(dl.getASTNode())){ dl.setRecursive(true); logError("Recursion detected for " + dl.getASTNode()); return false; } if ( dl.getDepth() >= LapsePlugin.getMaxCallDepth()) { dl.setMaxLevel(true); logError("Depth of " + LapsePlugin.getMaxCallDepth() + " exceeded"); return false; } //System.out.println("Recursing on " + dl.getASTNode() + " depth = " + dl.getDepth()); return true; } protected void refresh() { Display.getDefault().syncExec(new Runnable() { public void run() { setContentDescription("Created a slice with " + fContentProvider.getLeafElementCount() + " leaf element(s) and " + fContentProvider.getTotalElementCount() + " element(s) " + "located in " + fContentProvider.getFileCount() + " file(s) with " + fContentProvider.getTruncatedElementCount() + " element(s) truncated " + "with a maximum depth of " + fContentProvider.getDepth() + ". " + "Using a " + (fContentProvider instanceof ProvenanceContentProvider.FlatViewContentProvider ? "flat" : "hierarchical") + " viewer."); fViewer.refresh(); fViewer.expandAll(); fExpansionLevel = fContentProvider.getDepth(); ISelection selection = fContentProvider.getFirstElement(); if(selection != null) { fViewer.setSelection(selection, true); } } }); } public void setInput(ITextEditor editor) throws CoreException { if (fEditor != null) { uninstallModificationListener(); } fEditor = null; fRoot = null; if(editor == null) { logError("editor is set to null"); return; } IOpenable openable = getJavaInput(editor); if (openable == null) { throw new RuntimeException("Editor not showing a classfile"); } // reset the fields fOpenable = openable; fRoot = internalSetInput(openable); fEditor = editor; installModificationListener(); } private CompilationUnit internalSetInput(IOpenable input) throws CoreException { IBuffer buffer = input.getBuffer(); if (buffer == null) { JavaPlugin.logErrorMessage("Input has no buffer"); //$NON-NLS-1$ } if (input instanceof ICompilationUnit) { fParser.setSource((ICompilationUnit) input); } else { fParser.setSource((IClassFile) input); } try { CompilationUnit root = (CompilationUnit) fParser.createAST(null); log("Recomputed the AST for " + buffer.getUnderlyingResource().getName()); if (root == null) { JavaPlugin.logErrorMessage("Could not create AST"); //$NON-NLS-1$ } return root; } catch (RuntimeException e) { JavaPlugin.logErrorMessage("Could not create AST:\n" + e.getMessage()); //$NON-NLS-1$ return null; } } //--------------------------- Utilities --------------------------- private void installModificationListener() { fCurrentDocument = fEditor.getDocumentProvider().getDocument(fEditor.getEditorInput()); fCurrentDocument.addDocumentListener(fSuperListener); } void uninstallModificationListener() { if (fCurrentDocument != null) { fCurrentDocument.removeDocumentListener(fSuperListener); fCurrentDocument = null; } } void updateSlice(CompilationUnit unit, IResource resource, IProgressMonitor monitor) throws InvalidSlicingSelectionException { final NodeFinder finder = new NodeFinder(); Display.getDefault().syncExec(new Runnable() { public void run() { try { if(fEditor == null) { JavaPlugin.logErrorMessage("fEditor == null, can't compute the slice"); return; } ISelectionProvider selProv = fEditor.getSelectionProvider(); if(selProv == null) { throw new InvalidSlicingSelectionException("No selection provider. Something is wrong."); } ISelection selection = selProv.getSelection(); if(selection == null) { throw new InvalidSlicingSelectionException("No selection detected. Please select an identifier."); } if(! (selection instanceof ITextSelection) ) { throw new InvalidSlicingSelectionException("Selection type is not valid. Please select an identifier in a Java text editor."); } ITextSelection textSelection = (ITextSelection) selection; log("Working with " + textSelection.getText() + " in " + fEditor.getTitle()); /*if(textSelection.getLength() == 0) { throw new InvalidSlicingSelectionException("The selection is empty. Please select an identifier."); }*/ if(fRoot == null) { throw new InvalidSlicingSelectionException("No current document for the plugin. Something is wrong."); } finder.setOffset(textSelection.getOffset()); finder.setLength(textSelection.getLength()); fRoot.accept(finder); } catch (InvalidSlicingSelectionException e) { throw new RuntimeException(e); } } }); // found the relevant ASTNode ASTNode covering = finder.getCoveringNode(); updateSlice(covering, unit, resource, monitor); // restore editor position // TODO: //fEditor.setHighlightRange(textSelection.getOffset(), textSelection.getLength(), true); } void updateSlice(final ASTNode covering, CompilationUnit unit, IResource resource, IProgressMonitor monitor) throws InvalidSlicingSelectionException { log("Working with " + covering); if(covering instanceof SimpleName) { String initialLocation = resource.getFullPath().toOSString() + ":" + unit.getLineNumber(covering.getStartPosition()); monitor.setTaskName("Computing a backwards slice for " + covering + " at " + (initialLocation != null? initialLocation : "") ); monitor.beginTask("Started the computation", 100); processNodeSelection((SimpleName)covering,null,unit, resource, monitor); //String line = "sn: " + sn + ", var: " + var + ", line: " + fRoot.lineNumber(decl.getStartPosition()); // refresh the screen refresh(); // finish the job monitor.done(); } else if(covering instanceof InfixExpression){ InfixExpression infixExpr = (InfixExpression) covering; HistoryDefinitionLocation dl = new HistoryDefinitionLocation( infixExpr.toString(), (IFile)resource, unit.getLineNumber(0), covering,null, HistoryDefinitionLocation.STRING_CONCAT); processExpression(dl, infixExpr, unit, resource, new LinkedList<MethodInvocation>(), monitor, HistoryDefinitionLocation.INITIAL,true); setCurrentInput(dl); addHistoryEntry(dl); fContentProvider.addElement(dl); refresh(); } else if(covering instanceof ArrayAccess) { ArrayAccess ae=(ArrayAccess)covering; HistoryDefinitionLocation dl = new HistoryDefinitionLocation( ae.toString(), (IFile)resource, unit.getLineNumber(0), covering,null, HistoryDefinitionLocation.ARRAYACCESS); processExpression(dl, ae, unit, resource, new LinkedList<MethodInvocation>(), monitor, HistoryDefinitionLocation.INITIAL,true); setCurrentInput(dl); addHistoryEntry(dl); fContentProvider.addElement(dl); refresh(); } else if(covering instanceof CastExpression) { CastExpression ae=(CastExpression)covering; HistoryDefinitionLocation dl = new HistoryDefinitionLocation( ae.toString(), (IFile)resource, unit.getLineNumber(0), covering,null, HistoryDefinitionLocation.INITIAL); processExpression(dl, ae, unit, resource, new LinkedList<MethodInvocation>(), monitor, HistoryDefinitionLocation.CALL,true); setCurrentInput(dl); addHistoryEntry(dl); fContentProvider.addElement(dl); refresh(); } else if(covering instanceof ClassInstanceCreation) { ClassInstanceCreation ae=(ClassInstanceCreation)covering; HistoryDefinitionLocation dl = new HistoryDefinitionLocation( ae.toString(), (IFile)resource, unit.getLineNumber(0), covering,null, HistoryDefinitionLocation.CLASS_INSTANCE_CREATION); processExpression(dl, ae, unit, resource, new LinkedList<MethodInvocation>(), monitor, HistoryDefinitionLocation.CALL,true); setCurrentInput(dl); addHistoryEntry(dl); fContentProvider.addElement(dl); refresh(); } else if(covering instanceof MethodInvocation) { MethodInvocation mi=(MethodInvocation)covering; HistoryDefinitionLocation dl = new HistoryDefinitionLocation( mi.toString(), (IFile)resource, unit.getLineNumber(0), covering,null, HistoryDefinitionLocation.CALL); processExpression(dl, mi, unit, resource, new LinkedList<MethodInvocation>(), monitor, HistoryDefinitionLocation.CALL,true); setCurrentInput(dl); addHistoryEntry(dl); fContentProvider.addElement(dl); refresh(); } else{ // do nothing... //throw new InvalidSlicingSelectionException Display.getDefault().syncExec(new Runnable() { public void run() { showMessage("No valid identifier corresponds to " + covering); }} ); } } public boolean isFlatLayout() { return fContentProvider instanceof ProvenanceContentProvider.FlatViewContentProvider; } void toggleViewer() { if(fContentProvider == null) { JavaPlugin.logErrorMessage("In switchViewer with null"); return; } // do the switch fContentProvider = fContentProvider.switchType(); fViewer.setContentProvider(fContentProvider); //System.out.println("Switched the viewer type to " + fContentProvider); refresh(); } //-------------------------------------------- History --------------------------------------------- private LinkedList<HistoryDefinitionLocation> fInputHistory = new LinkedList<HistoryDefinitionLocation>(); // private HistoryDefinitionLocation fCurrentInput; /** * Adds the entry if new. Inserted at the beginning of the history entries list. */ private void addHistoryEntry(HistoryDefinitionLocation entry) { HistoryDefinitionLocation oldEntry = getHistoryEntry(entry); if (oldEntry != null) { fInputHistory.remove(oldEntry); } fInputHistory.add(0, entry); historyAction.setEnabled(true); } private HistoryDefinitionLocation getHistoryEntry(HistoryDefinitionLocation entry) { for (Iterator iter = fInputHistory.iterator(); iter.hasNext();) { HistoryDefinitionLocation element = (HistoryDefinitionLocation) iter.next(); if(element.getASTNode() == entry.getASTNode()) { return element; } } return null; } private void updateHistoryEntries() { for (int i = fInputHistory.size() - 1; i >= 0; i--) { HistoryDefinitionLocation type = fInputHistory.get(i); } historyAction.setEnabled(!fInputHistory.isEmpty()); } /** * Goes to the selected entry, without updating the order of history entries. */ public void gotoHistoryEntry(HistoryDefinitionLocation entry) { if (fInputHistory.contains(entry)) { setCurrentInput(entry); // TODO refresh(); } } /** * Gets all history entries. */ public HistoryDefinitionLocation[] getHistoryEntries() { if (fInputHistory.size() > 0) { updateHistoryEntries(); } return (HistoryDefinitionLocation[]) fInputHistory.toArray( new HistoryDefinitionLocation[fInputHistory.size()]); } /** * Sets the history entries */ public void setHistoryEntries(HistoryDefinitionLocation[] elems) { fInputHistory.clear(); for (int i= 0; i < elems.length; i++) { fInputHistory.add(elems[i]); } updateHistoryEntries(); } public HistoryDefinitionLocation getCurrentInput() { return fContentProvider.getCurrentInput(); } public void setCurrentInput(HistoryDefinitionLocation currentInput) { fContentProvider.setCurrentInput(currentInput); //if(lastVariable) refresh(); } public SlicingFromSinkJob getSinkSlicingJob() { return this.fSlicingFromSinkJob; } private static void log(String message, Throwable e) { LapsePlugin.trace(LapsePlugin.ALL_DEBUG, "Lapse view: " + message, e); } private static void logError(String message) { log(message, new Throwable()); } private static void log(String message) { log(message, null); } } //Tree Viewer for Provenance Tracker class LocationViewer extends TreeViewer { LocationViewer(Composite parent) { super(new Tree(parent, SWT.MULTI));//For Multiselection behaviour in lists or text fields // setAutoExpandLevel(ALL_LEVELS); } /** * Attaches a contextmenu listener to the tree */ void initContextMenu(IMenuListener menuListener, String popupId, IWorkbenchPartSite viewSite) { MenuManager menuMgr= new MenuManager(); menuMgr.setRemoveAllWhenShown(true); menuMgr.addMenuListener(menuListener); Menu menu= menuMgr.createContextMenu(getControl()); getControl().setMenu(menu); viewSite.registerContextMenu(popupId, menuMgr, this); } } class InvalidSlicingSelectionException extends Exception { /** * */ private static final long serialVersionUID = 1L; InvalidSlicingSelectionException(String message){ super(message); } } class LapseCopyAction extends Action { private static final char INDENTATION= '\t'; private LapseView fView; private LocationViewer fViewer; private final Clipboard fClipboard; public LapseCopyAction(LapseView view, Clipboard clipboard, LocationViewer viewer) { super("LapseCopyAction");//we create the copy action with the name given Assert.isNotNull(clipboard); //WorkbenchHelp.setHelp(this, IJavaHelpContextIds.CALL_HIERARCHY_COPY_ACTION); fView= view; fClipboard= clipboard; fViewer= viewer; } public void run() { StringBuffer buf= new StringBuffer(); addCalls(fViewer.getTree().getSelection()[0], 0, buf);//we get the node selected in the tree TextTransfer plainTextTransfer = TextTransfer.getInstance();//for converting plain text in a String into Platform specific representation try{ fClipboard.setContents( new String[]{ convertLineTerminators(buf.toString()) }, new Transfer[]{ plainTextTransfer }); } catch (SWTError e){ if (e.code != DND.ERROR_CANNOT_SET_CLIPBOARD) throw e; if (MessageDialog.openQuestion(fView.getViewSite().getShell(), ("CopyCallHierarchyAction.problem"), ("CopyCallHierarchyAction.clipboard_busy")) ) { run(); } } } private void addCalls(TreeItem item, int indent, StringBuffer buf) { for (int i= 0; i < indent; i++) { buf.append(INDENTATION); } buf.append(item.getText()); buf.append('\n'); if (item.getExpanded()) { TreeItem[] items= item.getItems(); for (int i= 0; i < items.length; i++) { addCalls(items[i], indent + 1, buf); } } } private String convertLineTerminators(String in) { StringWriter stringWriter = new StringWriter(); PrintWriter printWriter = new PrintWriter(stringWriter); StringReader stringReader = new StringReader(in); BufferedReader bufferedReader = new BufferedReader(stringReader); String line; try { while ((line= bufferedReader.readLine()) != null) { printWriter.println(line); } } catch (IOException e) { return in; // return the call hierarchy unfiltered } return stringWriter.toString(); } }