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