find usages & validation in project structure reworked
[fedora-idea.git] / java / idea-ui / src / com / intellij / openapi / roots / ui / configuration / projectRoot / daemon / ProjectStructureDaemonAnalyzer.java
blob04ff2888d7aeb1e40e2914c2832dcf63564e3fc3
1 package com.intellij.openapi.roots.ui.configuration.projectRoot.daemon;
3 import com.intellij.openapi.Disposable;
4 import com.intellij.openapi.application.ReadAction;
5 import com.intellij.openapi.application.Result;
6 import com.intellij.openapi.diagnostic.Logger;
7 import com.intellij.openapi.roots.ui.configuration.projectRoot.StructureConfigurableContext;
8 import com.intellij.openapi.util.Disposer;
9 import com.intellij.openapi.util.MultiValuesMap;
10 import com.intellij.util.ui.update.MergingUpdateQueue;
11 import com.intellij.util.ui.update.Update;
12 import org.jetbrains.annotations.NotNull;
14 import javax.swing.*;
15 import java.util.*;
17 /**
18 * @author nik
20 public class ProjectStructureDaemonAnalyzer implements Disposable {
21 private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.roots.ui.configuration.projectRoot.validation.ProjectStructureDaemonAnalyzer");
22 private Map<ProjectStructureElement, ProjectStructureProblemsHolder> myProblemHolders = new HashMap<ProjectStructureElement, ProjectStructureProblemsHolder>();
23 private MultiValuesMap<ProjectStructureElement, ProjectStructureElementUsage> mySourceElement2Usages = new MultiValuesMap<ProjectStructureElement, ProjectStructureElementUsage>();
24 private MultiValuesMap<ProjectStructureElement, ProjectStructureElementUsage> myContainingElement2Usages = new MultiValuesMap<ProjectStructureElement, ProjectStructureElementUsage>();
25 private Set<ProjectStructureElement> myElementWithNotCalculatedUsages = new HashSet<ProjectStructureElement>();
26 private MergingUpdateQueue myAnalyzerQueue;
27 private List<Runnable> myListeners = new ArrayList<Runnable>();
28 private boolean myDisposed;
30 public ProjectStructureDaemonAnalyzer(StructureConfigurableContext context) {
31 Disposer.register(context, this);
32 myAnalyzerQueue = new MergingUpdateQueue("Project Structure Daemon Analyzer", 300, false, null, this, null, false);
35 private void doUpdate(final ProjectStructureElement element, final boolean check, final boolean collectUsages) {
36 if (myDisposed) return;
38 if (check) {
39 doCheck(element);
41 if (collectUsages) {
42 doCollectUsages(element);
46 private void doCheck(final ProjectStructureElement element) {
47 final ProjectStructureProblemsHolder problemsHolder = new ProjectStructureProblemsHolder();
48 new ReadAction() {
49 protected void run(final Result result) {
50 if (LOG.isDebugEnabled()) {
51 LOG.debug("checking " + element);
53 element.check(problemsHolder);
55 }.execute();
56 invokeLater(new Runnable() {
57 public void run() {
58 if (LOG.isDebugEnabled()) {
59 LOG.debug("updating problems for " + element);
61 myProblemHolders.put(element, problemsHolder);
62 fireProblemsUpdated();
64 });
67 private void fireProblemsUpdated() {
68 Runnable[] listeners = myListeners.toArray(new Runnable[myListeners.size()]);
69 for (Runnable listener : listeners) {
70 listener.run();
74 private void doCollectUsages(final ProjectStructureElement element) {
75 final List<ProjectStructureElementUsage> usages = new ReadAction<List<ProjectStructureElementUsage>>() {
76 protected void run(final Result<List<ProjectStructureElementUsage>> result) {
77 if (LOG.isDebugEnabled()) {
78 LOG.debug("collecting usages in " + element);
80 result.setResult(element.getUsagesInElement());
82 }.execute().getResultObject();
84 invokeLater(new Runnable() {
85 public void run() {
86 if (LOG.isDebugEnabled()) {
87 LOG.debug("updating usages for " + element);
89 updateUsages(element, usages);
90 fireProblemsUpdated();
92 });
95 private void updateUsages(ProjectStructureElement element, List<ProjectStructureElementUsage> usages) {
96 removeUsagesInElement(element);
97 for (ProjectStructureElementUsage usage : usages) {
98 addUsage(usage);
100 myElementWithNotCalculatedUsages.remove(element);
103 private static void invokeLater(Runnable runnable) {
104 SwingUtilities.invokeLater(runnable);
107 public void queueUpdate(@NotNull final ProjectStructureElement element) {
108 queueUpdate(element, true, true);
111 public void queueUpdate(@NotNull final ProjectStructureElement element, final boolean check, final boolean collectUsages) {
112 if (LOG.isDebugEnabled()) {
113 LOG.debug("start " + (check ? "checking " : "") + (collectUsages ? "collecting usages " : "") + "for " + element);
115 if (collectUsages) {
116 myElementWithNotCalculatedUsages.add(element);
118 myAnalyzerQueue.queue(new AnalyzeElementUpdate(element, check, collectUsages));
121 public void removeElement(ProjectStructureElement element) {
122 myElementWithNotCalculatedUsages.remove(element);
123 myProblemHolders.remove(element);
124 final Collection<ProjectStructureElementUsage> usages = mySourceElement2Usages.removeAll(element);
125 if (usages != null) {
126 for (ProjectStructureElementUsage usage : usages) {
127 myProblemHolders.remove(usage.getContainingElement());
130 removeUsagesInElement(element);
131 fireProblemsUpdated();
134 public boolean isUnused(ProjectStructureElement element) {
135 if (!element.highlightIfUnused()) {
136 return false;
138 if (!myElementWithNotCalculatedUsages.isEmpty()) {
139 return false;
141 final Collection<ProjectStructureElementUsage> usages = mySourceElement2Usages.get(element);
142 return usages == null || usages.isEmpty();
145 private void removeUsagesInElement(ProjectStructureElement element) {
146 final Collection<ProjectStructureElementUsage> usages = myContainingElement2Usages.removeAll(element);
147 if (usages != null) {
148 for (ProjectStructureElementUsage usage : usages) {
149 mySourceElement2Usages.remove(usage.getSourceElement(), usage);
154 private void addUsage(@NotNull ProjectStructureElementUsage usage) {
155 mySourceElement2Usages.put(usage.getSourceElement(), usage);
156 myContainingElement2Usages.put(usage.getContainingElement(), usage);
159 public void stop() {
160 LOG.debug("analyzer stopped");
161 myAnalyzerQueue.cancelAllUpdates();
162 clearCaches();
163 myAnalyzerQueue.deactivate();
166 public void clearCaches() {
167 LOG.debug("clear caches");
168 myProblemHolders.clear();
169 mySourceElement2Usages.clear();
170 myContainingElement2Usages.clear();
171 myElementWithNotCalculatedUsages.clear();
174 public void clearAllProblems() {
175 myProblemHolders.clear();
176 fireProblemsUpdated();
179 public void dispose() {
180 myDisposed = true;
181 myAnalyzerQueue.cancelAllUpdates();
184 public ProjectStructureProblemsHolder getProblemsHolder(ProjectStructureElement element) {
185 return myProblemHolders.get(element);
188 public Collection<ProjectStructureElementUsage> getUsages(ProjectStructureElement selected) {
189 ProjectStructureElement[] elements = myElementWithNotCalculatedUsages.toArray(new ProjectStructureElement[myElementWithNotCalculatedUsages.size()]);
190 for (ProjectStructureElement element : elements) {
191 updateUsages(element, element.getUsagesInElement());
193 fireProblemsUpdated();
194 final Collection<ProjectStructureElementUsage> usages = mySourceElement2Usages.get(selected);
195 return usages != null ? usages : Collections.<ProjectStructureElementUsage>emptyList();
198 public void addListener(Runnable runnable) {
199 LOG.debug("listener added " + runnable);
200 myListeners.add(runnable);
203 public void reset() {
204 LOG.debug("analyzer started");
205 myAnalyzerQueue.activate();
206 myAnalyzerQueue.queue(new Update("reset") {
207 public void run() {
208 myDisposed = false;
213 private class AnalyzeElementUpdate extends Update {
214 private final ProjectStructureElement myElement;
215 private final boolean myCheck;
216 private final boolean myCollectUsages;
217 private final Object[] myEqualityObjects;
219 public AnalyzeElementUpdate(ProjectStructureElement element, boolean check, boolean collectUsages) {
220 super(element);
221 myElement = element;
222 myCheck = check;
223 myCollectUsages = collectUsages;
224 myEqualityObjects = new Object[]{myElement, myCheck, myCollectUsages};
227 @Override
228 public boolean canEat(Update update) {
229 if (!(update instanceof AnalyzeElementUpdate)) return false;
230 final AnalyzeElementUpdate other = (AnalyzeElementUpdate)update;
231 return myElement.equals(other.myElement) && (!other.myCheck || myCheck) && (!other.myCollectUsages || myCollectUsages);
234 @Override
235 public Object[] getEqualityObjects() {
236 return myEqualityObjects;
239 public void run() {
240 doUpdate(myElement, myCheck, myCollectUsages);