/* * Copyright 2013 Guidewire Software, Inc. */ package gw.plugin.ij.lang.psi.impl.statements.typedef; import com.intellij.lang.ASTNode; import com.intellij.navigation.ColoredItemPresentation; import com.intellij.openapi.editor.colors.CodeInsightColors; import com.intellij.openapi.editor.colors.TextAttributesKey; import com.intellij.openapi.util.Pair; import com.intellij.psi.HierarchicalMethodSignature; import com.intellij.psi.PsiAnonymousClass; import com.intellij.psi.PsiClass; import com.intellij.psi.PsiClassInitializer; import com.intellij.psi.PsiClassType; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiElementVisitor; import com.intellij.psi.PsiField; import com.intellij.psi.PsiFile; import com.intellij.psi.PsiIdentifier; import com.intellij.psi.PsiJavaToken; import com.intellij.psi.PsiMember; import com.intellij.psi.PsiMethod; import com.intellij.psi.PsiModifier; import com.intellij.psi.PsiModifierList; import com.intellij.psi.PsiNamedElement; import com.intellij.psi.PsiReferenceList; import com.intellij.psi.PsiSubstitutor; import com.intellij.psi.ResolveState; import com.intellij.psi.codeStyle.CodeStyleSettings; import com.intellij.psi.codeStyle.CodeStyleSettingsManager; import com.intellij.psi.impl.ElementBase; import com.intellij.psi.impl.InheritanceImplUtil; import com.intellij.psi.impl.JavaPsiImplementationHelperImpl; import com.intellij.psi.impl.PsiClassImplUtil; import com.intellij.psi.impl.PsiSuperMethodImplUtil; import com.intellij.psi.impl.source.tree.LeafPsiElement; import com.intellij.psi.javadoc.PsiDocComment; import com.intellij.psi.scope.PsiScopeProcessor; import com.intellij.psi.stubs.IStubElementType; import com.intellij.ui.RowIcon; import com.intellij.util.ArrayUtil; import com.intellij.util.IncorrectOperationException; import gw.lang.parser.statements.IClassStatement; import gw.lang.reflect.IType; import gw.lang.reflect.Modifier; import gw.lang.reflect.TypeSystem; import gw.lang.reflect.gs.IGosuClass; import gw.lang.reflect.module.IModule; import gw.plugin.ij.icons.GosuIcons; import gw.plugin.ij.lang.GosuTokenTypes; import gw.plugin.ij.lang.parser.GosuCompositeElement; import gw.plugin.ij.lang.parser.GosuElementTypes; import gw.plugin.ij.lang.parser.GosuSyntheticCompositeElement; import gw.plugin.ij.lang.psi.IGosuFile; import gw.plugin.ij.lang.psi.api.auxilary.IGosuModifier; import gw.plugin.ij.lang.psi.api.auxilary.IGosuModifierList; import gw.plugin.ij.lang.psi.api.statements.IGosuField; import gw.plugin.ij.lang.psi.api.statements.IGosuStatement; import gw.plugin.ij.lang.psi.api.statements.IGosuVariable; import gw.plugin.ij.lang.psi.api.statements.typedef.IGosuExtendsClause; import gw.plugin.ij.lang.psi.api.statements.typedef.IGosuImplementsClause; import gw.plugin.ij.lang.psi.api.statements.typedef.IGosuMembersDeclaration; import gw.plugin.ij.lang.psi.api.statements.typedef.IGosuMethod; import gw.plugin.ij.lang.psi.api.statements.typedef.IGosuTypeDefinition; import gw.plugin.ij.lang.psi.api.types.IGosuCodeReferenceElement; import gw.plugin.ij.lang.psi.api.types.IGosuTypeVariable; import gw.plugin.ij.lang.psi.api.types.IGosuTypeVariableList; import gw.plugin.ij.lang.psi.custom.CustomGosuClass; import gw.plugin.ij.lang.psi.impl.AbstractGosuClassFileImpl; import gw.plugin.ij.lang.psi.impl.GosuDeclaredElementImpl; import gw.plugin.ij.lang.psi.impl.GosuElementVisitor; import gw.plugin.ij.lang.psi.impl.GosuPsiImplUtil; import gw.plugin.ij.lang.psi.impl.expressions.GosuIdentifierImpl; import gw.plugin.ij.lang.psi.impl.resolvers.PsiTypeResolver; import gw.plugin.ij.lang.psi.impl.statements.GosuSyntheticModifierListImpl; import gw.plugin.ij.lang.psi.stubs.GosuTypeDefinitionStub; import gw.plugin.ij.lang.psi.util.GosuClassImplUtil; import gw.plugin.ij.lang.psi.util.GosuDocUtil; import gw.plugin.ij.lang.psi.util.PsiUtil; import gw.plugin.ij.util.GosuIconsUtil; import gw.plugin.ij.util.GosuModuleUtil; import gw.plugin.ij.util.TypeUtil; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.swing.*; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.List; public abstract class GosuTypeDefinitionImpl extends GosuDeclaredElementImpl<IClassStatement, GosuTypeDefinitionStub> implements IGosuTypeDefinition { @Nullable private volatile PsiClass[] myInnerClasses; @Nullable private volatile List<PsiMethod> myMethods; @Nullable private IGosuField[] myFields; @Nullable private volatile IGosuMethod[] myGosuMethods; @Nullable private volatile IGosuMethod[] myConstructors; public GosuTypeDefinitionImpl(@NotNull GosuCompositeElement node) { super(node); } protected GosuTypeDefinitionImpl(@NotNull GosuTypeDefinitionStub stub, @NotNull IStubElementType nodeType) { super(stub, nodeType); } public void accept(@NotNull GosuElementVisitor visitor) { visitor.visitTypeDefinition( this ); } @Override public void accept(@NotNull PsiElementVisitor visitor) { if( visitor instanceof GosuElementVisitor) { ((GosuElementVisitor)visitor).visitTypeDefinition(this); } else { visitor.visitElement( this ); } } public int getTextOffset() { final PsiIdentifier identifier = getNameIdentifier(); return identifier != null ? identifier.getTextRange().getStartOffset() : -1; } @Nullable public String getQualifiedName() { final GosuTypeDefinitionStub stub = getStub(); if (stub != null) { return stub.getQualifiedName(); } final PsiElement parent = getParent(); if (parent instanceof IGosuFile) { final String packageName = ((IGosuFile) parent).getPackageName(); return !packageName.isEmpty() ? packageName + "." + getName() : getName(); } final PsiClass containingClass = getContainingClass(); if (containingClass != null) { return containingClass.getQualifiedName() + "." + getName(); } return null; } @NotNull public IGosuMembersDeclaration[] getMemberDeclarations() { return findChildrenByClass(IGosuMembersDeclaration.class); } public ColoredItemPresentation getPresentation() { return new ColoredItemPresentation() { @Nullable public String getPresentableText() { return getName(); } @Nullable public String getLocationString() { final PsiFile file = getContainingFile(); if (file instanceof IGosuFile) { final String packageName = ((IGosuFile) file).getPackageName(); return packageName.isEmpty() ? "" : String.format("(%s)", packageName); } else { return ""; } } @Nullable public Icon getIcon(boolean open) { return GosuTypeDefinitionImpl.this.getIcon(ICON_FLAG_VISIBILITY | ICON_FLAG_READ_STATUS); } @Nullable public TextAttributesKey getTextAttributesKey() { if (isDeprecated()) { return CodeInsightColors.DEPRECATED_ATTRIBUTES; } return null; } }; } @Nullable public IGosuExtendsClause getExtendsClause() { return getStubOrPsiChild(GosuElementTypes.EXTENDS_CLAUSE); } @Nullable public IGosuImplementsClause getImplementsClause() { return getStubOrPsiChild(GosuElementTypes.IMPLEMENTS_CLAUSE); } @NotNull public String[] getSuperClassNames() { // final GosuTypeDefinitionStub stub = getStub(); // if( stub != null ) // { // return stub.getSuperClassNames(); // } return ArrayUtil.mergeArrays( getExtendsNames(), getImplementsNames(), String.class ); } @NotNull protected String[] getImplementsNames() { IGosuImplementsClause implementsClause = getImplementsClause(); IGosuCodeReferenceElement[] implementsRefs = implementsClause != null ? implementsClause.getReferenceElements() : IGosuCodeReferenceElement.EMPTY_ARRAY; ArrayList<String> implementsNames = new ArrayList<>(implementsRefs.length); for (IGosuCodeReferenceElement ref : implementsRefs) { String name = ref.getReferenceName(); if (name != null) { implementsNames.add(name); } } return ArrayUtil.toStringArray( implementsNames ); } @NotNull protected String[] getExtendsNames() { IGosuExtendsClause extendsClause = getExtendsClause(); IGosuCodeReferenceElement[] extendsRefs = extendsClause != null ? extendsClause.getReferenceElements() : IGosuCodeReferenceElement.EMPTY_ARRAY; ArrayList<String> extendsNames = new ArrayList<>(extendsRefs.length); for (IGosuCodeReferenceElement ref : extendsRefs) { String name = ref.getReferenceName(); if (name != null) { extendsNames.add(name); } } return ArrayUtil.toStringArray(extendsNames); } public void checkDelete() throws IncorrectOperationException { } public void delete() throws IncorrectOperationException { PsiElement parent = getParent(); if (parent instanceof AbstractGosuClassFileImpl) { AbstractGosuClassFileImpl file = (AbstractGosuClassFileImpl) parent; file.delete(); return; } ASTNode astNode = parent.getNode(); if (astNode != null) { astNode.removeChild(getNode()); } } public boolean processDeclarations(@NotNull PsiScopeProcessor processor, @NotNull ResolveState state, @Nullable PsiElement lastParent, @NotNull PsiElement place) { //## todo: return PsiClassImplUtil.processDeclarationsInClass(this, processor, state, null, lastParent, place, false); } public String getName() { final GosuTypeDefinitionStub stub = getStub(); if (stub != null) { return stub.getName(); } return GosuPsiImplUtil.getName( this ); } @Override public boolean isEquivalentTo(PsiElement another) { return GosuClassImplUtil.isClassEquivalentTo( this, another ); } public boolean isInterface() { return false; } public boolean isEnhancement() { return false; } public boolean isAnnotationType() { return false; } public boolean isEnum() { return false; } @Nullable public PsiReferenceList getExtendsList() { return null; } @Nullable public PsiReferenceList getImplementsList() { return null; } @NotNull public PsiClassType[] getExtendsListTypes() { return GosuClassImplUtil.getExtendsListTypes(this); } @NotNull public PsiClassType[] getImplementsListTypes() { return GosuClassImplUtil.getImplementsListTypes( this ); } @Nullable public PsiClass getSuperClass() { return GosuClassImplUtil.getSuperClass( this ); } public PsiClass[] getInterfaces() { return GosuClassImplUtil.getInterfaces( this ); } @NotNull public final PsiClass[] getSupers() { return GosuClassImplUtil.getSupers( this ); } @NotNull public PsiClassType[] getSuperTypes() { return GosuClassImplUtil.getSuperTypes( this ); } @NotNull public PsiMethod[] getMethods() { List<PsiMethod> cached = myMethods; if (cached == null) { cached = new ArrayList<>(); cached.addAll(Arrays.asList(findChildrenByClass(IGosuMethod.class))); myMethods = cached; } // yay List<PsiMethod> result = new ArrayList<>(cached); return result.toArray( new PsiMethod[result.size()] ); } public void subtreeChanged() { myMethods = null; myInnerClasses = null; myConstructors = null; myGosuMethods = null; myFields = null; super.subtreeChanged(); } @NotNull public PsiMethod[] getConstructors() { IGosuMethod[] cached = myConstructors; if (cached == null) { List<IGosuMethod> result = new ArrayList<>(); for (final PsiMethod method : getMethods()) { if (method.isConstructor()) { result.add((IGosuMethod) method); } } myConstructors = cached = result.toArray(new IGosuMethod[result.size()]); } return cached; } @NotNull public PsiClass[] getInnerClasses() { PsiClass[] inners = myInnerClasses; if (inners == null) { myInnerClasses = inners = findChildrenByClass( PsiClass.class ); } return inners; } @NotNull public PsiClassInitializer[] getInitializers() { return PsiClassInitializer.EMPTY_ARRAY; } @NotNull public PsiField[] getAllFields() { return GosuClassImplUtil.getAllFields(this); } @NotNull public PsiMethod[] getAllMethods() { return GosuClassImplUtil.getAllMethods( this ); } @NotNull public PsiClass[] getAllInnerClasses() { return PsiClassImplUtil.getAllInnerClasses( this ); } @Nullable public PsiField findFieldByName(String name, boolean checkBases) { return GosuClassImplUtil.findFieldByName( this, name, checkBases ); } @Nullable public PsiMethod findMethodBySignature(PsiMethod patternMethod, boolean checkBases) { return GosuClassImplUtil.findMethodBySignature( this, patternMethod, checkBases ); } @NotNull public PsiMethod[] findMethodsBySignature(PsiMethod patternMethod, boolean checkBases) { return GosuClassImplUtil.findMethodsBySignature( this, patternMethod, checkBases ); } @NotNull public PsiMethod[] findCodeMethodsBySignature(PsiMethod patternMethod, boolean checkBases) { return GosuClassImplUtil.findCodeMethodsBySignature( this, patternMethod, checkBases ); } @NotNull public PsiMethod[] findMethodsByName(@NonNls String name, boolean checkBases) { return GosuClassImplUtil.findMethodsByName( this, name, checkBases ); } @NotNull public PsiMethod[] findCodeMethodsByName(@NonNls String name, boolean checkBases) { return GosuClassImplUtil.findCodeMethodsByName( this, name, checkBases ); } @NotNull public List<Pair<PsiMethod, PsiSubstitutor>> findMethodsAndTheirSubstitutorsByName(String name, boolean checkBases) { return GosuClassImplUtil.findMethodsAndTheirSubstitutorsByName( this, name, checkBases ); } @NotNull public List<Pair<PsiMethod, PsiSubstitutor>> getAllMethodsAndTheirSubstitutors() { return GosuClassImplUtil.getAllMethodsAndTheirSubstitutors( this ); } @Nullable public PsiClass findInnerClassByName(String name, boolean checkBases) { return null; } public boolean isAnonymous() { return false; } @Nullable public PsiIdentifier getNameIdentifier() { PsiIdentifier id = getNameIdentifierImpl(); if (id == null) { return null; } if (id.getFirstChild() != null && id.getFirstChild() instanceof PsiIdentifier) { // Always return the leaf token node; we always want to patch in just the name and not mess with upper-level tree nodes id = (PsiIdentifier) id.getFirstChild(); } return id; } @Nullable public PsiElement getScope() { return null; } public boolean isInheritor(@NotNull PsiClass baseClass, boolean checkDeep) { // implemented in terns of TS for perf reasons // return InheritanceImplUtil.isInheritor(this, baseClass, checkDeep); return isInheritorFast(baseClass, checkDeep); } private boolean isInheritorFast(PsiClass baseClass, boolean checkDeep) { if (baseClass instanceof PsiAnonymousClass) { return false; } if (baseClass instanceof CustomGosuClass) { return false; } IType baseType = findBaseType( baseClass ); IType thisType = TypeUtil.getType(this); if (baseType == null || thisType == null) { return InheritanceImplUtil.isInheritor(this, baseClass, checkDeep); } if (checkDeep) { IModule module = baseType.getTypeLoader().getModule(); TypeSystem.pushModule(module); try { return baseType != thisType && baseType.isAssignableFrom(thisType); } finally { TypeSystem.popModule(module); } } else { return isDirectInheritor(thisType, baseType); } } // // Resolve IType using same module as this type's module, then resolve the PsiClass from the IType. // If the PsiClass is the same as the original baseClass, use this resolved IType. // Otherwise, resolve the IType using the module of the baseClass (as we normally would). // // Note we do all of this so that our IJ-Editor tests are recognized as TestCase classes. // The problem is that when IJ tests if this class inherits from TestCase, the junit.framework.TestCase // class passed in is from the JRE module, but the editor tests get their junit.framework.TestCase // from the ij-editor module, since the classes are not the same, IJ thinks our editor tests aren't // test classes. // // The trick here, and I'm not sure why it works, but is why InheritanceImplUtil.isInheritor() works, // is to resolve to an IType, then resolve that back to a PsiClass and see if we have the same class. // For some reason our junit.framework.TestCase from the ij-editor module resolves as a reference to // the junit.framework.TestCase in the *JRE* module. Each module does indeed have it's own copy of that // class, but somehow IJ consistently resolves PsiClass refs to the JRE one. // private IType findBaseType( PsiClass baseClass ) { IType baseType = null; IModule module = GosuModuleUtil.findModuleForPsiElement(this); if( !(baseClass instanceof IGosuTypeDefinition) ) { baseType = TypeUtil.getType(baseClass, module); if( baseType != null ) { PsiElement resolvedBase = PsiTypeResolver.resolveType( baseType, this ); if( resolvedBase != baseClass ) { baseType = TypeUtil.getType(baseClass, module); } } } if( baseType == null ) { baseType = TypeUtil.getType(baseClass, module); } return baseType; } private boolean isDirectInheritor(IType thisType, IType baseType) { IType supertype = thisType.getSupertype(); if (supertype != null && baseType.equals(TypeUtil.getConcreteType(supertype))) { return true; } for (IType iface : thisType.getInterfaces()) { if (!TypeSystem.isDeleted(iface) && baseType.equals(TypeUtil.getConcreteType(iface))) { return true; } } return false; } public boolean isInheritorDeep(@NotNull PsiClass baseClass, @Nullable PsiClass classToByPass) { // not called often, safe to leave as-is, non-performant return InheritanceImplUtil.isInheritorDeep(this, baseClass, classToByPass); } @Nullable public PsiClass getContainingClass() { PsiElement element = this.getParent(); while (element != null && !(element instanceof PsiClass)) { element = element.getParent(); } return element instanceof PsiClass ? (PsiClass) element : null; } @NotNull public Collection<HierarchicalMethodSignature> getVisibleSignatures() { return PsiSuperMethodImplUtil.getVisibleSignatures(this); } @NotNull public PsiElement setName(@NonNls @NotNull String newName) throws IncorrectOperationException { final PsiIdentifier identifier = getNameIdentifier(); if (identifier instanceof GosuIdentifierImpl) { ((GosuIdentifierImpl) identifier).setName(newName); } else { ((PsiNamedElement) identifier).setName(newName); } return this; } @Nullable public IGosuModifierList getModifierList() { return (IGosuModifierList)findChildByClass( PsiModifierList.class ); } @Override public boolean hasModifierProperty( @PsiModifier.ModifierConstant @NonNls @NotNull String name ) { IGosuModifierList modifierList = getModifierList(); modifierList = modifierList == null ? new GosuSyntheticModifierListImpl( new GosuSyntheticCompositeElement( GosuElementTypes.ELEM_TYPE_ModifierListClause ) ) : modifierList; return modifierList.hasModifierProperty( name ); } @Nullable public PsiDocComment getDocComment() { return GosuDocUtil.findDocCommnentNode(getNode()); } public boolean isDeprecated() { final String qualifiedName = getQualifiedName(); final IModule module = GosuModuleUtil.findModuleForPsiElement(this); TypeSystem.pushModule(module); try { IType type = TypeSystem.getByFullNameIfValid(qualifiedName, module); return type != null && type.getTypeInfo().isDeprecated(); } finally { TypeSystem.popModule(module); } } public boolean hasTypeParameters() { return getTypeParameters().length > 0; } @Nullable public IGosuTypeVariableList getTypeParameterList() { return (IGosuTypeVariableList) findChildByType(GosuElementTypes.ELEM_TYPE_TypeVariableListClause); } @NotNull public IGosuTypeVariable[] getTypeParameters() { final IGosuTypeVariableList list = getTypeParameterList(); if (list != null) { return (IGosuTypeVariable[]) list.getTypeParameters(); } return IGosuTypeVariable.EMPTY_ARRAY; } @Nullable public PsiElement getOriginalElement() { return GosuPsiImplUtil.getOriginalElement(this, getContainingFile()); } public PsiElement addAfter(@NotNull PsiElement element, @Nullable PsiElement anchor) throws IncorrectOperationException { if (anchor == null) { return add(element); } if (anchor.getParent() == this) { final PsiElement nextChild = anchor.getNextSibling(); if (nextChild == null) { add(element); return element; } ASTNode node = element.getNode(); assert node != null; //body.getNode().addLeaf(GosuElementTypes.mNLS, "\n", nextChild.getNode()); return addBefore(element, nextChild); } else { return super.addAfter(element, anchor); } } @NotNull public PsiElement addBefore(@NotNull PsiElement element, @Nullable PsiElement anchor) throws IncorrectOperationException { if (anchor == null) { add(element); return element; } ASTNode node = element.getNode(); assert node != null; final ASTNode bodyNode = getNode(); final ASTNode anchorNode = anchor.getNode(); bodyNode.addLeaf(GosuTokenTypes.TT_WHITESPACE, "\n", anchorNode); if (element instanceof PsiMethod) { bodyNode.addLeaf(GosuTokenTypes.TT_WHITESPACE, "\n", anchorNode); } bodyNode.addChild(node, anchorNode); bodyNode.addLeaf(GosuTokenTypes.TT_WHITESPACE, "\n", anchorNode); return element; } @NotNull public PsiElement add(@NotNull PsiElement psiElement) throws IncorrectOperationException { final PsiElement lBrace = getGosuLBrace(); if (lBrace == null) { throw new IncorrectOperationException("No left brace"); } PsiMember member = getAnyMember(psiElement); PsiElement anchor = member != null ? getDefaultAnchor(member) : null; if (anchor == null) { anchor = lBrace.getNextSibling(); } if (anchor != null) { ASTNode node = anchor.getNode(); assert node != null; if (GosuTokenTypes.TT_OP_semicolon.equals(node.getElementType())) { anchor = anchor.getNextSibling(); } psiElement = addBefore(psiElement, anchor); } else { add(psiElement); } return psiElement; } @Nullable private PsiElement getDefaultAnchor(PsiMember member) { CodeStyleSettings settings = CodeStyleSettingsManager.getSettings(getProject()); int order = JavaPsiImplementationHelperImpl.getMemberOrderWeight(member, settings); if (order < 0) { return null; } PsiElement lastMember = null; for (PsiElement child = getFirstChild(); child != null; child = child.getNextSibling()) { int order1 = JavaPsiImplementationHelperImpl.getMemberOrderWeight(getAnyMember(child), settings); if (order1 < 0) { continue; } if (order1 > order) { final PsiElement lBrace = getGosuLBrace(); if (lastMember != null) { PsiElement nextSibling = lastMember.getNextSibling(); while (nextSibling instanceof LeafPsiElement && (nextSibling.getText().equals(",") || nextSibling.getText().equals(";"))) { nextSibling = nextSibling.getNextSibling(); } return nextSibling == null && lBrace != null ? PsiUtil.skipWhitespaces(lBrace.getNextSibling(), true) : nextSibling; } else if (lBrace != null) { return PsiUtil.skipWhitespaces(lBrace.getNextSibling(), true); } } lastMember = child; } return getGosuRBrace(); } @Nullable private static PsiMember getAnyMember(@Nullable PsiElement psiElement) { if (psiElement instanceof PsiMember) { return (PsiMember) psiElement; } if (psiElement instanceof IGosuVariable) { return (PsiMember) psiElement; } return null; } @NotNull public <T extends IGosuMembersDeclaration> T addMemberDeclaration(@NotNull T decl, @Nullable PsiElement anchorBefore) throws IncorrectOperationException { if (anchorBefore == null) { return (T) add(decl); } decl = (T) addBefore(decl, anchorBefore); // node.addLeaf(GosuTokenTypes.mWS, " ", decl.getNode()); //add whitespaces before and after to hack over incorrect auto reformat // node.addLeaf(GosuTokenTypes.mWS, " ", anchorNode); return decl; } @NotNull public IGosuField[] getFields() { if (myFields == null) { IGosuVariable[] declarations = findChildrenByClass(IGosuVariable.class); if (declarations.length == 0) { return IGosuField.EMPTY_ARRAY; } List<IGosuField> result = new ArrayList<>(); for (IGosuVariable variable : declarations) { if (variable instanceof IGosuField) { result.add((IGosuField) variable); } } myFields = result.toArray(new IGosuField[result.size()]); } return myFields; } @Nullable public PsiJavaToken getLBrace() { return null; //(PsiJavaToken)findChildByType( GosuTokenTypes.TT_OP_brace_left ); } @Nullable public PsiJavaToken getRBrace() { return null; //(PsiJavaToken)findChildByType( GosuTokenTypes.TT_OP_brace_right ); } @Nullable public LeafPsiElement getGosuLBrace() { return (LeafPsiElement) findChildByType(GosuTokenTypes.TT_OP_brace_left); } @Nullable public LeafPsiElement getGosuRBrace() { return (LeafPsiElement) findChildByType(GosuTokenTypes.TT_OP_brace_right); } public void removeVariable(IGosuVariable variable) { throw new UnsupportedOperationException("Men at work"); //## todo: // GosuPsiImplUtil.removeVariable( variable ); } @NotNull public IGosuVariable addVariableBefore(@NotNull IGosuVariable variable, @Nullable IGosuStatement anchor) throws IncorrectOperationException { PsiElement rBrace = getGosuRBrace(); if (anchor == null && rBrace == null) { throw new IncorrectOperationException(); } if (anchor != null && !this.equals(anchor.getParent())) { throw new IncorrectOperationException(); } ASTNode elemNode = variable.getNode(); final ASTNode anchorNode = anchor != null ? anchor.getNode() : rBrace.getNode(); getNode().addChild(elemNode, anchorNode); return (IGosuVariable) elemNode.getPsi(); } @Nullable public String toString() { return "PsiClass:" + getName(); } @Override public Icon getElementIcon(@IconFlags int flags) { final String qualifiedName = getQualifiedName(); final Icon icon = getSpecificIcon(this); if (icon == null) { return null; } IType type = TypeSystem.getByFullNameIfValid(qualifiedName, GosuModuleUtil.findModuleForPsiElement(this)); if (!(type instanceof IGosuClass)) { return icon; } final boolean isLocked = (flags & ICON_FLAG_READ_STATUS) != 0 && !isWritable(); flags = GosuIconsUtil.getFlags(this, (IGosuClass) type, isLocked); final RowIcon rowIcon = ElementBase.createLayeredIcon(this, icon, flags); if ((flags & ICON_FLAG_VISIBILITY) != 0) { GosuIconsUtil.setVisibilityIcon((IGosuClass) type, rowIcon); } return rowIcon; } // TODO: move to subclasses private Icon getSpecificIcon(PsiElement element) { if (element instanceof GosuSyntheticClassDefinitionImpl) { return GosuIcons.FILE_PROGRAM; } else if (element instanceof GosuAnonymousClassDefinitionImpl) { return GosuIcons.ANONYMOUS_CLASS; } else if (element instanceof GosuClassDefinitionImpl) { return GosuIcons.CLASS; } else if (element instanceof GosuEnhancementDefinitionImpl) { return GosuIcons.ENHANCEMENT; } else if (element instanceof GosuEnumDefinitionImpl) { return GosuIcons.ENUM; } else if (element instanceof GosuInterfaceDefinitionImpl) { return GosuIcons.INTERFACE; } else if (element instanceof GosuAnnotationDefinitionImpl) { return GosuIcons.ANNOTATION; } else return null; } public boolean hasExplicitModifier(@NotNull @NonNls String name) { IType type = TypeUtil.getType(this); int modifiers = type != null ? type.getModifiers() : 0; if (name.equals(IGosuModifier.PUBLIC)) { return Modifier.isPublic(modifiers); } if (name.equals(IGosuModifier.ABSTRACT)) { return Modifier.isAbstract(modifiers); } if (name.equals(IGosuModifier.PRIVATE)) { return Modifier.isPrivate(modifiers); } if (name.equals(IGosuModifier.PROTECTED)) { return Modifier.isProtected(modifiers); } if (name.equals(IGosuModifier.PACKAGE_LOCAL) || name.equals(IGosuModifier.INTERNAL)) { return Modifier.isInternal(modifiers); } if (name.equals(IGosuModifier.STATIC)) { return Modifier.isStatic(modifiers); } if (name.equals(IGosuModifier.FINAL)) { return Modifier.isFinal(modifiers); } if (name.equals(IGosuModifier.TRANSIENT)) { return Modifier.isTransient(modifiers); } return false; } }