/**
* Aptana Studio
* Copyright (c) 2005-2011 by Appcelerator, Inc. All Rights Reserved.
* Licensed under the terms of the GNU Public License (GPL) v3 (with exceptions).
* Please see the license.html included with this distribution for details.
* Any modifications to this file must keep this entire header intact.
*/
package com.aptana.editor.php.internal.parser;
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;
/**
* PHP Heredoc and Nowdoc rule.
*
* @author Shalom Gibly <sgibly@aptana.com>
* @bugfixer Max Stepanov
*/
public class HeredocRule implements IPredicateRule
{
private IToken token;
private boolean isNowdoc;
private int readCount;
/**
* Constructs a new HeredocRule
*/
public HeredocRule(IToken token, boolean isNowdoc)
{
this.token = token;
this.isNowdoc = isNowdoc;
}
/* (non-Javadoc)
* @see org.eclipse.jface.text.rules.IPredicateRule#getSuccessToken()
*/
public IToken getSuccessToken()
{
return token;
}
/* (non-Javadoc)
* @see org.eclipse.jface.text.rules.IPredicateRule#evaluate(org.eclipse.jface.text.rules.ICharacterScanner, boolean)
*/
public IToken evaluate(ICharacterScanner scanner, boolean resume)
{
if (resume) {
return Token.UNDEFINED;
}
return evaluate(scanner);
}
/*
* (non-Javadoc)
* @see org.eclipse.jface.text.rules.IRule#evaluate(org.eclipse.jface.text.rules.ICharacterScanner)
*/
public IToken evaluate(ICharacterScanner scanner)
{
readCount = 0;
if (startsWith(scanner))
{
// read the heredoc/nowdoc identifier (name)
String identifier = readHeredocIdentifier(scanner);
if (identifier != null && identifier.length() > 0)
{
// look for an identifier match which starts at the beginning of a line.
findHeredocClose(scanner, identifier);
}
return token;
}
for (; readCount > 0; --readCount)
{
scanner.unread();
}
return Token.UNDEFINED;
}
/**
* @param scanner
* @return
*/
private boolean startsWith(ICharacterScanner scanner)
{
for (int heredocOpenCount = 3; heredocOpenCount > 0; --heredocOpenCount)
{
int character = scanner.read();
if (character == ICharacterScanner.EOF)
{
return false;
}
++readCount;
if (character != '<')
{
return false;
}
}
int character = scanner.read();
if (character == ICharacterScanner.EOF)
{
return false;
}
scanner.unread();
return (isNowdoc && character == '\'')
|| (!isNowdoc && (Character.isLetter(character) || character == '"'));
}
/**
* Reads and returns the HEREDOC/NOWDOC identifier. This identifier should appear in the same line as the 'heredoc'
* open mark, and should not contain any white-spaces before the line terminates.
*
* @param scanner
* @return A heredoc/nowdoc identifier; Null if something is wrong.
*/
private String readHeredocIdentifier(ICharacterScanner scanner)
{
StringBuilder buffer = new StringBuilder();
int character;
while ((character = scanner.read()) != ICharacterScanner.EOF) // $codepro.audit.disable assignmentInCondition
{
++readCount;
if (isNewLine(scanner, character))
{
break;
}
buffer.append((char) character);
}
if (isNowdoc)
{
buffer.deleteCharAt(0);
if (buffer.charAt(buffer.length()-1) == '\'')
{
buffer.deleteCharAt(buffer.length()-1);
} else
{
return null;
}
} else
{
if (buffer.charAt(0) == '"')
{
buffer.deleteCharAt(0);
if (buffer.charAt(buffer.length()-1) == '"')
{
buffer.deleteCharAt(buffer.length()-1);
} else
{
return null;
}
}
}
for (char ch : buffer.toString().toCharArray())
{
if (!Character.isLetterOrDigit(ch) && character != '_')
{
return null;
}
}
return buffer.toString();
}
/**
* Returns true if the
*
* @param identifier
* @return
*/
private void findHeredocClose(ICharacterScanner scanner, String identifier)
{
StringBuilder buffer = new StringBuilder();
int character;
while ((character = scanner.read()) != ICharacterScanner.EOF) // $codepro.audit.disable assignmentInCondition
{
++readCount;
if (isNewLine(scanner, character))
{
String line = buffer.toString();
if (line.equals(identifier) || line.equals(identifier + ';'))
{
scanner.unread(); // unread newline character
if (line.charAt(line.length()-1) == ';')
{
scanner.unread(); // unread semicolon
}
break;
}
buffer.setLength(0);
} else
{
buffer.append((char) character);
}
}
// We define that in case we have an illegal HEREDOC/NOWDOC, we grab to the end of the file.
}
private static boolean isNewLine(ICharacterScanner characterScanner, int c) {
for (char[] sequence : characterScanner.getLegalLineDelimiters()) {
if (c == sequence[0]) {
return true;
}
}
return false;
}
}