2 * Copyright 2000-2016 JetBrains s.r.o.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
16 package com.intellij.profile.codeInspection
18 import com.intellij.codeInspection.InspectionProfile
19 import com.intellij.codeInspection.ex.InspectionProfileImpl
20 import com.intellij.codeInspection.ex.InspectionToolRegistrar
21 import com.intellij.concurrency.runAsync
22 import com.intellij.configurationStore.SchemeDataHolder
23 import com.intellij.openapi.Disposable
24 import com.intellij.openapi.application.ApplicationManager
25 import com.intellij.openapi.components.PersistentStateComponent
26 import com.intellij.openapi.components.State
27 import com.intellij.openapi.components.Storage
28 import com.intellij.openapi.options.SchemeManager
29 import com.intellij.openapi.options.SchemeManagerFactory
30 import com.intellij.openapi.project.Project
31 import com.intellij.openapi.project.ProjectManager
32 import com.intellij.openapi.project.ProjectManagerListener
33 import com.intellij.openapi.startup.StartupActivity
34 import com.intellij.openapi.util.Disposer
35 import com.intellij.openapi.util.JDOMUtil
36 import com.intellij.openapi.util.text.StringUtil
37 import com.intellij.packageDependencies.DependencyValidationManager
38 import com.intellij.profile.Profile
39 import com.intellij.psi.search.scope.packageSet.NamedScopeManager
40 import com.intellij.psi.search.scope.packageSet.NamedScopesHolder
41 import com.intellij.util.ui.UIUtil
42 import com.intellij.util.xmlb.Accessor
43 import com.intellij.util.xmlb.SkipDefaultValuesSerializationFilters
44 import com.intellij.util.xmlb.XmlSerializer
45 import org.jdom.Element
46 import java.util.concurrent.CompletableFuture
47 import java.util.function.Function
49 const val PROFILE = "profile"
51 private const val VERSION = "1.0"
52 private const val SCOPE = "scope"
53 private const val NAME = "name"
54 private const val PROJECT_DEFAULT_PROFILE_NAME = "Project Default"
56 @State(name = "InspectionProjectProfileManager", storages = arrayOf(Storage(value = "inspectionProfiles/profiles_settings.xml", exclusive = true)))
57 class ProjectInspectionProfileManager(val project: Project,
58 private val applicationProfileManager: InspectionProfileManager,
59 private val scopeManager: DependencyValidationManager,
60 private val localScopesHolder: NamedScopeManager,
61 schemeManagerFactory: SchemeManagerFactory) : BaseInspectionProfileManager(project.messageBus), PersistentStateComponent<Element> {
64 fun getInstanceImpl(project: Project): ProjectInspectionProfileManager {
65 return InspectionProjectProfileManager.getInstance(project) as ProjectInspectionProfileManager
69 private var scopeListener: NamedScopesHolder.ScopeListener? = null
71 private var state = State()
73 private val initialLoadSchemesFuture: CompletableFuture<*>
75 private val skipDefaultsSerializationFilter = object : SkipDefaultValuesSerializationFilters(State()) {
76 override fun accepts(accessor: Accessor, bean: Any, beanValue: Any?): Boolean {
77 if (beanValue == null && accessor.name == "projectProfile") {
80 return super.accepts(accessor, bean, beanValue)
84 override val schemeManager: SchemeManager<InspectionProfileImpl>
86 private data class State(@field:com.intellij.util.xmlb.annotations.OptionTag("PROJECT_PROFILE") var projectProfile: String? = PROJECT_DEFAULT_PROFILE_NAME,
87 @field:com.intellij.util.xmlb.annotations.OptionTag("USE_PROJECT_PROFILE") var useProjectProfile: Boolean = true)
90 schemeManager = schemeManagerFactory.create("inspectionProfiles", object : InspectionProfileProcessor() {
91 override fun createScheme(dataHolder: SchemeDataHolder<InspectionProfileImpl>, name: String, attributeProvider: Function<String, String?>, duringLoad: Boolean): InspectionProfileImpl {
92 val profile = InspectionProfileImpl(name, InspectionToolRegistrar.getInstance(), this@ProjectInspectionProfileManager, InspectionProfileImpl.getDefaultProfile(), dataHolder)
93 profile.isProjectLevel = true
97 override fun isSchemeFile(name: CharSequence) = !StringUtil.equals(name, "profiles_settings.xml")
99 override fun onSchemeDeleted(scheme: InspectionProfileImpl) {
100 schemeRemoved(scheme)
103 override fun onSchemeAdded(scheme: InspectionProfileImpl) {
104 if (scheme.wasInitialized()) {
105 fireProfileChanged(scheme)
109 override fun onCurrentSchemeSwitched(oldScheme: InspectionProfileImpl?, newScheme: InspectionProfileImpl?) {
110 for (adapter in profileListeners) {
111 adapter.profileActivated(oldScheme, newScheme)
114 }, isUseOldFileNameSanitize = true)
116 val app = ApplicationManager.getApplication()
117 if (app.isUnitTestMode) {
118 initialLoadSchemesFuture = CompletableFuture.completedFuture(null)
121 initialLoadSchemesFuture = runAsync { schemeManager.loadSchemes() }
124 project.messageBus.connect().subscribe(ProjectManager.TOPIC, object: ProjectManagerListener {
125 override fun projectClosed(project: Project) {
126 val cleanupInspectionProfilesRunnable = {
127 cleanupSchemes(project)
128 (InspectionProfileManager.getInstance() as BaseInspectionProfileManager).cleanupSchemes(project)
129 fireProfilesShutdown()
132 if (app.isUnitTestMode || app.isHeadlessEnvironment) {
133 cleanupInspectionProfilesRunnable.invoke()
136 app.executeOnPooledThread(cleanupInspectionProfilesRunnable)
142 fun isCurrentProfileInitialized() = currentProfile.wasInitialized()
144 @Synchronized override fun updateProfile(profile: Profile) {
145 super.updateProfile(profile)
147 (profile as InspectionProfileImpl).initInspectionTools(project)
150 override fun schemeRemoved(scheme: InspectionProfile) {
151 scheme.cleanup(project)
155 private class ProjectInspectionProfileStartUpActivity : StartupActivity {
156 override fun runActivity(project: Project) {
157 getInstanceImpl(project).apply {
158 initialLoadSchemesFuture.thenAccept {
159 currentProfile.initInspectionTools(project)
160 fireProfilesInitialized()
162 val app = ApplicationManager.getApplication()
163 if (app.isUnitTestMode && app.isDispatchThread) {
164 // do not restart daemon in the middle of the test
165 //noinspection TestOnlyProblems
166 UIUtil.dispatchAllInvocationEvents()
170 scopeListener = NamedScopesHolder.ScopeListener {
171 for (profile in schemeManager.allSchemes) {
172 profile.scopesChanged()
176 scopeManager.addScopeListener(scopeListener!!)
177 localScopesHolder.addScopeListener(scopeListener!!)
178 Disposer.register(project, Disposable {
179 scopeManager.removeScopeListener(scopeListener!!)
180 localScopesHolder.removeScopeListener(scopeListener!!)
186 @Synchronized override fun loadState(state: Element) {
187 val data = state.getChild("settings")
189 val newState = State()
193 severityRegistrar.readExternal(it)
195 catch (e: Throwable) {
199 XmlSerializer.deserializeInto(newState, it)
202 this.state = newState
204 if (data != null && data.getChild("version")?.getAttributeValue("value") != VERSION) {
205 for (o in data.getChildren("option")) {
206 if (o.getAttributeValue("name") == "USE_PROJECT_LEVEL_SETTINGS") {
207 if (o.getAttributeValue("value").toBoolean()) {
208 if (newState.projectProfile != null) {
209 currentProfile.convert(data, project)
217 if (newState.useProjectProfile) {
218 schemeManager.currentSchemeName = newState.projectProfile
222 @Synchronized override fun getState(): Element? {
223 val result = Element("settings")
224 val state = this.state
225 state.projectProfile = schemeManager.currentSchemeName
226 XmlSerializer.serializeInto(state, result, skipDefaultsSerializationFilter)
227 if (!result.children.isEmpty()) {
228 result.addContent(Element("version").setAttribute("value", VERSION))
231 severityRegistrar.writeExternal(result)
232 if (JDOMUtil.isEmpty(result)) {
233 result.name = "state"
237 return Element("state").addContent(result)
241 override fun getScopesManager() = scopeManager
243 @Synchronized override fun getProfiles(): Collection<Profile> {
245 return schemeManager.allSchemes
248 @Synchronized override fun getAvailableProfileNames(): Array<String> = schemeManager.allSchemeNames.toTypedArray()
250 val projectProfile: String?
251 get() = schemeManager.currentSchemeName
253 @Synchronized override fun setRootProfile(name: String?) {
254 if (name != schemeManager.currentSchemeName) {
255 schemeManager.currentSchemeName = name
256 state.useProjectProfile = name != null
260 @Synchronized fun setCurrentProfile(profile: InspectionProfileImpl?) {
261 schemeManager.setCurrent(profile)
262 state.useProjectProfile = profile != null
265 @Synchronized override fun getCurrentProfile(): InspectionProfileImpl {
266 if (!state.useProjectProfile) {
267 return applicationProfileManager.currentProfile as InspectionProfileImpl
270 var currentScheme = schemeManager.currentScheme
271 if (currentScheme == null) {
272 currentScheme = schemeManager.allSchemes.firstOrNull()
273 if (currentScheme == null) {
274 currentScheme = InspectionProfileImpl(PROJECT_DEFAULT_PROFILE_NAME, InspectionToolRegistrar.getInstance(), this,
275 InspectionProfileImpl.getDefaultProfile(), null)
276 currentScheme.copyFrom(applicationProfileManager.currentProfile)
277 currentScheme.isProjectLevel = true
278 currentScheme.setName(PROJECT_DEFAULT_PROFILE_NAME)
279 schemeManager.addScheme(currentScheme)
281 schemeManager.setCurrent(currentScheme, false)
286 private fun fireProfilesInitialized() {
287 for (listener in profileListeners) {
288 listener.profilesInitialized()
292 private fun fireProfilesShutdown() {
293 for (profileChangeAdapter in profileListeners) {
294 profileChangeAdapter.profilesShutdown()
298 @Synchronized override fun getProfile(name: String, returnRootProfileIfNamedIsAbsent: Boolean): Profile? {
299 val profile = schemeManager.findSchemeByName(name)
300 return profile ?: applicationProfileManager.getProfile(name, returnRootProfileIfNamedIsAbsent)