Optimized Scanner
[phpeclipse.git] / net.sourceforge.phpeclipse / src / net / sourceforge / phpdt / internal / ui / text / PHPCodeReader.java
1 package net.sourceforge.phpdt.internal.ui.text;
2
3 /*
4  * (c) Copyright IBM Corp. 2000, 2001.
5  * All Rights Reserved.
6  */
7
8 import java.io.IOException;
9
10 import net.sourceforge.phpdt.internal.corext.phpdoc.SingleCharReader;
11
12 import org.eclipse.jface.text.BadLocationException;
13 import org.eclipse.jface.text.IDocument;
14
15 /**
16  * Reads from a document either forwards or backwards. May be configured to skip comments and strings.
17  */
18 public class PHPCodeReader extends SingleCharReader {
19
20   /** The EOF character */
21   public static final int EOF = -1;
22
23   private boolean fSkipComments = false;
24
25   private boolean fSkipStrings = false;
26
27   private boolean fForward = false;
28
29   private IDocument fDocument;
30
31   private int fOffset;
32
33   private int fEnd = -1;
34
35   private int fCachedLineNumber = -1;
36
37   private int fCachedLineOffset = -1;
38
39   public PHPCodeReader() {
40   }
41
42   /**
43    * Returns the offset of the last read character. Should only be called after read has been called.
44    */
45   public int getOffset() {
46     return fForward ? fOffset - 1 : fOffset;
47   }
48
49   public void configureForwardReader(IDocument document, int offset, int length, boolean skipComments, boolean skipStrings)
50       throws IOException {
51     fDocument = document;
52     fOffset = offset;
53     fSkipComments = skipComments;
54     fSkipStrings = skipStrings;
55
56     fForward = true;
57     fEnd = Math.min(fDocument.getLength(), fOffset + length);
58   }
59
60   public void configureBackwardReader(IDocument document, int offset, boolean skipComments, boolean skipStrings) throws IOException {
61     fDocument = document;
62     fOffset = offset;
63     fSkipComments = skipComments;
64     fSkipStrings = skipStrings;
65
66     fForward = false;
67     try {
68       fCachedLineNumber = fDocument.getLineOfOffset(fOffset);
69     } catch (BadLocationException x) {
70       throw new IOException(x.getMessage());
71     }
72   }
73
74   /*
75    * @see Reader#close()
76    */
77   public void close() throws IOException {
78     fDocument = null;
79   }
80
81   /*
82    * @see SingleCharReader#read()
83    */
84   public int read() throws IOException {
85     try {
86       return fForward ? readForwards() : readBackwards();
87     } catch (BadLocationException x) {
88       throw new IOException(x.getMessage());
89     }
90   }
91
92   private void gotoCommentEnd() throws BadLocationException {
93     while (fOffset < fEnd) {
94       char current = fDocument.getChar(fOffset++);
95       if (current == '*') {
96         if (fOffset < fEnd && fDocument.getChar(fOffset) == '/') {
97           ++fOffset;
98           return;
99         }
100       }
101     }
102   }
103
104   private void gotoStringEnd(char delimiter) throws BadLocationException {
105     while (fOffset < fEnd) {
106       char current = fDocument.getChar(fOffset++);
107       if (current == '\\') {
108         // ignore escaped characters
109         ++fOffset;
110       } else if (current == delimiter) {
111         return;
112       }
113     }
114   }
115
116   private void gotoLineEnd() throws BadLocationException {
117     int line = fDocument.getLineOfOffset(fOffset);
118     fOffset = fDocument.getLineOffset(line + 1);
119   }
120
121   private int readForwards() throws BadLocationException {
122     while (fOffset < fEnd) {
123       char current = fDocument.getChar(fOffset++);
124
125       switch (current) {
126       case '"':
127       case '\'':
128
129         if (fSkipStrings) {
130           gotoStringEnd(current);
131           continue;
132         }
133
134         return current;
135       case '#':
136
137         if (fSkipComments && fOffset < fEnd) {
138           gotoLineEnd();
139           continue;
140         }
141
142         return current;
143
144       case '/':
145
146         if (fSkipComments && fOffset < fEnd) {
147           char next = fDocument.getChar(fOffset);
148           if (next == '*') {
149             // a comment starts, advance to the comment end
150             ++fOffset;
151             gotoCommentEnd();
152             continue;
153           } else if (next == '/') {
154             // '//'-comment starts, advance to the line end
155             gotoLineEnd();
156             continue;
157           }
158         }
159
160         return current;
161
162       }
163
164       return current;
165     }
166
167     return EOF;
168   }
169
170   private void handleSingleLineComment() throws BadLocationException {
171     int line = fDocument.getLineOfOffset(fOffset);
172     if (line < fCachedLineNumber) {
173       fCachedLineNumber = line;
174       fCachedLineOffset = fDocument.getLineOffset(line);
175       int offset = fOffset;
176       while (fCachedLineOffset < offset) {
177         char current = fDocument.getChar(offset--);
178
179         if (current == '/' && fCachedLineOffset <= offset && fDocument.getChar(offset) == '/') {
180           fOffset = offset;
181           return;
182         }
183
184         if (current == '#' && fCachedLineOffset <= offset) {
185           fOffset = offset;
186           return;
187         }
188       }
189     }
190   }
191
192   private void gotoCommentStart() throws BadLocationException {
193     while (0 < fOffset) {
194       char current = fDocument.getChar(fOffset--);
195       if (current == '*' && 0 <= fOffset && fDocument.getChar(fOffset) == '/')
196         return;
197     }
198   }
199
200   private void gotoStringStart(char delimiter) throws BadLocationException {
201     while (0 < fOffset) {
202       char current = fDocument.getChar(fOffset);
203       if (current == delimiter) {
204         if (!(0 <= fOffset && fDocument.getChar(fOffset - 1) == '\\'))
205           return;
206       }
207       --fOffset;
208     }
209   }
210
211   private int readBackwards() throws BadLocationException {
212
213     while (0 < fOffset) {
214       --fOffset;
215
216       handleSingleLineComment();
217
218       char current = fDocument.getChar(fOffset);
219       switch (current) {
220       case '/':
221
222         if (fSkipComments && fOffset > 1) {
223           char next = fDocument.getChar(fOffset - 1);
224           if (next == '*') {
225             // a comment ends, advance to the comment start
226             fOffset -= 2;
227             gotoCommentStart();
228             continue;
229           }
230         }
231
232         return current;
233       case '"':
234       case '\'':
235
236         if (fSkipStrings) {
237           --fOffset;
238           gotoStringStart(current);
239           continue;
240         }
241
242         return current;
243       }
244
245       return current;
246     }
247
248     return EOF;
249   }
250 }
251