package com.aptana.editor.php.internal.contentAssist;
import java.io.CharArrayReader;
import java.io.IOException;
import javax.swing.text.Segment;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IDocumentExtension3;
import org.eclipse.jface.text.ITypedRegion;
import org.eclipse.jface.text.TextUtilities;
import org.eclipse.jface.text.rules.IToken;
import org.eclipse.jface.text.rules.ITokenScanner;
import org.eclipse.jface.text.rules.Token;
import org2.eclipse.php.internal.core.PHPVersion;
import org2.eclipse.php.internal.core.documentModel.parser.AbstractPhpLexer;
import org2.eclipse.php.internal.core.documentModel.parser.PhpLexerFactory;
import org2.eclipse.php.internal.core.documentModel.parser.regions.PHPRegionTypes;
import com.aptana.core.logging.IdeLog;
import com.aptana.editor.php.PHPEditorPlugin;
import com.aptana.editor.php.core.PHPVersionProvider;
import com.aptana.editor.php.internal.core.IPHPConstants;
import com.aptana.editor.php.internal.ui.editor.PHPVersionDocumentManager;
/**
* A PHP token scanner that scans and create tokens that are used when calculating the code assist scope.
*
* @author Shalom Gibly <sgibly@aptana.com>
*/
public class PHPScopeScanner implements ITokenScanner
{
private AbstractPhpLexer lexer;
private int regionOffset;
private int originalOffset;
private int originalLength;
private int prevTokenOffset;
private int duplicateStartCount;
private ITypedRegion[] partitions;
private char[] content;
private IDocument document;
public int getTokenLength()
{
return lexer.yylength();
}
public int getTokenOffset()
{
return regionOffset + lexer.getTokenStart();
}
public IToken nextToken()
{
try
{
String token = lexer.getNextToken();
int tokenOffset = getTokenOffset();
if (prevTokenOffset == tokenOffset)
{
// we stumble into a case where the lexer failed to notify us with the end
// token, so force a stop.
if (duplicateStartCount == 3)
{
duplicateStartCount = 0;
dispose();
return Token.EOF;
}
duplicateStartCount++;
}
else
{
duplicateStartCount = 0;
prevTokenOffset = tokenOffset;
if (token == null)
{
dispose();
return Token.EOF;
}
}
if (PHPRegionTypes.PHP_CLOSETAG.equals(token))
{
if (partitions == null)
{
try
{
partitions = TextUtilities.computePartitioning(document,
IDocumentExtension3.DEFAULT_PARTITIONING, originalOffset, originalLength, false);
}
catch (BadLocationException e)
{
IdeLog.logError(PHPEditorPlugin.getDefault(), "PHP scope-scanner error", e); //$NON-NLS-1$
}
}
// Check if we have more regions of PHP after this close tag.
// If so, reset the lexer for the next region.
ITypedRegion nextPhpRegion = getNextPhpRegion();
if (nextPhpRegion != null)
{
int nextRegionOffset = nextPhpRegion.getOffset() - this.originalOffset;
Segment segment = new Segment(content, nextRegionOffset, content.length - nextRegionOffset);
lexer.reset(segment);
regionOffset = nextPhpRegion.getOffset();
}
}
return new Token(token);
}
catch (IOException e)
{
IdeLog.logError(PHPEditorPlugin.getDefault(), "PHP scope-scanner error", e); //$NON-NLS-1$
}
dispose();
return Token.EOF;
}
/*
* Dispose this scanner.
*/
private void dispose()
{
this.content = null;
this.partitions = null;
this.lexer = null;
this.document = null;
}
/**
* Returns the next PHP region located after the current token end position
*
* @return The next PHP region, or null if non exists.
*/
protected ITypedRegion getNextPhpRegion()
{
if (partitions != null)
{
int offset = getTokenOffset() + getTokenLength();
for (ITypedRegion region : partitions)
{
if (region.getOffset() > offset)
{
if (region.getType().startsWith(IPHPConstants.DEFAULT))
{
return region;
}
}
}
}
return null;
}
public void setRange(IDocument document, int offset, int length)
{
this.document = document;
this.originalOffset = offset;
this.originalLength = length;
this.regionOffset = this.originalOffset;
this.prevTokenOffset = -1;
PHPVersion phpVersion = PHPVersionDocumentManager.getPHPVersion(document);
if (phpVersion == null)
{
phpVersion = PHPVersionProvider.getDefaultPHPVersion();
}
try
{
content = document.get(offset, length).toCharArray();
lexer = PhpLexerFactory.createLexer(new CharArrayReader(content), phpVersion); // $codepro.audit.disable
// closeWhereCreated
// set initial lexer state - we use reflection here since we don't
// know the constant value of
// of this state in specific PHP version lexer
int state = lexer.getClass().getField("ST_PHP_IN_SCRIPTING").getInt(lexer); //$NON-NLS-1$
lexer.initialize(state);
lexer.setPatterns(null);
lexer.setAspTags(true);
}
catch (Exception e)
{
IdeLog.logError(PHPEditorPlugin.getDefault(), "PHP scope-scanner error (setRange)", e); //$NON-NLS-1$
}
}
}