/* * Copyright 2000-2013 JetBrains s.r.o. * * 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.psi.impl.compiled; import com.intellij.navigation.ItemPresentation; import com.intellij.navigation.ItemPresentationProviders; import com.intellij.openapi.extensions.Extensions; import com.intellij.openapi.ui.Queryable; import com.intellij.openapi.util.Key; import com.intellij.openapi.util.Pair; import com.intellij.pom.java.LanguageLevel; import com.intellij.psi.*; import com.intellij.psi.impl.InheritanceImplUtil; import com.intellij.psi.impl.PsiClassImplUtil; import com.intellij.psi.impl.PsiImplUtil; import com.intellij.psi.impl.PsiSuperMethodImplUtil; import com.intellij.psi.impl.java.stubs.JavaStubElementTypes; import com.intellij.psi.impl.java.stubs.PsiClassStub; import com.intellij.psi.impl.source.*; import com.intellij.psi.impl.source.tree.TreeElement; import com.intellij.psi.scope.PsiScopeProcessor; import com.intellij.psi.scope.processor.MethodsProcessor; import com.intellij.psi.search.SearchScope; import com.intellij.psi.util.PsiUtil; import com.intellij.psi.util.PsiUtilCore; import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.swing.*; import java.util.Arrays; import java.util.Collection; import java.util.List; import java.util.Map; public class ClsClassImpl extends ClsMemberImpl<PsiClassStub<?>> implements PsiExtensibleClass, PsiQualifiedNamedElement, Queryable { public static final Key<PsiClass> DELEGATE_KEY = Key.create("DELEGATE"); private final ClassInnerStuffCache myInnersCache = new ClassInnerStuffCache(this); public ClsClassImpl(final PsiClassStub stub) { super(stub); } @Override @NotNull public PsiElement[] getChildren() { List<PsiElement> children = ContainerUtil.newArrayList(); ContainerUtil.addAll(children, getChildren(getDocComment(), getModifierList(), getNameIdentifier(), getExtendsList(), getImplementsList())); ContainerUtil.addAll(children, getOwnFields()); ContainerUtil.addAll(children, getOwnMethods()); ContainerUtil.addAll(children, getOwnInnerClasses()); return PsiUtilCore.toPsiElementArray(children); } @Override @NotNull public PsiTypeParameterList getTypeParameterList() { return getStub().findChildStubByType(JavaStubElementTypes.TYPE_PARAMETER_LIST).getPsi(); } @Override public boolean hasTypeParameters() { return PsiImplUtil.hasTypeParameters(this); } @Override @NotNull public String getQualifiedName() { return getStub().getQualifiedName(); } @Override @NotNull public PsiModifierList getModifierList() { return getStub().findChildStubByType(JavaStubElementTypes.MODIFIER_LIST).getPsi(); } @Override public boolean hasModifierProperty(@NotNull String name) { return getModifierList().hasModifierProperty(name); } @Override @NotNull public PsiReferenceList getExtendsList() { return getStub().findChildStubByType(JavaStubElementTypes.EXTENDS_LIST).getPsi(); } @Override @NotNull public PsiReferenceList getImplementsList() { return getStub().findChildStubByType(JavaStubElementTypes.IMPLEMENTS_LIST).getPsi(); } @Override @NotNull public PsiClassType[] getExtendsListTypes() { return PsiClassImplUtil.getExtendsListTypes(this); } @Override @NotNull public PsiClassType[] getImplementsListTypes() { return PsiClassImplUtil.getImplementsListTypes(this); } @Override public PsiClass getSuperClass() { return PsiClassImplUtil.getSuperClass(this); } @Override public PsiClass[] getInterfaces() { return PsiClassImplUtil.getInterfaces(this); } @Override @NotNull public PsiClass[] getSupers() { if (CommonClassNames.JAVA_LANG_OBJECT.equals(getQualifiedName())) { return PsiClass.EMPTY_ARRAY; } return PsiClassImplUtil.getSupers(this); } @Override @NotNull public PsiClassType[] getSuperTypes() { if (CommonClassNames.JAVA_LANG_OBJECT.equals(getQualifiedName())) { return PsiClassType.EMPTY_ARRAY; } return PsiClassImplUtil.getSuperTypes(this); } @Override public PsiClass getContainingClass() { PsiElement parent = getParent(); return parent instanceof PsiClass ? (PsiClass)parent : null; } @Override @NotNull public Collection<HierarchicalMethodSignature> getVisibleSignatures() { return PsiSuperMethodImplUtil.getVisibleSignatures(this); } @Override @NotNull public PsiField[] getFields() { return myInnersCache.getFields(); } @Override @NotNull public PsiMethod[] getMethods() { return myInnersCache.getMethods(); } @Override @NotNull public PsiMethod[] getConstructors() { return myInnersCache.getConstructors(); } @Override @NotNull public PsiClass[] getInnerClasses() { return myInnersCache.getInnerClasses(); } @NotNull @Override public List<PsiField> getOwnFields() { return Arrays.asList(getStub().getChildrenByType(Constants.FIELD_BIT_SET, PsiField.ARRAY_FACTORY)); } @NotNull @Override public List<PsiMethod> getOwnMethods() { return Arrays.asList(getStub().getChildrenByType(Constants.METHOD_BIT_SET, PsiMethod.ARRAY_FACTORY)); } @NotNull @Override public List<PsiClass> getOwnInnerClasses() { return Arrays.asList(getStub().getChildrenByType(JavaStubElementTypes.CLASS, PsiClass.ARRAY_FACTORY)); } @Override @NotNull public PsiClassInitializer[] getInitializers() { return PsiClassInitializer.EMPTY_ARRAY; } @Override @NotNull public PsiTypeParameter[] getTypeParameters() { return PsiImplUtil.getTypeParameters(this); } @Override @NotNull public PsiField[] getAllFields() { return PsiClassImplUtil.getAllFields(this); } @Override @NotNull public PsiMethod[] getAllMethods() { return PsiClassImplUtil.getAllMethods(this); } @Override @NotNull public PsiClass[] getAllInnerClasses() { return PsiClassImplUtil.getAllInnerClasses(this); } @Override public PsiField findFieldByName(String name, boolean checkBases) { return myInnersCache.findFieldByName(name, checkBases); } @Override public PsiMethod findMethodBySignature(PsiMethod patternMethod, boolean checkBases) { return PsiClassImplUtil.findMethodBySignature(this, patternMethod, checkBases); } @Override @NotNull public PsiMethod[] findMethodsBySignature(PsiMethod patternMethod, boolean checkBases) { return PsiClassImplUtil.findMethodsBySignature(this, patternMethod, checkBases); } @Override @NotNull public PsiMethod[] findMethodsByName(String name, boolean checkBases) { return myInnersCache.findMethodsByName(name, checkBases); } @Override @NotNull public List<Pair<PsiMethod, PsiSubstitutor>> findMethodsAndTheirSubstitutorsByName(String name, boolean checkBases) { return PsiClassImplUtil.findMethodsAndTheirSubstitutorsByName(this, name, checkBases); } @Override @NotNull public List<Pair<PsiMethod, PsiSubstitutor>> getAllMethodsAndTheirSubstitutors() { return PsiClassImplUtil.getAllWithSubstitutorsByMap(this, PsiClassImplUtil.MemberType.METHOD); } @Override public PsiClass findInnerClassByName(String name, boolean checkBases) { return myInnersCache.findInnerClassByName(name, checkBases); } @Override public boolean isDeprecated() { return getStub().isDeprecated(); } public String getSourceFileName() { final String sfn = getStub().getSourceFileName(); return sfn != null ? sfn : obtainSourceFileNameFromClassFileName(); } @NonNls private String obtainSourceFileNameFromClassFileName() { final String name = getContainingFile().getName(); int i = name.indexOf('$'); if (i < 0) { i = name.indexOf('.'); if (i < 0) { i = name.length(); } } return name.substring(0, i) + ".java"; } @Override public PsiJavaToken getLBrace() { return null; } @Override public PsiJavaToken getRBrace() { return null; } @Override public boolean isInterface() { return getStub().isInterface(); } @Override public boolean isAnnotationType() { return getStub().isAnnotationType(); } @Override public boolean isEnum() { return getStub().isEnum(); } @Override public void appendMirrorText(final int indentLevel, @NotNull @NonNls final StringBuilder buffer) { appendText(getDocComment(), indentLevel, buffer, NEXT_LINE); appendText(getModifierList(), indentLevel, buffer); buffer.append(isEnum() ? "enum " : isAnnotationType() ? "@interface " : isInterface() ? "interface " : "class "); appendText(getNameIdentifier(), indentLevel, buffer, " "); appendText(getTypeParameterList(), indentLevel, buffer, " "); appendText(getExtendsList(), indentLevel, buffer, " "); appendText(getImplementsList(), indentLevel, buffer, " "); buffer.append('{'); int newIndentLevel = indentLevel + getIndentSize(); List<PsiField> fields = getOwnFields(); List<PsiMethod> methods = getOwnMethods(); List<PsiClass> classes = getOwnInnerClasses(); if (fields.size() > 0) { goNextLine(newIndentLevel, buffer); for (int i = 0; i < fields.size(); i++) { PsiField field = fields.get(i); appendText(field, newIndentLevel, buffer); if (field instanceof ClsEnumConstantImpl) { if (i < fields.size() - 1 && fields.get(i + 1) instanceof ClsEnumConstantImpl) { buffer.append(", "); } else { buffer.append(';'); if (i < fields.size() - 1) { buffer.append('\n'); goNextLine(newIndentLevel, buffer); } } } else if (i < fields.size() - 1) { goNextLine(newIndentLevel, buffer); } } } else if (isEnum() && methods.size() + classes.size() > 0) { goNextLine(newIndentLevel, buffer); buffer.append(";"); } if (methods.size() > 0) { if (isEnum() || fields.size() > 0) { buffer.append('\n'); } goNextLine(newIndentLevel, buffer); for (int i = 0; i < methods.size(); i++) { appendText(methods.get(i), newIndentLevel, buffer); if (i < methods.size() - 1) { buffer.append('\n'); goNextLine(newIndentLevel, buffer); } } } if (classes.size() > 0) { if (fields.size() + methods.size() > 0) { buffer.append('\n'); } goNextLine(newIndentLevel, buffer); for (int i = 0; i < classes.size(); i++) { appendText(classes.get(i), newIndentLevel, buffer); if (i < classes.size() - 1) { buffer.append('\n'); goNextLine(newIndentLevel, buffer); } } } goNextLine(indentLevel, buffer); buffer.append('}'); } @Override public void setMirror(@NotNull TreeElement element) throws InvalidMirrorException { setMirrorCheckingType(element, null); PsiClass mirror = SourceTreeToPsiMap.treeToPsiNotNull(element); setMirrorIfPresent(getDocComment(), mirror.getDocComment()); setMirror(getModifierList(), mirror.getModifierList()); setMirror(getNameIdentifier(), mirror.getNameIdentifier()); setMirror(getTypeParameterList(), mirror.getTypeParameterList()); setMirror(getExtendsList(), mirror.getExtendsList()); setMirror(getImplementsList(), mirror.getImplementsList()); if (mirror instanceof PsiExtensibleClass) { PsiExtensibleClass extMirror = (PsiExtensibleClass)mirror; setMirrors(getOwnFields(), extMirror.getOwnFields()); setMirrors(getOwnMethods(), extMirror.getOwnMethods()); setMirrors(getOwnInnerClasses(), extMirror.getOwnInnerClasses()); } else { setMirrors(getOwnFields(), mirror.getFields()); setMirrors(getOwnMethods(), mirror.getMethods()); setMirrors(getOwnInnerClasses(), mirror.getInnerClasses()); } } @Override public void accept(@NotNull PsiElementVisitor visitor) { if (visitor instanceof JavaElementVisitor) { ((JavaElementVisitor)visitor).visitClass(this); } else { visitor.visitElement(this); } } @NonNls public String toString() { return "PsiClass:" + getName(); } @Override public boolean processDeclarations(@NotNull PsiScopeProcessor processor, @NotNull ResolveState state, PsiElement lastParent, @NotNull PsiElement place) { LanguageLevel languageLevel = processor instanceof MethodsProcessor ? ((MethodsProcessor)processor).getLanguageLevel() : PsiUtil.getLanguageLevel(place); return PsiClassImplUtil.processDeclarationsInClass(this, processor, state, null, lastParent, place, languageLevel, false); } @Override public PsiElement getScope() { return getParent(); } @Override public boolean isInheritorDeep(PsiClass baseClass, PsiClass classToByPass) { return InheritanceImplUtil.isInheritorDeep(this, baseClass, classToByPass); } @Override public boolean isInheritor(@NotNull PsiClass baseClass, boolean checkDeep) { return InheritanceImplUtil.isInheritor(this, baseClass, checkDeep); } @Nullable public PsiClass getSourceMirrorClass() { final PsiClass delegate = getUserData(DELEGATE_KEY); if (delegate instanceof ClsClassImpl) { return ((ClsClassImpl)delegate).getSourceMirrorClass(); } final String name = getName(); final PsiElement parent = getParent(); if (parent instanceof PsiFile) { if (!(parent instanceof PsiClassOwner)) return null; PsiClassOwner fileNavigationElement = (PsiClassOwner)parent.getNavigationElement(); if (fileNavigationElement == parent) return null; for (PsiClass aClass : fileNavigationElement.getClasses()) { if (name.equals(aClass.getName())) return aClass; } } else if (parent != null) { ClsClassImpl parentClass = (ClsClassImpl)parent; PsiClass parentSourceMirror = parentClass.getSourceMirrorClass(); if (parentSourceMirror == null) return null; PsiClass[] innerClasses = parentSourceMirror.getInnerClasses(); for (PsiClass innerClass : innerClasses) { if (name.equals(innerClass.getName())) return innerClass; } } else { throw new PsiInvalidElementAccessException(this); } return null; } @Override @NotNull public PsiElement getNavigationElement() { for (ClsCustomNavigationPolicy customNavigationPolicy : Extensions.getExtensions(ClsCustomNavigationPolicy.EP_NAME)) { PsiElement navigationElement = customNavigationPolicy.getNavigationElement(this); if (navigationElement != null) { return navigationElement; } } PsiClass aClass = getSourceMirrorClass(); if (aClass != null) { return aClass.getNavigationElement(); } if ("package-info".equals(getName())) { PsiElement parent = getParent(); if (parent instanceof ClsFileImpl) { PsiElement sourceFile = parent.getNavigationElement(); if (sourceFile instanceof PsiJavaFile) { return sourceFile; } } } return this; } @Override public ItemPresentation getPresentation() { return ItemPresentationProviders.getItemPresentation(this); } @Override public boolean isEquivalentTo(final PsiElement another) { return PsiClassImplUtil.isClassEquivalentTo(this, another); } @Override @NotNull public SearchScope getUseScope() { return PsiClassImplUtil.getClassUseScope(this); } @Override public PsiQualifiedNamedElement getContainer() { final PsiFile file = getContainingFile(); final PsiDirectory dir; return file == null ? null : (dir = file.getContainingDirectory()) == null ? null : JavaDirectoryService.getInstance().getPackage(dir); } @Override public void putInfo(@NotNull Map<String, String> info) { PsiClassImpl.putInfo(this, info); } @Override protected boolean isVisibilitySupported() { return true; } }