001: /*******************************************************************************
002: * Copyright (c) 2000, 2007 IBM Corporation and others.
003: * All rights reserved. This program and the accompanying materials
004: * are made available under the terms of the Eclipse Public License v1.0
005: * which accompanies this distribution, and is available at
006: * http://www.eclipse.org/legal/epl-v10.html
007: *
008: * Contributors:
009: * IBM Corporation - initial API and implementation
010: *******************************************************************************/package org.eclipse.jdt.internal.ui.viewsupport;
011:
012: import java.util.HashMap;
013: import java.util.Map;
014:
015: import org.eclipse.core.runtime.IProgressMonitor;
016: import org.eclipse.core.runtime.IStatus;
017: import org.eclipse.core.runtime.ListenerList;
018: import org.eclipse.core.runtime.NullProgressMonitor;
019: import org.eclipse.core.runtime.OperationCanceledException;
020: import org.eclipse.core.runtime.Status;
021: import org.eclipse.core.runtime.jobs.Job;
022:
023: import org.eclipse.jface.viewers.ISelection;
024: import org.eclipse.jface.viewers.ISelectionChangedListener;
025: import org.eclipse.jface.viewers.ISelectionProvider;
026: import org.eclipse.jface.viewers.SelectionChangedEvent;
027:
028: import org.eclipse.jface.text.ITextSelection;
029:
030: import org.eclipse.ui.ISelectionListener;
031: import org.eclipse.ui.IWorkbenchPart;
032: import org.eclipse.ui.texteditor.ITextEditor;
033:
034: import org.eclipse.jdt.core.IJavaElement;
035: import org.eclipse.jdt.core.ITypeRoot;
036: import org.eclipse.jdt.core.dom.CompilationUnit;
037:
038: import org.eclipse.jdt.ui.SharedASTProvider;
039:
040: import org.eclipse.jdt.internal.ui.JavaUIMessages;
041: import org.eclipse.jdt.internal.ui.javaeditor.EditorUtility;
042:
043: /**
044: * Infrastructure to share an AST for editor post selection listeners.
045: */
046: public class SelectionListenerWithASTManager {
047:
048: private static SelectionListenerWithASTManager fgDefault;
049:
050: /**
051: * @return Returns the default manager instance.
052: */
053: public static SelectionListenerWithASTManager getDefault() {
054: if (fgDefault == null) {
055: fgDefault = new SelectionListenerWithASTManager();
056: }
057: return fgDefault;
058: }
059:
060: private final static class PartListenerGroup {
061: private ITextEditor fPart;
062: private ISelectionListener fPostSelectionListener;
063: private ISelectionChangedListener fSelectionListener;
064: private Job fCurrentJob;
065: private ListenerList fAstListeners;
066: /**
067: * Lock to avoid having more than one calculateAndInform job in parallel.
068: * Only jobs may synchronize on this as otherwise deadlocks are possible.
069: */
070: private final Object fJobLock = new Object();
071:
072: public PartListenerGroup(ITextEditor editorPart) {
073: fPart = editorPart;
074: fCurrentJob = null;
075: fAstListeners = new ListenerList(ListenerList.IDENTITY);
076:
077: fSelectionListener = new ISelectionChangedListener() {
078: public void selectionChanged(SelectionChangedEvent event) {
079: ISelection selection = event.getSelection();
080: if (selection instanceof ITextSelection) {
081: fireSelectionChanged((ITextSelection) selection);
082: }
083: }
084: };
085:
086: fPostSelectionListener = new ISelectionListener() {
087: public void selectionChanged(IWorkbenchPart part,
088: ISelection selection) {
089: if (part == fPart
090: && selection instanceof ITextSelection)
091: firePostSelectionChanged((ITextSelection) selection);
092: }
093: };
094: }
095:
096: public boolean isEmpty() {
097: return fAstListeners.isEmpty();
098: }
099:
100: public void install(ISelectionListenerWithAST listener) {
101: if (isEmpty()) {
102: fPart.getEditorSite().getPage()
103: .addPostSelectionListener(
104: fPostSelectionListener);
105: ISelectionProvider selectionProvider = fPart
106: .getSelectionProvider();
107: if (selectionProvider != null)
108: selectionProvider
109: .addSelectionChangedListener(fSelectionListener);
110: }
111: fAstListeners.add(listener);
112: }
113:
114: public void uninstall(ISelectionListenerWithAST listener) {
115: fAstListeners.remove(listener);
116: if (isEmpty()) {
117: fPart.getEditorSite().getPage()
118: .removePostSelectionListener(
119: fPostSelectionListener);
120: ISelectionProvider selectionProvider = fPart
121: .getSelectionProvider();
122: if (selectionProvider != null)
123: selectionProvider
124: .removeSelectionChangedListener(fSelectionListener);
125: }
126: }
127:
128: /**
129: * A selection event has occurred.
130: *
131: * @param selection the selection
132: */
133: public void fireSelectionChanged(final ITextSelection selection) {
134: if (fCurrentJob != null) {
135: fCurrentJob.cancel();
136: }
137: }
138:
139: /**
140: * A post selection event has occurred.
141: *
142: * @param selection the selection
143: */
144: public void firePostSelectionChanged(
145: final ITextSelection selection) {
146: if (fCurrentJob != null) {
147: fCurrentJob.cancel();
148: }
149: IJavaElement input = EditorUtility
150: .getEditorInputJavaElement(fPart, false);
151: if (!(input instanceof ITypeRoot)) {
152: return;
153: }
154: final ITypeRoot typeRoot = (ITypeRoot) input;
155:
156: fCurrentJob = new Job(
157: JavaUIMessages.SelectionListenerWithASTManager_job_title) {
158: public IStatus run(IProgressMonitor monitor) {
159: if (monitor == null) {
160: monitor = new NullProgressMonitor();
161: }
162: synchronized (fJobLock) {
163: return calculateASTandInform(typeRoot,
164: selection, monitor);
165: }
166: }
167: };
168: fCurrentJob.setPriority(Job.DECORATE);
169: fCurrentJob.setSystem(true);
170: fCurrentJob.schedule();
171: }
172:
173: protected final IStatus calculateASTandInform(ITypeRoot input,
174: ITextSelection selection, IProgressMonitor monitor) {
175: if (monitor.isCanceled()) {
176: return Status.CANCEL_STATUS;
177: }
178: // create AST
179: try {
180: CompilationUnit astRoot = SharedASTProvider.getAST(
181: input, SharedASTProvider.WAIT_ACTIVE_ONLY,
182: monitor);
183:
184: if (astRoot != null && !monitor.isCanceled()) {
185: Object[] listeners;
186: synchronized (PartListenerGroup.this ) {
187: listeners = fAstListeners.getListeners();
188: }
189: for (int i = 0; i < listeners.length; i++) {
190: ((ISelectionListenerWithAST) listeners[i])
191: .selectionChanged(fPart, selection,
192: astRoot);
193: if (monitor.isCanceled()) {
194: return Status.CANCEL_STATUS;
195: }
196: }
197: return Status.OK_STATUS;
198: }
199: } catch (OperationCanceledException e) {
200: // thrown when canceling the AST creation
201: }
202: return Status.CANCEL_STATUS;
203: }
204: }
205:
206: private Map fListenerGroups;
207:
208: private SelectionListenerWithASTManager() {
209: fListenerGroups = new HashMap();
210: }
211:
212: /**
213: * Registers a selection listener for the given editor part.
214: * @param part The editor part to listen to.
215: * @param listener The listener to register.
216: */
217: public void addListener(ITextEditor part,
218: ISelectionListenerWithAST listener) {
219: synchronized (this ) {
220: PartListenerGroup partListener = (PartListenerGroup) fListenerGroups
221: .get(part);
222: if (partListener == null) {
223: partListener = new PartListenerGroup(part);
224: fListenerGroups.put(part, partListener);
225: }
226: partListener.install(listener);
227: }
228: }
229:
230: /**
231: * Unregisters a selection listener.
232: * @param part The editor part the listener was registered.
233: * @param listener The listener to unregister.
234: */
235: public void removeListener(ITextEditor part,
236: ISelectionListenerWithAST listener) {
237: synchronized (this ) {
238: PartListenerGroup partListener = (PartListenerGroup) fListenerGroups
239: .get(part);
240: if (partListener != null) {
241: partListener.uninstall(listener);
242: if (partListener.isEmpty()) {
243: fListenerGroups.remove(part);
244: }
245: }
246: }
247: }
248: }
|