Merge branch 'master' of git@git.labs.intellij.net:idea/community
[idea/community.git] / platform / lang-api / src / com / intellij / psi / impl / ElementBase.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
17 package com.intellij.psi.impl;
18
19 import com.intellij.navigation.ItemPresentation;
20 import com.intellij.openapi.diagnostic.Logger;
21 import com.intellij.openapi.util.IconLoader;
22 import com.intellij.openapi.util.Iconable;
23 import com.intellij.openapi.util.UserDataHolderBase;
24 import com.intellij.openapi.vfs.VirtualFile;
25 import com.intellij.openapi.progress.ProcessCanceledException;
26 import com.intellij.openapi.project.IndexNotReadyException;
27 import com.intellij.psi.PsiElement;
28 import com.intellij.psi.PsiFile;
29 import com.intellij.ui.IconDeferrer;
30 import com.intellij.ui.LayeredIcon;
31 import com.intellij.ui.RowIcon;
32 import com.intellij.util.*;
33 import com.intellij.util.ui.EmptyIcon;
34 import com.intellij.util.ui.update.ComparableObject;
35 import org.jetbrains.annotations.Nullable;
36
37 import javax.swing.*;
38 import java.util.ArrayList;
39 import java.util.List;
40
41 public abstract class ElementBase extends UserDataHolderBase implements Iconable {
42   private static final Logger LOG = Logger.getInstance("#com.intellij.psi.impl.ElementBase");
43
44   public static final int FLAGS_LOCKED = 0x800;
45   private Icon myBaseIcon;
46
47   @Nullable
48   public Icon getIcon(int flags) {
49     if (!(this instanceof PsiElement)) return null;
50
51     try {
52       Icon icon = computeIcon(flags);
53       Iconable.LastComputedIcon.put(this, icon, flags);
54       return icon;
55     }
56     catch (ProcessCanceledException e) {
57       throw e;
58     }
59     catch (IndexNotReadyException e) {
60       throw e;
61     }
62     catch(Exception e) {
63       LOG.error(e);
64         return null;
65       }
66     }
67
68   private Icon computeIcon(final int flags) {
69     PsiElement psiElement = (PsiElement)this;
70     Icon baseIcon = LastComputedIcon.get(psiElement, flags);
71     if (baseIcon == null) {
72       if (myBaseIcon == null) {
73         myBaseIcon = computeBaseIcon();
74       }
75       baseIcon = myBaseIcon;
76     }
77
78     return IconDeferrer.getInstance().defer(baseIcon, new ElementIconRequest(psiElement,flags), new Function<ElementIconRequest, Icon>() {
79       public Icon fun(ElementIconRequest request) {
80         if (!request.getElement().isValid()) return null;
81
82         final Icon providersIcon = PsiIconUtil.getProvidersIcon(request.getElement(), request.getFlags());
83         if (providersIcon != null) {
84           return providersIcon instanceof RowIcon ? (RowIcon)providersIcon : createLayeredIcon(providersIcon, request.getFlags());
85         }
86         return getElementIcon(request.getFlags());
87       }
88     });
89   }
90
91   protected Icon computeBaseIcon() {
92     return new EmptyIcon(IconLoader.getIcon("/nodes/class.png"));
93   }
94
95   public static class ElementIconRequest extends ComparableObject.Impl {
96     public ElementIconRequest(PsiElement element, int flags) {
97       super(new Object[] {element, flags});
98     }
99
100     public PsiElement getElement() {
101       return (PsiElement)getEqualityObjects()[0];
102     }
103
104     public int getFlags() {
105       return (Integer)getEqualityObjects()[1];
106     }
107   }
108
109   protected Icon getElementIcon(final int flags) {
110     final PsiElement element = (PsiElement)this;
111     RowIcon baseIcon;
112     final boolean isLocked = (flags & ICON_FLAG_READ_STATUS) != 0 && !element.isWritable();
113     int elementFlags = isLocked ? FLAGS_LOCKED : 0;
114     if (element instanceof ItemPresentation && ((ItemPresentation)element).getIcon(false) != null) {
115         baseIcon = createLayeredIcon(((ItemPresentation)element).getIcon(false), elementFlags);
116     }
117     else if (element instanceof PsiFile) {
118       PsiFile file = (PsiFile)element;
119
120       VirtualFile virtualFile = file.getVirtualFile();
121       final Icon fileTypeIcon;
122       if (virtualFile == null) {
123         fileTypeIcon = file.getFileType().getIcon();
124       }
125       else {
126         fileTypeIcon = IconUtil.getIcon(virtualFile, flags & ~ICON_FLAG_READ_STATUS, file.getProject());
127       }
128       return createLayeredIcon(fileTypeIcon, elementFlags);
129     }
130     else {
131       return null;
132     }
133     return baseIcon;
134   }
135
136   public static RowIcon createLayeredIcon(Icon icon, int flags) {
137     if (flags != 0) {
138       List<Icon> iconLayers = new SmartList<Icon>();
139       for(IconLayer l: ourIconLayers) {
140         if ((flags & l.flagMask) != 0) {
141           iconLayers.add(l.icon);
142         }
143       }
144       LayeredIcon layeredIcon = new LayeredIcon(1 + iconLayers.size());
145       layeredIcon.setIcon(icon, 0);
146       for (int i = 0; i < iconLayers.size(); i++) {
147         Icon icon1 = iconLayers.get(i);
148         layeredIcon.setIcon(icon1, i+1);
149       }
150       icon = layeredIcon;
151     }
152     RowIcon baseIcon = new RowIcon(2);
153     baseIcon.setIcon(icon, 0);
154     return baseIcon;
155   }
156
157   public static int transformFlags(PsiElement element, int _flags) {
158     int flags = 0;
159     final boolean isLocked = (_flags & ICON_FLAG_READ_STATUS) != 0 && !element.isWritable();
160     if (isLocked) flags |= FLAGS_LOCKED;
161     return flags;
162   }
163
164   private static class IconLayer {
165     int flagMask;
166     Icon icon;
167
168     IconLayer(final int flagMask, final Icon icon) {
169       this.flagMask = flagMask;
170       this.icon = icon;
171     }
172   }
173
174   private static final List<IconLayer> ourIconLayers = new ArrayList<IconLayer>();
175
176   public static void registerIconLayer(int flagMask, Icon icon) {
177     for(IconLayer iconLayer: ourIconLayers) {
178       if (iconLayer.flagMask == flagMask) return;
179     }
180     ourIconLayers.add(new IconLayer(flagMask, icon));
181   }
182
183   static {
184     registerIconLayer(FLAGS_LOCKED, Icons.LOCKED_ICON);
185   }
186 }