/*
*
* 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.Collection;
import java.util.Deque;
import java.util.EnumSet;
import java.util.LinkedList;
import org.apache.flex.compiler.common.ASImportTarget;
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.parsing.as.ASToken;
import org.apache.flex.compiler.internal.parsing.as.ASTokenTypes;
import org.apache.flex.compiler.internal.scopes.ASScope;
import org.apache.flex.compiler.internal.semantics.PostProcessStep;
import org.apache.flex.compiler.parsing.IASToken;
import org.apache.flex.compiler.problems.DuplicateImportAliasProblem;
import org.apache.flex.compiler.problems.ICompilerProblem;
import org.apache.flex.compiler.projects.ICompilerProject;
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.IImportNode;
/**
* ActionScript parse tree node representing an import statement
*/
public class ImportNode extends FixedChildrenNode implements IImportNode
{
/**
* Builds an ImportNode for a qname.
*/
public static ImportNode buildImportNode(String qname)
{
Deque<IASNode> nodeList = new LinkedList<IASNode>();
for (String s : qname.split("\\."))
{
nodeList.add(new IdentifierNode(s));
}
IASToken dotToken = new ASToken(ASTokenTypes.TOKEN_OPERATOR_MEMBER_ACCESS, -1, -1, -1, -1, ".");
while (nodeList.size() > 1)
{
ExpressionNodeBase first = (ExpressionNodeBase)nodeList.removeFirst();
ExpressionNodeBase second = (ExpressionNodeBase)nodeList.removeFirst();
nodeList.addFirst(new FullNameNode(first, dotToken, second));
}
ImportNode importNode = new ImportNode((ExpressionNodeBase)nodeList.getFirst());
return importNode;
}
/**
* Turns a qualified name such as <code>"flash.display.Sprite"</code>
* into a wildcard name like <code>"flash.display.*"</code>.
*/
public static String makeWildcardName(String name)
{
int lastDot = name.lastIndexOf('.');
if (lastDot != -1)
name = name.substring(0, lastDot) + ".*";
return name;
}
/**
* Constructor.
*
* @param targetImportNode package to import
*/
public ImportNode(ExpressionNodeBase targetImportNode)
{
this.targetImportNode = targetImportNode;
importKind = ImportKind.AS_SCOPED_IMPORT;
}
/**
* Package to import
*/
protected ExpressionNodeBase targetImportNode;
protected ImportKind importKind;
protected String importAlias;
//
// NodeBase overrides
//
@Override
public ASTNodeID getNodeID()
{
return ASTNodeID.ImportID;
}
@Override
public int getChildCount()
{
return targetImportNode != null ? 1 : 0;
}
@Override
public IASNode getChild(int i)
{
if (i == 0)
return targetImportNode;
return null;
}
@Override
protected void setChildren(boolean fillInOffsets)
{
if (targetImportNode != null)
{
targetImportNode.normalize(fillInOffsets);
targetImportNode.setParent(this);
}
}
@Override
public void normalize(boolean fillInOffsets)
{
setChildren(fillInOffsets);
super.normalize(fillInOffsets);
}
@Override
protected void analyze(EnumSet<PostProcessStep> set, ASScope scope, Collection<ICompilerProblem> problems)
{
if (set.contains(PostProcessStep.POPULATE_SCOPE))
{
if (importAlias != null)
{
if (scope.hasImportAlias(importAlias))
{
//we can't have duplicates, or it will be ambiguous -JT
FileNode fileNode = (FileNode) getAncestorOfType(FileNode.class);
fileNode.addProblem(new DuplicateImportAliasProblem(this, this.getImportAlias()));
}
else
{
scope.addImport(this.getImportName(), this.getImportAlias());
}
}
else
{
scope.addImport(this.getImportName());
}
}
}
/*
* For debugging only. Builds a string such as
* <code>"flash.display.Sprite"</code> from the import name.
*/
@Override
protected boolean buildInnerString(StringBuilder sb)
{
sb.append('"');
sb.append(getImportName());
sb.append('"');
return true;
}
//
// IImportNode implementations
//
@Override
public ImportKind getImportKind()
{
if (getAbsoluteStart() == getAbsoluteEnd())
return ImportKind.IMPLICIT_IMPORT;
return importKind;
}
@Override
public IExpressionNode getImportNameNode()
{
return targetImportNode;
}
@Override
public String getImportName()
{
return targetImportNode instanceof IIdentifierNode ? ((IIdentifierNode)targetImportNode).getName() : "";
}
@Override
public IImportTarget getImportTarget()
{
return ASImportTarget.get(getWorkspace(), getImportName());
}
@Override
public boolean isWildcardImport()
{
String targetPackage = getImportName();
return (targetPackage.endsWith(".") || targetPackage.endsWith("*"));
}
@Override
public IDefinition resolveImport(ICompilerProject project)
{
if (isWildcardImport())
return null;
String importName = getImportName();
IReference importReference = ReferenceFactory.packageQualifiedReference(
project.getWorkspace(), importName);
ASScope scope = (ASScope)getScopeNode().getScope();
return importReference.resolve(project, scope, null, true);
}
//
// Other methods
//
/**
* Sets the type of import we have encountered. Used during parsing,
* defaults to AS_SCOPED_IMPORT
*
* @param importKind The type of import.
*/
public void setImportKind(ImportKind importKind)
{
this.importKind = importKind;
}
/**
* Sets the import target. Used during parsing.
*
* @param targetImportNode The node representing the import target.
*/
public void setImportTarget(ExpressionNodeBase targetImportNode)
{
this.targetImportNode = targetImportNode;
}
/**
* Gets the optional alias of the import.
*/
public String getImportAlias()
{
return this.importAlias;
}
/**
* Sets the optional alias of the import.
*
* @param importAlias The alias of the import.
*/
public void setImportAlias(String importAlias)
{
this.importAlias = importAlias;
}
}