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.19 $
 
  11 package net.sourceforge.phpeclipse.phpeditor.php;
 
  14 import java.util.ArrayList;
 
  15 import java.util.HashMap;
 
  18 import org.eclipse.jface.text.Assert;
 
  19 import org.eclipse.jface.text.BadLocationException;
 
  20 import org.eclipse.jface.text.IDocument;
 
  21 import org.eclipse.jface.text.ITypedRegion;
 
  22 import org.eclipse.jface.text.rules.ICharacterScanner;
 
  23 import org.eclipse.jface.text.rules.IPartitionTokenScanner;
 
  24 import org.eclipse.jface.text.rules.IToken;
 
  25 import org.eclipse.jface.text.rules.Token;
 
  30 public class PHPPartitionScanner implements IPartitionTokenScanner
 
  32     private static final boolean DEBUG = false;
 
  33     private boolean fInString = false;
 
  34     private boolean fInDoubString = false;
 
  35     private IDocument fDocument = null;
 
  36     private int fOffset = -1;
 
  37     private String fContentType = IPHPPartitionScannerConstants.HTML;
 
  38     private String fPrevContentType = IPHPPartitionScannerConstants.HTML;
 
  39     private boolean partitionBorder = false;
 
  40     private int fTokenOffset;
 
  41     private int fEnd = -1;
 
  43     private int fCurrentLength;
 
  44     private Map tokens = new HashMap();
 
  46     public PHPPartitionScanner()
 
  49             IPHPPartitionScannerConstants.PHP,
 
  50             new Token(IPHPPartitionScannerConstants.PHP));
 
  52             IPHPPartitionScannerConstants.PHP_MULTILINE_COMMENT,
 
  53             new Token(IPHPPartitionScannerConstants.PHP_MULTILINE_COMMENT));
 
  55             IPHPPartitionScannerConstants.HTML,
 
  56             new Token(IPHPPartitionScannerConstants.HTML));
 
  58             IPHPPartitionScannerConstants.HTML_MULTILINE_COMMENT,
 
  59             new Token(IPHPPartitionScannerConstants.HTML_MULTILINE_COMMENT));
 
  61             IDocument.DEFAULT_CONTENT_TYPE,
 
  62             new Token(IDocument.DEFAULT_CONTENT_TYPE));
 
  65     private IToken getToken(String type)
 
  67         fLength = fCurrentLength;
 
  75                     int line = fDocument.getLineOfOffset(fOffset);
 
  81                                 fOffset - fDocument.getLineOffset(line)));
 
  84             catch (BadLocationException e)
 
  85             {   // should never happen
 
  86                 // TODO Write stacktrace to log
 
  90         Assert.isTrue(fLength > 0, "Partition length <= 0!");
 
  92         // String can never cross partition borders so reset string detection
 
  94         fInDoubString = false;
 
  95         IToken token = (IToken) this.tokens.get(type);
 
  96         Assert.isNotNull(token, "Token for type \"" + type + "\" not found!");
 
 100                 "Partition: fTokenOffset="
 
 111      * @see org.eclipse.jface.text.rules.IPartitionTokenScanner#setPartialRange(org.eclipse.jface.text.IDocument, int, int, java.lang.String, int)
 
 113     public void setPartialRange(
 
 123                 "PartialRange: contentType="
 
 125                     + " partitionOffset="
 
 131             if (partitionOffset > -1)
 
 133                 partitionBorder = false;
 
 134                 // because of strings we have to parse the whole partition
 
 138                     offset - partitionOffset + length);
 
 139                 // sometimes we get a wrong partition so we retrieve the partition
 
 140                 // directly from the document
 
 141                 fContentType = fDocument.getContentType(partitionOffset);
 
 144                 this.setRange(document, offset, length);
 
 147         catch (BadLocationException e)
 
 149             // should never happen
 
 150             // TODO print stack trace to log
 
 151             // fall back just scan the whole document again
 
 152             this.setRange(document, 0, fDocument.getLength());
 
 158      * @see org.eclipse.jface.text.rules.ITokenScanner#getTokenLength()
 
 160     public int getTokenLength()
 
 166      * @see org.eclipse.jface.text.rules.ITokenScanner#getTokenOffset()
 
 168     public int getTokenOffset()
 
 174      * @see org.eclipse.jface.text.rules.ITokenScanner#nextToken()
 
 176     public IToken nextToken()
 
 180         // check if we are not allready at the end of the
 
 182         if ((c = read()) == ICharacterScanner.EOF)
 
 184             partitionBorder = false;
 
 192             fTokenOffset = fOffset;
 
 193             partitionBorder = false;
 
 196         while ((c = read()) != ICharacterScanner.EOF)
 
 201                     if (!isInString(IPHPPartitionScannerConstants.PHP)
 
 203                             != IPHPPartitionScannerConstants.PHP_MULTILINE_COMMENT
 
 204                         && checkPattern(new char[] { '?', 'p', 'h', 'p' }, true))
 
 206                         if (fContentType != IPHPPartitionScannerConstants.PHP
 
 207                             && fCurrentLength > 5)
 
 210                             IToken token = getToken(fContentType);
 
 211                             // save previouse contenttype
 
 212                             //TODO build stack for previouse contenttype 
 
 213                             fPrevContentType = fContentType;
 
 215                             fContentType = IPHPPartitionScannerConstants.PHP;
 
 220                             fContentType = IPHPPartitionScannerConstants.PHP;
 
 222                         // remember offset of this partition
 
 223                         fTokenOffset = fOffset - 5;
 
 227                         !isInString(IPHPPartitionScannerConstants.PHP)
 
 229                                 != IPHPPartitionScannerConstants
 
 230                                     .PHP_MULTILINE_COMMENT
 
 231                             && checkPattern(new char[] { '?' }, false))
 
 233                         if (fContentType != IPHPPartitionScannerConstants.PHP
 
 234                             && fCurrentLength > 2)
 
 237                             IToken token = getToken(fContentType);
 
 238                             // save previouse contenttype
 
 239                             fPrevContentType = fContentType;
 
 240                             fContentType = IPHPPartitionScannerConstants.PHP;
 
 244                             fContentType = IPHPPartitionScannerConstants.PHP;
 
 245                         // remember offset of this partition
 
 246                         fTokenOffset = fOffset - 2;
 
 250                         !isInString(IPHPPartitionScannerConstants.PHP)
 
 251                             && checkPattern(new char[] { '!', '-', '-' }))
 
 252                     { // return previouse partition
 
 254                             != IPHPPartitionScannerConstants
 
 255                                 .HTML_MULTILINE_COMMENT
 
 256                             && fCurrentLength > 4)
 
 259                             IToken token = getToken(fContentType);
 
 261                                 IPHPPartitionScannerConstants
 
 262                                     .HTML_MULTILINE_COMMENT;
 
 267                                 IPHPPartitionScannerConstants
 
 268                                     .HTML_MULTILINE_COMMENT;
 
 270                         fTokenOffset = fOffset - 4;
 
 275                     if (!isInString(IPHPPartitionScannerConstants.PHP)
 
 276                         && fContentType == IPHPPartitionScannerConstants.PHP)
 
 278                         if ((c = read()) == '>')
 
 280                             if (fPrevContentType != null)
 
 281                                 fContentType = fPrevContentType;
 
 284                                     IPHPPartitionScannerConstants.HTML;
 
 285                             partitionBorder = true;
 
 286                             return getToken(IPHPPartitionScannerConstants.PHP);
 
 288                         else if (c != ICharacterScanner.EOF)
 
 293                     if (!isInString(IPHPPartitionScannerConstants.PHP)
 
 295                             == IPHPPartitionScannerConstants
 
 296                                 .HTML_MULTILINE_COMMENT
 
 297                         && checkPattern(new char[] { '-', '>' }))
 
 299                         fContentType = IPHPPartitionScannerConstants.HTML;
 
 300                         partitionBorder = true;
 
 302                             IPHPPartitionScannerConstants
 
 303                                 .HTML_MULTILINE_COMMENT);
 
 307                     if (!isInString(IPHPPartitionScannerConstants.PHP) && (c = read()) == '*')
 
 308                     { // MULTINE COMMENT JAVASCRIPT, CSS, PHP
 
 309                         if (fContentType == IPHPPartitionScannerConstants.PHP
 
 310                             && fCurrentLength > 2)
 
 313                             IToken token = getToken(fContentType);
 
 315                                 IPHPPartitionScannerConstants
 
 316                                     .PHP_MULTILINE_COMMENT;
 
 321                                 == IPHPPartitionScannerConstants
 
 322                                     .PHP_MULTILINE_COMMENT)
 
 325                             fTokenOffset = fOffset - 2;
 
 330                     else if (!isInString(IPHPPartitionScannerConstants.PHP) && c != ICharacterScanner.EOF)
 
 334                     if (!isInString(IPHPPartitionScannerConstants.PHP) && (c = read()) == '/')
 
 337                             == IPHPPartitionScannerConstants
 
 338                                 .PHP_MULTILINE_COMMENT)
 
 340                             fContentType = IPHPPartitionScannerConstants.PHP;
 
 341                             partitionBorder = true;
 
 343                                 IPHPPartitionScannerConstants
 
 344                                     .PHP_MULTILINE_COMMENT);
 
 348                                 == IPHPPartitionScannerConstants
 
 349                                     .CSS_MULTILINE_COMMENT)
 
 354                                 == IPHPPartitionScannerConstants
 
 355                                     .JS_MULTILINE_COMMENT)
 
 359                     else if (!isInString(IPHPPartitionScannerConstants.PHP) && c != ICharacterScanner.EOF)
 
 364                         fInString = !fInString;
 
 367                     // toggle String mode
 
 369                         fInDoubString = !fInDoubString;
 
 372         } // end of file reached but we have to return the
 
 374         return getToken(fContentType);
 
 377      * @see org.eclipse.jface.text.rules.ITokenScanner#setRange(org.eclipse.jface.text.IDocument, int, int)
 
 379     public void setRange(IDocument document, int offset, int length)
 
 384                 "SET RANGE: offset=" + offset + " length=" + length);
 
 387         fDocument = document;
 
 389         fTokenOffset = offset;
 
 392         fEnd = fOffset + length;
 
 394         fInDoubString = false;
 
 395         fContentType = IPHPPartitionScannerConstants.HTML;
 
 396 //        String[] prev = getPartitionStack(offset);
 
 406                 return fDocument.getChar(fOffset++);
 
 408             return ICharacterScanner.EOF;
 
 410         catch (BadLocationException e)
 
 412             // should never happen
 
 413             // TODO write stacktrace to log
 
 415             return ICharacterScanner.EOF;
 
 419     private void unread()
 
 425     private void unread(int num)
 
 428         fCurrentLength -= num;
 
 431     private boolean checkPattern(char[] pattern)
 
 433         return checkPattern(pattern, false);
 
 437      * Check if next character sequence read from document is equals to 
 
 438      * the provided pattern. Pattern is read from left to right until the 
 
 439      * first character read doesn't match. If this happens all read characters are
 
 441      * @param pattern The pattern to check.
 
 442      * @return <code>true</code> if pattern is equals else returns <code>false</code>.
 
 444     private boolean checkPattern(char[] pattern, boolean ignoreCase)
 
 446         int prevOffset = fOffset;
 
 447         int prevLength = fCurrentLength;
 
 448         for (int i = 0; i < pattern.length; i++)
 
 452             if (c == ICharacterScanner.EOF
 
 453                 || !letterEquals(c, pattern[i], ignoreCase))
 
 455                 fOffset = prevOffset;
 
 456                 fCurrentLength = prevLength;
 
 464     private boolean letterEquals(int test, char letter, boolean ignoreCase)
 
 470                 && Character.isLowerCase(letter)
 
 471                 && test == Character.toUpperCase(letter))
 
 475                 && Character.isUpperCase(letter)
 
 476                 && test == Character.toLowerCase(letter))
 
 483      * Checks wether the offset is in a <code>String</code> and the specified 
 
 484      * contenttype is the current content type.
 
 485      * Strings are delimited, mutual exclusive, by a " or by a '.
 
 487      * @param contentType The contenttype to check.
 
 488      * @return <code>true</code> if the current offset is in a string else 
 
 491     private  boolean isInString(String contentType)
 
 493         if(fContentType == contentType)
 
 494                 return (fInString || fInDoubString);
 
 500      * Returns the previouse partition stack for the given offset.
 
 502      * @param offset The offset to return the previouse partitionstack for.
 
 504      * @return The stack as a string array.
 
 506     private String[] getPartitionStack(int offset)
 
 508         ArrayList types = new ArrayList();
 
 512             ITypedRegion region = fDocument.getPartition(offset);
 
 513             tmpOffset = region.getOffset();
 
 514             while(tmpOffset-1 > 0)
 
 516                 region = fDocument.getPartition(tmpOffset-1);
 
 517                 tmpOffset = region.getOffset();
 
 518                 types.add(0, region.getType());
 
 521         catch (BadLocationException e)
 
 529                 String[] retVal = new String[types.size()];
 
 531         retVal = (String[])types.toArray(retVal);