package com.liferay.ide.velocity.scanner;
import org.eclipse.jface.text.rules.ICharacterScanner;
import org.eclipse.jface.text.rules.IPredicateRule;
import org.eclipse.jface.text.rules.IToken;
import org.eclipse.jface.text.rules.Token;
/**
* A modification of the PatternRule that skip over quoted strings.
*/
public class HTMLTagRule implements IPredicateRule
{
protected static final int UNDEFINED = -1;
/** The token to be returned on success */
protected IToken fToken;
/** The pattern's start sequence */
protected char[] fStartSequence;
/** The pattern's end sequence */
protected char[] fEndSequence;
/** The pattern's column constrain */
protected int fColumn = UNDEFINED;
/** The pattern's escape character */
protected char fEscapeCharacter;
/** Indicates whether end of line termines the pattern */
protected boolean fBreaksOnEOL;
/**
* Creates a rule for the given starting and ending sequence. When these
* sequences are detected the rule will return the specified token.
* Alternatively, the sequence can also be ended by the end of the line. Any
* character which follows the given escapeCharacter will be ignored.
*
* @param startSequence
* the pattern's start sequence
* @param endSequence
* the pattern's end sequence, <code>null</code> is a legal
* value
* @param token
* the token which will be returned on success
* @param escapeCharacter
* any character following this one will be ignored
* @param indicates
* whether the end of the line also termines the pattern
*/
public HTMLTagRule(String startSequence, String endSequence, IToken token, char escapeCharacter, boolean breaksOnEOL)
{
//
fStartSequence = startSequence.toCharArray();
fEndSequence = ((endSequence == null) ? new char[0] : endSequence.toCharArray());
fToken = token;
fEscapeCharacter = escapeCharacter;
fBreaksOnEOL = breaksOnEOL;
}
/**
* Sets a column constraint for this rule. If set, the rule's token will
* only be returned if the pattern is detected starting at the specified
* column. If the column is smaller then 0, the column constraint is
* considered removed.
*
* @param column
* the column in which the pattern starts
*/
public void setColumnConstraint(int column)
{
if (column < 0)
{
column = UNDEFINED;
}
fColumn = column;
}
/*
* @see IRule#evaluate
*/
public IToken evaluate(ICharacterScanner scanner)
{
return evaluate(scanner, false);
}
/*
* @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 (c == fStartSequence[0])
{
return ((fColumn == scanner.getColumn()) ? doEvaluate(scanner, resume) : Token.UNDEFINED);
} else
{
return Token.UNDEFINED;
}
}
/*
* @see IPredicateRule#getSuccessToken()
* @since 2.0
*/
public IToken getSuccessToken()
{
return fToken;
}
/**
* Evaluates this rules without considering any column constraints.
*
* @param scanner
* the character scanner to be used
* @return the token resulting from this evaluation
*/
protected IToken doEvaluate(ICharacterScanner scanner)
{
return doEvaluate(scanner, false);
}
/**
* Evaluates this rules without considering any column constraints. Resumes
* detection, i.e. look sonly 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 (c == fStartSequence[0])
{
if (sequenceDetected(scanner, fStartSequence, false))
{
if (endSequenceDetected(scanner)) { return fToken; }
}
}
}
scanner.unread();
return Token.UNDEFINED;
}
/**
* 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)
{
for (int i = 1; i < sequence.length; i++)
{
int c = scanner.read();
if ((c == ICharacterScanner.EOF) && eofAllowed)
{
return true;
} else if (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;
}
}
return true;
}
/**
* 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 ((c == '\"') || (c == '\''))
{
if (skipQuoted(scanner, c)) { return true; }
} 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; }
}
}
}
scanner.unread();
return true;
}
private boolean skipQuoted(ICharacterScanner scanner, int delim)
{
int c;
char[][] delimiters = scanner.getLegalLineDelimiters();
while ((c = scanner.read()) != ICharacterScanner.EOF)
{
if (c == fEscapeCharacter)
{
scanner.read();
} else if (c == delim)
{
return false;
} else if (c == '<')
{
scanner.unread();
return false;
} 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; }
}
}
}
return false;
}
}