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