Merge branch 'master' of git@git.labs.intellij.net:idea/community
[idea/community.git] / platform / lang-impl / src / com / intellij / ide / projectView / impl / nodes / AbstractPsiBasedNode.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.ide.projectView.impl.nodes;
18
19 import com.intellij.codeInsight.navigation.NavigationUtil;
20 import com.intellij.ide.projectView.PresentationData;
21 import com.intellij.ide.projectView.ProjectViewNode;
22 import com.intellij.ide.projectView.ProjectViewNodeDecorator;
23 import com.intellij.ide.projectView.ViewSettings;
24 import com.intellij.ide.util.treeView.AbstractTreeNode;
25 import com.intellij.ide.util.treeView.ValidateableNode;
26 import com.intellij.navigation.NavigationItem;
27 import com.intellij.openapi.diagnostic.Logger;
28 import com.intellij.openapi.editor.colors.CodeInsightColors;
29 import com.intellij.openapi.extensions.Extensions;
30 import com.intellij.openapi.module.Module;
31 import com.intellij.openapi.project.IndexNotReadyException;
32 import com.intellij.openapi.project.Project;
33 import com.intellij.openapi.util.Iconable;
34 import com.intellij.openapi.vcs.FileStatus;
35 import com.intellij.openapi.vcs.FileStatusManager;
36 import com.intellij.openapi.vfs.VirtualFile;
37 import com.intellij.psi.PsiDirectory;
38 import com.intellij.psi.PsiElement;
39 import com.intellij.psi.PsiFile;
40 import com.intellij.psi.util.PsiUtilBase;
41 import org.jetbrains.annotations.NotNull;
42 import org.jetbrains.annotations.Nullable;
43
44 import javax.swing.*;
45 import java.util.ArrayList;
46 import java.util.Collection;
47 import java.util.Collections;
48
49 /**
50  * Class for node descriptors based on PsiElements. Subclasses should define
51  * method that extract PsiElement from Value.
52  * @param <Value> Value of node descriptor
53  */
54 public abstract class AbstractPsiBasedNode<Value> extends ProjectViewNode<Value> implements ValidateableNode {
55   private static final Logger LOG = Logger.getInstance(AbstractPsiBasedNode.class.getName());
56
57   protected AbstractPsiBasedNode(final Project project,
58                                 final Value value,
59                                 final ViewSettings viewSettings) {
60     super(project, value, viewSettings);
61   }
62
63   @Nullable
64   protected abstract PsiElement extractPsiFromValue();
65   @Nullable
66   protected abstract Collection<AbstractTreeNode> getChildrenImpl();
67   protected abstract void updateImpl(final PresentationData data);
68
69   @NotNull
70   public final Collection<AbstractTreeNode> getChildren() {
71     final PsiElement psiElement = extractPsiFromValue();
72     if (psiElement == null) {
73       return new ArrayList<AbstractTreeNode>();
74     }
75     final boolean valid = psiElement.isValid();
76     if (!LOG.assertTrue(valid)) {
77       return Collections.emptyList();
78     }
79
80     final Collection<AbstractTreeNode> children = getChildrenImpl();
81     return children != null ? children : Collections.<AbstractTreeNode>emptyList();
82   }
83
84   public boolean isValid() {
85     final PsiElement psiElement = extractPsiFromValue();
86     return psiElement != null && psiElement.isValid();
87   }
88
89   protected boolean isMarkReadOnly() {
90     final AbstractTreeNode parent = getParent();
91     if (parent == null) {
92       return false;
93     }
94     if (parent instanceof AbstractPsiBasedNode) {
95       final PsiElement psiElement = ((AbstractPsiBasedNode)parent).extractPsiFromValue();
96       return psiElement instanceof PsiDirectory;
97     }
98
99     final Object parentValue = parent.getValue();
100     return parentValue instanceof PsiDirectory || parentValue instanceof Module;
101   }
102
103
104   public FileStatus getFileStatus() {
105     VirtualFile file = getVirtualFileForValue();
106     if (file == null) {
107       return FileStatus.NOT_CHANGED;
108     }
109     else {
110       return FileStatusManager.getInstance(getProject()).getStatus(file);
111     }
112   }
113
114   @Nullable
115   private VirtualFile getVirtualFileForValue() {
116     PsiElement psiElement = extractPsiFromValue();
117     if (psiElement == null) {
118       return null;
119     }
120     return PsiUtilBase.getVirtualFile(psiElement);
121   }
122
123   // Should be called in atomic action
124
125   public void update(final PresentationData data) {
126     if (!validate()) {
127       return;
128     }
129
130     final PsiElement value = extractPsiFromValue();
131     LOG.assertTrue(value.isValid());
132
133     int flags = getIconableFlags();
134
135     try {
136       Icon icon = value.getIcon(flags);
137       data.setClosedIcon(icon);
138       data.setOpenIcon(icon);
139     }
140     catch (IndexNotReadyException ignored) {
141     }
142     data.setPresentableText(myName);
143
144     try {
145       if (isDeprecated()) {
146         data.setAttributesKey(CodeInsightColors.DEPRECATED_ATTRIBUTES);
147       }
148     }
149     catch (IndexNotReadyException ignored) {
150     }
151     updateImpl(data);
152     for(ProjectViewNodeDecorator decorator: Extensions.getExtensions(ProjectViewNodeDecorator.EP_NAME, myProject)) {
153       decorator.decorate(this, data);
154     }
155
156     Iconable.LastComputedIcon.put(value, data.getIcon(false), flags);
157   }
158
159   protected int getIconableFlags() {
160     int flags = Iconable.ICON_FLAG_VISIBILITY;
161     if (isMarkReadOnly()) {
162       flags |= Iconable.ICON_FLAG_READ_STATUS;
163     }
164     return flags;
165   }
166
167   protected boolean isDeprecated() {
168     return false;
169   }
170
171   public boolean contains(@NotNull final VirtualFile file) {
172     final PsiElement psiElement = extractPsiFromValue();
173     if (psiElement == null || !psiElement.isValid()) {
174       return false;
175     }
176
177     final PsiFile containingFile = psiElement.getContainingFile();
178     if (containingFile == null) {
179       return false;
180     }
181     final VirtualFile valueFile = containingFile.getVirtualFile();
182     return valueFile != null && file.equals(valueFile);
183   }
184
185   @Nullable
186   public NavigationItem getNavigationItem() {
187     final PsiElement psiElement = extractPsiFromValue();
188     return (psiElement instanceof NavigationItem) ? (NavigationItem) psiElement : null;
189   }
190
191   public void navigate(boolean requestFocus) {
192     if (canNavigate()) {
193       if (requestFocus) {
194         NavigationUtil.activateFileWithPsiElement(extractPsiFromValue());
195       }
196       else {
197         getNavigationItem().navigate(requestFocus);
198       }
199     }
200   }
201
202   public boolean canNavigate() {
203     final NavigationItem item = getNavigationItem();
204     return item != null && item.canNavigate();
205   }
206
207   public boolean canNavigateToSource() {
208     final NavigationItem item = getNavigationItem();
209     return item != null && item.canNavigateToSource();
210   }
211
212   @Nullable
213   protected String calcTooltip() {
214     return null;
215   }
216
217   @Override
218   public boolean validate() {
219     final PsiElement psiElement = extractPsiFromValue();
220     if (psiElement == null || !psiElement.isValid()) {
221       setValue(null);
222     }
223
224     return getValue() != null;
225   }
226 }