1) Fixed issue #858: OpenPHPPerspectiveAction error when creating a new PHP project.
[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         private boolean fSkipComments = false;
24         private boolean fSkipStrings  = false;
25         private boolean fForward      = false;
26         
27         private IDocument fDocument;
28         
29         private int fOffset;                           // The current text position within the editor's text
30         private int fEnd              = -1;
31         private int fCachedLineNumber = -1;            // Holds the last line we checked for single line comments
32
33         public PHPCodeReader() {
34         }
35
36         /**
37          * Returns the offset of the last read character. Should only be called
38          * after read has been called.
39          */
40         public int getOffset() {
41                 return fForward ? fOffset - 1 : fOffset;
42         }
43
44         public void configureForwardReader(IDocument document, int offset,
45                         int length, boolean skipComments, boolean skipStrings)
46                         throws IOException {
47                 fDocument = document;
48                 fOffset = offset;
49                 fSkipComments = skipComments;
50                 fSkipStrings = skipStrings;
51
52                 fForward = true;
53                 fEnd = Math.min(fDocument.getLength(), fOffset + length);
54         }
55
56         public void configureBackwardReader(IDocument document, int offset,
57                         boolean skipComments, boolean skipStrings) throws IOException {
58                 fDocument = document;
59                 fOffset = offset;
60                 fSkipComments = skipComments;
61                 fSkipStrings = skipStrings;
62
63                 fForward = false;
64                 try {
65                         fCachedLineNumber = fDocument.getLineOfOffset(fOffset);
66                 } catch (BadLocationException x) {
67                         throw new IOException(x.getMessage());
68                 }
69         }
70
71         /*
72          * @see Reader#close()
73          */
74         public void close() throws IOException {
75                 fDocument = null;
76         }
77
78         /*
79          * @see SingleCharReader#read()
80          */
81         public int read() throws IOException {
82                 try {
83                         return fForward ? readForwards() : readBackwards();
84                 } catch (BadLocationException x) {
85                         throw new IOException(x.getMessage());
86                 }
87         }
88
89         private int gotoCommentEnd (int nTextPos) throws BadLocationException {
90                 while (nTextPos < fEnd) {
91                         char current = fDocument.getChar (nTextPos++);
92                         
93                         if (current == '*') {
94                                 if ((nTextPos < fEnd) && 
95                                     (fDocument.getChar (nTextPos) == '/')) {
96                                         ++nTextPos;
97                                         
98                                         return nTextPos;
99                                 }
100                         }
101                 }
102                 
103                 return nTextPos;
104         }
105
106         /**
107          * 
108          * @param delimiter
109          * @throws BadLocationException
110          */
111         private int gotoStringEnd (int nTextPos, char delimiter) throws BadLocationException {
112                 while (nTextPos < fEnd) {               // If long as we are not at the end of text
113                         char current = fDocument.getChar (nTextPos++);
114                         
115                         if (current == '\\') {                          // ignore escaped characters
116                                 ++nTextPos;
117                         } 
118                         else if (current == delimiter) {
119                                 return nTextPos;
120                         }
121                 }
122                 
123                 return nTextPos;                        // End position
124         }
125
126         /**
127          * 
128          * @param nTextPos               The current text position
129          * 
130          * @return                       The position of the start of next line
131          * 
132          * @throws BadLocationException
133          */
134         private int gotoLineEnd (int nTextPos) throws BadLocationException {
135                 int line = fDocument.getLineOfOffset (nTextPos); // Get the line number of the current text position
136                 
137                 return fDocument.getLineOffset (line + 1);
138         }
139
140         private int readForwards () throws BadLocationException {
141                 while (fOffset < fEnd) {
142                         char current = fDocument.getChar(fOffset++);
143
144                         switch (current) {
145                             case '"':
146                             case '\'':
147                                 if (fSkipStrings) {
148                                     fOffset = gotoStringEnd (fOffset, current);
149                                     continue;
150                                 }
151                                 return current;
152                                 
153                             case '#':
154                                 if (fSkipComments && fOffset < fEnd) {
155                                     fOffset = gotoLineEnd (fOffset);
156                                     continue;
157                                 }
158                                 return current;
159
160                             case '/':
161                                 if (fSkipComments && fOffset < fEnd) {
162                                     char next = fDocument.getChar(fOffset);
163                                     
164                                     if (next == '*') {                                  // A comment starts, advance to the comment end
165                                         ++fOffset;
166                                         fOffset = gotoCommentEnd (fOffset);
167                                         continue;
168                                     } else if (next == '/') {                   // '//'-comment starts, advance to the line end
169                                         fOffset = gotoLineEnd (fOffset);
170                                         continue;
171                                     }
172                                 }    
173                                 return current;
174                         }
175
176                         return current;
177                 }
178
179                 return EOF;
180         }
181
182         /**
183          * Check whether the current line contains a single line comment.
184          * If it contains a single line comment (// or #), than set fOffset (the global character index)
185          * to the character just before the single line comment.
186          * 
187          * @throws BadLocationException
188          */
189         
190         private void handleSingleLineComment () throws BadLocationException {
191             char current;
192                 int  line = fDocument.getLineOfOffset (fOffset);         // Get the line number which belongs to the current text position fOffset
193                 
194                 if (line < fCachedLineNumber) {                          // If we didn't parse this line already
195                         fCachedLineNumber = line;                            // Remember this line for the next time (so we don't search again!)   
196                         int nOffset       = fDocument.getLineOffset (line);  // Get the start position of current line      
197                         
198                         while (nOffset < fOffset) {                          // As long as the text position is within the current line 
199                                 current = fDocument.getChar (nOffset);           // Get the character from the current text position
200
201                                 switch (current) {
202                                     case '/':
203                                         if (fDocument.getChar (nOffset + 1) == '/') { // If the following character is '/'
204                                             fOffset = nOffset - 1;
205                                             
206                                             return;
207                                         }
208                                         break;    
209                                     
210                                     case '#':
211                         fOffset = nOffset - 1;
212                         return;
213                                     
214                                     case '"':                                           // It's a string start quote
215                                     case '\'':
216                                         nOffset++;                                      // Set to next character
217                                         
218                                         while (nOffset < fOffset) {                     // As long as we are within the same line
219                                             char cChar = fDocument.getChar (nOffset++);
220                                             
221                                             if (cChar == '\\') {                        // Ignore escaped characters
222                                                 ++nOffset;
223                                             } 
224                                             else if (cChar == current) {                // If end of string found 
225                                                 break;
226                                             }
227                                         }
228                                         break;
229                                 }
230                                 
231                                 nOffset++;                                              // Go for the next character
232                         }
233                 }
234         }
235
236         /**
237          * We search the for the block comment start sequence "/*"
238          * 
239          * The returned value points to the '/'
240          * 
241          * @throws BadLocationException
242          * 
243          * @return The new text position
244          */
245         
246         private int gotoCommentStart (int nTextPos) throws BadLocationException {
247                 while (0 < nTextPos) {                                // As long as we are not at the start of the editor text
248                     char current = fDocument.getChar (nTextPos--);    // Get the character from the current text position        
249                         
250                         if ((current == '*') &&                           // If current character is a '*' 
251                             (0 <= nTextPos) &&                            // and if we are not yet at the start of the editor text 
252                             (fDocument.getChar (nTextPos) == '/')) {      // and the previous character is a '/', 
253                                 return nTextPos;                              // We found the block comment start "/*"
254                         }
255                 }
256                 
257                 return nTextPos;
258         }
259
260         /**
261          * The string closing quote has been found, when reading the editor text backwards.
262          * So we have to search for the start of the string
263          * 
264          * The returned value points to '"' or '''
265          * 
266          * @param delimiter The string double quote '"' or single ''' quote we search for
267          * 
268          * @throws BadLocationException
269          */
270         private int gotoStringStart (int nTextPos, char delimiter) throws BadLocationException {
271                 while (0 < nTextPos) {                              // As long as we are not at the start of the editor text
272                         char current = fDocument.getChar (nTextPos);    // Get the character from the current text position 
273                                                                         
274                         if (current == delimiter) {                     // If we found the same character ('"' or ''') again, we have found the string start
275                                 if (!(0 <= nTextPos &&                        
276                                       fDocument.getChar (nTextPos - 1) == '\\')) // If the character before the string quote is not an '/' 
277                                 return nTextPos;                                 // we found the string start
278                         }
279                                                                             
280                         --nTextPos;                                          // Go one character back
281                 }
282                 
283                 return nTextPos;
284         }
285
286         /**
287          * Read the editor text backwards
288          * 
289          */
290         
291         private int readBackwards() throws BadLocationException {
292             char current;                                                // The character on position fOffset
293             
294                 while (0 < fOffset) {                                        // As long as we are not at the beginning of the editor text
295                         --fOffset;                                               // Step back one character
296
297                         handleSingleLineComment ();                              // Search for a single line comment within the current line
298                                                                                  // If there is a single line comment, fOffset is set to position just before the single line comment
299
300                         current = fDocument.getChar (fOffset);                   // Read the current character
301                         switch (current) {                                       // Process the current character
302                             case '/':                                            // Is it a single line comment? 
303                                 if (fSkipComments &&                             // When comments should be skipped when parsing
304                                     fOffset > 1) {                               // and if there is indeed an additional character before that '/'
305                                     char prev = fDocument.getChar (fOffset - 1); // Look at the character in front of the '/'
306                                     
307                                     if (prev == '*') {                           // If '*' than a block comment ends here, advance to the comment start
308                                         fOffset -= 2;                            // Set character pointer to character before "*/"
309                                         fOffset  = gotoCommentStart (fOffset);   // and search for the starting "/*" of the block comment
310                                         continue;
311                                     }
312                                 }
313                                 return current;                                  // No block comment end, so return the current character
314                                 
315                             case '"':                                            // Is it a string double quote closing '"'?                                                
316                             case '\'':                                           // or a string single quote closing '''?
317                                 if (fSkipStrings) {                              // If we should skip strings when parsing
318                                     --fOffset;                                   // Set pointer one before the string closing quote 
319                                     fOffset = gotoStringStart (fOffset, current);// and search the start of string ('"' or ''')
320                                     continue;
321                                 }
322                                 return current;                                  // No string skip, so return the current character
323                         }
324
325                         return current;                                          // No string and no comment
326                 }
327
328                 return EOF;                                                  // When the start of the text has been found
329         }
330 }