minor changes
[phpeclipse.git] / net.sourceforge.phpeclipse / src / net / sourceforge / phpeclipse / phpeditor / PHPContentOutlinePage.java
1 package net.sourceforge.phpeclipse.phpeditor;
2
3 /**********************************************************************
4 Copyright (c) 2000, 2002 IBM Corp. and others.
5 All rights reserved. This program and the accompanying materials
6 are made available under the terms of the Common Public License v1.0
7 which accompanies this distribution, and is available at
8 http://www.eclipse.org/legal/cpl-v10.html
9
10 Contributors:
11     IBM Corporation - Initial implementation
12     Klaus Hartlage - www.eclipseproject.de
13 **********************************************************************/
14
15 import java.util.ArrayList;
16 import java.util.Collections;
17 import java.util.Comparator;
18 import java.util.List;
19
20 import org.eclipse.jface.text.BadLocationException;
21 import org.eclipse.jface.text.BadPositionCategoryException;
22 import org.eclipse.jface.text.DefaultPositionUpdater;
23 import org.eclipse.jface.text.IDocument;
24 import org.eclipse.jface.text.IPositionUpdater;
25 import org.eclipse.jface.text.Position;
26 import org.eclipse.jface.viewers.ISelection;
27 import org.eclipse.jface.viewers.IStructuredSelection;
28 import org.eclipse.jface.viewers.ITreeContentProvider;
29 import org.eclipse.jface.viewers.LabelProvider;
30 import org.eclipse.jface.viewers.SelectionChangedEvent;
31 import org.eclipse.jface.viewers.TreeViewer;
32 import org.eclipse.jface.viewers.Viewer;
33 import org.eclipse.swt.widgets.Composite;
34 import org.eclipse.swt.widgets.Control;
35 import org.eclipse.ui.texteditor.IDocumentProvider;
36 import org.eclipse.ui.texteditor.ITextEditor;
37 import org.eclipse.ui.views.contentoutline.ContentOutlinePage;
38
39 /**
40  * A content outline page which always represents the functions of the
41  * connected PHPEditor.
42  */
43 public class PHPContentOutlinePage extends ContentOutlinePage {
44   private static final String ERROR = "error"; //$NON-NLS-1$
45   private static final String WARNING = "warning"; //$NON-NLS-1$
46   /**
47    * A segment element.
48    */
49   protected static class Segment {
50     public String name;
51     public Position position;
52
53     public Segment(String name, Position position) {
54       this.name = name;
55       this.position = position;
56     }
57
58     public String toString() {
59       return name;
60     }
61   };
62
63   protected static class SegmentComparator implements Comparator {
64     public int compare(Object o1, Object o2) {
65       return ((Segment) o1).name.compareToIgnoreCase(((Segment) o2).name);
66     }
67   }
68
69   /**
70    * Divides the editor's document into ten segments and provides elements for them.
71    */
72   protected class ContentProvider implements ITreeContentProvider {
73
74     protected final static String SEGMENTS = "__php_segments"; //$NON-NLS-1$
75     protected IPositionUpdater fPositionUpdater = new DefaultPositionUpdater(SEGMENTS);
76     protected List fContent = new ArrayList(10);
77     protected List fVariables = new ArrayList(100);
78
79     private String getIdentifier(String text, int firstIndex) {
80       int i = firstIndex;
81       char c;
82       int textLength = text.length();
83       StringBuffer identifier = new StringBuffer();
84       while (i < textLength) {
85         c = text.charAt(i++);
86         if (Character.isJavaIdentifierPart(c) || (c == '$')) {
87           identifier.append(c);
88         } else if ((i == firstIndex + 1) && (c == '$')) {
89           identifier.append(c);
90         } else {
91           return identifier.toString();
92         }
93       }
94       return null;
95     }
96
97     protected void parse(IDocument document) {
98
99       int lines = document.getNumberOfLines();
100       int increment = Math.max(Math.round((float) (lines / 10)), 10);
101
102       String text = document.get();
103       int lastIndex = 0;
104       int i = 0;
105       //      lastIndex = text.indexOf("function ", lastIndex);
106       //      while (lastIndex > 0) {
107       //
108       //        try {
109       //          i = lastIndex + 9;
110       //          while ((i < text.length()) && Character.isJavaIdentifierPart(text.charAt(i))) {
111       //            i++;
112       //          }
113       //          Position p = new Position(lastIndex, i - lastIndex);
114       //          document.addPosition(SEGMENTS, p);
115       //          fContent.add(new Segment(text.substring(lastIndex, i), p));
116       //          //     MessageFormat.format("function", new Object[] { new Integer(lastIndex)}), p)); //$NON-NLS-1$
117       //          lastIndex = text.indexOf("function", lastIndex + 1);
118       //        } catch (BadLocationException e) {
119       //        } catch (BadPositionCategoryException e) {
120       //        }
121       //
122       //      }
123
124       boolean lineCommentMode = false;
125       boolean multiLineCommentMode = false;
126       boolean stringMode = false;
127       boolean functionMode = false;
128       String identifier;
129       int c;
130       int c2;
131
132       int textLength = text.length() - 10;
133       while (i < textLength) {
134         c = text.charAt(i++);
135         if (c == '\n') {
136           lineCommentMode = false;
137           // read until end of line
138         } else if (c == '#') {
139           // read until end of line
140           lineCommentMode = true;
141           continue;
142         } else if (c == '/') {
143           c2 = text.charAt(i++);
144           if (c2 == '/') {
145             lineCommentMode = true;
146             continue;
147           } else if (c2 == '*') {
148             multiLineCommentMode = true;
149             continue;
150           } else {
151             i--;
152           }
153         } else if (c == '*' && multiLineCommentMode) {
154           c2 = text.charAt(i++);
155           if (c2 == '/') {
156             multiLineCommentMode = false;
157             continue;
158           } else {
159             i--;
160           }
161         } else if (c == '\\' && stringMode) {
162           c2 = text.charAt(i++);
163           if (c2 == '"') {
164             continue;
165           } else {
166             i--;
167           }
168         } else if (c == '"') {
169           if (stringMode) {
170             stringMode = false;
171           } else {
172             stringMode = true;
173           }
174           continue;
175         }
176         if (lineCommentMode || multiLineCommentMode || stringMode) {
177           continue;
178         }
179
180         if (functionMode && Character.isJavaIdentifierPart((char) c)) {
181           functionMode = false;
182           lastIndex = i - 1;
183           identifier = getIdentifier(text, lastIndex);
184           try {
185             i += identifier.length() - 1;
186             Position p = new Position(lastIndex, i - lastIndex);
187             document.addPosition(SEGMENTS, p);
188             fContent.add(new Segment(text.substring(lastIndex, i), p));
189             //     MessageFormat.format("function", new Object[] { new Integer(lastIndex)}), p)); //$NON-NLS-1$
190             //    lastIndex = text.indexOf("function", lastIndex + 1);
191           } catch (BadLocationException e) {
192           } catch (BadPositionCategoryException e) {
193           }
194
195         } else if (c == 'f') {
196           identifier = getIdentifier(text, i - 1);
197           if (identifier.equals("function")) {
198             functionMode = true;
199             i += 8;
200           }
201         } else if (c == '$') {
202           // get the variable name
203           identifier = getIdentifier(text, i - 1);
204           fVariables.add(identifier);
205         }
206
207       }
208       Collections.sort(fContent, new SegmentComparator());
209       Collections.sort(fVariables);
210
211       //                        for (int line = 0; line < lines; line += increment) {
212       //
213       //                                int length = increment;
214       //                                if (line + increment > lines)
215       //                                        length = lines - line;
216       //
217       //                                try {
218       //
219       //                                        int offset = document.getLineOffset(line);
220       //                                        int end = document.getLineOffset(line + length);
221       //                                        length = end - offset;
222       //                                        Position p = new Position(offset, length);
223       //                                        document.addPosition(SEGMENTS, p);
224       //                                        fContent.add(new Segment(MessageFormat.format(PHPEditorMessages.getString("OutlinePage.segment.title_pattern"), new Object[] { new Integer(offset)}), p)); //$NON-NLS-1$
225       //
226       //                                } catch (BadPositionCategoryException x) {
227       //                                } catch (BadLocationException x) {
228       //                                }
229       //                        }
230     }
231
232     /*
233      * @see IContentProvider#inputChanged(Viewer, Object, Object)
234      */
235     public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
236       if (oldInput != null) {
237         IDocument document = fDocumentProvider.getDocument(oldInput);
238         if (document != null) {
239           try {
240             document.removePositionCategory(SEGMENTS);
241           } catch (BadPositionCategoryException x) {
242           }
243           document.removePositionUpdater(fPositionUpdater);
244         }
245       }
246
247       fContent.clear();
248       fVariables.clear();
249
250       if (newInput != null) {
251         IDocument document = fDocumentProvider.getDocument(newInput);
252         if (document != null) {
253           document.addPositionCategory(SEGMENTS);
254           document.addPositionUpdater(fPositionUpdater);
255
256           parse(document);
257         }
258       }
259     }
260
261     /*
262      * @see IContentProvider#dispose
263      */
264     public void dispose() {
265       if (fContent != null) {
266         fContent.clear();
267         fContent = null;
268       }
269       if (fVariables != null) {
270         fVariables.clear();
271         fVariables = null;
272       }
273     }
274
275     /*
276      * @see IContentProvider#isDeleted(Object)
277      */
278     public boolean isDeleted(Object element) {
279       return false;
280     }
281
282     /*
283      * @see IStructuredContentProvider#getElements(Object)
284      */
285     public Object[] getElements(Object element) {
286       return fContent.toArray();
287     }
288
289     /**
290      * returns all PHP variables
291      */
292     public Object[] getVariables() {
293       return fVariables.toArray();
294     }
295     /*
296      * @see ITreeContentProvider#hasChildren(Object)
297      */
298     public boolean hasChildren(Object element) {
299       return element == fInput;
300     }
301
302     /*
303      * @see ITreeContentProvider#getParent(Object)
304      */
305     public Object getParent(Object element) {
306       if (element instanceof Segment)
307         return fInput;
308       return null;
309     }
310
311     /*
312      * @see ITreeContentProvider#getChildren(Object)
313      */
314     public Object[] getChildren(Object element) {
315       if (element == fInput)
316         return fContent.toArray();
317       return new Object[0];
318     }
319   };
320
321   protected Object fInput;
322   protected IDocumentProvider fDocumentProvider;
323   protected ITextEditor fTextEditor;
324
325   /**
326    * Creates a content outline page using the given provider and the given editor.
327    */
328   public PHPContentOutlinePage(IDocumentProvider provider, ITextEditor editor) {
329     super();
330     fDocumentProvider = provider;
331     fTextEditor = editor;
332   }
333
334   /* (non-Javadoc)
335    * Method declared on ContentOutlinePage
336    */
337   public void createControl(Composite parent) {
338
339     super.createControl(parent);
340
341     TreeViewer viewer = getTreeViewer();
342     viewer.setContentProvider(new ContentProvider());
343     viewer.setLabelProvider(new LabelProvider());
344     viewer.addSelectionChangedListener(this);
345
346     if (fInput != null)
347       viewer.setInput(fInput);
348   }
349
350   /* (non-Javadoc)
351    * Method declared on ContentOutlinePage
352    */
353   public void selectionChanged(SelectionChangedEvent event) {
354
355     super.selectionChanged(event);
356
357     ISelection selection = event.getSelection();
358     if (selection.isEmpty())
359       fTextEditor.resetHighlightRange();
360     else {
361       Segment segment = (Segment) ((IStructuredSelection) selection).getFirstElement();
362       int start = segment.position.getOffset();
363       int length = segment.position.getLength();
364       try {
365         fTextEditor.setHighlightRange(start, length, true);
366       } catch (IllegalArgumentException x) {
367         fTextEditor.resetHighlightRange();
368       }
369     }
370   }
371
372   /**
373    * Sets the input of the outline page
374    */
375   public void setInput(Object input) {
376     fInput = input;
377     update();
378   }
379
380   /**
381    * Updates the outline page.
382    */
383   public void update() {
384     TreeViewer viewer = getTreeViewer();
385
386     if (viewer != null) {
387       Control control = viewer.getControl();
388       if (control != null && !control.isDisposed()) {
389         control.setRedraw(false);
390         viewer.setInput(fInput);
391         viewer.expandAll();
392         control.setRedraw(true);
393       }
394     }
395   }
396 }