blob: 05238a919a93d1c09ec454b498c6134428e92f7f [file] [log] [blame]
/*
*
* 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.royale.compiler.internal.tree.as;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import org.apache.royale.abc.semantics.Name;
import org.apache.royale.compiler.constants.IASKeywordConstants;
import org.apache.royale.compiler.constants.INamespaceConstants;
import org.apache.royale.compiler.definitions.IDefinition;
import org.apache.royale.compiler.definitions.INamespaceDefinition;
import org.apache.royale.compiler.definitions.IQualifiers;
import org.apache.royale.compiler.definitions.references.INamespaceReference;
import org.apache.royale.compiler.internal.definitions.DefinitionBase;
import org.apache.royale.compiler.internal.definitions.InterfaceDefinition;
import org.apache.royale.compiler.internal.definitions.NamespaceDefinition;
import org.apache.royale.compiler.internal.scopes.ASScope;
import org.apache.royale.compiler.parsing.IASToken;
import org.apache.royale.compiler.projects.ICompilerProject;
import org.apache.royale.compiler.tree.ASTNodeID;
import org.apache.royale.compiler.tree.as.IASNode;
import org.apache.royale.compiler.tree.as.IDefinitionNode;
import org.apache.royale.compiler.tree.as.INamespaceDecorationNode;
import org.apache.royale.compiler.tree.as.IScopedNode;
import com.google.common.collect.ImmutableSet;
/**
* Identifier representing a namespace
*/
public class NamespaceIdentifierNode extends IdentifierNode implements INamespaceDecorationNode
{
/**
* Constructor.
*
* @param t The token representing the namespace.
*/
public NamespaceIdentifierNode(IASToken t)
{
super(t.getText());
span(t);
}
/**
* Constructor.
*
* @param text The namespace as a String.
*/
public NamespaceIdentifierNode(String text)
{
super(text);
}
/**
* Constructor.
*
* @param node The node reprsenting the namespace.
*/
public NamespaceIdentifierNode(IdentifierNode node)
{
super(node.getName());
span(node.getAbsoluteStart(), node.getAbsoluteEnd(), node.getLine(), node.getColumn(), node.getEndLine(), node.getEndColumn());
setSourcePath(node.getSourcePath());
}
/**
* Copy constructor.
*
* @param other The node to copy.
*/
protected NamespaceIdentifierNode(NamespaceIdentifierNode other)
{
super(other);
this.isConfig = other.isConfig;
}
private IDefinitionNode decoratedDefinitionNode = null;
/**
* Flag used to indicate if we are a config name
*/
private boolean isConfig;
//
// NodeBase overrides
//
@Override
public ASTNodeID getNodeID()
{
return ASTNodeID.NamespaceIdentifierID;
}
@Override
// TODO What is special about this class
// that requires it to override getParent()?
public IASNode getParent()
{
if (parent == null && decoratedDefinitionNode != null)
{
// if we don't have a parent, try the IDefinition
return decoratedDefinitionNode.getParent();
}
return super.getParent();
}
//
// ExpressionNodeBase overrides
//
@Override
public IDefinition resolve(ICompilerProject project)
{
boolean resolveToConcreteNs = true;
return resolve(project, resolveToConcreteNs);
}
@Override
protected NamespaceIdentifierNode copy()
{
return new NamespaceIdentifierNode(this);
}
@Override
public IScopedNode getScopeNode()
{
return (IScopedNode)getAncestorOfType(IScopedNode.class);
}
@Override
public ExpressionNodeBase getDecorationNode()
{
return this;
}
//
// IdentifierNode overrides
//
@Override
public Name getMName(ICompilerProject project)
{
// There is no AET name to refer to builtin identifiers.
// This gets called with the builtin identifiers by the BURM,
// but it is OK to return null - the names won't be used.
if (isBuiltinNamespaceIdentifier())
return null;
// resolve without following any namespace reference chains
// since we want the name for this definition, not whatever
// namespace it ends up refering to.
// for:
// namespace ns1 = ns2;
// ns1; // we want the name for ns1, not ns2.
IDefinition def = resolve(project, false);
if (canEarlyBind(project, def))
return ((DefinitionBase)def).getMName(project);
// If we can't early bind, then the super method does the right thing
return super.getMName(project);
}
//
// INamespaceDecorationNode implementations
//
@Override
public IDefinitionNode getDecoratedDefinitionNode()
{
return decoratedDefinitionNode;
}
@Override
public NamespaceDecorationKind getNamespaceDecorationKind()
{
return isConfig ? NamespaceDecorationKind.CONFIG : NamespaceDecorationKind.NAME;
}
//
// Other methods
//
/**
* Sets that this namespace is a config name used for conditional
* compilation
*
* @param isConfig trye if this namespace is part of a config expression
*/
public void setIsConfigNamespace(boolean isConfig)
{
this.isConfig = isConfig;
}
public void setDecorationTarget(IDefinitionNode decoratingParent)
{
// to save space, use the parent slot for the definition we are decorating
// we can use that to resolve our actual parent
decoratedDefinitionNode = decoratingParent;
}
/**
* Private implementation of resolve. Can resolve to the Namespace this node refers to,
* or it can resolve to the underlying namespace (in cases where the namespace is defined as
* 'namespace ns1 = ns2'.
*
* For computing a Name for this Node, we want the namespace this node refers to (ns1 in the above example).
* But for computing the URI for the namespace, we want to resolve to ns2, which is the actual namespace value
* it will hold.
* @param project project to resolve things in
* @param resolveToConcreteNs true if this method should resolve to the actual underlying namespace,
* @return the definition this node refers to.
*/
private IDefinition resolve (ICompilerProject project, boolean resolveToConcreteNs)
{
if (isBuiltinNamespaceIdentifier())
{
// Resolve public, private, internal, or protected special-like
// this is so exprs like public::x work correctly.
ASScope scope = getASScope();
INamespaceReference nsRef = NamespaceDefinition.createNamespaceReference(scope, this);
return nsRef != null ? nsRef.resolveNamespaceReference(project) : null;
}
IDefinition d = super.resolve(project);
if (resolveToConcreteNs &&
d instanceof NamespaceDefinition.INamepaceDeclarationDirective)
{
// Resolve to the actual underlying namespace, in case this namespace was defined as:
// namespace ns1 = otherNamespace;
d = ((NamespaceDefinition.INamepaceDeclarationDirective)d).resolveConcreteDefinition(project);
}
else if (d instanceof InterfaceDefinition)
{
// If an interface was used as the qualifier, then we really want to use the interface
// namespace as the qualifier.
d = ((InterfaceDefinition)d).getInterfaceNamespaceReference();
}
return d;
}
/**
* Helper method to determine if this node references one of the builtin
* access namespaces these consist of public, private, protected, internal,
* and the any namespace - '*'
* Helper method to determine if this node references one of the built-in
* access namespaces. These consist of <code>public</code>,
* <code>private</code>, <code>protected</code>, <code>internal</code>,
* and the 'any' namespace <code>*</code>.
* Resolve this namespace reference to a set of 1 or more namespaces.
*
* Some qualifiers, such as "public" or "protected" may resolve to more than 1 namespace when they
* are used as the qualifier of an expression, such as:
*
* f.public::foo;
*
* @param project The project to resolve things in
* @return The qualifier(s) that this namespace node referred to
*/
public IQualifiers resolveQualifier(ICompilerProject project )
{
IQualifiers result = null;
if( isBuiltinNamespaceIdentifier() )
{
ASScope scope = getASScope();
// Only do the multi namespace processing if there is a decent chance
// we will end up with many namespaces
if( NamespaceDefinition.qualifierCouldBeManyNamespaces(scope, this) )
{
// Get all the namespace refs
Collection<INamespaceReference> nsrefs = NamespaceDefinition.createNamespaceReferencesForQualifier(scope, this);
Set<INamespaceDefinition> namespaces = new HashSet<INamespaceDefinition>(nsrefs.size());
for( INamespaceReference ns : nsrefs )
{
namespaces.add(ns.resolveNamespaceReference(project));
}
// Return an IQualifiers implementation that deals with multiple namespaces
result = new MultiNamespaceQualifiers(namespaces);
}
}
if( result == null )
{
// didn't find multiple namespaces, so just go down the normal resolution
// path.
IDefinition d = resolve(project);
if( d instanceof IQualifiers )
result = (IQualifiers)d;
}
return result;
}
/**
* Helper class to return multiple namespaces
*/
private static class MultiNamespaceQualifiers implements IQualifiers
{
private Set<INamespaceDefinition> namespaces;
MultiNamespaceQualifiers(Set<INamespaceDefinition> namespaces)
{
this.namespaces = ImmutableSet.copyOf(namespaces);
}
public int getNamespaceCount ()
{
return namespaces.size();
}
public Set<INamespaceDefinition> getNamespaceSet ()
{
return namespaces;
}
public INamespaceDefinition getFirst ()
{
return namespaces.iterator().next();
}
}
/**
* Helper method to determine if this node references one of the builtin
* access namespaces these consist of public, private, protected, internal,
* and the any namespace - '*'
*
* @return true if this node is a reference to the public, private,
* protected, or internal namespace
*/
private boolean isBuiltinNamespaceIdentifier()
{
String name = getName();
return name == IASKeywordConstants.PUBLIC ||
name == IASKeywordConstants.PRIVATE ||
name == IASKeywordConstants.PROTECTED ||
name == IASKeywordConstants.INTERNAL ||
name == INamespaceConstants.ANY;
}
public boolean isExpressionQualifier()
{
IASNode p = getParent();
if( p instanceof NamespaceAccessExpressionNode )
{
return ((NamespaceAccessExpressionNode) p).getLeftOperandNode() == this;
}
return false;
}
}