JTidy integration
[phpeclipse.git] / net.sourceforge.phpeclipse / src / net / sourceforge / phpeclipse / phpeditor / php / PHPPartitionScanner.java
1 /**********************************************************************
2 Copyright (c) 2000, 2002 IBM Corp. 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 implementation
10     Klaus Hartlage - www.eclipseproject.de
11 **********************************************************************/
12 package net.sourceforge.phpeclipse.phpeditor.php;
13
14 import java.io.CharArrayWriter;
15 import java.util.ArrayList;
16 import java.util.List;
17
18 import org.eclipse.jface.text.rules.ICharacterScanner;
19 import org.eclipse.jface.text.rules.IPredicateRule;
20 import org.eclipse.jface.text.rules.IToken;
21 import org.eclipse.jface.text.rules.IWordDetector;
22 import org.eclipse.jface.text.rules.MultiLineRule;
23 import org.eclipse.jface.text.rules.RuleBasedPartitionScanner;
24 import org.eclipse.jface.text.rules.Token;
25 import org.eclipse.jface.text.rules.WordRule;
26
27 /**
28  * This scanner recognizes the JavaDoc comments and Java multi line comments.
29  */
30 public class PHPPartitionScanner extends RuleBasedPartitionScanner {
31
32   private final static String SKIP = "__skip"; //$NON-NLS-1$
33   public final static String HTML_MULTILINE_COMMENT = "__html_multiline_comment"; //$NON-NLS-1$
34   //    public final static String JAVA_DOC= "__java_javadoc"; //$NON-NLS-1$
35   public final static String PHP = "__php"; //$NON-NLS-1$
36   //public final static String HTML = "__html"; //$NON-NLS-1$
37
38   public final static IToken php = new Token(PHP);
39   //public final static IToken html = new Token(HTML);
40   public final static IToken comment = new Token(HTML_MULTILINE_COMMENT);
41
42   protected final static char[] php0EndSequence = { '<', '?' };
43   protected final static char[] php1EndSequence = { '<', '?', 'p', 'h', 'p' };
44   protected final static char[] php2EndSequence = { '<', '?', 'P', 'H', 'P' };
45
46   private StringBuffer test;
47
48   public class PHPMultiLineRule extends MultiLineRule {
49
50     public PHPMultiLineRule(String startSequence, String endSequence, IToken token) {
51       super(startSequence, endSequence, token);
52     }
53
54     public PHPMultiLineRule(String startSequence, String endSequence, IToken token, char escapeCharacter) {
55       super(startSequence, endSequence, token, escapeCharacter);
56     }
57
58     protected boolean endSequenceDetected(ICharacterScanner scanner) {
59       int c;
60       int c2;
61
62       boolean lineCommentMode = false;
63       boolean multiLineCommentMode = false;
64       boolean stringMode = false;
65
66       char[][] delimiters = scanner.getLegalLineDelimiters();
67       while ((c = scanner.read()) != ICharacterScanner.EOF) {
68         if (c == '#') {
69           // read until end of line
70           while ((c = scanner.read()) != ICharacterScanner.EOF) {
71             if (fEndSequence.length > 0 && c == fEndSequence[0]) {
72               // Check if the specified end sequence has been found.
73               if (sequenceDetected(scanner, fEndSequence, true))
74                 return true;
75             } else if (c == '\n') {
76               break;
77             }
78           }
79           continue;
80         } else if (c == '/' && (c = scanner.read()) != ICharacterScanner.EOF) {
81           if (c == '/') {
82             // read until end of line
83             while ((c = scanner.read()) != ICharacterScanner.EOF) {
84               if (fEndSequence.length > 0 && c == fEndSequence[0]) {
85                 // Check if the specified end sequence has been found.
86                 if (sequenceDetected(scanner, fEndSequence, true))
87                   return true;
88               } else if (c == '\n') {
89                 break;
90               }
91             }
92             continue;
93           } else if (c == '*') {
94             // multi-line comment
95             while ((c = scanner.read()) != ICharacterScanner.EOF) {
96               if (c == '*' && (c = scanner.read()) != ICharacterScanner.EOF) {
97                 if (c == '/') {
98                   break;
99                 }
100                 scanner.unread();
101               }
102             }
103
104             continue;
105           } else {
106             scanner.unread();
107           }
108         } else if (c == '"') {
109           // string mode
110           while ((c = scanner.read()) != ICharacterScanner.EOF) {
111             if (c == '\\') {
112               c = scanner.read();
113             } else if (c == '"') {
114               break;
115             }
116           }
117           continue;
118         } else if (c == '\'') {
119           // string mode
120           while ((c = scanner.read()) != ICharacterScanner.EOF) {
121             if (c == '\\') {
122               c = scanner.read();
123             } else if (c == '\'') {
124               break;
125             }
126           }
127           continue;
128         }
129
130         if (c == fEscapeCharacter) {
131           // Skip the escaped character.
132           scanner.read();
133         } else if (fEndSequence.length > 0 && c == fEndSequence[0]) {
134           // Check if the specified end sequence has been found.
135           if (sequenceDetected(scanner, fEndSequence, true))
136             return true;
137         } else if (fBreaksOnEOL) {
138           // Check for end of line since it can be used to terminate the pattern.
139           for (int i = 0; i < delimiters.length; i++) {
140             if (c == delimiters[i][0] && sequenceDetected(scanner, delimiters[i], false))
141               return true;
142           }
143         }
144       } 
145       boolean phpMode = false;
146       if (c == ICharacterScanner.EOF) {
147         phpMode = true;
148       }
149       scanner.unread();
150       return phpMode;
151     }
152   }
153
154   //  public class HTMLMultiLineRule extends MultiLineRule {
155   //
156   //    public HTMLMultiLineRule(String startSequence, String endSequence, IToken token) {
157   //      super(startSequence, endSequence, token);
158   //    }
159   //
160   //    public HTMLMultiLineRule(String startSequence, String endSequence, IToken token, char escapeCharacter) {
161   //      super(startSequence, endSequence, token, escapeCharacter);
162   //    }
163   //
164   //    protected boolean endSequenceDetected(ICharacterScanner scanner) {
165   //      int c;
166   //
167   //      char[][] delimiters = scanner.getLegalLineDelimiters();
168   //      while ((c = scanner.read()) != ICharacterScanner.EOF) {
169   //        if (c == '<') {
170   //          //       scanner.unread();
171   //          if (sequenceDetected(scanner, php2EndSequence, true)) {
172   //            // <?PHP
173   //            scanner.unread();
174   //            scanner.unread();
175   //            scanner.unread();
176   //            scanner.unread();
177   //            scanner.unread();
178   //            return true;
179   //          }
180   //          if (sequenceDetected(scanner, php1EndSequence, true)) {
181   //            // <?php
182   //            scanner.unread();
183   //            scanner.unread();
184   //            scanner.unread();
185   //            scanner.unread();
186   //            scanner.unread();
187   //            return true;
188   //          }
189   //          if (sequenceDetected(scanner, php0EndSequence, true)) {
190   //            // <?
191   //            scanner.unread();
192   //            scanner.unread();
193   //            return true;
194   //          }
195   //          //      scanner.read();
196   //        }
197   //
198   //      }
199   //      scanner.unread();
200   //      return false;
201   //    }
202   //
203   //    protected IToken doEvaluate(ICharacterScanner scanner, boolean resume) {
204   //
205   //      if (resume) {
206   //
207   //        if (endSequenceDetected(scanner))
208   //          return fToken;
209   //
210   //      } else {
211   //
212   //        int c = scanner.read();
213   //        //     if (c == fStartSequence[0]) {
214   //        //       if (sequenceDetected(scanner, fStartSequence, false)) {
215   //        if (endSequenceDetected(scanner))
216   //          return fToken;
217   //        //       }
218   //        //     }
219   //      }
220   //
221   //      scanner.unread();
222   //      return Token.UNDEFINED;
223   //    }
224   //
225   //    public IToken evaluate(ICharacterScanner scanner, boolean resume) {
226   //      if (fColumn == UNDEFINED)
227   //        return doEvaluate(scanner, resume);
228   //
229   //      int c = scanner.read();
230   //      scanner.unread();
231   //      //    if (c == fStartSequence[0])
232   //      return (fColumn == scanner.getColumn() ? doEvaluate(scanner, resume) : Token.UNDEFINED);
233   //      //    else
234   //      //      return Token.UNDEFINED;
235   //    }
236   //  }
237
238 //  public class HTMLPatternRule implements IPredicateRule {
239 //
240 //    protected static final int UNDEFINED = -1;
241 //
242 //    /** The token to be returned on success */
243 //    protected IToken fToken;
244 //
245 //    /** The pattern's column constrain */
246 //    protected int fColumn = UNDEFINED;
247 //    /** The pattern's escape character */
248 //    protected char fEscapeCharacter;
249 //    /** Indicates whether end of line termines the pattern */
250 //    protected boolean fBreaksOnEOL;
251 //
252 //    /**
253 //     * Creates a rule for the given starting and ending sequence.
254 //     * When these sequences are detected the rule will return the specified token.
255 //     * Alternatively, the sequence can also be ended by the end of the line.
256 //     * Any character which follows the given escapeCharacter will be ignored.
257 //     *
258 //     * @param startSequence the pattern's start sequence
259 //     * @param endSequence the pattern's end sequence, <code>null</code> is a legal value
260 //     * @param token the token which will be returned on success
261 //     * @param escapeCharacter any character following this one will be ignored
262 //     * @param indicates whether the end of the line also termines the pattern
263 //     */
264 //    public HTMLPatternRule(IToken token) {
265 //      fToken = token;
266 //      fEscapeCharacter = (char) 0;
267 //      fBreaksOnEOL = false;
268 //    }
269 //
270 //    /**
271 //     * Sets a column constraint for this rule. If set, the rule's token
272 //     * will only be returned if the pattern is detected starting at the 
273 //     * specified column. If the column is smaller then 0, the column
274 //     * constraint is considered removed.
275 //     *
276 //     * @param column the column in which the pattern starts
277 //     */
278 //    public void setColumnConstraint(int column) {
279 //      if (column < 0)
280 //        column = UNDEFINED;
281 //      fColumn = column;
282 //    }
283 //
284 //    /**
285 //     * Evaluates this rules without considering any column constraints.
286 //     *
287 //     * @param scanner the character scanner to be used
288 //     * @return the token resulting from this evaluation
289 //     */
290 //    protected IToken doEvaluate(ICharacterScanner scanner) {
291 //      return doEvaluate(scanner, false);
292 //    }
293 //
294 //    /**
295 //     * Evaluates this rules without considering any column constraints. Resumes
296 //     * detection, i.e. look sonly for the end sequence required by this rule if the
297 //     * <code>resume</code> flag is set.
298 //     *
299 //     * @param scanner the character scanner to be used
300 //     * @param resume <code>true</code> if detection should be resumed, <code>false</code> otherwise
301 //     * @return the token resulting from this evaluation
302 //     * @since 2.0
303 //     */
304 //    protected IToken doEvaluate(ICharacterScanner scanner, boolean resume) {
305 //
306 //      if (resume) {
307 //
308 //        if (endSequenceDetected(scanner))
309 //          return fToken;
310 //
311 //      } else {
312 //
313 //        int c = scanner.read();
314 //        //      if (c == fStartSequence[0]) {
315 //        //        if (sequenceDetected(scanner, fStartSequence, false)) {
316 //        if (endSequenceDetected(scanner))
317 //          return fToken;
318 //        //        }
319 //        //      }
320 //      }
321 //
322 //      scanner.unread();
323 //      return Token.UNDEFINED;
324 //    }
325 //
326 //    /*
327 //     * @see IRule#evaluate
328 //     */
329 //    public IToken evaluate(ICharacterScanner scanner) {
330 //      return evaluate(scanner, false);
331 //    }
332 //
333 //    /**
334 //     * Returns whether the end sequence was detected. As the pattern can be considered 
335 //     * ended by a line delimiter, the result of this method is <code>true</code> if the 
336 //     * rule breaks on the end  of the line, or if the EOF character is read.
337 //     *
338 //     * @param scanner the character scanner to be used
339 //     * @return <code>true</code> if the end sequence has been detected
340 //     */
341 //    protected boolean endSequenceDetected(ICharacterScanner scanner) {
342 //      int c;
343 //
344 //      char[][] delimiters = scanner.getLegalLineDelimiters();
345 //      while ((c = scanner.read()) != ICharacterScanner.EOF) {
346 //        if (c == '<') {
347 //          //       scanner.unread();
348 //          if (sequenceDetected(scanner, php2EndSequence, true)) {
349 //            // <?PHP
350 //            scanner.unread();
351 //            scanner.unread();
352 //            scanner.unread();
353 //            scanner.unread();
354 //            scanner.unread();
355 //            return true;
356 //          }
357 //          if (sequenceDetected(scanner, php1EndSequence, true)) {
358 //            // <?php
359 //            scanner.unread();
360 //            scanner.unread();
361 //            scanner.unread();
362 //            scanner.unread();
363 //            scanner.unread();
364 //            return true;
365 //          }
366 //          if (sequenceDetected(scanner, php0EndSequence, true)) {
367 //            // <?
368 //            scanner.unread();
369 //            scanner.unread();
370 //            return true;
371 //          }
372 //          //      scanner.read();
373 //        }
374 //
375 //      }
376 //      scanner.unread();
377 //      return false;
378 //    }
379 //
380 //    /**
381 //     * Returns whether the next characters to be read by the character scanner
382 //     * are an exact match with the given sequence. No escape characters are allowed 
383 //     * within the sequence. If specified the sequence is considered to be found
384 //     * when reading the EOF character.
385 //     *
386 //     * @param scanner the character scanner to be used
387 //     * @param sequence the sequence to be detected
388 //     * @param eofAllowed indicated whether EOF terminates the pattern
389 //     * @return <code>true</code> if the given sequence has been detected
390 //     */
391 //    protected boolean sequenceDetected(ICharacterScanner scanner, char[] sequence, boolean eofAllowed) {
392 //      for (int i = 1; i < sequence.length; i++) {
393 //        int c = scanner.read();
394 //        if (c == ICharacterScanner.EOF && eofAllowed) {
395 //          return true;
396 //        } else if (c != sequence[i]) {
397 //          // Non-matching character detected, rewind the scanner back to the start.
398 //          scanner.unread();
399 //          for (int j = i - 1; j > 0; j--)
400 //            scanner.unread();
401 //          return false;
402 //        }
403 //      }
404 //
405 //      return true;
406 //    }
407 //
408 //    /*
409 //     * @see IPredicateRule#evaluate(ICharacterScanner, boolean)
410 //     * @since 2.0
411 //     */
412 //    public IToken evaluate(ICharacterScanner scanner, boolean resume) {
413 //      if (fColumn == UNDEFINED)
414 //        return doEvaluate(scanner, resume);
415 //
416 //      int c = scanner.read();
417 //      scanner.unread();
418 //      //    if (c == fStartSequence[0])
419 //      return (fColumn == scanner.getColumn() ? doEvaluate(scanner, resume) : Token.UNDEFINED);
420 //      //    else
421 //      //      return Token.UNDEFINED;
422 //    }
423 //
424 //    /*
425 //     * @see IPredicateRule#getSuccessToken()
426 //     * @since 2.0
427 //     */
428 //    public IToken getSuccessToken() {
429 //      return fToken;
430 //    }
431 //  }
432   /**
433    * Detector for empty comments.
434    */
435 //  static class EmptyCommentDetector implements IWordDetector {
436 //
437 //    /* (non-Javadoc)
438 //    * Method declared on IWordDetector
439 //      */
440 //    public boolean isWordStart(char c) {
441 //      return (c == '/');
442 //    }
443 //
444 //    /* (non-Javadoc)
445 //    * Method declared on IWordDetector
446 //      */
447 //    public boolean isWordPart(char c) {
448 //      return (c == '*' || c == '/');
449 //    }
450 //  };
451
452   /**
453    * 
454    */
455 //  static class WordPredicateRule extends WordRule implements IPredicateRule {
456 //
457 //    private IToken fSuccessToken;
458 //
459 //    public WordPredicateRule(IToken successToken) {
460 //      super(new EmptyCommentDetector());
461 //      fSuccessToken = successToken;
462 //      addWord("/**/", fSuccessToken);
463 //    }
464 //
465 //    /*
466 //     * @see org.eclipse.jface.text.rules.IPredicateRule#evaluate(ICharacterScanner, boolean)
467 //     */
468 //    public IToken evaluate(ICharacterScanner scanner, boolean resume) {
469 //      return super.evaluate(scanner);
470 //    }
471 //
472 //    /*
473 //     * @see org.eclipse.jface.text.rules.IPredicateRule#getSuccessToken()
474 //     */
475 //    public IToken getSuccessToken() {
476 //      return fSuccessToken;
477 //    }
478 //  };
479
480   /**
481    * Creates the partitioner and sets up the appropriate rules.
482    */
483   public PHPPartitionScanner() {
484     super();
485
486     //    IToken php = new Token(PHP);
487     //    IToken html = new Token(HTML);
488     //    IToken comment = new Token(HTML_MULTILINE_COMMENT);
489
490     List rules = new ArrayList();
491
492     // Add rule for single line comments.
493     //  rules.add(new EndOfLineRule("//", Token.UNDEFINED));
494
495     // Add rule for strings and character constants.
496     //          rules.add(new SingleLineRule("\"", "\"", Token.UNDEFINED, '\\'));
497     //  rules.add(new SingleLineRule("'", "'", Token.UNDEFINED, '\\')); 
498
499     // Add special case word rule.
500     //    rules.add(new WordPredicateRule(comment));
501
502     // Add rules for multi-line comments and javadoc.
503     //rules.add(new MultiLineRule("/**", "*/", javaDoc));
504     //  rules.add(new HTMLMultiLineRule("<", "<?", html));
505
506     rules.add(new MultiLineRule("<!--", "-->", comment));
507     rules.add(new PHPMultiLineRule("<?\r", "?>", php));
508     rules.add(new PHPMultiLineRule("<?\n", "?>", php));
509     rules.add(new PHPMultiLineRule("<?\t", "?>", php));
510     rules.add(new PHPMultiLineRule("<? ", "?>", php));
511     rules.add(new PHPMultiLineRule("<?php", "?>", php));
512     rules.add(new PHPMultiLineRule("<?PHP", "?>", php));
513
514     //    rules.add(new HTMLPatternRule(html)); // "<", "<?",
515     //Add rule for processing instructions
516
517     IPredicateRule[] result = new IPredicateRule[rules.size()];
518     rules.toArray(result);
519     setPredicateRules(result);
520    // setDefaultReturnToken(html);
521   }
522
523   //    public IToken nextToken() {
524   //      
525   //      if (fContentType == null || fRules == null)
526   //        return getNextToken();
527   //      
528   //      fTokenOffset= fOffset;
529   //      fColumn= UNDEFINED;
530   //      boolean resume= (fPartitionOffset < fOffset);
531   //          
532   //      IPredicateRule rule;
533   //      IToken token;
534   //      
535   //      for (int i= 0; i < fRules.length; i++) {
536   //        rule= (IPredicateRule) fRules[i];
537   //        token= rule.getSuccessToken();
538   //        if (fContentType.equals(token.getData())) {
539   //          if (resume)
540   //            fTokenOffset= fPartitionOffset;
541   //          token= rule.evaluate(this, resume);
542   //          if (!token.isUndefined()) {
543   //            fContentType= null;
544   //            return token;
545   //          }
546   //        }
547   //      }
548   //      
549   //      fContentType= null;
550   //      return getNextToken();
551   //    }
552   //    
553   //    public IToken getNextToken() {
554   //      
555   //      IToken token;
556   //      
557   //      while (true) {
558   //        
559   //        fTokenOffset= fOffset;
560   //        fColumn= UNDEFINED;
561   //        
562   //        if (fRules != null) {
563   //          for (int i= 0; i < fRules.length; i++) {
564   //            token= (fRules[i].evaluate(this));
565   //            if (!token.isUndefined())
566   //              return token;
567   //          }
568   //        }
569   //        
570   //        if (read() == EOF)
571   //          return Token.EOF;
572   //        else
573   //          return fDefaultReturnToken;
574   //      }
575   //    }
576 }