/******************************************************************************* * Copyright © 2000, 2013 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation * *******************************************************************************/ package org.eclipse.edt.ide.ui.internal.editor; 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.IToken; import org.eclipse.jface.text.rules.MultiLineRule; import org.eclipse.jface.text.rules.Token; public class EGLMultiLineRule extends MultiLineRule { public EGLMultiLineRule(String startSequence, String endSequence, IToken token) { super(startSequence, endSequence, token, (char) 0); } /** * Evaluates this rules without considering any column constraints. Resumes * detection, i.e. looks only for the end sequence required by this rule if the * <code>resume</code> flag is set. * * @param scanner the character scanner to be used * @param resume <code>true</code> if detection should be resumed, <code>false</code> otherwise * @return the token resulting from this evaluation * @since 2.0 */ protected IToken doEvaluate(ICharacterScanner scanner, boolean resume) { if (resume) { if (endSequenceDetected(scanner)) return fToken; } else { int c = scanner.read(); if (Character.toLowerCase((char) c) == fStartSequence[0]) { if (sequenceDetected(scanner, fStartSequence, false, false)) { if (endSequenceDetected(scanner)) return fToken; } } } scanner.unread(); return Token.UNDEFINED; } /** * Returns whether the end sequence was detected. As the pattern can be considered * ended by a line delimiter, the result of this method is <code>true</code> if the * rule breaks on the end of the line, or if the EOF character is read. * * @param scanner the character scanner to be used * @return <code>true</code> if the end sequence has been detected */ protected boolean endSequenceDetected(ICharacterScanner scanner) { int c; char[][] delimiters = scanner.getLegalLineDelimiters(); while ((c = scanner.read()) != ICharacterScanner.EOF) { if (c == fEscapeCharacter) { // Skip the escaped character. scanner.read(); } else if ((fEndSequence.length > 0) && (Character.toLowerCase((char) c) == fEndSequence[0])) { // Check if the specified end sequence has been found. if (sequenceDetected(scanner, fEndSequence, true, 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], true, true)) return true; } } } if (fBreaksOnEOF) return true; scanner.unread(); return false; } /** * Returns whether the next characters to be read by the character scanner * are an exact match with the given sequence. No escape characters are allowed * within the sequence. If specified the sequence is considered to be found * when reading the EOF character. * * @param scanner the character scanner to be used * @param sequence the sequence to be detected * @param eofAllowed indicated whether EOF terminates the pattern * @return <code>true</code> if the given sequence has been detected */ protected boolean sequenceDetected(ICharacterScanner scanner, char[] sequence, boolean eofAllowed, boolean suffixCharAllowed) { for (int i = 1; i < sequence.length; i++) { int c = scanner.read(); if (c == ICharacterScanner.EOF && eofAllowed) { return true; } else if ((Character.toLowerCase((char) c) != sequence[i])) { // Non-matching character detected, rewind the scanner back to the start. // Do not unread the first character. scanner.unread(); for (int j = i - 1; j > 0; j--) scanner.unread(); return false; } } // If we've just detected the start sequence, peek one character ahead of it in the // document and see if the character before "sql" is whitespace. If so, the token counts // as the beginning of the sql partition. If not (ie, mySQL), we should not count this // as a match. if (sequence == fStartSequence) { // Get the document from the scanner IDocument doc = ((PartitionScanner) scanner).getDocument(); // get the offset of the next character int charOffset = ((PartitionScanner) scanner).getOffset(); try { // back up to the offset before the "sql" int offsetOfCharBeforeSQL = charOffset - fStartSequence.length - 1; // if we're not at the beginning of the document, peek at the character if (offsetOfCharBeforeSQL >= 0) { char prefix = doc.getChar(offsetOfCharBeforeSQL); // If the character isn't whitespace, don't count this as a match if (!Character.isWhitespace((char) prefix)) return false; } } catch (BadLocationException e) { e.printStackTrace(); } } //RATLC01077996 - commented out to allow other characters other than whitespace after #sql{ // For example #sql{select .... } // int nextChar = scanner.read(); // if ((nextChar == ICharacterScanner.EOF) || (Character.isWhitespace((char) nextChar))) { // scanner.unread(); // return true; // } else { // scanner.unread(); // // semi colon, and closing bracket and comma are allowed for the end sequence // if (suffixCharAllowed && (nextChar == ';' || nextChar == '}' || nextChar == ',')) // return true; // else // return false; // } return true; } /* * @see IPredicateRule#evaluate(ICharacterScanner, boolean) * @since 2.0 */ public IToken evaluate(ICharacterScanner scanner, boolean resume) { if (fColumn == UNDEFINED) return doEvaluate(scanner, resume); int c = scanner.read(); scanner.unread(); if (Character.toLowerCase((char) c) == fStartSequence[0]) return (fColumn == scanner.getColumn() ? doEvaluate(scanner, resume) : Token.UNDEFINED); else return Token.UNDEFINED; } }