IDEADEV-41116: exploded war artefact: add warning for dependent module
[fedora-idea.git] / java / idea-ui / src / com / intellij / openapi / roots / ui / configuration / projectRoot / daemon / ProjectStructureDaemonAnalyzer.java
blob53aa639815413db8809101b6ddd26b44db23324c
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.EventDispatcher;
11 import com.intellij.util.ui.update.MergingUpdateQueue;
12 import com.intellij.util.ui.update.Update;
13 import org.jetbrains.annotations.NotNull;
14 import org.jetbrains.annotations.Nullable;
16 import javax.swing.*;
17 import java.util.*;
18 import java.util.concurrent.atomic.AtomicBoolean;
20 /**
21 * @author nik
23 public class ProjectStructureDaemonAnalyzer implements Disposable {
24 private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.roots.ui.configuration.projectRoot.validation.ProjectStructureDaemonAnalyzer");
25 private Map<ProjectStructureElement, ProjectStructureProblemsHolderImpl> myProblemHolders = new HashMap<ProjectStructureElement, ProjectStructureProblemsHolderImpl>();
26 private MultiValuesMap<ProjectStructureElement, ProjectStructureElementUsage> mySourceElement2Usages = new MultiValuesMap<ProjectStructureElement, ProjectStructureElementUsage>();
27 private MultiValuesMap<ProjectStructureElement, ProjectStructureElementUsage> myContainingElement2Usages = new MultiValuesMap<ProjectStructureElement, ProjectStructureElementUsage>();
28 private Set<ProjectStructureElement> myElementWithNotCalculatedUsages = new HashSet<ProjectStructureElement>();
29 private MergingUpdateQueue myAnalyzerQueue;
30 private final EventDispatcher<ProjectStructureDaemonAnalyzerListener> myDispatcher = EventDispatcher.create(ProjectStructureDaemonAnalyzerListener.class);
31 private final AtomicBoolean myStopped = new AtomicBoolean(false);
33 public ProjectStructureDaemonAnalyzer(StructureConfigurableContext context) {
34 Disposer.register(context, this);
35 myAnalyzerQueue = new MergingUpdateQueue("Project Structure Daemon Analyzer", 300, false, null, this, null, false);
38 private void doUpdate(final ProjectStructureElement element, final boolean check, final boolean collectUsages) {
39 if (myStopped.get()) return;
41 if (check) {
42 doCheck(element);
44 if (collectUsages) {
45 doCollectUsages(element);
49 private void doCheck(final ProjectStructureElement element) {
50 final ProjectStructureProblemsHolderImpl problemsHolder = new ProjectStructureProblemsHolderImpl();
51 new ReadAction() {
52 protected void run(final Result result) {
53 if (myStopped.get()) return;
55 if (LOG.isDebugEnabled()) {
56 LOG.debug("checking " + element);
58 element.check(problemsHolder);
60 }.execute();
61 invokeLater(new Runnable() {
62 public void run() {
63 if (myStopped.get()) return;
65 if (LOG.isDebugEnabled()) {
66 LOG.debug("updating problems for " + element);
68 myProblemHolders.put(element, problemsHolder);
69 myDispatcher.getMulticaster().problemsChanged(element);
71 });
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 (myStopped.get()) return;
79 if (LOG.isDebugEnabled()) {
80 LOG.debug("collecting usages in " + element);
82 result.setResult(element.getUsagesInElement());
84 }.execute().getResultObject();
86 invokeLater(new Runnable() {
87 public void run() {
88 if (myStopped.get()) return;
90 if (LOG.isDebugEnabled()) {
91 LOG.debug("updating usages for " + element);
93 updateUsages(element, usages);
94 myDispatcher.getMulticaster().usagesCollected(element);
96 });
99 private void updateUsages(ProjectStructureElement element, List<ProjectStructureElementUsage> usages) {
100 removeUsagesInElement(element);
101 for (ProjectStructureElementUsage usage : usages) {
102 addUsage(usage);
104 myElementWithNotCalculatedUsages.remove(element);
107 private static void invokeLater(Runnable runnable) {
108 SwingUtilities.invokeLater(runnable);
111 public void queueUpdate(@NotNull final ProjectStructureElement element) {
112 queueUpdate(element, true, true);
115 public void queueUpdate(@NotNull final ProjectStructureElement element, final boolean check, final boolean collectUsages) {
116 if (LOG.isDebugEnabled()) {
117 LOG.debug("start " + (check ? "checking " : "") + (collectUsages ? "collecting usages " : "") + "for " + element);
119 if (collectUsages) {
120 myElementWithNotCalculatedUsages.add(element);
122 myAnalyzerQueue.queue(new AnalyzeElementUpdate(element, check, collectUsages));
125 public void removeElement(ProjectStructureElement element) {
126 myElementWithNotCalculatedUsages.remove(element);
127 myProblemHolders.remove(element);
128 final Collection<ProjectStructureElementUsage> usages = mySourceElement2Usages.removeAll(element);
129 if (usages != null) {
130 for (ProjectStructureElementUsage usage : usages) {
131 myProblemHolders.remove(usage.getContainingElement());
134 removeUsagesInElement(element);
135 myDispatcher.getMulticaster().problemsChanged(element);
138 public boolean isUnused(ProjectStructureElement element) {
139 if (!element.highlightIfUnused()) {
140 return false;
142 if (!myElementWithNotCalculatedUsages.isEmpty()) {
143 return false;
145 final Collection<ProjectStructureElementUsage> usages = mySourceElement2Usages.get(element);
146 return usages == null || usages.isEmpty();
149 private void removeUsagesInElement(ProjectStructureElement element) {
150 final Collection<ProjectStructureElementUsage> usages = myContainingElement2Usages.removeAll(element);
151 if (usages != null) {
152 for (ProjectStructureElementUsage usage : usages) {
153 mySourceElement2Usages.remove(usage.getSourceElement(), usage);
158 private void addUsage(@NotNull ProjectStructureElementUsage usage) {
159 mySourceElement2Usages.put(usage.getSourceElement(), usage);
160 myContainingElement2Usages.put(usage.getContainingElement(), usage);
163 public void stop() {
164 LOG.debug("analyzer stopped");
165 myStopped.set(true);
166 myAnalyzerQueue.cancelAllUpdates();
167 clearCaches();
168 myAnalyzerQueue.deactivate();
171 public void clearCaches() {
172 LOG.debug("clear caches");
173 myProblemHolders.clear();
176 public void clearAllProblems() {
177 myProblemHolders.clear();
178 myDispatcher.getMulticaster().allProblemsChanged();
181 public void dispose() {
182 myStopped.set(true);
183 myAnalyzerQueue.cancelAllUpdates();
186 @Nullable
187 public ProjectStructureProblemsHolderImpl getProblemsHolder(ProjectStructureElement element) {
188 return myProblemHolders.get(element);
191 public Collection<ProjectStructureElementUsage> getUsages(ProjectStructureElement selected) {
192 ProjectStructureElement[] elements = myElementWithNotCalculatedUsages.toArray(new ProjectStructureElement[myElementWithNotCalculatedUsages.size()]);
193 for (ProjectStructureElement element : elements) {
194 updateUsages(element, element.getUsagesInElement());
196 final Collection<ProjectStructureElementUsage> usages = mySourceElement2Usages.get(selected);
197 return usages != null ? usages : Collections.<ProjectStructureElementUsage>emptyList();
200 public void addListener(ProjectStructureDaemonAnalyzerListener listener) {
201 LOG.debug("listener added " + listener);
202 myDispatcher.addListener(listener);
205 public void reset() {
206 LOG.debug("analyzer started");
207 myAnalyzerQueue.activate();
208 myAnalyzerQueue.queue(new Update("reset") {
209 public void run() {
210 myStopped.set(false);
215 public void clear() {
216 mySourceElement2Usages.clear();
217 myContainingElement2Usages.clear();
218 myElementWithNotCalculatedUsages.clear();
221 private class AnalyzeElementUpdate extends Update {
222 private final ProjectStructureElement myElement;
223 private final boolean myCheck;
224 private final boolean myCollectUsages;
225 private final Object[] myEqualityObjects;
227 public AnalyzeElementUpdate(ProjectStructureElement element, boolean check, boolean collectUsages) {
228 super(element);
229 myElement = element;
230 myCheck = check;
231 myCollectUsages = collectUsages;
232 myEqualityObjects = new Object[]{myElement, myCheck, myCollectUsages};
235 @Override
236 public boolean canEat(Update update) {
237 if (!(update instanceof AnalyzeElementUpdate)) return false;
238 final AnalyzeElementUpdate other = (AnalyzeElementUpdate)update;
239 return myElement.equals(other.myElement) && (!other.myCheck || myCheck) && (!other.myCollectUsages || myCollectUsages);
242 @Override
243 public Object[] getEqualityObjects() {
244 return myEqualityObjects;
247 public void run() {
248 doUpdate(myElement, myCheck, myCollectUsages);