/* * Copyright 2000-2013 JetBrains s.r.o. * Copyright 2014-2014 AS3Boyan * Copyright 2014-2014 Elias Ku * * Licensed 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 com.intellij.plugins.haxe.lang.psi.impl; import com.intellij.lang.ASTNode; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.util.Pair; import com.intellij.plugins.haxe.lang.lexer.HaxeTokenTypes; import com.intellij.plugins.haxe.lang.psi.*; import com.intellij.psi.*; import com.intellij.psi.impl.PsiSuperMethodImplUtil; import com.intellij.psi.impl.source.tree.java.PsiTypeParameterImpl; import com.intellij.psi.javadoc.PsiDocComment; import com.intellij.psi.tree.IElementType; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.util.IncorrectOperationException; import org.apache.log4j.Level; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.ArrayList; import java.util.Collection; import java.util.List; /** * Created by ebishton on 10/22/14. */ public class HaxeTypeListPartPsiMixinImpl extends HaxePsiCompositeElementImpl implements HaxeTypeListPartPsiMixin { // XXX: TypeListPart might be better just being a private entry in the BNF. I'm not sure. // // The thing I most dislike about subclassing PsiTypeParameter is that it implements // the PsiClass interface. One of the (three possible)child elements that this class // holds is HaxeAnonymousType, which itself derives from HaxeClass (thus, PsiClass). // So, I'd prefer that HaxeTypePsiMixin implement the class interface. Then we // would have better balance between the child implementations (and less special code) // and we can cleanly ignore this class in the hierarchy. // // The third child type that we can hold is HaxeFunctionType, which is simply a // construct for holding (effectively) a typedef, described via the arrow notation: // type -> type -> return_type // This type doesn't really support a class, just gives a function pointer. So, now // we've got a mostly empty stub class as well... static Logger LOG = Logger.getInstance("#com.intellij.plugins.haxe.lang.psi.impl.HaxeTypeListPartPsiMixinImpl"); static { LOG.setLevel(Level.DEBUG); } // The child type that implements an interface for one of these three parameter types. // We can't assign this type in the constructor because the children aren't known // (to us) at construction time. We'll check it later when one of the interfaces // are hit. PsiClass myChildClass = null; HaxeTypeListPartPsiMixinImpl(ASTNode node) { super(node); } @NotNull private PsiClass getDelegate() { // We're going to try to cache our child. If the code changes, then this reference // may refer to the wrong class. In that case, it would be better to do the // lookup (resolveHaxeClass()), which has a caching algorithm of its own and // deals with naming changes accordingly. if (null == myChildClass) { PsiElement child = getFirstChild(); PsiElement target = null; ASTNode node = child.getNode(); if ( null != node ) { IElementType type = node.getElementType(); if (HaxeTokenTypes.TYPE_OR_ANONYMOUS.equals(type)) { target = child.getFirstChild(); IElementType targetType = target.getNode().getElementType(); if (HaxeTokenTypes.TYPE.equals(targetType)) { myChildClass = new HaxeClassDelegateForTypeChild((HaxeType)target); } else if (HaxeTokenTypes.ANONYMOUS_TYPE.equals(targetType)){ myChildClass = (HaxeAnonymousType) target; } else { LOG.debug("Target: " + target.toString()); LOG.assertTrue(false, "Unexpected token type for child of TYPE_OR_ANONYMOUS"); myChildClass = AbstractHaxePsiClass.EMPTY_FACADE; } } else if (HaxeTokenTypes.FUNCTION_TYPE.equals(type)) { // TODO: Fix this. Temporary hack in place so I can test. myChildClass = (HaxeAnonymousType) child; } else { myChildClass = AbstractHaxePsiClass.EMPTY_FACADE; } } else { // No child node? How can this be? LOG.assertTrue(false, "No child node found for TYPE_LIST_PART"); myChildClass = AbstractHaxePsiClass.EMPTY_FACADE; } } return myChildClass; } // // PsiTypeParameter overrides // @Override public PsiTypeParameterListOwner getOwner() { final PsiElement parent = getParent(); if (parent == null) throw new PsiInvalidElementAccessException(this); return PsiTreeUtil.getParentOfType(this, PsiTypeParameterListOwner.class); } @Override public int getIndex() { int ret = 0; PsiElement element = getPrevSibling(); while (element != null) { if (element instanceof PsiTypeParameter) { ret++; } element = element.getPrevSibling(); } return ret; } // // PsiClass overrides // @Nullable @Override public String getQualifiedName() { return getDelegate().getQualifiedName(); } @Override public boolean isInterface() { return getDelegate().isInterface(); } @Override public boolean isAnnotationType() { return getDelegate().isAnnotationType(); } @Override public boolean isEnum() { return getDelegate().isEnum(); } @Override @NotNull public PsiField[] getFields() { return getDelegate().getFields(); } @Override @NotNull public PsiMethod[] getMethods() { return getDelegate().getMethods(); } @Override public PsiMethod findMethodBySignature(PsiMethod patternMethod, boolean checkBases) { return getDelegate().findMethodBySignature(patternMethod, checkBases); } @Override @NotNull public PsiMethod[] findMethodsBySignature(PsiMethod patternMethod, boolean checkBases) { return getDelegate().findMethodsBySignature(patternMethod, checkBases); } @Override public PsiField findFieldByName(String name, boolean checkBases) { return getDelegate().findFieldByName(name, checkBases); } @Override @NotNull public PsiMethod[] findMethodsByName(String name, boolean checkBases) { return getDelegate().findMethodsByName(name, checkBases); } @Override @NotNull public List<Pair<PsiMethod, PsiSubstitutor>> findMethodsAndTheirSubstitutorsByName(String name, boolean checkBases) { return getDelegate().findMethodsAndTheirSubstitutorsByName(name, checkBases); } @Override @NotNull public List<Pair<PsiMethod, PsiSubstitutor>> getAllMethodsAndTheirSubstitutors() { return getDelegate().getAllMethodsAndTheirSubstitutors(); } @Override public PsiClass findInnerClassByName(String name, boolean checkBases) { return getDelegate().findInnerClassByName(name, checkBases); } @Override public PsiTypeParameterList getTypeParameterList() { return getDelegate().getTypeParameterList(); } @Override public boolean hasTypeParameters() { return getDelegate().hasTypeParameters(); } @Override public PsiElement getScope() { return getDelegate().getScope(); } @Override public boolean isInheritorDeep(PsiClass baseClass, PsiClass classToByPass) { return getDelegate().isInheritorDeep(baseClass, classToByPass); } @Override public boolean isInheritor(@NotNull PsiClass baseClass, boolean checkDeep) { return getDelegate().isInheritor(baseClass, checkDeep); } @Override @Nullable public PsiIdentifier getNameIdentifier() { return getDelegate().getNameIdentifier(); } @Override public PsiElement setName(@NotNull String name) throws IncorrectOperationException { return getDelegate().setName(name); } @Override @NotNull public PsiMethod[] getConstructors() { return getDelegate().getConstructors(); } @Override public PsiDocComment getDocComment() { return getDelegate().getDocComment(); } @Override public boolean isDeprecated() { return getDelegate().isDeprecated(); } @Override @NotNull public PsiReferenceList getExtendsList() { return getDelegate().getExtendsList(); } @Override public PsiReferenceList getImplementsList() { return getDelegate().getImplementsList(); } @Override @NotNull public PsiClassType[] getExtendsListTypes() { return getDelegate().getExtendsListTypes(); } @Override @NotNull public PsiClassType[] getImplementsListTypes() { return getDelegate().getImplementsListTypes(); } @Override @NotNull public PsiClass[] getInnerClasses() { return getDelegate().getInnerClasses(); } @Override @NotNull public PsiField[] getAllFields() { return getDelegate().getAllFields(); } @Override @NotNull public PsiMethod[] getAllMethods() { return getDelegate().getAllMethods(); } @Override @NotNull public PsiClass[] getAllInnerClasses() { return getDelegate().getAllInnerClasses(); } @Override @NotNull public PsiClassInitializer[] getInitializers() { return getDelegate().getInitializers(); } @Override @NotNull public PsiTypeParameter[] getTypeParameters() { return getDelegate().getTypeParameters(); } @Override public PsiClass getSuperClass() { return getDelegate().getSuperClass(); } @Override public PsiClass[] getInterfaces() { return getDelegate().getInterfaces(); } @Override @NotNull public PsiClass[] getSupers() { return getDelegate().getSupers(); } @Override @NotNull public PsiClassType[] getSuperTypes() { return getDelegate().getSuperTypes(); } @Override public PsiClass getContainingClass() { return getDelegate().getContainingClass(); } @Override @NotNull public Collection<HierarchicalMethodSignature> getVisibleSignatures() { return getDelegate().getVisibleSignatures(); } @Override public HaxeModifierList getModifierList() { return (HaxeModifierList) getDelegate().getModifierList(); } @Override public boolean hasModifierProperty(@NotNull String name) { return getDelegate().hasModifierProperty(name); } @Override public PsiJavaToken getLBrace() { // Casting this is only doable because HaxeAstFactory // creates PsiJavaTokens. return (PsiJavaToken)getDelegate().getLBrace(); } @Override public PsiJavaToken getRBrace() { return (PsiJavaToken) getDelegate().getRBrace(); } // // PsiAnnotationOwner // @Override @NotNull public PsiAnnotation[] getAnnotations() { // Type parameters don't get modifiers. return PsiAnnotation.EMPTY_ARRAY; } @Override public PsiAnnotation findAnnotation(@NotNull @NonNls String qualifiedName) { // Type parameters don't get modifiers. return null; } @Override @NotNull public PsiAnnotation addAnnotation(@NotNull @NonNls String qualifiedName) { throw new IncorrectOperationException(); } @Override @NotNull public PsiAnnotation[] getApplicableAnnotations() { return getAnnotations(); } // ////////////////////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////////////////////// // // HaxeTypeListPartForTypeChild // // ////////////////////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////////////////////// /** * Deals with type references that appear as children to HaxeTypeOrAnonymous * child elements. */ private class HaxeClassDelegateForTypeChild extends PsiTypeParameterImpl { private HaxeType myChildType = null; public HaxeClassDelegateForTypeChild(@NotNull HaxeType childType) { super(childType.getNode()); myChildType = childType; } @NotNull protected HaxeReference getDelegate() { return myChildType.getReferenceExpression(); } @Nullable protected HaxeClass getResolvedDelegate() { PsiElement elem = getDelegate().resolve(); return elem instanceof HaxeClass? (HaxeClass) elem : null; } // // PsiTypeParameter overrides // // PsiTypeParameter extends PsiClass instead of a reference. It just stubs // out most of the functionality. @Override @Nullable public PsiMethod findMethodBySignature(PsiMethod patternMethod, boolean checkBases) { // XXX: If it turns out that we need this method, we need to find the // actual class implementation (getDelegate().resolveHaxeClass()) // and call that. LOG.warn("Unexpected call to findMethodBySignature()"); return null; } @Override @NotNull public PsiMethod[] findMethodsBySignature(PsiMethod patternMethod, boolean checkBases) { LOG.warn("Unexpected call to findMethodsBySignature()"); return PsiMethod.EMPTY_ARRAY; } @Override public PsiField findFieldByName(String name, boolean checkBases) { LOG.warn("Unexpected call to findFieldByName()"); return null; } @Override @NotNull public PsiMethod[] findMethodsByName(String name, boolean checkBases) { LOG.warn("Unexpected call to findMethodsByName()"); return PsiMethod.EMPTY_ARRAY; } @Override @NotNull public List<Pair<PsiMethod, PsiSubstitutor>> findMethodsAndTheirSubstitutorsByName(String name, boolean checkBases) { LOG.warn("Unexpected call to findMethodsAndTheirSubstitutorsByName()"); return new ArrayList<Pair<PsiMethod,PsiSubstitutor>>(); } @Override @NotNull public List<Pair<PsiMethod, PsiSubstitutor>> getAllMethodsAndTheirSubstitutors() { LOG.warn("Unexpected call to getAllMethodsAndTheirSubstitutors"); return new ArrayList<Pair<PsiMethod,PsiSubstitutor>>(); } @Override public PsiClass findInnerClassByName(String name, boolean checkBases) { return null; // No named inner classes in Haxe. } @Override public PsiElement getScope() { /** * Returns the PSI member in which the class has been declared (for example, * the method containing the anonymous inner class, or the file containing a regular * class, or the class owning a type parameter). */ // As a TypeListPart, we are only children of extends, implements, and generic parameters. // In each of these cases, the class for which we are being declared (which may be // an anonymous type) is the scope. PsiElement parent = getParent(); while (parent != null) { IElementType parentType = parent.getNode().getElementType(); if (HaxeTokenTypes.ANONYMOUS_TYPE.equals(parentType) || (HaxeTokenTypes.CLASS_DECLARATION.equals(parentType)) || parent instanceof HaxeFile ) // HaxeFile is a catchall.. { break; } parent = parent.getParent(); } return parent; } @NotNull @Override public PsiIdentifier getNameIdentifier() { // For a HaxeType, the identifier is two children below. The first is // a reference. HaxeReferenceExpression ref = PsiTreeUtil.getRequiredChildOfType(this, HaxeReferenceExpression.class); HaxeIdentifier id = PsiTreeUtil.getRequiredChildOfType(ref, HaxeIdentifier.class); return id; } @Override public boolean isInheritorDeep(PsiClass baseClass, PsiClass classToByPass) { HaxeClass resolved = getResolvedDelegate(); return null != resolved && resolved.isInheritorDeep(baseClass, classToByPass); } @Override public boolean isInheritor(@NotNull PsiClass baseClass, boolean checkDeep) { HaxeClass resolved = getResolvedDelegate(); return null != resolved && resolved.isInheritor(baseClass, checkDeep); } @Override @NotNull public PsiReferenceList getExtendsList() { // Haxe BNF doesn't allow for extends in this position. return new HaxeExtendsDeclarationImpl(new HaxeDummyASTNode("Empty Extends List")); } @Override public PsiReferenceList getImplementsList() { // Haxe BNF doesn't allow for implements in this position. return new HaxeImplementsDeclarationImpl(new HaxeDummyASTNode("Empty Implements List")); } @Override @NotNull public PsiClassType[] getExtendsListTypes() { return PsiClassType.EMPTY_ARRAY; } @Override @NotNull public PsiClassType[] getImplementsListTypes() { return PsiClassType.EMPTY_ARRAY; } @Override public PsiClass getSuperClass() { HaxeClass resolved = getResolvedDelegate(); return null == resolved ? null : resolved.getSuperClass(); } @Override public PsiClass[] getInterfaces() { HaxeClass resolved = getResolvedDelegate(); return null == resolved ? PsiClass.EMPTY_ARRAY : resolved.getInterfaces(); } @Override @NotNull public PsiClass[] getSupers() { HaxeClass resolved = getResolvedDelegate(); return null == resolved ? PsiClass.EMPTY_ARRAY : resolved.getSupers(); } @Override @NotNull public PsiClassType[] getSuperTypes() { HaxeClass resolved = getResolvedDelegate(); return null == resolved ? PsiClassType.EMPTY_ARRAY : resolved.getSuperTypes(); } @Override @NotNull public Collection<HierarchicalMethodSignature> getVisibleSignatures() { HaxeClass resolved = getResolvedDelegate(); return PsiSuperMethodImplUtil.getVisibleSignatures(resolved); } } }