/******************************************************************************* * Copyright (c) 2000, 2007 IBM Corporation 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: * IBM Corporation - initial API and implementation *******************************************************************************/ package org.eclipse.debug.internal.ui; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.Map; import java.util.Set; import org.eclipse.debug.core.DebugException; import org.eclipse.debug.core.model.IDebugTarget; import org.eclipse.debug.core.model.IStackFrame; import org.eclipse.debug.core.model.IThread; import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.IRegion; import org.eclipse.jface.text.Position; import org.eclipse.jface.text.source.Annotation; import org.eclipse.jface.text.source.IAnnotationModel; import org.eclipse.ui.IEditorInput; import org.eclipse.ui.IPageListener; import org.eclipse.ui.IPartListener2; import org.eclipse.ui.IWorkbenchPage; import org.eclipse.ui.IWorkbenchPart; import org.eclipse.ui.IWorkbenchPartReference; import org.eclipse.ui.texteditor.IDocumentProvider; import org.eclipse.ui.texteditor.ITextEditor; /** * This class tracks instruction pointer contexts for all active debug targets and threads * in the current workbench. There should only ever be one instance of this class, obtained * via 'getDefault()'. */ public class InstructionPointerManager{ /** * The singleton instance of this class. */ private static InstructionPointerManager fgDefault; /** * Set containing all instruction pointer contexts this class manages */ private Set fIPCSet = new HashSet(); /** * Maps ITextEditors to the set of instruction pointer contexts that are displayed in the editor */ private Map fEditorMap = new HashMap(); /** * Part listener added to editors that contain annotations. Allows instruction pointer contexts to * be removed when the editor they are displayed in is removed. */ private IPartListener2 fPartListener; /** * Page listener added to the workbench window to remove part listeners when the page is closed. */ private IPageListener fPageListener; /** * Clients must not instantiate this class. */ private InstructionPointerManager() { } /** * Return the singleton instance of this class, creating it if necessary. */ public static InstructionPointerManager getDefault() { if (fgDefault == null) { fgDefault = new InstructionPointerManager(); } return fgDefault; } /** * Adds an instruction pointer annotation in the specified editor for the * specified stack frame. */ public void addAnnotation(ITextEditor textEditor, IStackFrame frame, Annotation annotation) { IDocumentProvider docProvider = textEditor.getDocumentProvider(); IEditorInput editorInput = textEditor.getEditorInput(); // If there is no annotation model, there's nothing more to do IAnnotationModel annModel = docProvider.getAnnotationModel(editorInput); if (annModel == null) { return; } // Create the Position object that specifies a location for the annotation Position position = null; int charStart = -1; int length = -1; try { charStart = frame.getCharStart(); length = frame.getCharEnd() - charStart; } catch (DebugException de) { } if (charStart < 0) { IDocument doc = docProvider.getDocument(editorInput); if (doc == null) { return; } try { int lineNumber = frame.getLineNumber() - 1; IRegion region = doc.getLineInformation(lineNumber); charStart = region.getOffset(); length = region.getLength(); } catch (BadLocationException ble) { return; } catch (DebugException de) { return; } } if (charStart < 0) { return; } position = new Position(charStart, length); if (frame.isTerminated()) { return; } synchronized (fIPCSet) { // Add the annotation at the position to the editor's annotation model. annModel.removeAnnotation(annotation); annModel.addAnnotation(annotation, position); // Create the instruction pointer context InstructionPointerContext ipc = new InstructionPointerContext(frame.getDebugTarget(), frame.getThread(), textEditor, annotation); // Add the IPC to the set and map Set editorIPCs = (Set)fEditorMap.get(textEditor); if (editorIPCs == null){ editorIPCs = new HashSet(); fEditorMap.put(textEditor, editorIPCs); } else { editorIPCs.remove(ipc); } editorIPCs.add(ipc); fIPCSet.remove(ipc); fIPCSet.add(ipc); // Add a listener to the editor so we can remove the IPC when the editor is closed textEditor.getSite().getPage().addPartListener(getPartListener()); textEditor.getSite().getPage().getWorkbenchWindow().addPageListener(getPageListener()); } } /** * Remove all annotations associated with the specified debug target that this class * is tracking. */ public void removeAnnotations(IDebugTarget debugTarget) { synchronized (fIPCSet) { Iterator ipcIter = fIPCSet.iterator(); while (ipcIter.hasNext()) { InstructionPointerContext currentIPC = (InstructionPointerContext) ipcIter.next(); if (currentIPC.getDebugTarget().equals(debugTarget)){ removeAnnotationFromModel(currentIPC); ipcIter.remove(); removeAnnotationFromEditorMapping(currentIPC); } } } } /** * Remove all annotations associated with the specified thread that this class * is tracking. */ public void removeAnnotations(IThread thread) { synchronized (fIPCSet) { Iterator ipcIter = fIPCSet.iterator(); while (ipcIter.hasNext()) { InstructionPointerContext currentIPC = (InstructionPointerContext) ipcIter.next(); if (currentIPC.getThread().equals(thread)){ removeAnnotationFromModel(currentIPC); ipcIter.remove(); removeAnnotationFromEditorMapping(currentIPC); } } } } /** * Remove all annotations associated with the specified editor that this class * is tracking. */ public void removeAnnotations(ITextEditor editor) { synchronized (fIPCSet) { Set editorIPCs = (Set)fEditorMap.get(editor); if (editorIPCs != null){ Iterator ipcIter = editorIPCs.iterator(); while (ipcIter.hasNext()) { InstructionPointerContext currentIPC = (InstructionPointerContext) ipcIter.next(); removeAnnotationFromModel(currentIPC); fIPCSet.remove(currentIPC); } fEditorMap.remove(editor); } } } /** * Remove the given ipc from the mapping of editors. */ private void removeAnnotationFromEditorMapping(InstructionPointerContext ipc) { Set editorIPCs = (Set)fEditorMap.get(ipc.getEditor()); if (editorIPCs != null){ editorIPCs.remove(ipc); if (editorIPCs.isEmpty()){ fEditorMap.remove(ipc.getEditor()); } } } /** * Remove the annotation from the document model. */ private void removeAnnotationFromModel(InstructionPointerContext ipc){ IDocumentProvider docProvider = ipc.getEditor().getDocumentProvider(); if (docProvider != null) { IAnnotationModel annotationModel = docProvider.getAnnotationModel(ipc.getEditor().getEditorInput()); if (annotationModel != null) { annotationModel.removeAnnotation(ipc.getAnnotation()); } } } /** * Returns the number of instruction pointers. * Used by the test suite. * * @return the number of instruction pointers * @since 3.2 */ public int getInstructionPointerCount() { return fIPCSet.size(); } /** * Returns the number of keys in the editor to IPC mapping * Used by the test suite. * * @return the number of keys in the editor mapping * @since 3.3 */ public int getEditorMappingCount() { return fEditorMap.size(); } /** * @return the page listener to add to workbench window. */ private IPageListener getPageListener(){ if (fPageListener == null){ fPageListener = new PageListener(); } return fPageListener; } /** * @return the part listener to add to editors. */ private IPartListener2 getPartListener(){ if (fPartListener == null){ fPartListener = new PartListener(); } return fPartListener; } /** * Part listener that is added to editors to track when the editor is no longer is displaying * the input containing instruction pointer annotations. */ class PartListener implements IPartListener2{ public void partActivated(IWorkbenchPartReference partRef) {} public void partDeactivated(IWorkbenchPartReference partRef) {} public void partHidden(IWorkbenchPartReference partRef) {} public void partOpened(IWorkbenchPartReference partRef) {} public void partVisible(IWorkbenchPartReference partRef) {} public void partBroughtToTop(IWorkbenchPartReference partRef) {} /* (non-Javadoc) * @see org.eclipse.ui.IPartListener2#partClosed(org.eclipse.ui.IWorkbenchPartReference) */ public void partClosed(IWorkbenchPartReference partRef) { IWorkbenchPart part = partRef.getPart(false); if (part instanceof ITextEditor){ removeAnnotations((ITextEditor)part); } } /* (non-Javadoc) * @see org.eclipse.ui.IPartListener2#partInputChanged(org.eclipse.ui.IWorkbenchPartReference) */ public void partInputChanged(IWorkbenchPartReference partRef) { IWorkbenchPart part = partRef.getPart(false); if (part instanceof ITextEditor){ removeAnnotations((ITextEditor)part); } } } /** * Page listener that is added to the workbench to remove the part listener when the page is closed. */ class PageListener implements IPageListener{ public void pageActivated(IWorkbenchPage page) {} public void pageOpened(IWorkbenchPage page) {} /* (non-Javadoc) * @see org.eclipse.ui.IPageListener#pageClosed(org.eclipse.ui.IWorkbenchPage) */ public void pageClosed(IWorkbenchPage page) { page.removePartListener(getPartListener()); page.getWorkbenchWindow().removePageListener(getPageListener()); } } }