1 package net.sourceforge.phpdt.internal.ui.text;
4 * (c) Copyright IBM Corp. 2000, 2001.
8 import java.io.IOException;
10 import net.sourceforge.phpdt.internal.corext.phpdoc.SingleCharReader;
12 import org.eclipse.jface.text.BadLocationException;
13 import org.eclipse.jface.text.IDocument;
16 * Reads from a document either forwards or backwards. May be configured to skip
17 * comments and strings.
19 public class PHPCodeReader extends SingleCharReader {
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;
27 private IDocument fDocument;
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
33 public PHPCodeReader() {
37 * Returns the offset of the last read character. Should only be called
38 * after read has been called.
40 public int getOffset() {
41 return fForward ? fOffset - 1 : fOffset;
44 public void configureForwardReader(IDocument document, int offset,
45 int length, boolean skipComments, boolean skipStrings)
49 fSkipComments = skipComments;
50 fSkipStrings = skipStrings;
53 fEnd = Math.min(fDocument.getLength(), fOffset + length);
56 public void configureBackwardReader(IDocument document, int offset,
57 boolean skipComments, boolean skipStrings) throws IOException {
60 fSkipComments = skipComments;
61 fSkipStrings = skipStrings;
65 fCachedLineNumber = fDocument.getLineOfOffset(fOffset);
66 } catch (BadLocationException x) {
67 throw new IOException(x.getMessage());
74 public void close() throws IOException {
79 * @see SingleCharReader#read()
81 public int read() throws IOException {
83 return fForward ? readForwards() : readBackwards();
84 } catch (BadLocationException x) {
85 throw new IOException(x.getMessage());
89 private int gotoCommentEnd (int nTextPos) throws BadLocationException {
90 while (nTextPos < fEnd) {
91 char current = fDocument.getChar (nTextPos++);
94 if ((nTextPos < fEnd) &&
95 (fDocument.getChar (nTextPos) == '/')) {
109 * @throws BadLocationException
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++);
115 if (current == '\\') { // ignore escaped characters
118 else if (current == delimiter) {
123 return nTextPos; // End position
128 * @param nTextPos The current text position
130 * @return The position of the start of next line
132 * @throws BadLocationException
134 private int gotoLineEnd (int nTextPos) throws BadLocationException {
135 int line = fDocument.getLineOfOffset (nTextPos); // Get the line number of the current text position
137 return fDocument.getLineOffset (line + 1);
140 private int readForwards () throws BadLocationException {
141 while (fOffset < fEnd) {
142 char current = fDocument.getChar(fOffset++);
148 fOffset = gotoStringEnd (fOffset, current);
154 if (fSkipComments && fOffset < fEnd) {
155 fOffset = gotoLineEnd (fOffset);
161 if (fSkipComments && fOffset < fEnd) {
162 char next = fDocument.getChar(fOffset);
164 if (next == '*') { // A comment starts, advance to the comment end
166 fOffset = gotoCommentEnd (fOffset);
168 } else if (next == '/') { // '//'-comment starts, advance to the line end
169 fOffset = gotoLineEnd (fOffset);
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.
187 * @throws BadLocationException
190 private void handleSingleLineComment () throws BadLocationException {
192 int line = fDocument.getLineOfOffset (fOffset); // Get the line number which belongs to the current text position fOffset
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
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
203 if (fDocument.getChar (nOffset + 1) == '/') { // If the following character is '/'
204 fOffset = nOffset - 1;
211 fOffset = nOffset - 1;
214 case '"': // It's a string start quote
216 nOffset++; // Set to next character
218 while (nOffset < fOffset) { // As long as we are within the same line
219 char cChar = fDocument.getChar (nOffset++);
221 if (cChar == '\\') { // Ignore escaped characters
224 else if (cChar == current) { // If end of string found
231 nOffset++; // Go for the next character
237 * We search the for the block comment start sequence "/*"
239 * The returned value points to the '/'
241 * @throws BadLocationException
243 * @return The new text position
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
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 "/*"
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
264 * The returned value points to '"' or '''
266 * @param delimiter The string double quote '"' or single ''' quote we search for
268 * @throws BadLocationException
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
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
280 --nTextPos; // Go one character back
287 * Read the editor text backwards
291 private int readBackwards() throws BadLocationException {
292 char current; // The character on position fOffset
294 while (0 < fOffset) { // As long as we are not at the beginning of the editor text
295 --fOffset; // Step back one character
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
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 '/'
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
313 return current; // No block comment end, so return the current character
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 ''')
322 return current; // No string skip, so return the current character
325 return current; // No string and no comment
328 return EOF; // When the start of the text has been found