Merge remote-tracking branch 'origin/master'
[idea/community.git] / platform / lang-impl / src / com / intellij / codeInspection / ex / ApplicationInspectionProfileManager.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 package com.intellij.codeInspection.ex;
17
18 import com.intellij.codeHighlighting.HighlightDisplayLevel;
19 import com.intellij.codeInsight.daemon.InspectionProfileConvertor;
20 import com.intellij.codeInsight.daemon.impl.DaemonListeners;
21 import com.intellij.codeInsight.daemon.impl.HighlightInfoType;
22 import com.intellij.codeInsight.daemon.impl.SeveritiesProvider;
23 import com.intellij.codeInsight.daemon.impl.SeverityRegistrar;
24 import com.intellij.codeInsight.daemon.impl.analysis.HighlightingSettingsPerFile;
25 import com.intellij.codeInspection.InspectionsBundle;
26 import com.intellij.configurationStore.BundledSchemeEP;
27 import com.intellij.configurationStore.SchemeDataHolder;
28 import com.intellij.lang.annotation.HighlightSeverity;
29 import com.intellij.openapi.application.ApplicationManager;
30 import com.intellij.openapi.application.ModalityState;
31 import com.intellij.openapi.components.PersistentStateComponent;
32 import com.intellij.openapi.components.ServiceManager;
33 import com.intellij.openapi.components.State;
34 import com.intellij.openapi.components.Storage;
35 import com.intellij.openapi.editor.colors.TextAttributesKey;
36 import com.intellij.openapi.extensions.ExtensionPointName;
37 import com.intellij.openapi.extensions.Extensions;
38 import com.intellij.openapi.options.SchemeManager;
39 import com.intellij.openapi.options.SchemeManagerFactory;
40 import com.intellij.openapi.project.Project;
41 import com.intellij.openapi.project.ProjectManager;
42 import com.intellij.openapi.ui.Messages;
43 import com.intellij.profile.Profile;
44 import com.intellij.profile.codeInspection.*;
45 import com.intellij.util.ArrayUtil;
46 import com.intellij.util.ObjectUtils;
47 import com.intellij.util.messages.MessageBus;
48 import com.intellij.util.ui.UIUtil;
49 import org.jdom.Element;
50 import org.jdom.JDOMException;
51 import org.jetbrains.annotations.NotNull;
52 import org.jetbrains.annotations.Nullable;
53 import org.jetbrains.annotations.TestOnly;
54
55 import javax.swing.*;
56 import java.io.File;
57 import java.io.IOException;
58 import java.util.Collection;
59 import java.util.Collections;
60 import java.util.concurrent.atomic.AtomicBoolean;
61 import java.util.function.Function;
62
63 @State(
64   name = "InspectionProfileManager",
65   storages = {
66     @Storage("editor.xml"),
67     @Storage(value = "other.xml", deprecated = true)
68   },
69   additionalExportFile = InspectionProfileManager.INSPECTION_DIR
70 )
71 public class ApplicationInspectionProfileManager extends BaseInspectionProfileManager implements InspectionProfileManager,
72                                                                                                  SeverityProvider,
73                                                                                                  PersistentStateComponent<Element> {
74   private static final ExtensionPointName<BundledSchemeEP> BUNDLED_EP_NAME = ExtensionPointName.create("com.intellij.bundledInspectionProfile");
75
76   private final InspectionToolRegistrar myRegistrar;
77   private final SchemeManager<InspectionProfileImpl> mySchemeManager;
78   private final AtomicBoolean myProfilesAreInitialized = new AtomicBoolean(false);
79
80   public static ApplicationInspectionProfileManager getInstanceImpl() {
81     return (ApplicationInspectionProfileManager)ServiceManager.getService(InspectionProfileManager.class);
82   }
83
84   public ApplicationInspectionProfileManager(@NotNull InspectionToolRegistrar registrar, @NotNull SchemeManagerFactory schemeManagerFactory, @NotNull MessageBus messageBus) {
85     super(messageBus);
86
87     myRegistrar = registrar;
88     registerProvidedSeverities();
89
90     mySchemeManager = schemeManagerFactory.create(INSPECTION_DIR, new InspectionProfileProcessor() {
91       @NotNull
92       @Override
93       public String getName(@NotNull Function<String, String> attributeProvider, @NotNull String fileNameWithoutExtension) {
94         return fileNameWithoutExtension;
95       }
96
97       @NotNull
98       public InspectionProfileImpl createScheme(@NotNull SchemeDataHolder<? super InspectionProfileImpl> dataHolder,
99                                                 @NotNull String name,
100                                                 @NotNull Function<String, String> attributeProvider,
101                                                 boolean isBundled) {
102         return new InspectionProfileImpl(name, myRegistrar, ApplicationInspectionProfileManager.this, dataHolder);
103       }
104
105       @Override
106       public void onSchemeAdded(@NotNull InspectionProfileImpl scheme) {
107         fireProfileChanged(scheme);
108         onProfilesChanged();
109       }
110
111       @Override
112       public void onSchemeDeleted(@NotNull InspectionProfileImpl scheme) {
113         onProfilesChanged();
114       }
115
116       @Override
117       public void onCurrentSchemeSwitched(@Nullable InspectionProfileImpl oldScheme, @Nullable InspectionProfileImpl newScheme) {
118         if (newScheme != null) {
119           fireProfileChanged(oldScheme, newScheme);
120         }
121         onProfilesChanged();
122       }
123     });
124   }
125
126   @NotNull
127   @Override
128   protected SchemeManager<InspectionProfileImpl> getSchemeManager() {
129     return mySchemeManager;
130   }
131
132   @NotNull
133   private InspectionProfileImpl createSampleProfile(@NotNull String name, InspectionProfileImpl baseProfile) {
134     return new InspectionProfileImpl(name, InspectionToolRegistrar.getInstance(), this, baseProfile, null);
135   }
136
137   // It should be public to be available from Upsource
138   public static void registerProvidedSeverities() {
139     for (SeveritiesProvider provider : Extensions.getExtensions(SeveritiesProvider.EP_NAME)) {
140       for (HighlightInfoType t : provider.getSeveritiesHighlightInfoTypes()) {
141         HighlightSeverity highlightSeverity = t.getSeverity(null);
142         SeverityRegistrar.registerStandard(t, highlightSeverity);
143         TextAttributesKey attributesKey = t.getAttributesKey();
144         Icon icon = t instanceof HighlightInfoType.Iconable ? ((HighlightInfoType.Iconable)t).getIcon() : null;
145         HighlightDisplayLevel.registerSeverity(highlightSeverity, attributesKey, icon);
146       }
147     }
148   }
149
150   @Override
151   @NotNull
152   public Collection<Profile> getProfiles() {
153     initProfiles();
154     return Collections.unmodifiableList(mySchemeManager.getAllSchemes());
155   }
156
157   private volatile boolean LOAD_PROFILES = !ApplicationManager.getApplication().isUnitTestMode();
158   @TestOnly
159   public void forceInitProfiles(boolean flag) {
160     LOAD_PROFILES = flag;
161     myProfilesAreInitialized.set(false);
162   }
163
164   public void initProfiles() {
165     if (!myProfilesAreInitialized.compareAndSet(false, true) || !LOAD_PROFILES) {
166       return;
167     }
168
169     loadBundledSchemes();
170     mySchemeManager.loadSchemes();
171
172     if (mySchemeManager.isEmpty()) {
173       mySchemeManager.addScheme(createSampleProfile(InspectionProfileImpl.DEFAULT_PROFILE_NAME, InspectionProfileImpl.getBaseProfile()));
174     }
175   }
176
177   private void loadBundledSchemes() {
178     if (!isUnitTestOrHeadlessMode()) {
179       for (BundledSchemeEP ep : BUNDLED_EP_NAME.getExtensions()) {
180         mySchemeManager.loadBundledScheme(ep.getPath() + ".xml", ep);
181       }
182     }
183   }
184
185   private static boolean isUnitTestOrHeadlessMode() {
186     return ApplicationManager.getApplication().isUnitTestMode() || ApplicationManager.getApplication().isHeadlessEnvironment();
187   }
188
189   public Profile loadProfile(@NotNull String path) throws IOException, JDOMException {
190     final File file = new File(path);
191     if (file.exists()) {
192       try {
193         return InspectionProfileLoadUtil.load(file, myRegistrar, this);
194       }
195       catch (IOException | JDOMException e) {
196         throw e;
197       }
198       catch (Exception ignored) {
199         ApplicationManager.getApplication().invokeLater(() -> Messages
200           .showErrorDialog(InspectionsBundle.message("inspection.error.loading.message", 0, file),
201                            InspectionsBundle.message("inspection.errors.occurred.dialog.title")), ModalityState.NON_MODAL);
202       }
203     }
204     return getProfile(path, false);
205   }
206
207   @Override
208   public void updateProfile(@NotNull Profile profile) {
209     super.updateProfile(profile);
210     updateProfileImpl((InspectionProfileImpl)profile);
211   }
212
213   private static void updateProfileImpl(@NotNull InspectionProfileImpl profile) {
214     for (Project project : ProjectManager.getInstance().getOpenProjects()) {
215       profile.initInspectionTools(project);
216     }
217   }
218
219   @Nullable
220   @Override
221   public Element getState() {
222     Element state = new Element("state");
223     getSeverityRegistrar().writeExternal(state);
224     return state;
225   }
226
227   @Override
228   public void loadState(Element state) {
229     getSeverityRegistrar().readExternal(state);
230   }
231
232   public InspectionProfileConvertor getConverter() {
233     return new InspectionProfileConvertor(this);
234   }
235
236   @Override
237   public void setRootProfile(@Nullable String profileName) {
238     mySchemeManager.setCurrentSchemeName(profileName);
239   }
240
241   @Override
242   public Profile getProfile(@NotNull final String name, boolean returnRootProfileIfNamedIsAbsent) {
243     Profile found = mySchemeManager.findSchemeByName(name);
244     if (found != null) return found;
245     //profile was deleted
246     if (returnRootProfileIfNamedIsAbsent) {
247       return getCurrentProfile();
248     }
249     return null;
250   }
251
252   @NotNull
253   @Override
254   public InspectionProfileImpl getCurrentProfile() {
255     initProfiles();
256
257     InspectionProfileImpl current = mySchemeManager.getCurrentScheme();
258     if (current != null) {
259       return current;
260     }
261
262     // use default as base, not random custom profile
263     InspectionProfileImpl result = mySchemeManager.findSchemeByName(InspectionProfileImpl.DEFAULT_PROFILE_NAME);
264     if (result == null) {
265       return createSampleProfile(InspectionProfileImpl.DEFAULT_PROFILE_NAME, null);
266     }
267     return result;
268   }
269
270   @NotNull
271   public String getRootProfileName() {
272     return ObjectUtils.chooseNotNull(mySchemeManager.getCurrentSchemeName(), InspectionProfileImpl.DEFAULT_PROFILE_NAME);
273   }
274
275   @Override
276   @NotNull
277   public String[] getAvailableProfileNames() {
278     return ArrayUtil.toStringArray(mySchemeManager.getAllSchemeNames());
279   }
280
281   public static void onProfilesChanged() {
282     //cleanup caches blindly for all projects in case ide profile was modified
283     for (final Project project : ProjectManager.getInstance().getOpenProjects()) {
284       //noinspection EmptySynchronizedStatement
285       synchronized (HighlightingSettingsPerFile.getInstance(project)) {
286       }
287
288       UIUtil.invokeLaterIfNeeded(() -> {
289         if (!project.isDisposed()) {
290           DaemonListeners.getInstance(project).updateStatusBar();
291         }
292       });
293     }
294   }
295 }