ExcludedFileIndex -> FileIndexFacade; use it in PsiClassImplUtil
[idea/community.git] / java / java-impl / src / com / intellij / psi / impl / ElementPresentationUtil.java
1 /*
2  * Copyright 2000-2009 JetBrains s.r.o.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.intellij.psi.impl;
17
18 import com.intellij.codeInsight.CodeInsightBundle;
19 import com.intellij.codeInsight.TestUtil;
20 import com.intellij.compiler.CompilerConfiguration;
21 import com.intellij.openapi.components.ServiceManager;
22 import com.intellij.openapi.project.DumbService;
23 import com.intellij.openapi.project.Project;
24 import com.intellij.openapi.roots.FileIndexFacade;
25 import com.intellij.openapi.util.IconLoader;
26 import com.intellij.openapi.util.Iconable;
27 import com.intellij.openapi.util.Key;
28 import com.intellij.openapi.util.text.StringUtil;
29 import com.intellij.openapi.vfs.VirtualFile;
30 import com.intellij.psi.*;
31 import com.intellij.psi.util.*;
32 import com.intellij.ui.RowIcon;
33 import com.intellij.util.PlatformIcons;
34 import com.intellij.util.VisibilityIcons;
35 import gnu.trove.TIntObjectHashMap;
36
37 import javax.swing.*;
38
39 public class ElementPresentationUtil implements PlatformIcons {
40   private static final Icon STATIC_MARK_ICON = IconLoader.getIcon("/nodes/staticMark.png");
41   private static final Icon FINAL_MARK_ICON = IconLoader.getIcon("/nodes/finalMark.png");
42   public static final Icon JUNIT_TEST_MARK = IconLoader.getIcon("/nodes/junitTestMark.png");
43   private static final Icon RUNNABLE_MARK = IconLoader.getIcon("/nodes/runnableMark.png");
44
45
46   private ElementPresentationUtil() {
47   }
48
49
50   public static int getFlags(PsiModifierListOwner element, final boolean isLocked) {
51     final PsiFile containingFile = element.getContainingFile();
52     final VirtualFile vFile = containingFile == null ? null : containingFile.getVirtualFile();
53     final boolean isEnum = element instanceof PsiClass && ((PsiClass)element).isEnum();
54     int flags = (element.hasModifierProperty(PsiModifier.FINAL) && !isEnum ? FLAGS_FINAL : 0)
55                 | (element.hasModifierProperty(PsiModifier.STATIC) && !isEnum ? FLAGS_STATIC : 0)
56                 | (isLocked ? ElementBase.FLAGS_LOCKED : 0)
57                 | (isExcluded(vFile, element.getProject()) ? FLAGS_EXCLUDED : 0);
58     if (element instanceof PsiClass) {
59       final PsiClass aClass = (PsiClass)element;
60       if (element.hasModifierProperty(PsiModifier.ABSTRACT) && !((PsiClass)element).isInterface()) {
61         flags |= FLAGS_ABSTRACT;
62       }
63       int kind = getClassKind(aClass);
64       if (kind == CLASS_KIND_JUNIT_TEST) {
65         flags |= FLAGS_JUNIT_TEST;
66       }
67       else if (kind == CLASS_KIND_RUNNABLE) {
68         flags |= FLAGS_RUNNABLE;
69       }
70     }
71     return flags;
72   }
73
74   private static final Icon ABSTRACT_EXCEPTION_CLASS_ICON = IconLoader.getIcon("/nodes/abstractException.png");
75
76   private static final int CLASS_KIND_INTERFACE     = 10;
77   private static final int CLASS_KIND_ANNOTATION    = 20;
78   public static final int CLASS_KIND_CLASS         = 30;
79   private static final int CLASS_KIND_ANONYMOUS     = 40;
80   private static final int CLASS_KIND_ENUM          = 50;
81   private static final int CLASS_KIND_ASPECT        = 60;
82   public static final int CLASS_KIND_JSP           = 70;
83   public static final int CLASS_KIND_EXCEPTION = 80;
84   private static final int CLASS_KIND_JUNIT_TEST = 90;
85   private static final int CLASS_KIND_RUNNABLE = 100;
86
87   private static final int FLAGS_ABSTRACT = 0x100;
88   private static final int FLAGS_STATIC = 0x200;
89   private static final int FLAGS_FINAL = 0x400;
90   public static final int FLAGS_EXCLUDED = 0x1000;
91   private static final int FLAGS_JUNIT_TEST = 0x2000;
92   private static final int FLAGS_RUNNABLE = 0x4000;
93
94   private static final Key<CachedValue<Integer>> CLASS_KIND_KEY = new Key<CachedValue<Integer>>("CLASS_KIND_KEY");
95
96   public static boolean isExcluded(final VirtualFile vFile, final Project project) {
97     return vFile != null
98            && ServiceManager.getService(project, FileIndexFacade.class).isInSource(vFile)
99            && CompilerConfiguration.getInstance(project).isExcludedFromCompilation(vFile);
100   }
101
102   public static int getBasicClassKind(PsiClass aClass) {
103     if (!aClass.isValid()) return CLASS_KIND_CLASS;
104
105     if (aClass.isAnnotationType()) return CLASS_KIND_ANNOTATION;
106     if (aClass.isEnum()) return CLASS_KIND_ENUM;
107     if (aClass.isInterface()) return CLASS_KIND_INTERFACE;
108     if (aClass instanceof PsiAnonymousClass) return CLASS_KIND_ANONYMOUS;
109
110     return CLASS_KIND_CLASS;
111   }
112
113   public static int getClassKind(final PsiClass aClass) {
114     if (!aClass.isValid()) {
115       aClass.putUserData(CLASS_KIND_KEY, null);
116       return CLASS_KIND_CLASS;
117     }
118
119     CachedValue<Integer> value = aClass.getUserData(CLASS_KIND_KEY);
120     if (value == null) {
121       value = CachedValuesManager.getManager(aClass.getProject()).createCachedValue(new CachedValueProvider<Integer>() {
122         public Result<Integer> compute() {
123           return Result.createSingleDependency(Integer.valueOf(getClassKindImpl(aClass)), aClass);
124         }
125       }, false);
126       aClass.putUserData(CLASS_KIND_KEY, value);
127     }
128     return value.getValue().intValue();
129   }
130
131   private static int getClassKindImpl(PsiClass aClass) {
132     if (!aClass.isValid()) return CLASS_KIND_CLASS;
133
134     if (aClass.isAnnotationType()) {
135       return CLASS_KIND_ANNOTATION;
136     }
137     if (aClass.isEnum()) {
138       return CLASS_KIND_ENUM;
139     }
140     if (aClass.isInterface()) {
141       return CLASS_KIND_INTERFACE;
142     }
143     if (aClass instanceof PsiAnonymousClass) {
144       return CLASS_KIND_ANONYMOUS;
145     }
146
147     if (!DumbService.getInstance(aClass.getProject()).isDumb()) {
148       final PsiManager manager = aClass.getManager();
149       final PsiClass javaLangTrowable =
150         JavaPsiFacade.getInstance(manager.getProject()).findClass("java.lang.Throwable", aClass.getResolveScope());
151       final boolean isException = javaLangTrowable != null && InheritanceUtil.isInheritorOrSelf(aClass, javaLangTrowable, true);
152       if (isException) {
153         return CLASS_KIND_EXCEPTION;
154       }
155
156       if (TestUtil.isTestClass(aClass)) {
157         return CLASS_KIND_JUNIT_TEST;
158       }
159     }
160     if (PsiClassUtil.isRunnableClass(aClass, false) && PsiMethodUtil.findMainMethod(aClass) != null) {
161       return CLASS_KIND_RUNNABLE;
162     }
163     return CLASS_KIND_CLASS;
164   }
165
166   private static final TIntObjectHashMap<Icon> BASE_ICON = new TIntObjectHashMap<Icon>(20);
167   static {
168     BASE_ICON.put(CLASS_KIND_CLASS, CLASS_ICON);
169     BASE_ICON.put(CLASS_KIND_CLASS | FLAGS_ABSTRACT, ABSTRACT_CLASS_ICON);
170     BASE_ICON.put(CLASS_KIND_ANNOTATION, ANNOTATION_TYPE_ICON);
171     BASE_ICON.put(CLASS_KIND_ANNOTATION | FLAGS_ABSTRACT, ANNOTATION_TYPE_ICON);
172     BASE_ICON.put(CLASS_KIND_ANONYMOUS, ANONYMOUS_CLASS_ICON);
173     BASE_ICON.put(CLASS_KIND_ANONYMOUS | FLAGS_ABSTRACT, ANONYMOUS_CLASS_ICON);
174     BASE_ICON.put(CLASS_KIND_ASPECT, ASPECT_ICON);
175     BASE_ICON.put(CLASS_KIND_ASPECT | FLAGS_ABSTRACT, ASPECT_ICON);
176     BASE_ICON.put(CLASS_KIND_ENUM, ENUM_ICON);
177     BASE_ICON.put(CLASS_KIND_ENUM | FLAGS_ABSTRACT, ENUM_ICON);
178     BASE_ICON.put(CLASS_KIND_EXCEPTION, EXCEPTION_CLASS_ICON);
179     BASE_ICON.put(CLASS_KIND_EXCEPTION | FLAGS_ABSTRACT, ABSTRACT_EXCEPTION_CLASS_ICON);
180     BASE_ICON.put(CLASS_KIND_INTERFACE, INTERFACE_ICON);
181     BASE_ICON.put(CLASS_KIND_INTERFACE | FLAGS_ABSTRACT, INTERFACE_ICON);
182     BASE_ICON.put(CLASS_KIND_JUNIT_TEST, CLASS_ICON);
183     BASE_ICON.put(CLASS_KIND_JUNIT_TEST | FLAGS_ABSTRACT, ABSTRACT_CLASS_ICON);
184     BASE_ICON.put(CLASS_KIND_RUNNABLE, CLASS_ICON);
185   }
186
187   public static Icon getClassIconOfKind(PsiClass aClass, int classKind) {
188     final boolean isAbstract = aClass.hasModifierProperty(PsiModifier.ABSTRACT);
189     return BASE_ICON.get(classKind | (isAbstract ? FLAGS_ABSTRACT : 0));
190   }
191
192   public static String getDescription(PsiModifierListOwner member) {
193     String noun;
194     if (member instanceof PsiClass) noun = getClassNoun((PsiClass)member);
195     else if (member instanceof PsiMethod) noun = CodeInsightBundle.message("node.method.tooltip");
196     else if (member instanceof PsiField) noun = CodeInsightBundle.message("node.field.tooltip");
197     else return null;
198     String adj = getFlagsDescription(member);
199     return (adj + " " + noun).trim();
200   }
201
202   private static String getClassNoun(final PsiClass aClass) {
203     String noun;
204     int kind = getClassKind(aClass);
205     switch (kind) {
206       case CLASS_KIND_ANNOTATION: noun = CodeInsightBundle.message("node.annotation.tooltip"); break;
207       case CLASS_KIND_ANONYMOUS: noun = CodeInsightBundle.message("node.anonymous.class.tooltip"); break;
208       case CLASS_KIND_ENUM: noun = CodeInsightBundle.message("node.enum.tooltip"); break;
209       case CLASS_KIND_EXCEPTION: noun = CodeInsightBundle.message("node.exception.tooltip"); break;
210       case CLASS_KIND_INTERFACE: noun = CodeInsightBundle.message("node.interface.tooltip"); break;
211       case CLASS_KIND_JUNIT_TEST: noun = CodeInsightBundle.message("node.junit.test.tooltip"); break;
212       case CLASS_KIND_RUNNABLE: noun = CodeInsightBundle.message("node.runnable.class.tooltip"); break;
213       default:
214       case CLASS_KIND_CLASS: noun = CodeInsightBundle.message("node.class.tooltip"); break;
215     }
216     return noun;
217   }
218
219   private static String getFlagsDescription(final PsiModifierListOwner aClass) {
220     int flags = getFlags(aClass, false);
221     String adj = "";
222     if ((flags & FLAGS_EXCLUDED) != 0) adj += " " + CodeInsightBundle.message("node.excluded.flag.tooltip");
223     if ((flags & FLAGS_ABSTRACT) != 0) adj += " " + CodeInsightBundle.message("node.abstract.flag.tooltip");
224     if ((flags & FLAGS_FINAL) != 0) adj += " " + CodeInsightBundle.message("node.final.flag.tooltip");
225     if ((flags & FLAGS_STATIC) != 0) adj += " " + CodeInsightBundle.message("node.static.flag.tooltip");
226     PsiModifierList list = aClass.getModifierList();
227     if (list != null) {
228       int level = PsiUtil.getAccessLevel(list);
229       if (level != PsiUtil.ACCESS_LEVEL_PUBLIC) {
230         adj += " " + StringUtil.capitalize(PsiBundle.visibilityPresentation(PsiUtil.getAccessModifier(level)));
231       }
232     }
233     return adj;
234   }
235
236
237   static {
238     ElementBase.registerIconLayer(FLAGS_STATIC, STATIC_MARK_ICON);
239     ElementBase.registerIconLayer(FLAGS_EXCLUDED, EXCLUDED_FROM_COMPILE_ICON);
240     ElementBase.registerIconLayer(FLAGS_FINAL, FINAL_MARK_ICON);
241     ElementBase.registerIconLayer(FLAGS_JUNIT_TEST, JUNIT_TEST_MARK);
242     ElementBase.registerIconLayer(FLAGS_RUNNABLE, RUNNABLE_MARK);
243   }
244
245   public static Icon addVisibilityIcon(final PsiModifierListOwner element, final int flags, final RowIcon baseIcon) {
246     if ((flags & Iconable.ICON_FLAG_VISIBILITY) != 0) {
247       VisibilityIcons.setVisibilityIcon(element.getModifierList(), baseIcon);
248     }
249     return baseIcon;
250   }
251 }