/*
* Copyright 2013 Guidewire Software, Inc.
*/
package gw.internal.gosu.parser;
import gw.config.CommonServices;
import gw.internal.gosu.parser.expressions.AnnotationExpression;
import gw.internal.gosu.parser.expressions.Program;
import gw.internal.gosu.parser.statements.ClassFileStatement;
import gw.internal.gosu.parser.statements.ClassStatement;
import gw.internal.gosu.parser.statements.NoOpStatement;
import gw.internal.gosu.parser.statements.UsesStatement;
import gw.lang.parser.IExpression;
import gw.lang.parser.IParseIssue;
import gw.lang.parser.IParseTree;
import gw.lang.parser.IParsedElement;
import gw.lang.parser.IParsedElementWithAtLeastOneDeclaration;
import gw.lang.parser.IScriptPartId;
import gw.lang.parser.IToken;
import gw.lang.parser.exceptions.IWarningSuppressor;
import gw.lang.parser.exceptions.ParseException;
import gw.lang.parser.exceptions.ParseIssue;
import gw.lang.parser.exceptions.ParseWarning;
import gw.lang.parser.resources.ResourceKey;
import gw.lang.parser.statements.IUsesStatement;
import gw.lang.reflect.IFeatureInfo;
import gw.lang.reflect.ITypeInfo;
import gw.lang.reflect.TypeSystem;
import gw.lang.reflect.gs.IGosuClass;
import gw.lang.reflect.module.IModule;
import gw.util.GosuObjectUtil;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Stack;
/**
* ParsedElement is the root class for all non-terminal elements represented in
* a parse tree i.e., all non-terminal expressions and statements derive either
* directly or indirectly from this class.
*/
public abstract class ParsedElement implements IParsedElement
{
private static final List<IParseTree> EMPTY_PARSETREE_LIST = Collections.emptyList();
public static final String UNDEF_MODULE = "[undefined-module]";
public static final String UNDEF_FUNCTION = "[undefined-function]";
public static final String UNDEF_FILE = "undefined-module.gs";
private ParseTree _location;
private IParsedElement _parent;
private int _iLineNum;
private int _iColumn;
private LikelyNullFields _lnf;
private IGosuProgramInternal _gosuProgram;
protected List<IToken> _tokens;
private static class LikelyNullFields
{
private List<IParseIssue> _parseExceptions = Collections.emptyList();
private List<IParseIssue> _parseWarnings = Collections.emptyList();
private Map<String, IParsedElementWithAtLeastOneDeclaration> _declaringStatements;
private boolean _bSynthetic;
}
ParsedElement()
{
_tokens = new ArrayList<IToken>( 2 );
}
public IGosuProgramInternal getGosuProgram()
{
return _gosuProgram;
}
public void setGosuProgram( IGosuProgramInternal gosuProgram )
{
_gosuProgram = gosuProgram;
}
public void addExceptionsFrom( IParsedElement elem )
{
List<IParseIssue> exceptions = elem.getParseExceptions();
if( !exceptions.isEmpty() )
{
maybeInitLikelyNullFields();
if( _lnf._parseExceptions == Collections.EMPTY_LIST )
{
_lnf._parseExceptions = new ArrayList<IParseIssue>( exceptions.size() );
}
_lnf._parseExceptions.addAll( exceptions );
}
List<IParseIssue> warnings = elem.getParseWarnings();
if( !warnings.isEmpty() )
{
maybeInitLikelyNullFields();
if( _lnf._parseWarnings == Collections.EMPTY_LIST )
{
_lnf._parseWarnings = new ArrayList<IParseIssue>( warnings.size() );
}
_lnf._parseWarnings.addAll( warnings );
}
}
private void maybeInitLikelyNullFields()
{
if( _lnf == null )
{
_lnf = new LikelyNullFields();
}
}
/**
* @return The location of this parsed element within the source.
*/
public ParseTree getLocation()
{
return _location;
}
/**
* Specifiy the location of this parsed element within the source.
*
* @param location The location of this parsed element within the source.
*/
public void setLocation( IParseTree location )
{
_location = (ParseTree)location;
}
public ParseTree initLocation( int offset, int length, int lineNumber, int iColumn, IScriptPartId scriptPart )
{
_iLineNum = lineNumber;
_iColumn = iColumn;
if( _location == null )
{
_location = new ParseTree( this, offset, length, scriptPart );
}
else
{
_location.initLocation( this, offset, length );
}
return _location;
}
public void initEmptyParseTree()
{
initLocation( -1, -1, -1, -1, null );
}
public boolean hasParseIssues()
{
return hasParseExceptions() || hasParseWarnings();
}
public List<IParseIssue> getParseIssues()
{
List<IParseIssue> issues = new ArrayList<IParseIssue>();
getParseExceptions( issues );
getParseWarnings( issues );
if( issues.isEmpty() )
{
issues = Collections.emptyList();
}
else
{
((ArrayList)issues).trimToSize();
}
return issues;
}
@Override
public List<IParseIssue> getImmediateParseIssues()
{
ArrayList<IParseIssue> issues = null;
if( _lnf != null )
{
if( _lnf._parseExceptions != null && _lnf._parseExceptions.size() > 0 )
{
if( issues == null )
{
issues = new ArrayList<IParseIssue>();
}
issues.addAll( _lnf._parseExceptions );
}
if( _lnf._parseWarnings != null && _lnf._parseWarnings.size() > 0 )
{
if( issues == null )
{
issues = new ArrayList<IParseIssue>();
}
for( IParseIssue exc : _lnf._parseWarnings )
{
if( !isSuppressed( exc ) )
{
issues.add( exc );
}
}
}
}
return issues != null ? issues : Collections.<IParseIssue>emptyList();
}
public boolean hasParseExceptions()
{
if( _lnf != null && !_lnf._parseExceptions.isEmpty() )
{
return true;
}
ParseTree location = getLocation();
List<IParseTree> children = location != null ? location.getChildren() : null;
if (children != null) {
//!! Don't use iterator here, it throws ConcurrentModificationExceptions if children is modified concurrently (and it allocates memory!)
for( int i = 0; i < children.size(); i++ )
{
IParseTree child;
try
{
child = children.get( i );
}
catch( Throwable t )
{
// The child list may be accessed concurrently, but we don't want to
// incur the overhead of locking or copying and since we don't care if
// the current list is overrun, we can safely return false (I think).
return false;
}
if( child != null )
{
IParsedElement pe = child.getParsedElement();
if( pe != null && pe.hasParseExceptions() )
{
return true;
}
}
}
}
return false;
}
public boolean hasParseException( ResourceKey errKey )
{
for( IParseIssue err : getParseExceptions() )
{
if( err.getMessageKey() == errKey )
{
return true;
}
}
return false;
}
public IParseIssue getImmediateParseIssue( ResourceKey errKey )
{
for( IParseIssue err : getImmediateParseIssues() )
{
if( err.getMessageKey() == errKey )
{
return err;
}
}
return null;
}
public boolean hasImmediateParseIssue( ResourceKey errKey )
{
return getImmediateParseIssue( errKey ) != null;
}
public boolean hasParseWarning( ResourceKey errKey )
{
for (int i = 0; i < getParseWarnings().size(); i++) {
IParseIssue warning = getParseWarnings().get( i );
if (warning.getMessageKey() == errKey) {
return true;
}
}
return false;
}
public void addParseWarnings( List<IParseIssue> parseWarnings )
{
for (int i = 0; i < parseWarnings.size(); i++) {
IParseIssue w = parseWarnings.get(i);
addParseWarning(w);
}
}
public void addParseExceptions( List<IParseIssue> parseExceptions )
{
for( IParseIssue w : parseExceptions )
{
addParseException( w );
}
}
public void addParseIssues( List<IParseIssue> parseIssues )
{
for( IParseIssue p : parseIssues )
{
if( p instanceof ParseException )
{
addParseException( p );
}
else if( p instanceof ParseWarning )
{
addParseWarning( p );
}
}
}
public List<IParseIssue> getParseExceptions()
{
List<IParseIssue> list = new ArrayList<IParseIssue>();
getParseExceptions( list );
return list.isEmpty() ? Collections.<IParseIssue>emptyList() : list;
}
private void getParseExceptions( List<IParseIssue> allParseExceptions )
{
if( _lnf != null )
{
allParseExceptions.addAll( _lnf._parseExceptions );
}
ParseTree location = getLocation();
List<IParseTree> children = location == null ? null : location.getChildren();
if( children != null )
{
for( int i = 0; i < children.size(); i++ )
{
IParseTree child = children.get( i );
IParsedElement pe = child.getParsedElement();
if( pe != null )
{
((ParsedElement)pe).getParseExceptions( allParseExceptions );
}
}
}
}
public void addParseException( ResourceKey msgKey, Object... args )
{
String src = getSource();
addParseException( new ParseException( new StandardParserState( this, src, false ), msgKey, args ) );
}
/**
* Removes the specified parse exception or removes them all if the specified
* exception is null.
*/
public ParseException removeParseException( ResourceKey keyToRemove )
{
if( _lnf != null )
{
return (ParseException)removeParseIssue( keyToRemove, _lnf._parseExceptions );
}
return null;
}
public ParseWarning removeParseWarning( ResourceKey keyToRemove )
{
if( _lnf != null )
{
return (ParseWarning)removeParseIssue( keyToRemove, _lnf._parseWarnings );
}
return null;
}
public void removeParseWarningRecursively( ResourceKey keyToRemove )
{
//noinspection StatementWithEmptyBody,ThrowableResultOfMethodCallIgnored
while( _lnf != null && removeParseWarning( keyToRemove ) != null );
ParseTree location = getLocation();
List<IParseTree> children = location == null ? null : location.getChildren();
if( children != null )
{
for( int i = 0; i < children.size(); i++ )
{
IParseTree child = children.get( i );
IParsedElement pe = child.getParsedElement();
if( pe != null )
{
((ParsedElement)pe).removeParseWarningRecursively( keyToRemove );
}
}
}
}
private <E extends IParseIssue> E removeParseIssue( ResourceKey keyToRemove, List<E> issues )
{
E pe = null;
if( _lnf != null && !issues.isEmpty() )
{
for( java.util.Iterator it = issues.iterator(); it.hasNext(); )
{
E parseIssue = (E)it.next();
if( keyToRemove == null || keyToRemove.equals( parseIssue.getMessageKey() ) )
{
pe = parseIssue;
it.remove();
}
}
}
return pe;
}
private String getSource()
{
String src = null;
IParsedElement element = this;
while( element != null )
{
if( element instanceof ClassStatement )
{
src = ((ClassStatement)element).getGosuClass().getSource();
break;
}
else if( element instanceof Program )
{
src = element.toString();
break;
}
element = element.getParent();
}
return src;
}
public IGosuClass getGosuClass()
{
IParsedElement parent = getParent();
if( parent != null )
{
return parent.getGosuClass();
}
return null;
}
public void addParseWarning( ResourceKey msgKey, Object... args )
{
String src = getSource();
addParseWarning( new ParseWarning( new StandardParserState( this, src, false ), msgKey, args ) );
}
public void addParseException( IParseIssue pe )
{
if( hasParseIssue( pe ) )
{
return;
}
maybeInitLikelyNullFields();
if( _lnf._parseExceptions == Collections.EMPTY_LIST )
{
_lnf._parseExceptions = new ArrayList<IParseIssue>( 1 );
}
_lnf._parseExceptions.add( pe );
((ArrayList)_lnf._parseExceptions).trimToSize();
((ParseException)pe).setSource( this );
}
public void clearParseExceptions()
{
if( _lnf != null )
{
_lnf._parseExceptions = Collections.emptyList();
}
ParseTree location = getLocation();
List<IParseTree> children = location == null ? EMPTY_PARSETREE_LIST : location.getChildren();
if( !children.isEmpty() )
{
for( IParseTree child : children )
{
child.getParsedElement().clearParseExceptions();
}
}
}
public void clearParseWarnings()
{
if( _lnf != null )
{
_lnf._parseWarnings = Collections.emptyList();
}
ParseTree location = getLocation();
List<IParseTree> children = location == null ? EMPTY_PARSETREE_LIST : location.getChildren();
if( !children.isEmpty() )
{
for( IParseTree child : children )
{
child.getParsedElement().clearParseWarnings();
}
}
}
public boolean hasImmediateParseWarnings()
{
return _lnf != null && _lnf._parseWarnings.size() > 0;
}
public boolean hasImmediateParseWarning( ResourceKey errKey )
{
if( _lnf == null || _lnf._parseWarnings.isEmpty() )
{
return false;
}
for( IParseIssue w : _lnf._parseWarnings )
{
if( w.getMessageKey().equals( errKey ) )
{
return true;
}
}
return false;
}
public boolean hasParseWarnings()
{
if( _lnf != null && !_lnf._parseWarnings.isEmpty() )
{
return true;
}
ParseTree location = getLocation();
List<IParseTree> children = location != null ? location.getChildren() : EMPTY_PARSETREE_LIST;
if( !children.isEmpty() )
{
for( IParseTree child : children )
{
IParsedElement pe = child.getParsedElement();
if( pe != null && pe.hasParseWarnings() )
{
return true;
}
}
}
return false;
}
public List<IParseIssue> getParseWarnings()
{
List<IParseIssue> list = new ArrayList<IParseIssue>();
getParseWarnings( list );
return list.isEmpty() ? Collections.<IParseIssue>emptyList() : list;
}
private void getParseWarnings( List<IParseIssue> allWarnings )
{
if( _lnf != null )
{
for( IParseIssue exc : _lnf._parseWarnings )
{
if( !isSuppressed( exc ) )
{
allWarnings.add( exc );
}
}
}
ParseTree location = getLocation();
List<IParseTree> children = location == null ? EMPTY_PARSETREE_LIST : location.getChildren();
if( !children.isEmpty() )
{
for (int i = 0; i < children.size(); i++) {
IParseTree child = children.get(i);
IParsedElement pe = child.getParsedElement();
if (pe != null) {
((ParsedElement) pe).getParseWarnings(allWarnings);
}
}
}
}
public void addParseWarning( IParseIssue warning )
{
if( hasParseIssue( warning ) )
{
return;
}
if( getLocation() == null ||
getLocation().getEnclosingType() == null ||
CommonServices.getEntityAccess().shouldAddWarning( getLocation().getEnclosingType(), warning ) )
{
maybeInitLikelyNullFields();
if( _lnf._parseWarnings == Collections.<IParseIssue>emptyList() )
{
_lnf._parseWarnings = new ArrayList<IParseIssue>( 1 );
}
_lnf._parseWarnings.add( warning );
((ArrayList)_lnf._parseWarnings).trimToSize();
((ParseIssue)warning).setSource( this );
}
}
public boolean hasParseIssue( IParseIssue pi )
{
if( _lnf == null )
{
return false;
}
for( IParseIssue pw : getParseWarnings() )
{
if( GosuObjectUtil.equals( pw.getTokenStart(), pi.getTokenStart() ) &&
pw.getMessageKey() == pi.getMessageKey() &&
GosuObjectUtil.equals( pw.getConsoleMessage(), pi.getConsoleMessage() ) )
{
return true;
}
}
for( IParseIssue pe : _lnf._parseExceptions )
{
if( pe.getTokenStart() != null && pe.getTokenStart().equals( pi.getTokenStart() ) &&
pe.getMessageKey() == pi.getMessageKey() &&
GosuObjectUtil.equals( pe.getPlainMessage(), pi.getPlainMessage() ) )
{
return true;
}
}
return false;
}
public boolean isSuppressed( IParseIssue issue )
{
return issue instanceof IWarningSuppressor &&
isSuppressed( (IWarningSuppressor)issue );
}
public boolean isSuppressed( IWarningSuppressor suppressor )
{
IModule mod = getGosuClass() == null ? null : getModule();
if( mod != null ) {
TypeSystem.pushModule( mod );
}
try
{
for( IGosuAnnotation anno: getAnnotations() )
{
if( anno.getType() == TypeSystem.get( SuppressWarnings.class ) )
{
IExpression annoExpr = anno.getExpression();
if( annoExpr instanceof AnnotationExpression )
{
if( ((AnnotationExpression)annoExpr).getArgs() != null )
{
for( Expression expr : ((AnnotationExpression)annoExpr).getArgs() )
{
Object value = expr.evaluate();
if( value instanceof String )
{
if( suppressor.isSuppressed( (String)value ) )
{
return true;
}
}
else if( value instanceof Object[] )
{
for( Object o: (Object[])value )
{
if( suppressor.isSuppressed( (String)o ) )
{
return true;
}
}
}
else if( value instanceof List )
{
for( Object o: (List)value )
{
if( suppressor.isSuppressed( (String)o ) )
{
return true;
}
}
}
}
}
}
}
}
ParsedElement parent = (ParsedElement)getParent();
return parent != null && parent.isSuppressed( suppressor );
}
finally
{
if( mod != null )
{
TypeSystem.popModule( mod );
}
}
}
public List<IGosuAnnotation> getAnnotations()
{
return Collections.emptyList();
}
public boolean isCompileTimeConstant()
{
return false;
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
// -- Helper Methods --
/**
* Find all the parsed elements of a given type contained within this parsed
* element.
*
* @param parsedElementType The type of parsed element to find.
* @param listResults A list of all the contained parsed elements matching the
* specified type. Can be null if you are only interested in whether or not
* parsedElementType exists in this element.
*
* @return True iff one or more parseElementType are found.
*/
@SuppressWarnings("unchecked" )
public <E extends IParsedElement> boolean getContainedParsedElementsByType( Class<E> parsedElementType, List<E> listResults )
{
return getContainedParsedElementsByTypes( (List<IParsedElement>)listResults, parsedElementType );
}
public boolean getContainedParsedElementsByTypes( List<IParsedElement> listResults, Class<? extends IParsedElement>... parsedElementTypes )
{
//noinspection unchecked
return getContainedParsedElementsByTypesWithIgnoreSet( listResults, Collections.EMPTY_SET, parsedElementTypes );
}
public boolean getContainedParsedElementsByTypesWithIgnoreSet( List<IParsedElement> listResults,
Set<Class<? extends IParsedElement>> ignoreSet,
Class<? extends IParsedElement>... parsedElementTypes )
{
boolean isInstance = false;
for( Class<? extends IParsedElement> parsedElementType : parsedElementTypes )
{
if( parsedElementType.isInstance( this ) )
{
isInstance = true;
break;
}
}
if( isInstance )
{
if( listResults != null )
{
listResults.add( this );
}
else
{
return true;
}
}
boolean bIgnore = false;
for( Class ignore : ignoreSet )
{
if( ignore.isInstance( this ) )
{
bIgnore = true;
break;
}
}
if( !bIgnore )
{
ParseTree location = getLocation();
if (location != null) {
List children = location.getChildren();
if( !children.isEmpty() )
{
for( int i = 0; i < children.size(); i++ )
{
IParseTree child = (IParseTree)children.get( i );
IParsedElement parsedElement = child.getParsedElement();
if( parsedElement.getContainedParsedElementsByTypesWithIgnoreSet( listResults, ignoreSet, parsedElementTypes ) )
{
if( listResults == null )
{
return true;
}
}
}
}
}
}
return listResults != null && listResults.size() > 0;
}
public final Integer makeInteger( Object obj )
{
if( obj == null )
{
return null;
}
return CommonServices.getCoercionManager().makeIntegerFrom( obj );
}
public static Long makeLong( Object obj )
{
if( obj == null )
{
return null;
}
return CommonServices.getCoercionManager().makeLongFrom( obj );
}
/**
* Just like makeDouble(), but creates a double primitive value instead of a
* Double object. Much more efficient if you don't need the object.
*
* @param obj Any double convertible object
*
* @return The double primitive value.
*/
public static double makeDoubleValue( Object obj )
{
if( obj == null )
{
return Double.NaN;
}
return CommonServices.getCoercionManager().makePrimitiveDoubleFrom( obj );
}
/**
* Just like makeFloat(), but creates a float primitive value instead of a
* Float object. Much more efficient if you don't need the object.
*
* @param obj Any float convertible object
*
* @return The float primitive value.
*/
public static float makeFloatValue( Object obj )
{
if( obj == null )
{
return Float.NaN;
}
return CommonServices.getCoercionManager().makePrimitiveFloatFrom( obj );
}
public void compactParseTree() {
if( _location != null )
{
_location.compactParseTree();
}
}
public void clearParseTreeInformation()
{
if (shouldClearParseInfo()) {
TypeSystem.lock();
try
{
if( _location != null )
{
IParseTree loc = _location;
IParseTree parent = loc.getParent();
_location.clearParseTreeInformation();
if( parent != null )
{
parent.removeChild(loc);
}
}
_location = null;
if( _lnf != null )
{
_lnf._declaringStatements = null;
}
}
finally
{
TypeSystem.unlock();
}
}
}
public IParsedElement getParent()
{
return _parent;
}
public void setParent( IParsedElement parent )
{
_parent = parent;
}
public int getLineNum()
{
return _iLineNum;
}
public void adjustLineNum( int offset )
{
_iLineNum += offset;
}
public void setLineNum( int iLineNum )
{
_iLineNum = iLineNum;
}
public int getColumn()
{
return _iColumn;
}
public void adjustColumn(int offset) {
_iColumn += offset;
}
public String getFunctionName()
{
return getParent() == null ? UNDEF_FUNCTION : getParent().getFunctionName();
}
@Override
public boolean isSynthetic()
{
return _lnf != null && _lnf._bSynthetic;
}
public void setSynthetic( boolean bSynthetic )
{
maybeInitLikelyNullFields();
_lnf._bSynthetic = bSynthetic;
}
public IModule getModule() {
IParsedElement parent = getParent();
return parent == null ? null : parent.getModule();
}
public static IFeatureInfo getEnclosingFeatureInfo( Stack<IFeatureInfo> enclosingFeatureInfos )
{
if( enclosingFeatureInfos.empty() )
{
return null;
}
else
{
return enclosingFeatureInfos.peek();
}
}
public static ITypeInfo getQualifyingEnclosingTypeInfo( Stack<IFeatureInfo> enclosingFeatureInfos )
{
if( enclosingFeatureInfos.empty() )
{
return null;
}
else
{
return (ITypeInfo)enclosingFeatureInfos.firstElement();
}
}
public int findLineNumberOfDeclaration( String identifierName )
{
IParsedElementWithAtLeastOneDeclaration statement = findDeclaringStatement( this, identifierName );
return statement.getLineNum();
}
public IParsedElementWithAtLeastOneDeclaration findDeclaringStatement( IParsedElement element, String identifierName )
{
IParsedElementWithAtLeastOneDeclaration declaringStatement;
declaringStatement = _lnf == null || _lnf._declaringStatements == null
? null
: _lnf._declaringStatements.get( identifierName );
if( declaringStatement == null )
{
// More likely to be self
declaringStatement = checkIfDeclaringStatement( element, identifierName );
if( declaringStatement == null )
{
// Else check the children
declaringStatement = findDeclaringStatementInChildren( element, identifierName );
if( declaringStatement == null )
{
// if not found yet, go to parent
IParsedElement parent = element.getParent();
if( parent != null )
{
declaringStatement = findDeclaringStatement( parent, identifierName );
}
else
{
// We got to the root and couldn't find the declaring statement
declaringStatement = null;
}
}
}
if( element == this &&
declaringStatement != null )
{
maybeInitLikelyNullFields();
_lnf._declaringStatements = new HashMap<String, IParsedElementWithAtLeastOneDeclaration>( 0 );
_lnf._declaringStatements.put( identifierName, declaringStatement );
}
}
return declaringStatement;
}
private static IParsedElementWithAtLeastOneDeclaration findDeclaringStatementInChildren( IParsedElement element, String identifierName )
{
IParsedElementWithAtLeastOneDeclaration declaringStatement;
if( element.getLocation() == null )
{
return null;
}
List<IParseTree> children = element.getLocation().getChildren();
for( IParseTree child : children )
{
IParsedElement parsedElement = child.getParsedElement();
declaringStatement = checkIfDeclaringStatement( parsedElement, identifierName );
if( declaringStatement != null )
{
return declaringStatement;
}
else if(( parsedElement instanceof ClassStatement ) || ( parsedElement instanceof ClassFileStatement ))
{
// If it's a class statement, check its children. Special case for inner classes.
declaringStatement = findDeclaringStatementInChildren( parsedElement, identifierName );
if( declaringStatement != null )
{
return declaringStatement;
}
}
}
return null;
}
private static IParsedElementWithAtLeastOneDeclaration checkIfDeclaringStatement( IParsedElement parsedElement, String identifierName )
{
if( parsedElement instanceof IParsedElementWithAtLeastOneDeclaration )
{
IParsedElementWithAtLeastOneDeclaration declarativeStatement = (IParsedElementWithAtLeastOneDeclaration)parsedElement;
if( declarativeStatement.declares( identifierName ) )
{
return declarativeStatement;
}
}
return null;
}
public IParsedElement findRootParsedElement()
{
IParsedElement parent = getParent();
if( parent == null )
{
return this;
}
else
{
return parent.findRootParsedElement();
}
}
/**
* @param parsedElementClasses List of statement types to find
*
* @return The nearest ancestor statement that is any one of the given types. null if this element does not have
* an ancestor of any of the given types
*/
public IParsedElement findAncestorParsedElementByType( Class... parsedElementClasses )
{
IParsedElement parent = getParent();
while( (parent != null) && !(elementIsOneOfType( parent, parsedElementClasses )) )
{
parent = parent.getParent();
}
if( parent != null )
{
return parent;
}
else
{
return null;
}
}
private static boolean elementIsOneOfType( IParsedElement element, Class[] parsedElementClasses )
{
for( Class statementClass : parsedElementClasses )
{
if( statementClass.isAssignableFrom( element.getClass() ) )
{
return true;
}
}
return false;
}
private Map<CharSequence, UsesStatement> computeUsesStatementsMap() {
IGosuClassInternal gosuClass = null;
if (this instanceof ClassStatement) {
gosuClass = ((ClassStatement) this).getGosuClass();
} else if (this instanceof ClassFileStatement) {
ClassStatement classStatement = ((ClassFileStatement) this).getClassStatement();
if (classStatement != null) {
gosuClass = classStatement.getGosuClass();
}
}
if ((gosuClass != null) && (gosuClass.getTypeUsesMap() != null)) {
Set<IUsesStatement> usesStatements = gosuClass.getTypeUsesMap().getUsesStatements();
final int initialCapacity = usesStatements.size();
Map<CharSequence, UsesStatement> usesMap = new HashMap<CharSequence, UsesStatement>( initialCapacity );
for (IUsesStatement usesStatement : usesStatements) {
usesMap.put(usesStatement.getTypeName(), (UsesStatement)usesStatement );
}
return usesMap;
} else {
return Collections.emptyMap();
}
}
public boolean shouldClearParseInfo() {
return true;
}
public void assignTokens( List<IToken> tokens )
{
// Note we don't sort the children because we need to let inner classes of
// programs get first crack at consuming tokens so NoOpStatements inside
// the synthetic evaluate method don't bogart them.
List<IParseTree> children = getLocation().getChildren(); //getLocation().getChildrenSorted( getLocation() );
for( IParseTree child : children )
{
((ParsedElement)child.getParsedElement()).assignTokens( tokens );
}
assignTokensToJustMe( tokens );
}
protected void addToken( IToken token, IParseTree after )
{
token.setAfter( after );
if( !containsToken( _tokens, token ) )
{
_tokens.add( token );
}
}
public List<IToken> getTokens()
{
return _tokens;
}
private void assignTokensToJustMe( List<IToken> tokens )
{
IParseTree parseTree = getLocation();
int iStartOffset = -1;
int iEndOffset = -1;
IParseTree after = null;
//int a = 0;
int iTreeOffset = parseTree.getOffset();
int iTreeEnd = parseTree.getExtent() + 1;
boolean bZeroLengthTree = parseTree.getLength() == 0;
int iStartIndex = binarySearchForFirstToken( tokens, iTreeOffset, iTreeEnd, bZeroLengthTree );
//int iStartIndex = 0;
if( iStartIndex < 0 ) {
return;
}
int i;
for( i = iStartIndex; i < tokens.size(); i++ )
{
IToken token = tokens.get( i );
int iTokenStart = token.getTokenStart();
int iTokenEnd = token.getTokenEnd();
if( iTokenStart >= iTreeOffset && iTokenEnd <= iTreeEnd )
{
if( iStartOffset < 0 )
{
iStartOffset = iTokenStart;
}
iEndOffset = iTokenEnd;
if( this instanceof NoOpStatement && !isDescendent( after ) )
{
break;
}
tokens.remove( i-- );
if( !(token instanceof PositionToken) )
{
//a++;
addToken( token, after );
}
else
{
after = ((PositionToken)token).getPos();
}
}
else if( bZeroLengthTree &&
iTreeOffset >= iTokenStart && iTreeOffset < iTokenEnd )
{
tokens.add( i, new PositionToken( parseTree, iTreeOffset, iTreeOffset ) );
break;
}
else if( iStartOffset >= 0 && !tokens.isEmpty() )
{
tokens.add( i, new PositionToken( parseTree, iStartOffset, iEndOffset ) );
break;
}
}
//System.out.println( "#" + (i-iStartIndex) + ", " + a );
}
private int binarySearchForFirstToken( List<IToken> tokens, int iTreeOffset, int iTreeEnd, boolean bZeroLengthTree ) {
int iStart = 0;
int iEnd = tokens.size() - 1;
int iIndex;
while( iStart <= iEnd ) {
iIndex = (iStart + iEnd) / 2;
IToken token = tokens.get( iIndex );
int iTokenStart = token.getTokenStart();
int iTokenEnd = token.getTokenEnd();
if( !bZeroLengthTree && iTokenStart >= iTreeOffset && iTokenEnd <= iTreeEnd ) {
// Found a token for the tree, now backtrack to the first token for the tree
int iSaveIndex = iIndex;
//int times = 0;
if( iIndex > 0 ) {
for( iIndex-=1;
iIndex >= 0 && ((token = tokens.get( iIndex )) != null) && token.getTokenStart() >= iTreeOffset && token.getTokenEnd() <= iTreeEnd;
iIndex-- ) {
iSaveIndex = iIndex;
//times++;
}
}
//System.out.println( times );
return iSaveIndex;
}
else if( bZeroLengthTree && iTreeOffset >= iTokenStart && iTreeOffset < iTokenEnd ) {
return iIndex;
}
else if( iTokenStart < iTreeOffset ) {
iStart = iIndex + 1;
}
else {
iEnd = iIndex - 1;
}
}
return -1;
}
private boolean containsToken( List<IToken> tokens, IToken target ) {
int iStart = 0;
int iEnd = tokens.size() - 1;
int iOffset = target.getTokenStart();
int iIndex;
while( iStart <= iEnd ) {
iIndex = (iStart + iEnd) / 2;
IToken token = tokens.get( iIndex );
if( token == target ) {
return true;
}
else if( token.getTokenStart() < iOffset ) {
iStart = iIndex + 1;
}
else {
iEnd = iIndex - 1;
}
}
return false;
}
private boolean isDescendent( IParseTree after )
{
if( after == null )
{
return true;
}
if( after.getParsedElement() == this )
{
return true;
}
if( after.getParent() == null )
{
return false;
}
return isDescendent( after.getParent() );
}
}