Save PHPPartitionScanner for refactoring
[phpeclipse.git] / net.sourceforge.phpeclipse / src / net / sourceforge / phpdt / internal / ui / text / FastJavaPartitionScanner.java
1 /*******************************************************************************
2  * Copyright (c) 2000, 2004 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Common Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/cpl-v10.html
7  *
8  * Contributors:
9  *     IBM Corporation - initial API and implementation
10  *******************************************************************************/
11 package net.sourceforge.phpdt.internal.ui.text;
12
13 import net.sourceforge.phpeclipse.ui.text.rules.AbstractPartitioner;
14
15 import org.eclipse.jface.text.Assert;
16 import org.eclipse.jface.text.IDocument;
17 import org.eclipse.jface.text.rules.ICharacterScanner;
18 import org.eclipse.jface.text.rules.IPartitionTokenScanner;
19 import org.eclipse.jface.text.rules.IToken;
20 import org.eclipse.jface.text.rules.Token;
21
22 /**
23  * This scanner recognizes the JavaDoc comments, Java multi line comments, Java single line comments, Java strings.
24  */
25 public class FastJavaPartitionScanner implements IPartitionTokenScanner, IPHPPartitions {
26
27   // states
28   private static final int PHP = 0;
29
30   private static final int SINGLE_LINE_COMMENT = 1;
31
32   private static final int MULTI_LINE_COMMENT = 2;
33
34   private static final int PHPDOC = 3;
35
36   private static final int STRING_DQ = 4;
37
38   private static final int STRING_SQ = 5;
39
40   //    private static final int CHARACTER= 4;
41
42   // beginning of prefixes and postfixes
43   private static final int NONE = 0;
44
45   private static final int BACKSLASH = 1; // postfix for STRING_DQ and CHARACTER
46
47   private static final int SLASH = 2; // prefix for SINGLE_LINE or MULTI_LINE or JAVADOC
48
49   private static final int SLASH_STAR = 3; // prefix for MULTI_LINE_COMMENT or JAVADOC
50
51   private static final int SLASH_STAR_STAR = 4; // prefix for MULTI_LINE_COMMENT or JAVADOC
52
53   private static final int STAR = 5; // postfix for MULTI_LINE_COMMENT or JAVADOC
54
55   private static final int CARRIAGE_RETURN = 6; // postfix for STRING_DQ, CHARACTER and SINGLE_LINE_COMMENT
56
57   /** The scanner. */
58   private final BufferedDocumentScanner fScanner = new BufferedDocumentScanner(1000); // faster implementation
59
60   /** The offset of the last returned token. */
61   private int fTokenOffset;
62
63   /** The length of the last returned token. */
64   private int fTokenLength;
65
66   /** The state of the scanner. */
67   private int fState;
68
69   /** The last significant characters read. */
70   private int fLast;
71
72   /** The amount of characters already read on first call to nextToken(). */
73   private int fPrefixLength;
74
75   // emulate JavaPartitionScanner
76   private boolean fEmulate = false;
77
78   private int fJavaOffset;
79
80   private int fJavaLength;
81
82   private final IToken[] fTokens = new IToken[] {
83       new Token(null),
84       new Token(PHP_SINGLELINE_COMMENT),
85       new Token(PHP_MULTILINE_COMMENT),
86       new Token(PHP_PHPDOC_COMMENT),
87       new Token(PHP_STRING_DQ),
88       new Token(PHP_STRING_SQ) };
89
90   public FastJavaPartitionScanner(boolean emulate) {
91     fEmulate = emulate;
92   }
93
94   public FastJavaPartitionScanner() {
95     this(false);
96   }
97
98   /*
99    * @see org.eclipse.jface.text.rules.ITokenScanner#nextToken()
100    */
101   public IToken nextToken() {
102
103     // emulate JavaPartitionScanner
104     if (fEmulate) {
105       if (fJavaOffset != -1 && fTokenOffset + fTokenLength != fJavaOffset + fJavaLength) {
106         fTokenOffset += fTokenLength;
107         return fTokens[PHP];
108       } else {
109         fJavaOffset = -1;
110         fJavaLength = 0;
111       }
112     }
113
114     fTokenOffset += fTokenLength;
115     fTokenLength = fPrefixLength;
116
117     while (true) {
118       final int ch = fScanner.read();
119
120       // characters
121       switch (ch) {
122       case ICharacterScanner.EOF:
123         if (fTokenLength > 0) {
124           fLast = NONE; // ignore last
125           return preFix(fState, PHP, NONE, 0);
126
127         } else {
128           fLast = NONE;
129           fPrefixLength = 0;
130           return Token.EOF;
131         }
132
133       case '\r':
134         // emulate JavaPartitionScanner
135         if (!fEmulate && fLast != CARRIAGE_RETURN) {
136           fLast = CARRIAGE_RETURN;
137           fTokenLength++;
138           continue;
139
140         } else {
141
142           switch (fState) {
143           case SINGLE_LINE_COMMENT:
144             //                                  case CHARACTER:
145             //                                  case STRING_DQ:
146             //                                  case STRING_SQ:
147             if (fTokenLength > 0) {
148               IToken token = fTokens[fState];
149
150               // emulate JavaPartitionScanner
151               if (fEmulate) {
152                 fTokenLength++;
153                 fLast = NONE;
154                 fPrefixLength = 0;
155               } else {
156                 fLast = CARRIAGE_RETURN;
157                 fPrefixLength = 1;
158               }
159
160               fState = PHP;
161               return token;
162
163             } else {
164               consume();
165               continue;
166             }
167
168           default:
169             consume();
170             continue;
171           }
172         }
173
174       case '\n':
175         switch (fState) {
176         case SINGLE_LINE_COMMENT:
177           //                            case CHARACTER:
178           //                            case STRING_DQ:
179           //                            case STRING_SQ:
180           // assert(fTokenLength > 0);
181           return postFix(fState);
182
183         default:
184           consume();
185           continue;
186         }
187
188       case '?':
189         if (fState == SINGLE_LINE_COMMENT) {
190           int nextch = fScanner.read();
191           if (nextch == '>') {
192             // <h1>This is an <?php # echo 'simple' ?> example.</h1>
193             fTokenLength--;
194             fScanner.unread();
195             fScanner.unread();
196             return postFix(fState);
197           }
198           fScanner.unread();
199         }
200
201       default:
202         if (!fEmulate && fLast == CARRIAGE_RETURN) {
203           switch (fState) {
204           case SINGLE_LINE_COMMENT:
205             //                                  case CHARACTER:
206             //                                  case STRING_DQ:
207             //                                  case STRING_SQ:
208             int last;
209             int newState;
210             switch (ch) {
211             case '/':
212               last = SLASH;
213               newState = PHP;
214               break;
215
216             case '*':
217               last = STAR;
218               newState = PHP;
219               break;
220
221             case '\'':
222               last = NONE;
223               newState = STRING_SQ;
224               break;
225
226             case '"':
227               last = NONE;
228               newState = STRING_DQ;
229               break;
230
231             case '\r':
232               last = CARRIAGE_RETURN;
233               newState = PHP;
234               break;
235
236             case '\\':
237               last = BACKSLASH;
238               newState = PHP;
239               break;
240
241             default:
242               last = NONE;
243               newState = PHP;
244               break;
245             }
246
247             fLast = NONE; // ignore fLast
248             return preFix(fState, newState, last, 1);
249
250           default:
251             break;
252           }
253         }
254       }
255
256       // states
257       switch (fState) {
258       case PHP:
259         switch (ch) {
260         case '#':
261           if (fTokenLength > 0) {
262             return preFix(PHP, SINGLE_LINE_COMMENT, NONE, 1);
263           } else {
264             preFix(PHP, SINGLE_LINE_COMMENT, NONE, 1);
265             fTokenOffset += fTokenLength;
266             fTokenLength = fPrefixLength;
267             break;
268           }
269         case '/':
270           if (fLast == SLASH) {
271             if (fTokenLength - getLastLength(fLast) > 0) {
272               return preFix(PHP, SINGLE_LINE_COMMENT, NONE, 2);
273             } else {
274               preFix(PHP, SINGLE_LINE_COMMENT, NONE, 2);
275               fTokenOffset += fTokenLength;
276               fTokenLength = fPrefixLength;
277               break;
278             }
279
280           } else {
281             fTokenLength++;
282             fLast = SLASH;
283             break;
284           }
285
286         case '*':
287           if (fLast == SLASH) {
288             if (fTokenLength - getLastLength(fLast) > 0)
289               return preFix(PHP, MULTI_LINE_COMMENT, SLASH_STAR, 2);
290             else {
291               preFix(PHP, MULTI_LINE_COMMENT, SLASH_STAR, 2);
292               fTokenOffset += fTokenLength;
293               fTokenLength = fPrefixLength;
294               break;
295             }
296
297           } else {
298             consume();
299             break;
300           }
301
302         case '\'':
303           fLast = NONE; // ignore fLast
304           if (fTokenLength > 0)
305             return preFix(PHP, STRING_SQ, NONE, 1);
306           else {
307             preFix(PHP, STRING_SQ, NONE, 1);
308             fTokenOffset += fTokenLength;
309             fTokenLength = fPrefixLength;
310             break;
311           }
312
313         case '"':
314           fLast = NONE; // ignore fLast
315           if (fTokenLength > 0)
316             return preFix(PHP, STRING_DQ, NONE, 1);
317           else {
318             preFix(PHP, STRING_DQ, NONE, 1);
319             fTokenOffset += fTokenLength;
320             fTokenLength = fPrefixLength;
321             break;
322           }
323
324         default:
325           consume();
326           break;
327         }
328         break;
329
330       case SINGLE_LINE_COMMENT:
331         consume();
332         break;
333
334       case PHPDOC:
335         switch (ch) {
336         case '/':
337           switch (fLast) {
338           case SLASH_STAR_STAR:
339             return postFix(MULTI_LINE_COMMENT);
340
341           case STAR:
342             return postFix(PHPDOC);
343
344           default:
345             consume();
346             break;
347           }
348           break;
349
350         case '*':
351           fTokenLength++;
352           fLast = STAR;
353           break;
354
355         default:
356           consume();
357           break;
358         }
359         break;
360
361       case MULTI_LINE_COMMENT:
362         switch (ch) {
363         case '*':
364           if (fLast == SLASH_STAR) {
365             fLast = SLASH_STAR_STAR;
366             fTokenLength++;
367             fState = PHPDOC;
368           } else {
369             fTokenLength++;
370             fLast = STAR;
371           }
372           break;
373
374         case '/':
375           if (fLast == STAR) {
376             return postFix(MULTI_LINE_COMMENT);
377           } else {
378             consume();
379             break;
380           }
381
382         default:
383           consume();
384           break;
385         }
386         break;
387
388       case STRING_DQ:
389         switch (ch) {
390         case '\\':
391           fLast = (fLast == BACKSLASH) ? NONE : BACKSLASH;
392           fTokenLength++;
393           break;
394
395         case '\"':
396           if (fLast != BACKSLASH) {
397             return postFix(STRING_DQ);
398
399           } else {
400             consume();
401             break;
402           }
403
404         default:
405           consume();
406           break;
407         }
408         break;
409       case STRING_SQ:
410         switch (ch) {
411         case '\\':
412           fLast = (fLast == BACKSLASH) ? NONE : BACKSLASH;
413           fTokenLength++;
414           break;
415
416         case '\'':
417           if (fLast != BACKSLASH) {
418             return postFix(STRING_SQ);
419
420           } else {
421             consume();
422             break;
423           }
424
425         default:
426           consume();
427           break;
428         }
429         break;
430       //                        case CHARACTER:
431       //                                switch (ch) {
432       //                                case '\\':
433       //                                        fLast= (fLast == BACKSLASH) ? NONE : BACKSLASH;
434       //                                        fTokenLength++;
435       //                                        break;
436       //
437       //                                case '\'':
438       //                                        if (fLast != BACKSLASH) {
439       //                                                return postFix(CHARACTER);
440       //
441       //                                        } else {
442       //                                                consume();
443       //                                                break;
444       //                                        }
445       //
446       //                                default:
447       //                                        consume();
448       //                                        break;
449       //                                }
450       //                                break;
451       }
452     }
453   }
454
455   private static final int getLastLength(int last) {
456     switch (last) {
457     default:
458       return -1;
459
460     case NONE:
461       return 0;
462
463     case CARRIAGE_RETURN:
464     case BACKSLASH:
465     case SLASH:
466     case STAR:
467       return 1;
468
469     case SLASH_STAR:
470       return 2;
471
472     case SLASH_STAR_STAR:
473       return 3;
474     }
475   }
476
477   private final void consume() {
478     fTokenLength++;
479     fLast = NONE;
480   }
481
482   private final IToken postFix(int state) {
483     fTokenLength++;
484     fLast = NONE;
485     fState = PHP;
486     fPrefixLength = 0;
487     return fTokens[state];
488   }
489
490   private final IToken preFix(int state, int newState, int last, int prefixLength) {
491     // emulate JavaPartitionScanner
492     if (fEmulate && state == PHP && (fTokenLength - getLastLength(fLast) > 0)) {
493       fTokenLength -= getLastLength(fLast);
494       fJavaOffset = fTokenOffset;
495       fJavaLength = fTokenLength;
496       fTokenLength = 1;
497       fState = newState;
498       fPrefixLength = prefixLength;
499       fLast = last;
500       return fTokens[state];
501
502     } else {
503       fTokenLength -= getLastLength(fLast);
504       fLast = last;
505       fPrefixLength = prefixLength;
506       IToken token = fTokens[state];
507       fState = newState;
508       return token;
509     }
510   }
511
512   private static int getState(String contentType) {
513
514     if (contentType == null)
515       return PHP;
516
517     else if (contentType.equals(PHP_SINGLELINE_COMMENT))
518       return SINGLE_LINE_COMMENT;
519
520     else if (contentType.equals(PHP_MULTILINE_COMMENT))
521       return MULTI_LINE_COMMENT;
522
523     else if (contentType.equals(PHP_PHPDOC_COMMENT))
524       return PHPDOC;
525
526     else if (contentType.equals(PHP_STRING_DQ))
527       return STRING_DQ;
528
529     else if (contentType.equals(PHP_STRING_SQ))
530       return STRING_SQ;
531     //          else if (contentType.equals(JAVA_CHARACTER))
532     //                  return CHARACTER;
533
534     else
535       return PHP;
536   }
537
538   /*
539    * @see IPartitionTokenScanner#setPartialRange(IDocument, int, int, String, int)
540    */
541   public void setPartialRange(IDocument document, int offset, int length, String contentType, int partitionOffset) {
542     fScanner.setRange(document, offset, length);
543     setRange(document, offset, length);
544     fTokenOffset = partitionOffset;
545     fTokenLength = 0;
546     fPrefixLength = offset - partitionOffset;
547     fLast = NONE;
548
549     if (offset == partitionOffset) {
550       // restart at beginning of partition
551       fState = PHP;
552     } else {
553       fState = getState(contentType);
554     }
555
556     // emulate JavaPartitionScanner
557     if (fEmulate) {
558       fJavaOffset = -1;
559       fJavaLength = 0;
560     }
561   }
562
563   /*
564    * @see ITokenScanner#setRange(IDocument, int, int)
565    */
566   public void setRange(IDocument document, int offset, int length) {
567     fScanner.setRange(document, offset, length);
568     fTokenOffset = offset;
569     fTokenLength = 0;
570     fPrefixLength = 0;
571     fLast = NONE;
572     fState = PHP;
573
574     // emulate JavaPartitionScanner
575     if (fEmulate) {
576       fJavaOffset = -1;
577       fJavaLength = 0;
578     }
579   }
580
581   /*
582    * @see ITokenScanner#getTokenLength()
583    */
584   public int getTokenLength() {
585     return fTokenLength;
586   }
587
588   /*
589    * @see ITokenScanner#getTokenOffset()
590    */
591   public int getTokenOffset() {
592     if (AbstractPartitioner.DEBUG) {
593       Assert.isTrue(fTokenOffset >= 0, Integer.toString(fTokenOffset));
594     }
595     return fTokenOffset;
596   }
597
598 }