2  * This program and the accompanying materials
 
   3  * are made available under the terms of the Common Public License v1.0
 
   4  * which accompanies this distribution, and is available at
 
   5  * http://www.eclipse.org/legal/cpl-v10.html
 
   6  * Created on 05.03.2003
 
   8  * @author Stefan Langer (musk)
 
   9  * @version $Revision: 1.16 $
 
  11 package net.sourceforge.phpeclipse.phpeditor.php;
 
  15 import org.eclipse.jface.text.*;
 
  16 import org.eclipse.jface.text.rules.*;
 
  21 public class PHPPartitionScanner implements IPartitionTokenScanner
 
  23     private static final boolean DEBUG = true;
 
  24     private boolean fInString = false;
 
  25     private boolean fInDoubString = false;
 
  26     private IDocument fDocument = null;
 
  27     private int fOffset = -1;
 
  28     private String fContentType = IPHPPartitionScannerConstants.HTML;
 
  29     private String fPrevContentType = IPHPPartitionScannerConstants.HTML;
 
  31     private boolean partitionBorder = false;
 
  32     private int fTokenOffset;
 
  33     private int fEnd = -1;
 
  35     private int fCurrentLength;
 
  36     private Map tokens = new HashMap();
 
  38     public PHPPartitionScanner()
 
  41             IPHPPartitionScannerConstants.PHP,
 
  42             new Token(IPHPPartitionScannerConstants.PHP));
 
  44             IPHPPartitionScannerConstants.PHP_MULTILINE_COMMENT,
 
  45             new Token(IPHPPartitionScannerConstants.PHP_MULTILINE_COMMENT));
 
  47             IPHPPartitionScannerConstants.HTML,
 
  48             new Token(IPHPPartitionScannerConstants.HTML));
 
  50             IPHPPartitionScannerConstants.HTML_MULTILINE_COMMENT,
 
  51             new Token(IPHPPartitionScannerConstants.HTML_MULTILINE_COMMENT));
 
  53             IDocument.DEFAULT_CONTENT_TYPE,
 
  54             new Token(IDocument.DEFAULT_CONTENT_TYPE));
 
  57     private IToken getToken(String type)
 
  59         fLength = fCurrentLength;
 
  67                     int line = fDocument.getLineOfOffset(fOffset);
 
  73                                 fOffset - fDocument.getLineOffset(line)));
 
  76             catch (BadLocationException e)
 
  78                 // TODO Auto-generated catch block
 
  82         Assert.isTrue(fLength > 0, "Partition length <= 0!");
 
  84         // String can never cross partition borders so reset string detection
 
  86         fInDoubString = false;
 
  87         IToken token = (IToken) this.tokens.get(type);
 
  88         Assert.isNotNull(token, "Token for type \"" + type + "\" not found!");
 
  92                 "Partition: fTokenOffset="
 
 103      * @see org.eclipse.jface.text.rules.IPartitionTokenScanner#setPartialRange(org.eclipse.jface.text.IDocument, int, int, java.lang.String, int)
 
 105     public void setPartialRange(
 
 115                 "PartialRange: contentType="
 
 117                     + " partitionOffset="
 
 123             if (partitionOffset > -1)
 
 125                 partitionBorder = false;
 
 126                 // because of strings we have to parse the whole partition
 
 130                     offset - partitionOffset + length);
 
 131                 // sometimes we get a wrong partition so we retrieve the partition
 
 132                 // directly from the document
 
 133                 fContentType = fDocument.getContentType(partitionOffset);
 
 134                 //TODO determine the previouse contenttypes as a stack
 
 135                 //if(partitionOffset > 1)
 
 136                 //      fPrevContentType = fDocument.getContentType(partitionOffset-1);
 
 139                 this.setRange(document, offset, length);
 
 142         catch (BadLocationException e)
 
 144             // should never happen
 
 145             // TODO print stack trace to log
 
 146             // fall back just scan the whole document again
 
 147             this.setRange(document, 0, fDocument.getLength());
 
 153      * @see org.eclipse.jface.text.rules.ITokenScanner#getTokenLength()
 
 155     public int getTokenLength()
 
 161      * @see org.eclipse.jface.text.rules.ITokenScanner#getTokenOffset()
 
 163     public int getTokenOffset()
 
 169      * @see org.eclipse.jface.text.rules.ITokenScanner#nextToken()
 
 171     public IToken nextToken()
 
 175         // check if we are not allready at the end of the
 
 177         if ((c = read()) == ICharacterScanner.EOF)
 
 179             partitionBorder = false;
 
 187             fTokenOffset = fOffset;
 
 188             partitionBorder = false;
 
 191         while ((c = read()) != ICharacterScanner.EOF)
 
 196                     if (!isInString(IPHPPartitionScannerConstants.PHP)
 
 198                             != IPHPPartitionScannerConstants.PHP_MULTILINE_COMMENT
 
 199                         && checkPattern(new char[] { '?', 'p', 'h', 'p' }, true))
 
 201                         if (fContentType != IPHPPartitionScannerConstants.PHP
 
 202                             && fCurrentLength > 5)
 
 205                             IToken token = getToken(fContentType);
 
 206                             // save previouse contenttype
 
 207                             //TODO build stack for previouse contenttype 
 
 208                             fPrevContentType = fContentType;
 
 210                             fContentType = IPHPPartitionScannerConstants.PHP;
 
 215                             fContentType = IPHPPartitionScannerConstants.PHP;
 
 217                         // remember offset of this partition
 
 218                         fTokenOffset = fOffset - 5;
 
 222                         !isInString(IPHPPartitionScannerConstants.PHP)
 
 224                                 != IPHPPartitionScannerConstants
 
 225                                     .PHP_MULTILINE_COMMENT
 
 226                             && checkPattern(new char[] { '?' }, false))
 
 228                         if (fContentType != IPHPPartitionScannerConstants.PHP
 
 229                             && fCurrentLength > 2)
 
 232                             IToken token = getToken(fContentType);
 
 233                             // save previouse contenttype
 
 234                             fPrevContentType = fContentType;
 
 235                             fContentType = IPHPPartitionScannerConstants.PHP;
 
 239                             fContentType = IPHPPartitionScannerConstants.PHP;
 
 240                         // remember offset of this partition
 
 241                         fTokenOffset = fOffset - 2;
 
 245                         !isInString(IPHPPartitionScannerConstants.PHP)
 
 246                             && checkPattern(new char[] { '!', '-', '-' }))
 
 247                     { // return previouse partition
 
 249                             != IPHPPartitionScannerConstants
 
 250                                 .HTML_MULTILINE_COMMENT
 
 251                             && fCurrentLength > 4)
 
 254                             IToken token = getToken(fContentType);
 
 256                                 IPHPPartitionScannerConstants
 
 257                                     .HTML_MULTILINE_COMMENT;
 
 262                                 IPHPPartitionScannerConstants
 
 263                                     .HTML_MULTILINE_COMMENT;
 
 265                         fTokenOffset = fOffset - 4;
 
 270                     if (!isInString(IPHPPartitionScannerConstants.PHP)
 
 271                         && fContentType == IPHPPartitionScannerConstants.PHP)
 
 273                         if ((c = read()) == '>')
 
 275                             if (fPrevContentType != null)
 
 276                                 fContentType = fPrevContentType;
 
 279                                     IPHPPartitionScannerConstants.HTML;
 
 280                             partitionBorder = true;
 
 281                             return getToken(IPHPPartitionScannerConstants.PHP);
 
 283                         else if (c != ICharacterScanner.EOF)
 
 288                     if (!isInString(IPHPPartitionScannerConstants.PHP)
 
 290                             == IPHPPartitionScannerConstants
 
 291                                 .HTML_MULTILINE_COMMENT
 
 292                         && checkPattern(new char[] { '-', '>' }))
 
 294                         fContentType = IPHPPartitionScannerConstants.HTML;
 
 295                         partitionBorder = true;
 
 297                             IPHPPartitionScannerConstants
 
 298                                 .HTML_MULTILINE_COMMENT);
 
 302                     if (!isInString(IPHPPartitionScannerConstants.PHP) && (c = read()) == '*')
 
 303                     { // MULTINE COMMENT JAVASCRIPT, CSS, PHP
 
 304                         if (fContentType == IPHPPartitionScannerConstants.PHP
 
 305                             && fCurrentLength > 2)
 
 308                             IToken token = getToken(fContentType);
 
 310                                 IPHPPartitionScannerConstants
 
 311                                     .PHP_MULTILINE_COMMENT;
 
 316                                 == IPHPPartitionScannerConstants
 
 317                                     .PHP_MULTILINE_COMMENT)
 
 320                             fTokenOffset = fOffset - 2;
 
 325                     else if (!isInString(IPHPPartitionScannerConstants.PHP) && c != ICharacterScanner.EOF)
 
 329                     if (!isInString(IPHPPartitionScannerConstants.PHP) && (c = read()) == '/')
 
 332                             == IPHPPartitionScannerConstants
 
 333                                 .PHP_MULTILINE_COMMENT)
 
 335                             fContentType = IPHPPartitionScannerConstants.PHP;
 
 336                             partitionBorder = true;
 
 338                                 IPHPPartitionScannerConstants
 
 339                                     .PHP_MULTILINE_COMMENT);
 
 343                                 == IPHPPartitionScannerConstants
 
 344                                     .CSS_MULTILINE_COMMENT)
 
 349                                 == IPHPPartitionScannerConstants
 
 350                                     .JS_MULTILINE_COMMENT)
 
 354                     else if (!isInString(IPHPPartitionScannerConstants.PHP) && c != ICharacterScanner.EOF)
 
 359                         fInString = !fInString;
 
 362                     // toggle String mode
 
 364                         fInDoubString = !fInDoubString;
 
 367         } // end of file reached but we have to return the
 
 369         return getToken(fContentType);
 
 372      * @see org.eclipse.jface.text.rules.ITokenScanner#setRange(org.eclipse.jface.text.IDocument, int, int)
 
 374     public void setRange(IDocument document, int offset, int length)
 
 379                 "SET RANGE: offset=" + offset + " length=" + length);
 
 382         fDocument = document;
 
 384         fTokenOffset = offset;
 
 387         fEnd = fOffset + length;
 
 389         fInDoubString = false;
 
 390         //partitionBorder = false;
 
 400                 return fDocument.getChar(fOffset++);
 
 402             return ICharacterScanner.EOF;
 
 404         catch (BadLocationException e)
 
 406             // should never happen
 
 407             // TODO write stacktrace to log
 
 409             return ICharacterScanner.EOF;
 
 413     private void unread()
 
 419     private void unread(int num)
 
 422         fCurrentLength -= num;
 
 425     private boolean checkPattern(char[] pattern)
 
 427         return checkPattern(pattern, false);
 
 431      * Check if next character sequence read from document is equals to 
 
 432      * the provided pattern. Pattern is read from left to right until the 
 
 433      * first character read doesn't match. If this happens all read characters are
 
 435      * @param pattern The pattern to check.
 
 436      * @return <code>true</code> if pattern is equals else returns <code>false</code>.
 
 438     private boolean checkPattern(char[] pattern, boolean ignoreCase)
 
 440         int prevOffset = fOffset;
 
 441         int prevLength = fCurrentLength;
 
 442         for (int i = 0; i < pattern.length; i++)
 
 446             if (c == ICharacterScanner.EOF
 
 447                 || !letterEquals(c, pattern[i], ignoreCase))
 
 449                 fOffset = prevOffset;
 
 450                 fCurrentLength = prevLength;
 
 458     private boolean letterEquals(int test, char letter, boolean ignoreCase)
 
 464                 && Character.isLowerCase(letter)
 
 465                 && test == Character.toUpperCase(letter))
 
 469                 && Character.isUpperCase(letter)
 
 470                 && test == Character.toLowerCase(letter))
 
 477      * Checks wether the offset is in a <code>String</code> and the specified 
 
 478      * contenttype is the current content type.
 
 479      * Strings are delimited, mutual exclusive, by a " or by a '.
 
 481      * @param contentType The contenttype to check.
 
 482      * @return <code>true</code> if the current offset is in a string else 
 
 485     private  boolean isInString(String contentType)
 
 487         if(fContentType == contentType)
 
 488                 return (fInString || fInDoubString);