/* * * 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 org.apache.flex.abc.semantics.Name; import org.apache.flex.compiler.constants.IASLanguageConstants; import org.apache.flex.compiler.definitions.IDefinition; import org.apache.flex.compiler.definitions.ITypeDefinition; import org.apache.flex.compiler.definitions.references.INamespaceReference; import org.apache.flex.compiler.definitions.references.IReference; import org.apache.flex.compiler.internal.parsing.as.ASTokenTypes; 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.IIdentifierNode; import org.apache.flex.compiler.tree.as.ILanguageIdentifierNode; import org.apache.flex.compiler.tree.as.IMemberAccessExpressionNode; import org.apache.flex.compiler.tree.as.ILanguageIdentifierNode.LanguageIdentifierKind; /** * ActionScript parse tree node representing a member access expression of one * of the following types: * <ul> * <li><b>Member access</b> - {@code object.fieldName}</li> * <li><b>Query</b> - {@code object..descendantsName}</li> * <li><b>E4X filter</b> - {@code xmlObject.(expr)}</li> * </ul> */ public class MemberAccessExpressionNode extends BinaryOperatorNodeBase implements IMemberAccessExpressionNode { /** * Constructor. * * @param left the expression on the left of the member access (the object) * @param operatorToken the ASToken holding the member access operator (".") * @param right the expression on the right of the member access (the * member) */ public MemberAccessExpressionNode(ExpressionNodeBase left, IASToken operatorToken, ExpressionNodeBase right) { super(operatorToken, left, right); if (operatorToken != null) { int tokenType = operatorToken.getType(); if (tokenType == ASTokenTypes.TOKEN_OPERATOR_MEMBER_ACCESS) operator = OperatorType.MEMBER_ACCESS; else if (tokenType == ASTokenTypes.TOKEN_OPERATOR_DESCENDANT_ACCESS) operator = OperatorType.DESCENDANT_ACCESS; else assert false : "Unexpected token '" + operatorToken.getText() + "' for MemberAccessExpressionNode"; } } /** * Copy constructor. * * @param other The node to copy. */ public MemberAccessExpressionNode(MemberAccessExpressionNode other) { super(other); this.operator = other.operator; } private OperatorType operator = OperatorType.MEMBER_ACCESS; // // NodeBase overrides // @Override public ASTNodeID getNodeID() { final ASTNodeID nodeID; if (getOperator() == OperatorType.DESCENDANT_ACCESS) { nodeID = ASTNodeID.Op_DescendantsID; } else if (rightOperandNode != null && rightOperandNode.hasParenthesis()) { nodeID = ASTNodeID.E4XFilterID; } else { nodeID = ASTNodeID.MemberAccessExpressionID; } return nodeID; } // // ExpressionNodeBase overrides // @Override public IDefinition resolve(ICompilerProject project) { // A reference to a package won't resolve to anything. if (isPackageReference()) return null; // The definition that a member access expression resolves to will // always be the definition that its rightmost child resolves to // (e.g., the definition of c in a.b.c). return this.rightOperandNode.resolve(project); } @Override public ITypeDefinition resolveType(ICompilerProject project) { // If the node is a E4XFilterID, the resolved type is XMLList. // Otherwise, the resolved type is the same as its // right-hand-side expression. if (ASTNodeID.E4XFilterID == getNodeID()) return project.getBuiltinType(IASLanguageConstants.BuiltinType.XMLLIST); else return rightOperandNode.resolveType(project); } @Override public boolean isDynamicExpression(ICompilerProject project) { // If this is a package reference, we're not dynamic. if (isPackageReference()) return false; return super.isDynamicExpression(project); } @Override protected MemberAccessExpressionNode copy() { return new MemberAccessExpressionNode(this); } @Override public Name getMName(ICompilerProject project) { return rightOperandNode.getMName(project); } @Override String computeSimpleReference() { return getDisplayString(); } @Override IReference computeTypeReference() { return rightOperandNode.computeTypeReference(); } @Override public INamespaceReference computeNamespaceReference() { return rightOperandNode.computeNamespaceReference(); } @Override boolean isPartOfMemberRef(ExpressionNodeBase e) { // If e is the rhs, then this it is always a member ref. // If e is the lhs, then it is a member ref // if the MemberAccessExpression is a member ref. if (e == this.rightOperandNode) { return true; } else if (e == this.leftOperandNode) { IASNode p = getParent(); if (p instanceof ExpressionNodeBase) return ((ExpressionNodeBase)p).isPartOfMemberRef(this); } return false; } @Override ExpressionNodeBase getBaseForMemberRef(ExpressionNodeBase e) { // If e is the rhs, then the base is the lhs. // Of e is the lhs, then the base is determined by the parent. if (e == this.rightOperandNode) { return this.leftOperandNode; } else if (e == this.leftOperandNode) { ExpressionNodeBase p = getParentExpression(); if (p != null) return p.getBaseForMemberRef(this); } return null; } // // OperatorNodeBase overrides // @Override public OperatorType getOperator() { return operator; } @Override public String getOperatorText() { switch (getNodeID()) { case E4XFilterID: return ".()"; default: return super.getOperatorText(); } } // // IMemberAccessExpressionNode implementations // @Override public String getDisplayString() { StringBuilder builder = new StringBuilder(); if (leftOperandNode != null && leftOperandNode instanceof IIdentifierNode) { builder.append(((IIdentifierNode)leftOperandNode).getName()); } else if (leftOperandNode != null && leftOperandNode instanceof MemberAccessExpressionNode) { builder.append(((MemberAccessExpressionNode)leftOperandNode).getDisplayString()); } if (builder.length() > 0) builder.append("."); if (rightOperandNode != null && rightOperandNode instanceof IIdentifierNode) builder.append(((IIdentifierNode)rightOperandNode).getName()); return builder.toString(); } // // Other methods // public boolean isSuper(ExpressionNodeBase node) { if (!(node instanceof ILanguageIdentifierNode)) return false; ILanguageIdentifierNode idBase = (ILanguageIdentifierNode)node; if (idBase.getKind() != LanguageIdentifierKind.SUPER) return false; return true; } /** * Determine if the left side of this Node refers to a package name * * @return true if the left side is a package name */ public boolean stemIsPackage() { return leftOperandNode.isPackageReference(); } /** * Tell the MemberAccessExpression that it's "stem" ('a.b' in 'a.b.Foo') * should always be treated as a package, regardless of whether it is * imported or not. This is needed by MXML because sometimes it does not * require users to import a package before using it as a package (such as * in MXMLClassNode). */ public void setStemAsPackage(boolean b) { leftOperandNode.setTreatAsPackage(b); } /** * Is the given node this node's member reference? * @param node - the node of interest. * @return true iff node is this node's member reference. */ public boolean isMemberReference(IASNode node) { return node == this.rightOperandNode; } }