6741cba30886bb513945480392fdf05d65af275e
[phpeclipse.git] / net.sourceforge.phpeclipse / src / net / sourceforge / phpdt / internal / ui / viewsupport / SelectionListenerWithASTManager.java
1 /*******************************************************************************
2  * Copyright (c) 2000, 2005 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Eclipse Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/epl-v10.html
7  *
8  * Contributors:
9  *     IBM Corporation - initial API and implementation
10  *******************************************************************************/
11 package net.sourceforge.phpdt.internal.ui.viewsupport;
12
13 import java.util.HashMap;
14 import java.util.Map;
15
16 import net.sourceforge.phpdt.core.IJavaElement;
17
18 import org.eclipse.core.runtime.IProgressMonitor;
19 import org.eclipse.core.runtime.IStatus;
20 import org.eclipse.core.runtime.NullProgressMonitor;
21 import org.eclipse.core.runtime.OperationCanceledException;
22 import org.eclipse.core.runtime.Status;
23 import org.eclipse.core.runtime.jobs.Job;
24 import org.eclipse.jface.text.ITextSelection;
25 import org.eclipse.jface.util.ListenerList;
26 import org.eclipse.jface.viewers.IPostSelectionProvider;
27 import org.eclipse.jface.viewers.ISelection;
28 import org.eclipse.jface.viewers.ISelectionChangedListener;
29 import org.eclipse.jface.viewers.ISelectionProvider;
30 import org.eclipse.jface.viewers.SelectionChangedEvent;
31 import org.eclipse.ui.IEditorInput;
32 import org.eclipse.ui.texteditor.ITextEditor;
33
34 /**
35  * Infrastructure to share an AST for editor post selection listeners.
36  */
37 public class SelectionListenerWithASTManager {
38
39         private static SelectionListenerWithASTManager fgDefault;
40
41         /**
42          * @return Returns the default manager instance.
43          */
44         public static SelectionListenerWithASTManager getDefault() {
45                 if (fgDefault == null) {
46                         fgDefault = new SelectionListenerWithASTManager();
47                 }
48                 return fgDefault;
49         }
50
51         private final static class PartListenerGroup {
52                 private ITextEditor fPart;
53
54                 private ISelectionChangedListener fSelectionListener,
55                                 fPostSelectionListener;
56
57                 private Job fCurrentJob;
58
59                 private ListenerList fAstListeners;
60
61                 /**
62                  * Lock to avoid having more than one calculateAndInform job in
63                  * parallel. Only jobs may synchronize on this as otherwise deadlocks
64                  * are possible.
65                  */
66                 private final Object fJobLock = new Object();
67
68                 public PartListenerGroup(ITextEditor part) {
69                         fPart = part;
70                         fCurrentJob = null;
71                         fAstListeners = new ListenerList();
72
73                         fSelectionListener = new ISelectionChangedListener() {
74                                 public void selectionChanged(SelectionChangedEvent event) {
75                                         ISelection selection = event.getSelection();
76                                         if (selection instanceof ITextSelection) {
77                                                 fireSelectionChanged((ITextSelection) selection);
78                                         }
79                                 }
80                         };
81
82                         fPostSelectionListener = new ISelectionChangedListener() {
83                                 public void selectionChanged(SelectionChangedEvent event) {
84                                         ISelection selection = event.getSelection();
85                                         if (selection instanceof ITextSelection) {
86                                                 firePostSelectionChanged((ITextSelection) selection);
87                                         }
88                                 }
89                         };
90                 }
91
92                 public boolean isEmpty() {
93                         return fAstListeners.isEmpty();
94                 }
95
96                 public void install(ISelectionListenerWithAST listener) {
97                         if (isEmpty()) {
98                                 ISelectionProvider selectionProvider = fPart
99                                                 .getSelectionProvider();
100                                 if (selectionProvider instanceof IPostSelectionProvider) {
101                                         ((IPostSelectionProvider) selectionProvider)
102                                                         .addPostSelectionChangedListener(fPostSelectionListener);
103                                         selectionProvider
104                                                         .addSelectionChangedListener(fSelectionListener);
105                                 }
106                         }
107                         fAstListeners.add(listener);
108                 }
109
110                 public void uninstall(ISelectionListenerWithAST listener) {
111                         fAstListeners.remove(listener);
112                         if (isEmpty()) {
113                                 ISelectionProvider selectionProvider = fPart
114                                                 .getSelectionProvider();
115                                 if (selectionProvider instanceof IPostSelectionProvider) {
116                                         ((IPostSelectionProvider) selectionProvider)
117                                                         .removePostSelectionChangedListener(fPostSelectionListener);
118                                         selectionProvider
119                                                         .removeSelectionChangedListener(fSelectionListener);
120                                 }
121                         }
122                 }
123
124                 public void fireSelectionChanged(final ITextSelection selection) {
125                         if (fCurrentJob != null) {
126                                 fCurrentJob.cancel();
127                         }
128                 }
129
130                 public void firePostSelectionChanged(final ITextSelection selection) {
131                         if (fCurrentJob != null) {
132                                 fCurrentJob.cancel();
133                         }
134                         final IJavaElement input = getJavaElement();
135                         if (input == null) {
136                                 return;
137                         }
138
139                         fCurrentJob = new Job("SelectionListenerWithASTManager Job") {// JavaUIMessages.SelectionListenerWithASTManager_job_title)
140                                                                                                                                                         // {
141                                 public IStatus run(IProgressMonitor monitor) {
142                                         if (monitor == null) {
143                                                 monitor = new NullProgressMonitor();
144                                         }
145                                         synchronized (fJobLock) {
146                                                 return calculateASTandInform(input, selection, monitor);
147                                         }
148                                 }
149                         };
150                         fCurrentJob.setPriority(Job.DECORATE);
151                         fCurrentJob.setSystem(true);
152                         fCurrentJob.schedule();
153                 }
154
155                 private IJavaElement getJavaElement() {
156                         IEditorInput editorInput = fPart.getEditorInput();
157                         if (editorInput != null)
158                                 return (IJavaElement) editorInput
159                                                 .getAdapter(IJavaElement.class);
160
161                         return null;
162                 }
163
164                 protected IStatus calculateASTandInform(IJavaElement input,
165                                 ITextSelection selection, IProgressMonitor monitor) {
166                         if (monitor.isCanceled()) {
167                                 return Status.CANCEL_STATUS;
168                         }
169                         // create AST
170                         try {
171                                 // CompilationUnit astRoot=
172                                 // PHPeclipsePlugin.getDefault().getASTProvider().getAST(input,
173                                 // ASTProvider.WAIT_ACTIVE_ONLY, monitor);
174
175                                 // if (astRoot != null && !monitor.isCanceled()) {
176                                 Object[] listeners;
177                                 synchronized (PartListenerGroup.this) {
178                                         listeners = fAstListeners.getListeners();
179                                 }
180                                 for (int i = 0; i < listeners.length; i++) {
181                                         ((ISelectionListenerWithAST) listeners[i])
182                                                         .selectionChanged(fPart, selection);// , astRoot);
183                                         if (monitor.isCanceled()) {
184                                                 return Status.CANCEL_STATUS;
185                                         }
186                                 }
187                                 return Status.OK_STATUS;
188                                 // }
189                         } catch (OperationCanceledException e) {
190                                 // thrown when cancelling the AST creation
191                         }
192                         return Status.CANCEL_STATUS;
193                 }
194         }
195
196         private Map fListenerGroups;
197
198         private SelectionListenerWithASTManager() {
199                 fListenerGroups = new HashMap();
200         }
201
202         /**
203          * Registers a selection listener for the given editor part.
204          * 
205          * @param part
206          *            The editor part to listen to.
207          * @param listener
208          *            The listener to register.
209          */
210         public void addListener(ITextEditor part, ISelectionListenerWithAST listener) {
211                 synchronized (this) {
212                         PartListenerGroup partListener = (PartListenerGroup) fListenerGroups
213                                         .get(part);
214                         if (partListener == null) {
215                                 partListener = new PartListenerGroup(part);
216                                 fListenerGroups.put(part, partListener);
217                         }
218                         partListener.install(listener);
219                 }
220         }
221
222         /**
223          * Unregisters a selection listener.
224          * 
225          * @param part
226          *            The editor part the listener was registered.
227          * @param listener
228          *            The listener to unregister.
229          */
230         public void removeListener(ITextEditor part,
231                         ISelectionListenerWithAST listener) {
232                 synchronized (this) {
233                         PartListenerGroup partListener = (PartListenerGroup) fListenerGroups
234                                         .get(part);
235                         if (partListener != null) {
236                                 partListener.uninstall(listener);
237                                 if (partListener.isEmpty()) {
238                                         fListenerGroups.remove(part);
239                                 }
240                         }
241                 }
242         }
243
244         /**
245          * Forces a selection changed event that is sent to all listeners registered
246          * to the given editor part. The event is sent from a background thread:
247          * this method call can return before the listeners are informed.
248          * 
249          * @param part
250          *            The editor part that has a changed selection
251          * @param selection
252          *            The new text selection
253          */
254         public void forceSelectionChange(ITextEditor part, ITextSelection selection) {
255                 synchronized (this) {
256                         PartListenerGroup partListener = (PartListenerGroup) fListenerGroups
257                                         .get(part);
258                         if (partListener != null) {
259                                 partListener.firePostSelectionChanged(selection);
260                         }
261                 }
262         }
263 }