/*
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.apache.flex.compiler.internal.tree.as;
import java.util.ArrayList;
import java.util.Collection;
import java.util.EnumSet;
import org.apache.flex.compiler.common.ASImportTarget;
import org.apache.flex.compiler.common.ASModifier;
import org.apache.flex.compiler.common.IImportTarget;
import org.apache.flex.compiler.definitions.IDefinition;
import org.apache.flex.compiler.definitions.references.IReference;
import org.apache.flex.compiler.definitions.references.ReferenceFactory;
import org.apache.flex.compiler.internal.definitions.FunctionDefinition;
import org.apache.flex.compiler.internal.definitions.InterfaceDefinition;
import org.apache.flex.compiler.internal.definitions.NamespaceDefinition;
import org.apache.flex.compiler.internal.scopes.ASScope;
import org.apache.flex.compiler.internal.scopes.TypeScope;
import org.apache.flex.compiler.internal.semantics.PostProcessStep;
import org.apache.flex.compiler.parsing.IASToken;
import org.apache.flex.compiler.problems.ICompilerProblem;
import org.apache.flex.compiler.tree.ASTNodeID;
import org.apache.flex.compiler.tree.as.IASNode;
import org.apache.flex.compiler.tree.as.IExpressionNode;
import org.apache.flex.compiler.tree.as.IIdentifierNode;
import org.apache.flex.compiler.tree.as.IInterfaceNode;
/**
* ActionScript parse tree node representing an interface definition
*/
public class InterfaceNode extends MemberedNode implements IInterfaceNode
{
/**
* Constructor.
*
* @param name The node holding the interface name.
*/
public InterfaceNode(ExpressionNodeBase name)
{
init(name);
}
/**
* The class keyword
*/
protected KeywordNode interfaceKeywordNode;
/**
* The extends keyword (if one is present)
*/
protected KeywordNode extendsKeywordNode;
/**
* The collection of base interfaces
*/
protected TransparentContainerNode baseInterfacesNode;
/**
* Generated FunctionNode to represent cast function
*/
protected FunctionNode castFunctionNode;
//
// NodeBase overrides
//
@Override
public ASTNodeID getNodeID()
{
return ASTNodeID.InterfaceID;
}
@Override
public int getSpanningStart()
{
return getNodeStartForTooling();
}
@Override
protected void setChildren(boolean fillInOffsets)
{
addDecorationChildren(fillInOffsets);
addChildInOrder(interfaceKeywordNode, fillInOffsets);
addChildInOrder(nameNode, fillInOffsets);
addChildInOrder(extendsKeywordNode, fillInOffsets);
addChildInOrder(contentsNode, fillInOffsets);
if (extendsKeywordNode != null || baseInterfacesNode.getChildCount() > 0)
addChildInOrder(baseInterfacesNode, fillInOffsets);
}
@Override
protected void analyze(EnumSet<PostProcessStep> set, ASScope scope, Collection<ICompilerProblem> problems)
{
if (set.contains(PostProcessStep.POPULATE_SCOPE))
{
InterfaceDefinition definition = buildDefinition();
setDefinition(definition);
scope.addDefinition(definition);
TypeScope typeScope = new TypeScope(scope, contentsNode, definition);
definition.setContainedScope(typeScope);
scope = typeScope;
setupCastFunction(set, definition, scope);
}
if (set.contains(PostProcessStep.RECONNECT_DEFINITIONS))
{
reconnectDef(scope);
scope = this.getDefinition().getContainedScope();
contentsNode.reconnectScope(scope);
}
// Recurse on the interface block.
contentsNode.analyze(set, scope, problems);
}
/*
* For debugging only. Builds a string such as <code>"IFoo"</code> from the
* name of the interface being defined.
*/
@Override
protected boolean buildInnerString(StringBuilder sb)
{
sb.append('"');
sb.append(getName());
sb.append('"');
return true;
}
//
// TreeNode overrides
//
@Override
protected int getInitialChildCount()
{
return 2;
}
//
// BaseDefinitionNode overrides
//
@Override
protected void init(ExpressionNodeBase nameNode)
{
super.init(nameNode);
extendsKeywordNode = null;
baseInterfacesNode = new TransparentContainerNode();
castFunctionNode = null;
}
@Override
public boolean hasModifier(ASModifier modifier)
{
return false;
}
@Override
public InterfaceDefinition getDefinition()
{
return (InterfaceDefinition)super.getDefinition();
}
@Override
protected void setDefinition(IDefinition definition)
{
assert definition instanceof InterfaceDefinition;
super.setDefinition(definition);
}
//
// IInterfaceNode implementations
//
@Override
public boolean isImplicit()
{
return false;
}
@Override
public String getQualifiedName()
{
IImportTarget importTarget = ASImportTarget.buildImportFromPackageName(getWorkspace(), getPackageName());
String qualifiedName = importTarget.getQualifiedName(getName());
if (qualifiedName == null)
return getShortName();
return qualifiedName;
}
@Override
public String getShortName()
{
String name = getName();
int lastDot = name.lastIndexOf(".");
if (lastDot != -1)
name = name.substring(lastDot + 1);
return name;
}
@Override
public InterfaceClassification getInterfaceClassification()
{
if (getParent() instanceof FileNode)
return InterfaceClassification.INNER_INTERFACE;
return InterfaceClassification.PACKAGE_MEMBER;
}
@Override
public IExpressionNode[] getExtendedInterfaceNodes()
{
ArrayList<IExpressionNode> interfaceNodeList = new ArrayList<IExpressionNode>();
int childCount = baseInterfacesNode.getChildCount();
if (baseInterfacesNode != null && childCount > 0)
{
for (int i = 0; i < childCount; i++)
{
IASNode child = baseInterfacesNode.getChild(i);
if (child instanceof IIdentifierNode)
{
interfaceNodeList.add(((IIdentifierNode)child));
}
}
}
return interfaceNodeList.toArray(new IExpressionNode[0]);
}
@Override
public String[] getExtendedInterfaces()
{
ArrayList<String> interfaceNodeList = new ArrayList<String>();
int childCount = baseInterfacesNode.getChildCount();
if (baseInterfacesNode != null && childCount > 0)
{
for (int i = 0; i < childCount; i++)
{
IASNode child = baseInterfacesNode.getChild(i);
if (child instanceof IIdentifierNode)
{
interfaceNodeList.add(((IIdentifierNode)child).getName());
}
}
}
return interfaceNodeList.toArray(new String[0]);
}
//
// Other methods
//
/**
* Sets the interface keyword. Used during parsing.
*
* @param interfaceKeyword token containing the keyword
*/
public void setInterfaceKeyword(IASToken interfaceKeyword)
{
interfaceKeywordNode = new KeywordNode(interfaceKeyword);
}
/**
* Get the node containing the class keyword
*
* @return the node containing class
*/
public KeywordNode getClassKeywordNode()
{
return interfaceKeywordNode;
}
/**
* Get the node containing the extends keyword, if one exists
*
* @return the node containing extends
*/
public KeywordNode getExtendsKeywordNode()
{
return extendsKeywordNode;
}
/**
* Sets the extends keyword if one is present. Used during parsing.
*
* @param extendsKeyword token containing the keyword
*/
public void setExtendsKeyword(IASToken extendsKeyword)
{
extendsKeywordNode = new KeywordNode(extendsKeyword);
}
/**
* Add an implemented interface to this class. Used during parsing.
*
* @param interfaceName node containing the interface name
*/
public void addBaseInterface(ExpressionNodeBase interfaceName)
{
baseInterfacesNode.addChild(interfaceName);
}
/**
* Get the container of base interfaces for this interfaces
*
* @return the node containing the base interfaces
*/
public ContainerNode getBaseInterfacesNode()
{
return baseInterfacesNode;
}
private void setupCastFunction(EnumSet<PostProcessStep> set, InterfaceDefinition def, ASScope scope)
{
// Unlike ClassNode, InterfaceNode doesn't create implicit definitions
// for "this" or "super" for interfaces; but it does create an implicit
// function that helps us resolve casting expressions such as IFoo(foo).
FunctionDefinition castFunc = new FunctionDefinition(getName());
castFunc.setNamespaceReference(NamespaceDefinition.getCodeModelImplicitDefinitionNamespace());
castFunc.setReturnTypeReference(ReferenceFactory.resolvedReference(def));
castFunc.setCastFunction();
castFunc.setImplicit();
// Add this definition to the interface scope.
scope.addDefinition(castFunc);
}
/**
* Get the dummy node representing the cast function
*
* @return cast function
*/
public FunctionNode getCastFunctionNode()
{
return castFunctionNode;
}
InterfaceDefinition buildDefinition()
{
String definitionName = nameNode.computeSimpleReference();
InterfaceDefinition definition = new InterfaceDefinition(definitionName);
definition.setNode(this);
fillInNamespaceAndModifiers(definition);
fillInMetadata(definition);
// Set the interfaces that this interface extends.
if (baseInterfacesNode != null)
{
int n = baseInterfacesNode.getChildCount();
IReference[] baseInterfaces = new IReference[n];
for (int i = 0; i < n; i++)
{
IASNode child = baseInterfacesNode.getChild(i);
if (child instanceof ExpressionNodeBase)
baseInterfaces[i] = ((ExpressionNodeBase)child).computeTypeReference();
}
definition.setExtendedInterfaceReferences(baseInterfaces);
}
return definition;
}
}