Cleanup: NotNull/Nullable
[idea/community.git] / java / java-psi-impl / src / com / intellij / psi / impl / ElementPresentationUtil.java
1 // Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
2 package com.intellij.psi.impl;
3
4 import com.intellij.codeInsight.CodeInsightBundle;
5 import com.intellij.codeInsight.TestFrameworks;
6 import com.intellij.icons.AllIcons;
7 import com.intellij.ide.IconLayerProvider;
8 import com.intellij.openapi.project.DumbService;
9 import com.intellij.openapi.util.Iconable;
10 import com.intellij.openapi.util.Key;
11 import com.intellij.openapi.util.text.StringUtil;
12 import com.intellij.psi.*;
13 import com.intellij.psi.util.*;
14 import com.intellij.ui.RowIcon;
15 import com.intellij.util.BitUtil;
16 import com.intellij.util.PlatformIcons;
17 import com.intellij.util.VisibilityIcons;
18 import gnu.trove.TIntObjectHashMap;
19
20 import javax.swing.*;
21
22 public class ElementPresentationUtil implements PlatformIcons {
23   private ElementPresentationUtil() {
24   }
25
26
27   public static int getFlags(PsiModifierListOwner element, final boolean isLocked) {
28     final boolean isEnum = element instanceof PsiClass && ((PsiClass)element).isEnum();
29     int flags = (element.hasModifierProperty(PsiModifier.FINAL) && !isEnum ? FLAGS_FINAL : 0)
30                 | (element.hasModifierProperty(PsiModifier.STATIC) && !isEnum ? FLAGS_STATIC : 0)
31                 | (isLocked ? ElementBase.FLAGS_LOCKED : 0);
32     if (element instanceof PsiClass) {
33       final PsiClass aClass = (PsiClass)element;
34       if (element.hasModifierProperty(PsiModifier.ABSTRACT) && !((PsiClass)element).isInterface()) {
35         flags |= FLAGS_ABSTRACT;
36       }
37       int kind = getClassKind(aClass);
38       if (kind == CLASS_KIND_JUNIT_TEST) {
39         flags |= FLAGS_JUNIT_TEST;
40       }
41       else if (kind == CLASS_KIND_RUNNABLE) {
42         flags |= FLAGS_RUNNABLE;
43       }
44     }
45     return flags;
46   }
47
48   public static RowIcon createLayeredIcon(Icon baseIcon, PsiModifierListOwner element, boolean isLocked) {
49     return ElementBase.createLayeredIcon(element, baseIcon, getFlags(element, isLocked));
50   }
51
52   private static final int CLASS_KIND_INTERFACE     = 10;
53   private static final int CLASS_KIND_ANNOTATION    = 20;
54   public static final int CLASS_KIND_CLASS         = 30;
55   private static final int CLASS_KIND_ANONYMOUS     = 40;
56   private static final int CLASS_KIND_ENUM          = 50;
57   private static final int CLASS_KIND_ASPECT        = 60;
58   public static final int CLASS_KIND_JSP           = 70;
59   public static final int CLASS_KIND_EXCEPTION = 80;
60   private static final int CLASS_KIND_JUNIT_TEST = 90;
61   private static final int CLASS_KIND_RUNNABLE = 100;
62
63   private static final int FLAGS_ABSTRACT = 0x100;
64   private static final int FLAGS_STATIC = 0x200;
65   private static final int FLAGS_FINAL = 0x400;
66   private static final int FLAGS_JUNIT_TEST = 0x2000;
67   public static final int FLAGS_RUNNABLE = 0x4000;
68
69   private static final Key<CachedValue<Integer>> CLASS_KIND_KEY = new Key<>("CLASS_KIND_KEY");
70
71   public static int getBasicClassKind(PsiClass aClass) {
72     if (!aClass.isValid()) return CLASS_KIND_CLASS;
73
74     if (aClass.isAnnotationType()) return CLASS_KIND_ANNOTATION;
75     if (aClass.isEnum()) return CLASS_KIND_ENUM;
76     if (aClass.isInterface()) return CLASS_KIND_INTERFACE;
77     if (aClass instanceof PsiAnonymousClass) return CLASS_KIND_ANONYMOUS;
78
79     return CLASS_KIND_CLASS;
80   }
81
82   public static int getClassKind(final PsiClass aClass) {
83     if (!aClass.isValid()) {
84       aClass.putUserData(CLASS_KIND_KEY, null);
85       return CLASS_KIND_CLASS;
86     }
87
88     CachedValue<Integer> value = aClass.getUserData(CLASS_KIND_KEY);
89     if (value == null) {
90       value = CachedValuesManager.getManager(aClass.getProject()).createCachedValue(
91         () -> CachedValueProvider.Result.createSingleDependency(Integer.valueOf(getClassKindImpl(aClass)), aClass), false);
92       aClass.putUserData(CLASS_KIND_KEY, value);
93     }
94     return value.getValue().intValue();
95   }
96
97   private static int getClassKindImpl(PsiClass aClass) {
98     if (!aClass.isValid()) return CLASS_KIND_CLASS;
99
100     if (aClass.isAnnotationType()) {
101       return CLASS_KIND_ANNOTATION;
102     }
103     if (aClass.isEnum()) {
104       return CLASS_KIND_ENUM;
105     }
106     if (aClass.isInterface()) {
107       return CLASS_KIND_INTERFACE;
108     }
109     if (aClass instanceof PsiAnonymousClass) {
110       return CLASS_KIND_ANONYMOUS;
111     }
112
113     if (!DumbService.getInstance(aClass.getProject()).isDumb()) {
114       final PsiManager manager = aClass.getManager();
115       final PsiClass javaLangTrowable =
116         JavaPsiFacade.getInstance(manager.getProject()).findClass("java.lang.Throwable", aClass.getResolveScope());
117       final boolean isException = javaLangTrowable != null && InheritanceUtil.isInheritorOrSelf(aClass, javaLangTrowable, true);
118       if (isException) {
119         return CLASS_KIND_EXCEPTION;
120       }
121
122       if (TestFrameworks.getInstance().isTestClass(aClass)) {
123         return CLASS_KIND_JUNIT_TEST;
124       }
125       if (PsiClassUtil.isRunnableClass(aClass, false) && PsiMethodUtil.findMainMethod(aClass) != null) {
126         return CLASS_KIND_RUNNABLE;
127       }
128     }
129     return CLASS_KIND_CLASS;
130   }
131
132   private static final TIntObjectHashMap<Icon> BASE_ICON = new TIntObjectHashMap<>(20);
133   static {
134     BASE_ICON.put(CLASS_KIND_CLASS, CLASS_ICON);
135     BASE_ICON.put(CLASS_KIND_CLASS | FLAGS_ABSTRACT, ABSTRACT_CLASS_ICON);
136     BASE_ICON.put(CLASS_KIND_ANNOTATION, ANNOTATION_TYPE_ICON);
137     BASE_ICON.put(CLASS_KIND_ANNOTATION | FLAGS_ABSTRACT, ANNOTATION_TYPE_ICON);
138     BASE_ICON.put(CLASS_KIND_ANONYMOUS, ANONYMOUS_CLASS_ICON);
139     BASE_ICON.put(CLASS_KIND_ANONYMOUS | FLAGS_ABSTRACT, ANONYMOUS_CLASS_ICON);
140     BASE_ICON.put(CLASS_KIND_ASPECT, ASPECT_ICON);
141     BASE_ICON.put(CLASS_KIND_ASPECT | FLAGS_ABSTRACT, ASPECT_ICON);
142     BASE_ICON.put(CLASS_KIND_ENUM, ENUM_ICON);
143     BASE_ICON.put(CLASS_KIND_ENUM | FLAGS_ABSTRACT, ENUM_ICON);
144     BASE_ICON.put(CLASS_KIND_EXCEPTION, EXCEPTION_CLASS_ICON);
145     BASE_ICON.put(CLASS_KIND_EXCEPTION | FLAGS_ABSTRACT, AllIcons.Nodes.AbstractException);
146     BASE_ICON.put(CLASS_KIND_INTERFACE, INTERFACE_ICON);
147     BASE_ICON.put(CLASS_KIND_INTERFACE | FLAGS_ABSTRACT, INTERFACE_ICON);
148     BASE_ICON.put(CLASS_KIND_JUNIT_TEST, CLASS_ICON);
149     BASE_ICON.put(CLASS_KIND_JUNIT_TEST | FLAGS_ABSTRACT, ABSTRACT_CLASS_ICON);
150     BASE_ICON.put(CLASS_KIND_RUNNABLE, CLASS_ICON);
151   }
152
153   public static Icon getClassIconOfKind(PsiClass aClass, int classKind) {
154     final boolean isAbstract = aClass.hasModifierProperty(PsiModifier.ABSTRACT);
155     return BASE_ICON.get(classKind | (isAbstract ? FLAGS_ABSTRACT : 0));
156   }
157
158   public static String getDescription(PsiModifierListOwner member) {
159     String noun;
160     if (member instanceof PsiClass) noun = getClassNoun((PsiClass)member);
161     else if (member instanceof PsiMethod) noun = CodeInsightBundle.message("node.method.tooltip");
162     else if (member instanceof PsiField) noun = CodeInsightBundle.message("node.field.tooltip");
163     else return null;
164     String adj = getFlagsDescription(member);
165     return (adj + " " + noun).trim();
166   }
167
168   private static String getClassNoun(final PsiClass aClass) {
169     String noun;
170     int kind = getClassKind(aClass);
171     switch (kind) {
172       case CLASS_KIND_ANNOTATION: noun = CodeInsightBundle.message("node.annotation.tooltip"); break;
173       case CLASS_KIND_ANONYMOUS: noun = CodeInsightBundle.message("node.anonymous.class.tooltip"); break;
174       case CLASS_KIND_ENUM: noun = CodeInsightBundle.message("node.enum.tooltip"); break;
175       case CLASS_KIND_EXCEPTION: noun = CodeInsightBundle.message("node.exception.tooltip"); break;
176       case CLASS_KIND_INTERFACE: noun = CodeInsightBundle.message("node.interface.tooltip"); break;
177       case CLASS_KIND_JUNIT_TEST: noun = CodeInsightBundle.message("node.junit.test.tooltip"); break;
178       case CLASS_KIND_RUNNABLE: noun = CodeInsightBundle.message("node.runnable.class.tooltip"); break;
179       default:
180       case CLASS_KIND_CLASS: noun = CodeInsightBundle.message("node.class.tooltip"); break;
181     }
182     return noun;
183   }
184
185   private static String getFlagsDescription(final PsiModifierListOwner aClass) {
186     int flags = getFlags(aClass, false);
187     String adj = "";
188     for (IconLayerProvider provider : IconLayerProvider.EP_NAME.getExtensionList()) {
189       if (provider.getLayerIcon(aClass, false) != null) {
190         adj += " " + provider.getLayerDescription();
191       }
192     }
193     if (BitUtil.isSet(flags, FLAGS_ABSTRACT)) adj += " " + CodeInsightBundle.message("node.abstract.flag.tooltip");
194     if (BitUtil.isSet(flags, FLAGS_FINAL)) adj += " " + CodeInsightBundle.message("node.final.flag.tooltip");
195     if (BitUtil.isSet(flags, FLAGS_STATIC)) adj += " " + CodeInsightBundle.message("node.static.flag.tooltip");
196     PsiModifierList list = aClass.getModifierList();
197     if (list != null) {
198       int level = PsiUtil.getAccessLevel(list);
199       if (level != PsiUtil.ACCESS_LEVEL_PUBLIC) {
200         adj += " " + StringUtil.capitalize(PsiBundle.visibilityPresentation(PsiUtil.getAccessModifier(level)));
201       }
202     }
203     return adj;
204   }
205
206
207   static {
208     ElementBase.registerIconLayer(FLAGS_STATIC, AllIcons.Nodes.StaticMark);
209     ElementBase.registerIconLayer(FLAGS_FINAL, AllIcons.Nodes.FinalMark);
210     ElementBase.registerIconLayer(FLAGS_JUNIT_TEST, AllIcons.Nodes.JunitTestMark);
211     ElementBase.registerIconLayer(FLAGS_RUNNABLE, AllIcons.Nodes.RunnableMark);
212   }
213
214   public static Icon addVisibilityIcon(final PsiModifierListOwner element, final int flags, final RowIcon baseIcon) {
215     if (BitUtil.isSet(flags, Iconable.ICON_FLAG_VISIBILITY)) {
216       VisibilityIcons.setVisibilityIcon(element.getModifierList(), baseIcon);
217     }
218     return baseIcon;
219   }
220 }