restore DataFlowRunner instruction logging and commented printlns
[idea/community.git] / platform / analysis-impl / src / com / intellij / codeInspection / ex / ToolsImpl.java
1 /*
2  * Copyright 2000-2016 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 /*
18  * User: anna
19  * Date: 15-Apr-2009
20  */
21 package com.intellij.codeInspection.ex;
22
23 import com.intellij.codeHighlighting.HighlightDisplayLevel;
24 import com.intellij.codeInsight.daemon.impl.SeverityRegistrar;
25 import com.intellij.codeInspection.InspectionProfile;
26 import com.intellij.openapi.project.Project;
27 import com.intellij.openapi.util.Comparing;
28 import com.intellij.openapi.util.InvalidDataException;
29 import com.intellij.openapi.util.WriteExternalException;
30 import com.intellij.packageDependencies.DependencyValidationManager;
31 import com.intellij.profile.ProfileEx;
32 import com.intellij.profile.ProfileManager;
33 import com.intellij.profile.codeInspection.SeverityProvider;
34 import com.intellij.psi.PsiElement;
35 import com.intellij.psi.PsiFile;
36 import com.intellij.psi.search.scope.packageSet.*;
37 import com.intellij.psi.util.PsiUtilCore;
38 import org.jdom.Element;
39 import org.jetbrains.annotations.NonNls;
40 import org.jetbrains.annotations.NotNull;
41 import org.jetbrains.annotations.Nullable;
42 import org.jetbrains.annotations.TestOnly;
43
44 import java.util.ArrayList;
45 import java.util.Collections;
46 import java.util.List;
47 import java.util.Map;
48
49 public class ToolsImpl implements Tools {
50   @NonNls static final String ENABLED_BY_DEFAULT_ATTRIBUTE = "enabled_by_default";
51   @NonNls static final String ENABLED_ATTRIBUTE = "enabled";
52   @NonNls static final String LEVEL_ATTRIBUTE = "level";
53
54   private final String myShortName;
55   private final ScopeToolState myDefaultState;
56   private List<ScopeToolState> myTools;
57   private boolean myEnabled;
58
59   public ToolsImpl(@NotNull InspectionToolWrapper toolWrapper, @NotNull HighlightDisplayLevel level, boolean enabled, boolean enabledByDefault) {
60     myShortName = toolWrapper.getShortName();
61     myDefaultState = new ScopeToolState(CustomScopesProviderEx.getAllScope(), toolWrapper, enabledByDefault, level);
62     myTools = null;
63     myEnabled = enabled;
64   }
65
66   @TestOnly
67   public ToolsImpl(@NotNull InspectionToolWrapper toolWrapper, @NotNull HighlightDisplayLevel level, boolean enabled) {
68     this(toolWrapper, level, enabled, enabled);
69   }
70
71   @NotNull
72   public ScopeToolState addTool(@NotNull NamedScope scope, @NotNull InspectionToolWrapper toolWrapper, boolean enabled, @NotNull HighlightDisplayLevel level) {
73     return insertTool(scope, toolWrapper, enabled, level, myTools != null ? myTools.size() : 0);
74   }
75
76   @NotNull
77   public ScopeToolState prependTool(@NotNull NamedScope scope, @NotNull InspectionToolWrapper toolWrapper, boolean enabled, @NotNull HighlightDisplayLevel level) {
78     return insertTool(scope, toolWrapper, enabled, level, 0);
79   }
80
81   public ScopeToolState addTool(@NotNull String scopeName, @NotNull InspectionToolWrapper toolWrapper, boolean enabled, @NotNull HighlightDisplayLevel level) {
82     return insertTool(new ScopeToolState(scopeName, toolWrapper, enabled, level), myTools != null ? myTools.size() : 0);
83   }
84
85   @NotNull
86   private ScopeToolState insertTool(@NotNull NamedScope scope, @NotNull InspectionToolWrapper toolWrapper, boolean enabled, @NotNull HighlightDisplayLevel level, int idx) {
87     return insertTool(new ScopeToolState(scope, toolWrapper, enabled, level), idx);
88   }
89
90   @NotNull
91   private ScopeToolState insertTool(@NotNull final ScopeToolState scopeToolState, final int idx) {
92     if (myTools == null) {
93       myTools = new ArrayList<>();
94       if (scopeToolState.isEnabled()) {
95         setEnabled(true);
96       }
97     }
98     myTools.add(idx, scopeToolState);
99     return scopeToolState;
100   }
101
102   @NotNull
103   @Override
104   public InspectionToolWrapper getInspectionTool(@Nullable PsiElement element) {
105     if (myTools != null) {
106       final PsiFile containingFile = element == null ? null : element.getContainingFile();
107       final Project project = containingFile == null ? null : containingFile.getProject();
108       for (ScopeToolState state : myTools) {
109         if (element == null) {
110           return state.getTool();
111         }
112         NamedScope scope = state.getScope(project);
113         if (scope != null) {
114           final PackageSet packageSet = scope.getValue();
115           if (packageSet != null) {
116             if (containingFile != null && packageSet.contains(containingFile, DependencyValidationManager.getInstance(project))) {
117               return state.getTool();
118             }
119           }
120         }
121       }
122
123     }
124     return myDefaultState.getTool();
125   }
126
127   @NotNull
128   @Override
129   public String getShortName() {
130     return myShortName;
131   }
132
133   public void cleanupTools(@NotNull Project project) {
134     for (ScopeToolState state : getTools()) {
135       state.getTool().cleanup(project);
136     }
137   }
138
139   public void writeExternal(@NotNull Element inspectionElement) throws WriteExternalException {
140     if (myTools != null) {
141       for (ScopeToolState state : myTools) {
142         final Element scopeElement = new Element("scope");
143         scopeElement.setAttribute("name", state.getScopeName());
144         scopeElement.setAttribute(LEVEL_ATTRIBUTE, state.getLevel().getName());
145         scopeElement.setAttribute(ENABLED_ATTRIBUTE, Boolean.toString(state.isEnabled()));
146         InspectionToolWrapper toolWrapper = state.getTool();
147         if (toolWrapper.isInitialized()) {
148           toolWrapper.getTool().writeSettings(scopeElement);
149         }
150         inspectionElement.addContent(scopeElement);
151       }
152     }
153     inspectionElement.setAttribute(ENABLED_ATTRIBUTE, Boolean.toString(isEnabled()));
154     inspectionElement.setAttribute(LEVEL_ATTRIBUTE, getLevel().getName());
155     inspectionElement.setAttribute(ENABLED_BY_DEFAULT_ATTRIBUTE, Boolean.toString(myDefaultState.isEnabled()));
156     InspectionToolWrapper toolWrapper = myDefaultState.getTool();
157     if (toolWrapper.isInitialized()) {
158       toolWrapper.getTool().writeSettings(inspectionElement);
159     }
160   }
161
162   void readExternal(@NotNull Element toolElement, @NotNull InspectionProfile profile, Map<String, List<String>> dependencies) throws InvalidDataException {
163     final String levelName = toolElement.getAttributeValue(LEVEL_ATTRIBUTE);
164     final ProfileManager profileManager = profile.getProfileManager();
165     final SeverityRegistrar registrar = ((SeverityProvider)profileManager).getOwnSeverityRegistrar();
166     HighlightDisplayLevel level = levelName != null ? HighlightDisplayLevel.find(registrar.getSeverity(levelName)) : null;
167     if (level == null) {
168       level = HighlightDisplayLevel.WARNING;
169     }
170     myDefaultState.setLevel(level);
171     final String enabled = toolElement.getAttributeValue(ENABLED_ATTRIBUTE);
172     final boolean isEnabled = enabled != null && Boolean.parseBoolean(enabled);
173
174     final String enabledTool = toolElement.getAttributeValue(ENABLED_BY_DEFAULT_ATTRIBUTE);
175     myDefaultState.setEnabled(enabledTool != null ? Boolean.parseBoolean(enabledTool) : isEnabled);
176     final InspectionToolWrapper toolWrapper = myDefaultState.getTool();
177
178     final List<Element> scopeElements = toolElement.getChildren(ProfileEx.SCOPE);
179     final List<String> scopeNames = new ArrayList<>();
180     for (Element scopeElement : scopeElements) {
181       final String scopeName = scopeElement.getAttributeValue(ProfileEx.NAME);
182       if (scopeName == null) {
183         continue;
184       }
185       final NamedScopesHolder scopesHolder = profileManager.getScopesManager();
186       NamedScope namedScope = null;
187       if (scopesHolder != null) {
188         namedScope = scopesHolder.getScope(scopeName);
189       }
190       final String errorLevel = scopeElement.getAttributeValue(LEVEL_ATTRIBUTE);
191       final String enabledInScope = scopeElement.getAttributeValue(ENABLED_ATTRIBUTE);
192       final InspectionToolWrapper copyToolWrapper = toolWrapper.createCopy();
193     // check if unknown children exists
194       if (scopeElement.getAttributes().size() > 3 || !scopeElement.getChildren().isEmpty()) {
195         copyToolWrapper.getTool().readSettings(scopeElement);
196       }
197       HighlightDisplayLevel scopeLevel = errorLevel != null ?
198                                          HighlightDisplayLevel.find(registrar.getSeverity(errorLevel)) : null;
199       if (scopeLevel == null) {
200         scopeLevel = level;
201       }
202       if (namedScope != null) {
203         addTool(namedScope, copyToolWrapper, enabledInScope != null && Boolean.parseBoolean(enabledInScope), scopeLevel);
204       }
205       else {
206         addTool(scopeName, copyToolWrapper, enabledInScope != null && Boolean.parseBoolean(enabledInScope), scopeLevel);
207       }
208
209       scopeNames.add(scopeName);
210     }
211
212     for (int i = 0; i < scopeNames.size(); i++) {
213       String scopeName = scopeNames.get(i);
214       List<String> order = dependencies.get(scopeName);
215       if (order == null) {
216         order = new ArrayList<>();
217         dependencies.put(scopeName, order);
218       }
219       for (int j = i + 1; j < scopeNames.size(); j++) {
220         order.add(scopeNames.get(j));
221       }
222     }
223
224     // check if unknown children exists
225     if (toolElement.getAttributes().size() > 4 || toolElement.getChildren().size() > scopeElements.size()) {
226       toolWrapper.getTool().readSettings(toolElement);
227     }
228
229     myEnabled = isEnabled;
230   }
231
232   @NotNull
233   @Override
234   public InspectionToolWrapper getTool() {
235     if (myTools == null) return myDefaultState.getTool();
236     return myTools.iterator().next().getTool();
237   }
238
239   @Override
240   @NotNull
241   public List<ScopeToolState> getTools() {
242     if (myTools == null) {
243       return Collections.singletonList(myDefaultState);
244     }
245
246     List<ScopeToolState> result = new ArrayList<>(myTools);
247     result.add(myDefaultState);
248     return result;
249   }
250
251   @Override
252   public void collectTools(@NotNull List<ScopeToolState> result) {
253     if (myTools != null) {
254       result.addAll(myTools);
255     }
256     result.add(myDefaultState);
257   }
258
259   @Override
260   @NotNull
261   public ScopeToolState getDefaultState() {
262     return myDefaultState;
263   }
264
265   public void removeScope(int scopeIdx) {
266     if (myTools != null && scopeIdx >= 0 && myTools.size() > scopeIdx) {
267       myTools.remove(scopeIdx);
268       checkToolsIsEmpty();
269     }
270   }
271
272   public void removeScope(@NotNull final String scopeName) {
273     if (myTools != null) {
274       for (ScopeToolState tool : myTools) {
275         if (scopeName.equals(tool.getScopeName())) {
276           myTools.remove(tool);
277           break;
278         }
279       }
280       checkToolsIsEmpty();
281     }
282   }
283
284   private void checkToolsIsEmpty() {
285     if (myTools.isEmpty()) {
286       myTools = null;
287       setEnabled(myDefaultState.isEnabled());
288     }
289   }
290
291   public void removeAllScopes() {
292     myTools = null;
293   }
294
295   public void setScope(int idx, NamedScope namedScope) {
296     if (myTools != null && myTools.size() > idx && idx >= 0) {
297       final ScopeToolState scopeToolState = myTools.get(idx);
298       InspectionToolWrapper toolWrapper = scopeToolState.getTool();
299       myTools.remove(idx);
300       myTools.add(idx, new ScopeToolState(namedScope, toolWrapper, scopeToolState.isEnabled(), scopeToolState.getLevel()));
301     }
302   }
303
304   public void moveScope(int idx, int dir) {
305     if (myTools != null && idx >= 0 && idx < myTools.size() && idx + dir >= 0 && idx + dir < myTools.size()) {
306       final ScopeToolState state = myTools.get(idx);
307       myTools.set(idx, myTools.get(idx + dir));
308       myTools.set(idx + dir, state);
309     }
310   }
311
312   public boolean isEnabled(NamedScope namedScope, Project project) {
313     if (!myEnabled) return false;
314     if (namedScope != null && myTools != null) {
315       for (ScopeToolState state : myTools) {
316         if (Comparing.equal(namedScope, state.getScope(project))) return state.isEnabled();
317       }
318     }
319     return myDefaultState.isEnabled();
320   }
321
322   public HighlightDisplayLevel getLevel(PsiElement element) {
323     if (myTools == null || element == null) return myDefaultState.getLevel();
324     final Project project = element.getProject();
325     final DependencyValidationManager manager = DependencyValidationManager.getInstance(project);
326     for (ScopeToolState state : myTools) {
327       final NamedScope scope = state.getScope(project);
328       final PackageSet set = scope != null ? scope.getValue() : null;
329       if (set != null && set.contains(element.getContainingFile(), manager)) {
330         return state.getLevel();
331       }
332     }
333     return myDefaultState.getLevel();
334   }
335
336   public HighlightDisplayLevel getLevel() {
337     return myDefaultState.getLevel();
338   }
339
340   @Override
341   public boolean isEnabled() {
342     return myEnabled;
343   }
344
345
346   @Override
347   public boolean isEnabled(PsiElement element) {
348     if (!myEnabled) return false;
349     if (myTools == null || element == null) return myDefaultState.isEnabled();
350     final Project project = element.getProject();
351     final DependencyValidationManager manager = DependencyValidationManager.getInstance(project);
352     for (ScopeToolState state : myTools) {
353       final NamedScope scope = state.getScope(project);
354       if (scope != null) {
355         final PackageSet set = scope.getValue();
356         if (set != null && set.contains(element.getContainingFile(), manager)) {
357           return state.isEnabled();
358         }
359       }
360     }
361     return myDefaultState.isEnabled();
362   }
363
364   @Nullable
365   @Override
366   public InspectionToolWrapper getEnabledTool(@Nullable PsiElement element, boolean includeDoNotShow) {
367     if (!myEnabled) return null;
368     if (myTools != null && element != null) {
369       final Project project = element.getProject();
370       final DependencyValidationManager manager = DependencyValidationManager.getInstance(project);
371       for (ScopeToolState state : myTools) {
372         final NamedScope scope = state.getScope(project);
373         if (scope != null) {
374           final PackageSet set = scope.getValue();
375           if (set != null && set.contains(element.getContainingFile(), manager)) {
376             return state.isEnabled() && (includeDoNotShow || !HighlightDisplayLevel.DO_NOT_SHOW.equals(state.getLevel())) ? state.getTool() : null;
377           }
378         }
379       }
380     }
381     return myDefaultState.isEnabled() && (includeDoNotShow || !HighlightDisplayLevel.DO_NOT_SHOW.equals(myDefaultState.getLevel())) ? myDefaultState.getTool() : null;
382   }
383
384   @Nullable
385   @Override
386   public InspectionToolWrapper getEnabledTool(@Nullable PsiElement element) {
387     return getEnabledTool(element, true);
388   }
389
390   public void setEnabled(boolean enabled) {
391     myEnabled = enabled;
392   }
393
394   public void enableTool(NamedScope namedScope, Project project) {
395     if (myTools != null) {
396       for (ScopeToolState state : myTools) {
397         if (Comparing.equal(state.getScope(project), namedScope)) {
398           state.setEnabled(true);
399         }
400       }
401     }
402     setEnabled(true);
403   }
404
405   public void disableTool(NamedScope namedScope, Project project) {
406     if (myTools != null) {
407       for (ScopeToolState state : myTools) {
408         if (Comparing.equal(state.getScope(project), namedScope)) {
409           state.setEnabled(false);
410         }
411       }
412     }
413   }
414
415   public void disableTool(@NotNull PsiElement element) {
416     final Project project = element.getProject();
417     final DependencyValidationManager validationManager = DependencyValidationManager.getInstance(project);
418     if (myTools != null) {
419       for (ScopeToolState state : myTools) {
420         final NamedScope scope = state.getScope(project);
421         if (scope != null) {
422           final PackageSet packageSet = scope.getValue();
423           if (packageSet != null) {
424             final PsiFile file = element.getContainingFile();
425             if (file != null) {
426               if (packageSet.contains(file, validationManager)) {
427                 state.setEnabled(false);
428                 return;
429               }
430             }
431             else {
432               if (packageSet instanceof PackageSetBase &&
433                   ((PackageSetBase)packageSet).contains(PsiUtilCore.getVirtualFile(element), project, validationManager)) {
434                 state.setEnabled(false);
435                 return;
436               }
437             }
438           }
439         }
440       }
441       myDefaultState.setEnabled(false);
442     }
443     else {
444       myDefaultState.setEnabled(false);
445       setEnabled(false);
446     }
447   }
448
449   @NotNull
450   public HighlightDisplayLevel getLevel(final NamedScope scope, Project project) {
451     if (myTools != null && scope != null){
452       for (ScopeToolState state : myTools) {
453         if (Comparing.equal(state.getScope(project), scope)) {
454           return state.getLevel();
455         }
456       }
457     }
458     return myDefaultState.getLevel();
459   }
460
461    @Override
462    public boolean equals(Object o) {
463      ToolsImpl tools = (ToolsImpl)o;
464       if (myEnabled != tools.myEnabled) return false;
465       if (getTools().size() != tools.getTools().size()) return false;
466       for (int i = 0; i < getTools().size(); i++) {
467         final ScopeToolState state = getTools().get(i);
468         final ScopeToolState toolState = tools.getTools().get(i);
469         if (!state.equalTo(toolState)){
470           return false;
471         }
472       }
473       return true;
474
475   }
476
477   public void setLevel(@NotNull HighlightDisplayLevel level, @Nullable String scopeName, Project project) {
478     if (scopeName == null) {
479       myDefaultState.setLevel(level);
480     } else {
481       if (myTools == null) {
482         return;
483       }
484       ScopeToolState scopeToolState = null;
485       int index = -1;
486       for (int i = 0; i < myTools.size(); i++) {
487         ScopeToolState tool = myTools.get(i);
488         if (scopeName.equals(tool.getScopeName())) {
489           scopeToolState = tool;
490           myTools.remove(tool);
491           index = i;
492           break;
493         }
494       }
495       if (index < 0) {
496         throw new IllegalStateException("Scope " + scopeName + " not found");
497       }
498       final InspectionToolWrapper toolWrapper = scopeToolState.getTool();
499       final NamedScope scope = scopeToolState.getScope(project);
500       if (scope != null) {
501         myTools.add(index, new ScopeToolState(scope, toolWrapper, scopeToolState.isEnabled(), level));
502       }
503       else {
504         myTools.add(index, new ScopeToolState(scopeToolState.getScopeName(), toolWrapper, scopeToolState.isEnabled(), level));
505       }
506     }
507   }
508
509   public void setDefaultState(@NotNull InspectionToolWrapper toolWrapper, boolean enabled, @NotNull HighlightDisplayLevel level) {
510     myDefaultState.setTool(toolWrapper);
511     myDefaultState.setLevel(level);
512     myDefaultState.setEnabled(enabled);
513   }
514
515   public void setLevel(@NotNull HighlightDisplayLevel level) {
516     myDefaultState.setLevel(level);
517   }
518
519   @Nullable
520   public List<ScopeToolState> getNonDefaultTools() {
521     return myTools;
522   }
523 }