/* * * 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.HashSet; import java.util.Set; import org.apache.flex.abc.semantics.Name; import org.apache.flex.compiler.constants.IASKeywordConstants; import org.apache.flex.compiler.constants.INamespaceConstants; import org.apache.flex.compiler.definitions.IDefinition; import org.apache.flex.compiler.definitions.INamespaceDefinition; import org.apache.flex.compiler.definitions.IQualifiers; import org.apache.flex.compiler.definitions.references.INamespaceReference; import org.apache.flex.compiler.internal.definitions.DefinitionBase; 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.parsing.IASToken; 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.IDefinitionNode; import org.apache.flex.compiler.tree.as.INamespaceDecorationNode; import org.apache.flex.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()); 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; } }