[duplicates] enable duplicates analysis in PyCharm/WebStorm/PhpStorm/RubyMine
[idea/community.git] / platform / core-api / src / com / intellij / psi / impl / ElementBase.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
3 package com.intellij.psi.impl;
4
5 import com.intellij.icons.AllIcons;
6 import com.intellij.ide.IconLayerProvider;
7 import com.intellij.navigation.ItemPresentation;
8 import com.intellij.openapi.diagnostic.Logger;
9 import com.intellij.openapi.fileTypes.FileType;
10 import com.intellij.openapi.fileTypes.INativeFileType;
11 import com.intellij.openapi.fileTypes.UnknownFileType;
12 import com.intellij.openapi.progress.ProcessCanceledException;
13 import com.intellij.openapi.project.IndexNotReadyException;
14 import com.intellij.openapi.project.Project;
15 import com.intellij.openapi.util.Iconable;
16 import com.intellij.openapi.util.NotNullLazyValue;
17 import com.intellij.openapi.util.UserDataHolderBase;
18 import com.intellij.openapi.util.registry.Registry;
19 import com.intellij.openapi.vfs.VirtualFile;
20 import com.intellij.psi.PsiElement;
21 import com.intellij.psi.PsiFile;
22 import com.intellij.psi.SmartPointerManager;
23 import com.intellij.psi.SmartPsiElementPointer;
24 import com.intellij.ui.IconDeferrer;
25 import com.intellij.ui.LayeredIcon;
26 import com.intellij.ui.RowIcon;
27 import com.intellij.util.*;
28 import com.intellij.util.containers.ContainerUtil;
29 import com.intellij.util.ui.EmptyIcon;
30 import org.jetbrains.annotations.NotNull;
31 import org.jetbrains.annotations.Nullable;
32
33 import javax.swing.*;
34 import java.util.List;
35
36 public abstract class ElementBase extends UserDataHolderBase implements Iconable {
37   private static final Logger LOG = Logger.getInstance("#com.intellij.psi.impl.ElementBase");
38
39   public static final int FLAGS_LOCKED = 0x800;
40   private static final NullableFunction<ElementIconRequest,Icon> ICON_COMPUTE = request -> {
41     PsiElement element = request.myPointer.getElement();
42     if (element == null) return null;
43
44     Icon icon = computeIconNow(element, request.myFlags);
45     LastComputedIcon.put(element, icon, request.myFlags);
46     return icon;
47   };
48
49   private static final NotNullLazyValue<Icon> VISIBILITY_ICON_PLACEHOLDER = new NotNullLazyValue<Icon>() {
50     @NotNull
51     @Override
52     protected Icon compute() {
53       return EmptyIcon.create(PlatformIcons.PUBLIC_ICON);
54     }
55   };
56
57   public static final NotNullLazyValue<Icon> ICON_PLACEHOLDER = new NotNullLazyValue<Icon>() {
58     @NotNull
59     @Override
60     protected Icon compute() {
61       return AllIcons.Nodes.NodePlaceholder;
62     }
63   };
64
65   @Override
66   @Nullable
67   public Icon getIcon(int flags) {
68     if (!(this instanceof PsiElement)) return null;
69
70     try {
71       return computeIcon(flags);
72     }
73     catch (ProcessCanceledException | IndexNotReadyException e) {
74       throw e;
75     }
76     catch (Exception e) {
77       LOG.error(e);
78       return null;
79     }
80   }
81
82   @Nullable
83   private Icon computeIcon(@Iconable.IconFlags int flags) {
84     PsiElement psiElement = (PsiElement)this;
85     if (!psiElement.isValid()) return null;
86
87     if (Registry.is("psi.deferIconLoading")) {
88       Icon baseIcon = LastComputedIcon.get(psiElement, flags);
89       if (baseIcon == null) {
90         baseIcon = AstLoadingFilter.disallowTreeLoading(() -> computeBaseIcon(flags));
91       }
92       return IconDeferrer.getInstance().defer(baseIcon, new ElementIconRequest(psiElement, psiElement.getProject(), flags), ICON_COMPUTE);
93     }
94
95     return computeIconNow(psiElement, flags);
96   }
97
98   @Nullable
99   private static Icon computeIconNow(@NotNull PsiElement element, @Iconable.IconFlags int flags) {
100     return AstLoadingFilter.disallowTreeLoading(() -> doComputeIconNow(element, flags));
101   }
102
103   private static Icon doComputeIconNow(@NotNull PsiElement element, @Iconable.IconFlags int flags) {
104     final Icon providersIcon = PsiIconUtil.getProvidersIcon(element, flags);
105     if (providersIcon != null) {
106       return providersIcon instanceof RowIcon ? (RowIcon)providersIcon : createLayeredIcon(element, providersIcon, flags);
107     }
108     return ((ElementBase)element).getElementIcon(flags);
109   }
110
111   protected Icon computeBaseIcon(@Iconable.IconFlags int flags) {
112     Icon baseIcon = isVisibilitySupported() ? getAdjustedBaseIcon(getBaseIcon(), flags) : getBaseIcon();
113
114     // to prevent blinking, base icon should be created with the layers
115     if (this instanceof PsiElement) {
116       PsiFile file = ((PsiElement)this).getContainingFile();
117       if (file != null) {
118         return createLayeredIcon(file, baseIcon, flags);
119       }
120     }
121     return baseIcon;
122   }
123
124   protected Icon getBaseIcon() {
125     if (this instanceof PsiElement) {
126       PsiFile file = ((PsiElement)this).getContainingFile();
127       if (file != null) {
128         if (!isNativeFileType(file.getFileType())) {
129           return file.getFileType().getIcon();
130         }
131       }
132     }
133     return ICON_PLACEHOLDER.getValue();
134   }
135
136   public static boolean isNativeFileType(FileType fileType) {
137     return fileType instanceof INativeFileType && ((INativeFileType) fileType).useNativeIcon() || fileType instanceof UnknownFileType;
138   }
139
140   protected Icon getAdjustedBaseIcon(Icon icon, @Iconable.IconFlags int flags) {
141     if (BitUtil.isSet(flags, ICON_FLAG_VISIBILITY)) {
142       return new RowIcon(icon, VISIBILITY_ICON_PLACEHOLDER.getValue());
143     }
144     return icon;
145   }
146
147   protected boolean isVisibilitySupported() {
148     return false;
149   }
150
151   @NotNull
152   public static Icon overlayIcons(@NotNull Icon ... icons) {
153     final LayeredIcon icon = new LayeredIcon(icons.length);
154     int i = 0;
155     for (Icon ic : icons) {
156       icon.setIcon(ic, i++);
157     }
158     return icon;
159   }
160
161   @NotNull
162   public static RowIcon buildRowIcon(final Icon baseIcon, Icon visibilityIcon) {
163     RowIcon icon = new RowIcon(2);
164     icon.setIcon(baseIcon, 0);
165     icon.setIcon(visibilityIcon, 1);
166     return icon;
167   }
168
169   public static Icon iconWithVisibilityIfNeeded(@Iconable.IconFlags int flags, Icon baseIcon, Icon visibility) {
170     return BitUtil.isSet(flags, ICON_FLAG_VISIBILITY) ? buildRowIcon(baseIcon, visibility) : baseIcon;
171   }
172
173   private static class ElementIconRequest {
174     private final SmartPsiElementPointer<?> myPointer;
175     @Iconable.IconFlags private final int myFlags;
176
177     private ElementIconRequest(@NotNull PsiElement element, @NotNull Project project, @IconFlags int flags) {
178       myPointer = SmartPointerManager.getInstance(project).createSmartPsiElementPointer(element);
179       myFlags = flags;
180     }
181
182     @Override
183     public boolean equals(Object o) {
184       if (this == o) return true;
185       if (!(o instanceof ElementIconRequest)) return false;
186
187       ElementIconRequest request = (ElementIconRequest)o;
188
189       if (myFlags != request.myFlags) return false;
190       if (!myPointer.equals(request.myPointer)) return false;
191
192       return true;
193     }
194
195     @Override
196     public int hashCode() {
197       int result = myPointer.hashCode();
198       result = 31 * result + myFlags;
199       return result;
200     }
201   }
202
203   @Nullable
204   protected Icon getElementIcon(@Iconable.IconFlags int flags) {
205     PsiElement element = (PsiElement)this;
206     if (!element.isValid()) return null;
207
208     boolean isLocked = BitUtil.isSet(flags, ICON_FLAG_READ_STATUS) && !element.isWritable();
209     int elementFlags = isLocked ? FLAGS_LOCKED : 0;
210
211     if (element instanceof ItemPresentation) {
212       Icon baseIcon = ((ItemPresentation)element).getIcon(false);
213       if (baseIcon != null) {
214         return createLayeredIcon(this, baseIcon, elementFlags);
215       }
216     }
217
218     if (element instanceof PsiFile) {
219       PsiFile psiFile = (PsiFile)element;
220       VirtualFile vFile = psiFile.getVirtualFile();
221       Icon baseIcon = vFile != null ? IconUtil.getIcon(vFile, flags & ~ICON_FLAG_READ_STATUS, psiFile.getProject())
222                                     : psiFile.getFileType().getIcon();
223       return createLayeredIcon(this, baseIcon, elementFlags);
224     }
225
226     return null;
227   }
228
229   @NotNull
230   public static RowIcon createLayeredIcon(@NotNull Iconable instance, Icon icon, int flags) {
231     List<Icon> layersFromProviders = new SmartList<>();
232     for (IconLayerProvider provider : IconLayerProvider.EP_NAME.getExtensionList()) {
233       final Icon layerIcon = provider.getLayerIcon(instance, BitUtil.isSet(flags, FLAGS_LOCKED));
234       if (layerIcon != null) {
235         layersFromProviders.add(layerIcon);
236       }
237     }
238     if (flags != 0 || !layersFromProviders.isEmpty()) {
239       List<Icon> iconLayers = new SmartList<>();
240       for(IconLayer l: ourIconLayers) {
241         if (BitUtil.isSet(flags, l.flagMask)) {
242           iconLayers.add(l.icon);
243         }
244       }
245       iconLayers.addAll(layersFromProviders);
246       LayeredIcon layeredIcon = new LayeredIcon(1 + iconLayers.size());
247       layeredIcon.setIcon(icon, 0);
248       for (int i = 0; i < iconLayers.size(); i++) {
249         Icon icon1 = iconLayers.get(i);
250         layeredIcon.setIcon(icon1, i+1);
251       }
252       icon = layeredIcon;
253     }
254     RowIcon baseIcon = new RowIcon(2);
255     baseIcon.setIcon(icon, 0);
256     return baseIcon;
257   }
258
259   public static int transformFlags(PsiElement element, @IconFlags int _flags) {
260     int flags = BitUtil.clear(_flags, ICON_FLAG_READ_STATUS);
261     final boolean isLocked = BitUtil.isSet(_flags, ICON_FLAG_READ_STATUS) && !element.isWritable();
262     if (isLocked) flags |= FLAGS_LOCKED;
263     return flags;
264   }
265
266   private static class IconLayer {
267     private final int flagMask;
268     @NotNull
269     private final Icon icon;
270
271     private IconLayer(final int flagMask, @NotNull Icon icon) {
272       BitUtil.assertOneBitMask(flagMask);
273       this.flagMask = flagMask;
274       this.icon = icon;
275     }
276   }
277
278   private static final List<IconLayer> ourIconLayers = ContainerUtil.createLockFreeCopyOnWriteList();
279
280   public static void registerIconLayer(int flagMask, @NotNull Icon icon) {
281     for(IconLayer iconLayer: ourIconLayers) {
282       if (iconLayer.flagMask == flagMask) return;
283     }
284     ourIconLayers.add(new IconLayer(flagMask, icon));
285   }
286 }