/**********************************************************************
-Copyright (c) 2000, 2002 IBM Corp. and others.
-All rights reserved. This program and the accompanying materials
-are made available under the terms of the Common Public License v1.0
-which accompanies this distribution, and is available at
-http://www.eclipse.org/legal/cpl-v10.html
-
-Contributors:
- IBM Corporation - Initial implementation
- Klaus Hartlage - www.eclipseproject.de
-**********************************************************************/
+ Copyright (c) 2002 Widespace, OU and others.
+ All rights reserved. This program and the accompanying materials
+ are made available under the terms of the Common Public License v1.0
+ which accompanies this distribution, and is available at
+ http://solareclipse.sourceforge.net/legal/cpl-v10.html
+
+ Contributors:
+ Igor Malinin - initial contribution
+
+ $Id: PHPPartitionScanner.java,v 1.35 2007-03-17 14:07:31 axelcl Exp $
+ **********************************************************************/
package net.sourceforge.phpeclipse.phpeditor.php;
-import java.util.ArrayList;
-import java.util.List;
-import org.eclipse.jface.text.rules.EndOfLineRule;
+import java.util.HashMap;
+import java.util.Map;
+
+import net.sourceforge.phpdt.internal.compiler.parser.Scanner;
+import net.sourceforge.phpeclipse.ui.text.rules.AbstractPartitioner;
+
+import org.eclipse.jface.text.Assert;
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.rules.ICharacterScanner;
-import org.eclipse.jface.text.rules.IPredicateRule;
-import org.eclipse.jface.text.rules.IRule;
+import org.eclipse.jface.text.rules.IPartitionTokenScanner;
import org.eclipse.jface.text.rules.IToken;
-import org.eclipse.jface.text.rules.IWordDetector;
-import org.eclipse.jface.text.rules.MultiLineRule;
-import org.eclipse.jface.text.rules.RuleBasedPartitionScanner;
-import org.eclipse.jface.text.rules.RuleBasedScanner;
-import org.eclipse.jface.text.rules.SingleLineRule;
import org.eclipse.jface.text.rules.Token;
-import org.eclipse.jface.text.rules.WordRule;
/**
- * This scanner recognizes the JavaDoc comments and Java multi line comments.
+ *
+ *
+ * @author Igor Malinin
*/
-public class PHPPartitionScanner extends RuleBasedPartitionScanner {
+public class PHPPartitionScanner implements IPartitionTokenScanner {
+ public static final String PHP_SCRIPTING_AREA = "__php_scripting_area ";
- private final static String SKIP = "__skip"; //$NON-NLS-1$
- public final static String JAVA_MULTILINE_COMMENT = "__html_multiline_comment"; //$NON-NLS-1$
- // public final static String JAVA_DOC= "__java_javadoc"; //$NON-NLS-1$
- public final static String PHP = "__php";
+ public static final int STATE_DEFAULT = 0;
- public class PHPMultiLineRule extends MultiLineRule {
+ // public static final int STATE_TAG = 1;
+ // public static final int STATE_SCRIPT = 2;
- public PHPMultiLineRule(String startSequence, String endSequence, IToken token) {
- super(startSequence, endSequence, token);
- }
+ private IDocument document;
+
+ // private int begin;
+
+ private int end;
+
+ private int offset;
+
+ private int length;
+
+ private int position;
+
+ // private int state;
+
+ private Map tokens = new HashMap();
+
+ public PHPPartitionScanner() {
+ }
+
+ /*
+ * @see org.eclipse.jface.text.rules.ITokenScanner#nextToken()
+ */
+ public IToken nextToken() {
+ offset += length;
+
+ /*
+ * switch (state) { case STATE_TAG: return nextTagToken(); }
+ */
+
+ switch (read()) {
+ case ICharacterScanner.EOF:
+ // state = STATE_DEFAULT;
+ return getToken(null);
- public PHPMultiLineRule(String startSequence, String endSequence, IToken token, char escapeCharacter) {
- super(startSequence, endSequence, token, escapeCharacter);
+ case '<':
+ switch (read()) {
+ case ICharacterScanner.EOF:
+ // state = STATE_DEFAULT;
+ return getToken(null);
+
+ case '?': // <?
+ // int ch = read();
+ //
+ // switch (ch) {
+ // case ICharacterScanner.EOF:
+ // state = STATE_DEFAULT;
+ // return getToken(PHP_SCRIPTING_AREA);
+ // }
+ return scanUntilPHPEndToken(PHP_SCRIPTING_AREA);
+ }
+
+ unread();
}
- protected boolean endSequenceDetected(ICharacterScanner scanner) {
- int c;
- int c2;
+ loop: while (true) {
+ switch (read()) {
+ case ICharacterScanner.EOF:
+ // state = STATE_DEFAULT;
+ return getToken(null);
- boolean lineCommentMode = false;
- boolean multiLineCommentMode = false;
- boolean stringMode = false;
+ case '<':
+ switch (read()) {
+ case ICharacterScanner.EOF:
+ // state = STATE_DEFAULT;
+ return getToken(null);
- char[][] delimiters = scanner.getLegalLineDelimiters();
- while ((c = scanner.read()) != ICharacterScanner.EOF) {
- if (c == '\n') {
- lineCommentMode = false;
- // read until end of line
- } else if (c == '#') {
- // read until end of line
- lineCommentMode = true;
- continue;
- } else if (c == '/') {
- c2 = scanner.read();
- if (c2 == '/') {
- lineCommentMode = true;
- continue;
- } else if(c2 == '*') {
- multiLineCommentMode = true;
- continue;
- } else {
- scanner.unread();
- }
- } else if (c == '*' && multiLineCommentMode) {
- c2 = scanner.read();
- if (c2 == '/') {
- multiLineCommentMode = false;
- continue;
- } else {
- scanner.unread();
+ case '?':
+ unread();
+ break;
+
+ case '<':
+ unread();
+
+ default:
+ continue loop;
+ }
+
+ unread();
+
+ // state = STATE_DEFAULT;
+ return getToken(null);
+ }
+ }
+ }
+
+ private IToken scanUntilPHPEndToken(String token) {
+ int ch = read();
+ while (true) {
+ switch (ch) {
+ case ICharacterScanner.EOF:
+ // state = STATE_DEFAULT;
+ return getToken(token);
+ case '"': // double quoted string
+ // read until end of double quoted string
+ if (!readUntilEscapedDQ()) {
+ // state = STATE_DEFAULT;
+ return getToken(token);
+ }
+ break;
+ case '<': // heredoc string
+ ch = read();
+ switch (ch) {
+ case ICharacterScanner.EOF:
+ break;
+ case '<':
+ ch = read();
+ switch (ch) {
+ case ICharacterScanner.EOF:
+ break;
+ case '<':
+ // read until end of heredoc string
+ if (!readUntilEscapedHEREDOC()) {
+ // state = STATE_DEFAULT;
+ return getToken(token);
+ }
}
- } else if (c == '\\' && stringMode) {
- c2 = scanner.read();
- if (c2 == '"') {
- continue;
- } else {
- scanner.unread();
+ }
+ break;
+ case '\'': // single quoted string
+ // read until end of single quoted string
+ if (!readUntilEscapedSQ()) {
+ // state = STATE_DEFAULT;
+ return getToken(token);
+ }
+ break;
+ case '/': // comment start?
+ ch = read();
+ switch (ch) {
+ case ICharacterScanner.EOF:
+ break;
+ case '/':
+ // read until end of line
+ if (!readSingleLine()) {
+ // state = STATE_DEFAULT;
+ return getToken(token);
}
- } else if (c == '"') {
- if (stringMode) {
- stringMode = false;
- } else {
- stringMode = true;
+ break;
+ case '*':
+ // read until end of comment
+ if (!readMultiLineComment()) {
+ // state = STATE_DEFAULT;
+ return getToken(token);
}
+ break;
+ default:
continue;
}
- if (lineCommentMode || multiLineCommentMode || stringMode) {
- continue;
+ break;
+ case '#': // line comment
+ // read until end of line
+ if (!readSingleLine()) {
+ // state = STATE_DEFAULT;
+ return getToken(token);
}
+ break;
+ case '?':
+ ch = read();
+ switch (ch) {
+ case ICharacterScanner.EOF:
+ case '>':
+ // state = STATE_DEFAULT;
+ return getToken(token);
- if (c == fEscapeCharacter) {
- // Skip the escaped character.
- scanner.read();
- } else if (fEndSequence.length > 0 && c == fEndSequence[0]) {
- // Check if the specified end sequence has been found.
- if (sequenceDetected(scanner, fEndSequence, true))
- return true;
- } else if (fBreaksOnEOL) {
- // Check for end of line since it can be used to terminate the pattern.
- for (int i = 0; i < delimiters.length; i++) {
- if (c == delimiters[i][0] && sequenceDetected(scanner, delimiters[i], false))
- return true;
- }
+ case '?':
+ continue;
+ default:
+ continue;
}
}
- scanner.unread();
- return true;
+
+ ch = read();
}
}
- /**
- * Detector for empty comments.
- */
- static class EmptyCommentDetector implements IWordDetector {
- /* (non-Javadoc)
- * Method declared on IWordDetector
- */
- public boolean isWordStart(char c) {
- return (c == '/');
+ private IToken getToken(String type) {
+ length = position - offset;
+
+ if (length == 0) {
+ return Token.EOF;
}
- /* (non-Javadoc)
- * Method declared on IWordDetector
- */
- public boolean isWordPart(char c) {
- return (c == '*' || c == '/');
+ // if (length<0) {
+ // try {
+ // System.out.println("Length<0:"+document.get(offset,5)+""+length);
+ // } catch (BadLocationException e) {
+ // e.printStackTrace();
+ // }
+ // }
+
+ if (type == null) {
+ return Token.UNDEFINED;
}
- };
- /**
- *
- */
- static class WordPredicateRule extends WordRule implements IPredicateRule {
+ IToken token = (IToken) tokens.get(type);
+ if (token == null) {
+ token = new Token(type);
+ tokens.put(type, token);
+ }
- private IToken fSuccessToken;
+ return token;
+ }
- public WordPredicateRule(IToken successToken) {
- super(new EmptyCommentDetector());
- fSuccessToken = successToken;
- addWord("/**/", fSuccessToken);
+ private int read() {
+ if (position >= end) {
+ return ICharacterScanner.EOF;
}
- /*
- * @see org.eclipse.jface.text.rules.IPredicateRule#evaluate(ICharacterScanner, boolean)
- */
- public IToken evaluate(ICharacterScanner scanner, boolean resume) {
- return super.evaluate(scanner);
+ try {
+ return document.getChar(position++);
+ } catch (BadLocationException e) {
+ --position;
+ return ICharacterScanner.EOF;
}
+ }
- /*
- * @see org.eclipse.jface.text.rules.IPredicateRule#getSuccessToken()
- */
- public IToken getSuccessToken() {
- return fSuccessToken;
+ private boolean readUntilEscapedDQ() {
+ // search last double quoted character
+ try {
+ char ch;
+ while (true) {
+ if (position >= end) {
+ return false;
+ }
+ ch = document.getChar(position++);
+ if (ch == '\\') {
+ if (position >= end) {
+ return false;
+ }
+ ch = document.getChar(position++); // ignore escaped
+ // character
+ } else if (ch == '"') {
+ return true;
+ }
+ }
+ } catch (BadLocationException e) {
+ --position;
}
- };
+ return false;
+ }
- /**
- * Creates the partitioner and sets up the appropriate rules.
- */
- public PHPPartitionScanner() {
- super();
+ private boolean readUntilEscapedSQ() {
+ // search last single quoted character
+ try {
+ char ch;
+ while (true) {
+ if (position >= end) {
+ return false;
+ }
+ ch = document.getChar(position++);
+ if (ch == '\\') {
+ if (position >= end) {
+ return false;
+ }
+ ch = document.getChar(position++); // ignore escaped
+ // character
+ } else if (ch == '\'') {
+ return true;
+ }
+ }
+ } catch (BadLocationException e) {
+ --position;
+ }
+ return false;
+ }
+
+ private boolean readUntilEscapedHEREDOC() {
+ // search until heredoc ends
+ try {
+ char ch;
+ StringBuffer buf = new StringBuffer();
+ char[] heredocIdent;
+ if (position >= end) {
+ return false;
+ }
+ ch = document.getChar(position++);
+ // #1493165 start
+ while (ch == ' ') {
+ if (position >= end) {
+ return false;
+ }
+ ch = document.getChar(position++);
+ }
+ // #1493165 end
+ if (!Scanner.isPHPIdentifierStart(ch)) {
+ return false;
+ }
+ while (Scanner.isPHPIdentifierPart(ch)) {
+ buf.append(ch);
+ if (position >= end) {
+ return false;
+ }
+ ch = document.getChar(position++);
+ }
+ heredocIdent = buf.toString().toCharArray();
+ while (true) {
+ if (position >= end) {
+ return false;
+ }
+ ch = document.getChar(position++);
+ if (ch == '\n') { // heredoc could end after a newline
+ int pos = 0;
+ while (true) {
+ if (position >= end) {
+ return false;
+ }
+ if (pos == heredocIdent.length) {
+ return true;
+ }
+ ch = document.getChar(position++); // ignore escaped
+ // character
+ if (ch != heredocIdent[pos]) {
+ break;
+ }
+ pos++;
+ }
+ }
+ }
+ } catch (BadLocationException e) {
+ --position;
+ }
+ return false;
+ }
+
+ private boolean readSingleLine() {
+ try {
+ do {
+ if (position >= end) {
+ return false;
+ }
+ } while (document.getChar(position++) != '\n');
+ return true;
+ } catch (BadLocationException e) {
+ --position;
+ }
+ return false;
+ }
- // IToken javaDoc= new Token(JAVA_DOC);
- IToken comment = new Token(JAVA_MULTILINE_COMMENT);
- IToken php = new Token(PHP);
+ private boolean readMultiLineComment() {
+ try {
+ char ch;
+ while (true) {
+ if (position >= end) {
+ return false;
+ }
+ ch = document.getChar(position++);
+ if (ch == '*') {
+ if (position >= end) {
+ return false;
+ }
+ if (document.getChar(position) == '/') {
+ position++;
+ return true;
+ }
+ }
+ }
+ } catch (BadLocationException e) {
+ --position;
+ }
+ return false;
+ }
- List rules = new ArrayList();
+ private void unread() {
+ --position;
+ }
- // Add rule for single line comments.
- // rules.add(new EndOfLineRule("//", Token.UNDEFINED));
+ /*
+ * @see org.eclipse.jface.text.rules.ITokenScanner#getTokenOffset()
+ */
+ public int getTokenOffset() {
+ if (AbstractPartitioner.DEBUG) {
+ Assert.isTrue(offset >= 0, Integer.toString(offset));
+ }
+ return offset;
+ }
- // Add rule for strings and character constants.
- // rules.add(new SingleLineRule("\"", "\"", Token.UNDEFINED, '\\'));
- // rules.add(new SingleLineRule("'", "'", Token.UNDEFINED, '\\'));
+ /*
+ * @see org.eclipse.jface.text.rules.ITokenScanner#getTokenLength()
+ */
+ public int getTokenLength() {
+ return length;
+ }
- // Add special case word rule.
- rules.add(new WordPredicateRule(comment));
+ /*
+ * @see org.eclipse.jface.text.rules.ITokenScanner#setRange(IDocument, int,
+ * int)
+ */
+ public void setRange(IDocument document, int offset, int length) {
+ this.document = document;
+ // this.begin = offset;
+ this.end = offset + length;
- // Add rules for multi-line comments and javadoc.
- //rules.add(new MultiLineRule("/**", "*/", javaDoc));
- rules.add(new MultiLineRule("<!--", "-->", comment));
- rules.add(new PHPMultiLineRule("<? ", "?>", php));
- rules.add(new PHPMultiLineRule("<?php", "?>", php));
- rules.add(new PHPMultiLineRule("<?PHP", "?>", php));
- //Add rule for processing instructions
+ this.offset = offset;
+ this.position = offset;
+ this.length = 0;
+ }
- IPredicateRule[] result = new IPredicateRule[rules.size()];
- rules.toArray(result);
- setPredicateRules(result);
+ /*
+ * @see org.eclipse.jface.text.rules.IPartitionTokenScanner
+ */
+ public void setPartialRange(IDocument document, int offset, int length,
+ String contentType, int partitionOffset) {
+ // state = STATE_DEFAULT;
+ if (partitionOffset > -1) {
+ int delta = offset - partitionOffset;
+ if (delta > 0) {
+ setRange(document, partitionOffset, length + delta);
+ return;
+ }
+ }
+ setRange(document, partitionOffset, length);
}
-}
+
+ // private boolean isContinuationPartition(IDocument document, int offset) {
+ // try {
+ // String type = document.getContentType(offset - 1);
+ //
+ // if (type != IDocument.DEFAULT_CONTENT_TYPE) {
+ // return true;
+ // }
+ // } catch (BadLocationException e) {}
+ //
+ // return false;
+ // }
+}
\ No newline at end of file