fix #774 infinite loop in net.sourceforge.phpeclipse.builder.IdentifierIndexManager...
[phpeclipse.git] / net.sourceforge.phpeclipse / src / net / sourceforge / phpdt / internal / ui / text / java / hover / AnnotationExpandHover.java
1 /*******************************************************************************
2  * Copyright (c) 2000, 2006 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.text.java.hover;
12
13 import java.util.ArrayList;
14 import java.util.Collections;
15 import java.util.Comparator;
16 import java.util.HashMap;
17 import java.util.Iterator;
18 import java.util.List;
19 import java.util.Map;
20
21 import net.sourceforge.phpdt.internal.ui.text.java.hover.AnnotationExpansionControl.AnnotationHoverInput;
22
23 import org.eclipse.jface.text.BadLocationException;
24 import org.eclipse.jface.text.IDocument;
25 import org.eclipse.jface.text.IInformationControl;
26 import org.eclipse.jface.text.IInformationControlCreator;
27 import org.eclipse.jface.text.IInformationControlCreatorExtension;
28 import org.eclipse.jface.text.ITextViewerExtension5;
29 import org.eclipse.jface.text.Position;
30 import org.eclipse.jface.text.TextViewer;
31 import org.eclipse.jface.text.source.Annotation;
32 import org.eclipse.jface.text.source.CompositeRuler;
33 import org.eclipse.jface.text.source.IAnnotationAccess;
34 import org.eclipse.jface.text.source.IAnnotationAccessExtension;
35 import org.eclipse.jface.text.source.IAnnotationHover;
36 import org.eclipse.jface.text.source.IAnnotationHoverExtension;
37 import org.eclipse.jface.text.source.IAnnotationModel;
38 import org.eclipse.jface.text.source.ILineRange;
39 import org.eclipse.jface.text.source.ISourceViewer;
40 import org.eclipse.jface.text.source.IVerticalRulerListener;
41 import org.eclipse.jface.text.source.LineRange;
42 import org.eclipse.jface.text.source.VerticalRulerEvent;
43 import org.eclipse.jface.viewers.IDoubleClickListener;
44 import org.eclipse.swt.SWT;
45 import org.eclipse.swt.custom.StyledText;
46 import org.eclipse.swt.graphics.Point;
47 import org.eclipse.swt.widgets.Menu;
48 import org.eclipse.swt.widgets.Shell;
49
50 /**
51  * This class got moved here form Platform Text since it was not used there and
52  * caused discouraged access warnings. It will be moved down again once
53  * annotation roll-over support is provided by Platform Text.
54  * 
55  * @since 3.2
56  */
57 public class AnnotationExpandHover implements IAnnotationHover,
58                 IAnnotationHoverExtension {
59
60         private class InformationControlCreator implements
61                         IInformationControlCreator, IInformationControlCreatorExtension {
62
63                 /*
64                  * @see org.eclipse.jface.text.IInformationControlCreator#createInformationControl(org.eclipse.swt.widgets.Shell)
65                  */
66                 public IInformationControl createInformationControl(Shell parent) {
67                         return new AnnotationExpansionControl(parent, SWT.NONE,
68                                         fAnnotationAccess);
69                 }
70
71                 /*
72                  * @see org.eclipse.jface.text.IInformationControlCreatorExtension#canReuse(org.eclipse.jface.text.IInformationControl)
73                  */
74                 public boolean canReuse(IInformationControl control) {
75                         return control instanceof AnnotationExpansionControl;
76                 }
77
78                 /*
79                  * @see org.eclipse.jface.text.IInformationControlCreatorExtension#canReplace(org.eclipse.jface.text.IInformationControlCreator)
80                  */
81                 public boolean canReplace(IInformationControlCreator creator) {
82                         return creator == this;
83                 }
84         }
85
86         private class VerticalRulerListener implements IVerticalRulerListener {
87
88                 /*
89                  * @see org.eclipse.jface.text.source.IVerticalRulerListener#annotationSelected(org.eclipse.jface.text.source.VerticalRulerEvent)
90                  */
91                 public void annotationSelected(VerticalRulerEvent event) {
92                         fCompositeRuler.fireAnnotationSelected(event);
93                 }
94
95                 /*
96                  * @see org.eclipse.jface.text.source.IVerticalRulerListener#annotationDefaultSelected(org.eclipse.jface.text.source.VerticalRulerEvent)
97                  */
98                 public void annotationDefaultSelected(VerticalRulerEvent event) {
99                         fCompositeRuler.fireAnnotationDefaultSelected(event);
100                 }
101
102                 /*
103                  * @see org.eclipse.jface.text.source.IVerticalRulerListener#annotationContextMenuAboutToShow(org.eclipse.jface.text.source.VerticalRulerEvent,
104                  *      org.eclipse.swt.widgets.Menu)
105                  */
106                 public void annotationContextMenuAboutToShow(VerticalRulerEvent event,
107                                 Menu menu) {
108                         fCompositeRuler.fireAnnotationContextMenuAboutToShow(event, menu);
109                 }
110         }
111
112         private final IInformationControlCreator fgCreator = new InformationControlCreator();
113
114         protected final IVerticalRulerListener fgListener = new VerticalRulerListener();
115
116         protected CompositeRuler fCompositeRuler;
117
118         protected IDoubleClickListener fDblClickListener;
119
120         protected IAnnotationAccess fAnnotationAccess;
121
122         /**
123          * Creates a new hover instance.
124          * 
125          * @param ruler
126          * @param access
127          * @param doubleClickListener
128          */
129         public AnnotationExpandHover(CompositeRuler ruler,
130                         IAnnotationAccess access, IDoubleClickListener doubleClickListener) {
131                 fCompositeRuler = ruler;
132                 fAnnotationAccess = access;
133                 fDblClickListener = doubleClickListener;
134         }
135
136         /*
137          * @see org.eclipse.jface.text.source.IAnnotationHover#getHoverInfo(org.eclipse.jface.text.source.ISourceViewer,
138          *      int)
139          */
140         public String getHoverInfo(ISourceViewer sourceViewer, int line) {
141                 // we don't have any sensible return value as text
142                 return null;
143         }
144
145         protected Object getHoverInfoForLine(ISourceViewer viewer, int line) {
146                 IAnnotationModel model = viewer.getAnnotationModel();
147                 IDocument document = viewer.getDocument();
148
149                 if (model == null)
150                         return null;
151
152                 List exact = new ArrayList();
153                 HashMap messagesAtPosition = new HashMap();
154
155                 Iterator e = model.getAnnotationIterator();
156                 while (e.hasNext()) {
157                         Annotation annotation = (Annotation) e.next();
158                         Position position = model.getPosition(annotation);
159                         if (position == null)
160                                 continue;
161
162                         if (compareRulerLine(position, document, line) == 1) {
163                                 if (isDuplicateMessage(messagesAtPosition, position, annotation
164                                                 .getText()))
165                                         continue;
166
167                                 exact.add(annotation);
168                         }
169                 }
170
171                 if (exact.size() < 1)
172                         return null;
173
174                 sort(exact, model);
175
176                 if (exact.size() > 0)
177                         setLastRulerMouseLocation(viewer, line);
178
179                 AnnotationHoverInput input = new AnnotationHoverInput();
180                 input.fAnnotations = (Annotation[]) exact.toArray(new Annotation[0]);
181                 input.fViewer = viewer;
182                 input.fRulerInfo = fCompositeRuler;
183                 input.fAnnotationListener = fgListener;
184                 input.fDoubleClickListener = fDblClickListener;
185                 input.model = model;
186
187                 return input;
188         }
189
190         protected void sort(List exact, final IAnnotationModel model) {
191                 class AnnotationComparator implements Comparator {
192
193                         /*
194                          * @see java.util.Comparator#compare(java.lang.Object,
195                          *      java.lang.Object)
196                          */
197                         public int compare(Object o1, Object o2) {
198                                 Annotation a1 = (Annotation) o1;
199                                 Annotation a2 = (Annotation) o2;
200
201                                 Position p1 = model.getPosition(a1);
202                                 Position p2 = model.getPosition(a2);
203
204                                 // annotation order:
205                                 // primary order: by position in line
206                                 // secondary: annotation importance
207                                 if (p1.offset == p2.offset)
208                                         return getOrder(a2) - getOrder(a1);
209                                 return p1.offset - p2.offset;
210                         }
211                 }
212
213                 Collections.sort(exact, new AnnotationComparator());
214
215         }
216
217         protected int getOrder(Annotation annotation) {
218                 if (fAnnotationAccess instanceof IAnnotationAccessExtension) {
219                         IAnnotationAccessExtension extension = (IAnnotationAccessExtension) fAnnotationAccess;
220                         return extension.getLayer(annotation);
221                 }
222                 return IAnnotationAccessExtension.DEFAULT_LAYER;
223         }
224
225         protected boolean isDuplicateMessage(Map messagesAtPosition,
226                         Position position, String message) {
227                 if (message == null)
228                         return false;
229
230                 if (messagesAtPosition.containsKey(position)) {
231                         Object value = messagesAtPosition.get(position);
232                         if (message == null || message.equals(value))
233                                 return true;
234
235                         if (value instanceof List) {
236                                 List messages = (List) value;
237                                 if (messages.contains(message))
238                                         return true;
239                                 messages.add(message);
240                         } else {
241                                 ArrayList messages = new ArrayList();
242                                 messages.add(value);
243                                 messages.add(message);
244                                 messagesAtPosition.put(position, messages);
245                         }
246                 } else
247                         messagesAtPosition.put(position, message);
248                 return false;
249         }
250
251         protected void setLastRulerMouseLocation(ISourceViewer viewer, int line) {
252                 // set last mouse activity in order to get the correct context menu
253                 if (fCompositeRuler != null) {
254                         StyledText st = viewer.getTextWidget();
255                         if (st != null && !st.isDisposed()) {
256                                 if (viewer instanceof ITextViewerExtension5) {
257                                         int widgetLine = ((ITextViewerExtension5) viewer)
258                                                         .modelLine2WidgetLine(line);
259                                         Point loc = st.getLocationAtOffset(st
260                                                         .getOffsetAtLine(widgetLine));
261                                         fCompositeRuler.setLocationOfLastMouseButtonActivity(0,
262                                                         loc.y);
263                                 } else if (viewer instanceof TextViewer) {
264                                         // TODO remove once TextViewer implements the extension
265                                         int widgetLine = ((TextViewer) viewer)
266                                                         .modelLine2WidgetLine(line);
267                                         Point loc = st.getLocationAtOffset(st
268                                                         .getOffsetAtLine(widgetLine));
269                                         fCompositeRuler.setLocationOfLastMouseButtonActivity(0,
270                                                         loc.y);
271                                 }
272                         }
273                 }
274         }
275
276         /**
277          * Returns the distance to the ruler line.
278          * 
279          * @param position
280          *            the position
281          * @param document
282          *            the document
283          * @param line
284          *            the line number
285          * @return the distance to the ruler line
286          */
287         protected int compareRulerLine(Position position, IDocument document,
288                         int line) {
289
290                 if (position.getOffset() > -1 && position.getLength() > -1) {
291                         try {
292                                 int firstLine = document.getLineOfOffset(position.getOffset());
293                                 if (line == firstLine)
294                                         return 1;
295                                 if (firstLine <= line
296                                                 && line <= document.getLineOfOffset(position
297                                                                 .getOffset()
298                                                                 + position.getLength()))
299                                         return 2;
300                         } catch (BadLocationException x) {
301                         }
302                 }
303
304                 return 0;
305         }
306
307         /*
308          * @see org.eclipse.jface.text.source.IAnnotationHoverExtension#getHoverControlCreator()
309          */
310         public IInformationControlCreator getHoverControlCreator() {
311                 return fgCreator;
312         }
313
314         /*
315          * @see org.eclipse.jface.text.source.IAnnotationHoverExtension#getHoverInfo(org.eclipse.jface.text.source.ISourceViewer,
316          *      org.eclipse.jface.text.source.ILineRange, int)
317          */
318         public Object getHoverInfo(ISourceViewer sourceViewer,
319                         ILineRange lineRange, int visibleLines) {
320                 return getHoverInfoForLine(sourceViewer, lineRange.getStartLine());
321         }
322
323         /*
324          * @see org.eclipse.jface.text.source.IAnnotationHoverExtension#getHoverLineRange(org.eclipse.jface.text.source.ISourceViewer,
325          *      int)
326          */
327         public ILineRange getHoverLineRange(ISourceViewer viewer, int lineNumber) {
328                 return new LineRange(lineNumber, 1);
329         }
330
331         /*
332          * @see org.eclipse.jface.text.source.IAnnotationHoverExtension#canHandleMouseCursor()
333          */
334         public boolean canHandleMouseCursor() {
335                 return true;
336         }
337 }