mirror of
https://gitflic.ru/project/openide/openide.git
synced 2025-12-14 18:05:27 +07:00
[project model] report an error if 'moduleConfigurationEditorProvider' extension requires 'Module' as constructor argument (IJPL-179175)
'moduleConfigurationEditorProvider' extension point was registered as a module-level. However, it seems that no implementations use this ability, and can be converted to application-level extensions automatically. To avoid breaking compatibility, the extension point was converted to 'bean class', and tries to call the constructor with 'Module' parameter if no-arg constructor isn't available, and report an error in such cases. GitOrigin-RevId: 8390fda7aa09f09b6df8178d40b6269b26b0292f
This commit is contained in:
committed by
intellij-monorepo-bot
parent
a8cdcef3e5
commit
b94efffb44
@@ -20,6 +20,7 @@ import com.intellij.openapi.roots.*;
|
||||
import com.intellij.openapi.roots.impl.libraries.LibraryEx;
|
||||
import com.intellij.openapi.roots.libraries.Library;
|
||||
import com.intellij.openapi.roots.libraries.LibraryTable;
|
||||
import com.intellij.openapi.roots.ui.configuration.impl.ModuleConfigurationEditorProviderEp;
|
||||
import com.intellij.platform.workspace.storage.MutableEntityStorage;
|
||||
import com.intellij.ui.navigation.History;
|
||||
import com.intellij.ui.navigation.Place;
|
||||
@@ -43,7 +44,7 @@ import java.util.List;
|
||||
* @author Eugene Zhuravlev
|
||||
*/
|
||||
public abstract class ModuleEditor implements Place.Navigator, Disposable {
|
||||
private static final ExtensionPointName<ModuleConfigurationEditorProvider> EP_NAME =
|
||||
private static final ExtensionPointName<ModuleConfigurationEditorProviderEp> EP_NAME =
|
||||
new ExtensionPointName<>("com.intellij.moduleConfigurationEditorProvider");
|
||||
|
||||
private static final Logger LOG = Logger.getInstance(ModuleEditor.class);
|
||||
@@ -160,7 +161,8 @@ public abstract class ModuleEditor implements Place.Navigator, Disposable {
|
||||
|
||||
private void createEditors(@NotNull Module module) {
|
||||
ModuleConfigurationState state = createModuleConfigurationState();
|
||||
for (ModuleConfigurationEditorProvider provider : EP_NAME.getExtensionList(module)) {
|
||||
for (ModuleConfigurationEditorProviderEp providerEp : EP_NAME.getExtensionList()) {
|
||||
ModuleConfigurationEditorProvider provider = providerEp.getOrCreateInstance(module);
|
||||
ModuleConfigurationEditor[] editors = provider.createEditors(state);
|
||||
if (editors.length > 0 && provider instanceof ModuleConfigurationEditorProviderEx &&
|
||||
((ModuleConfigurationEditorProviderEx)provider).isCompleteEditorSet()) {
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.openapi.roots.ui.configuration.impl
|
||||
|
||||
import com.intellij.diagnostic.PluginException
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.diagnostic.fileLogger
|
||||
import com.intellij.openapi.extensions.PluginAware
|
||||
import com.intellij.openapi.extensions.PluginDescriptor
|
||||
import com.intellij.openapi.module.Module
|
||||
import com.intellij.openapi.module.ModuleConfigurationEditor
|
||||
import com.intellij.openapi.roots.ui.configuration.ModuleConfigurationEditorProvider
|
||||
import com.intellij.util.xmlb.annotations.Attribute
|
||||
import org.jetbrains.annotations.ApiStatus
|
||||
import java.lang.invoke.MethodHandle
|
||||
import java.lang.invoke.MethodHandles
|
||||
import java.lang.invoke.MethodType
|
||||
|
||||
/**
|
||||
* Implements [ModuleConfigurationEditorProvider] extension point supporting the deprecated way when an extension is instantiated with
|
||||
* [Module] as a constructor parameter.
|
||||
* When this possibility is removed, the extension point can be converted to a regular 'interface' extension point.
|
||||
*/
|
||||
class ModuleConfigurationEditorProviderEp : PluginAware {
|
||||
private var pluginDescriptor: PluginDescriptor? = null
|
||||
|
||||
@Attribute("implementation")
|
||||
@JvmField
|
||||
var implementationClass: String? = null
|
||||
|
||||
@Volatile
|
||||
private var cachedInstance: ModuleConfigurationEditorProvider? = null
|
||||
|
||||
override fun setPluginDescriptor(pluginDescriptor: PluginDescriptor) {
|
||||
this.pluginDescriptor = pluginDescriptor
|
||||
}
|
||||
|
||||
@ApiStatus.Internal
|
||||
fun getOrCreateInstance(module: Module): ModuleConfigurationEditorProvider {
|
||||
try {
|
||||
cachedInstance?.let {
|
||||
return it
|
||||
}
|
||||
|
||||
val implementationClass = implementationClass ?: error("'implementation' attribute is not specified'")
|
||||
val pluginDescriptor = pluginDescriptor ?: error("plugin descriptor is not set")
|
||||
val instanceClass = ApplicationManager.getApplication().loadClass<ModuleConfigurationEditor>(implementationClass, pluginDescriptor)
|
||||
val lookup = MethodHandles.privateLookupIn(instanceClass, methodLookup)
|
||||
val defaultConstructor: MethodHandle
|
||||
try {
|
||||
defaultConstructor = lookup.findConstructor(instanceClass, emptyConstructorMethodType)
|
||||
}
|
||||
catch (_: NoSuchMethodException) {
|
||||
val constructorWithModule = lookup.findConstructor(instanceClass, moduleMethodType)
|
||||
LOG.error(PluginException("""
|
||||
|'moduleConfigurationEditorProvider' extensions must have a constructor without parameters and take Module instance from
|
||||
|the parameter of 'createEditors' function ('state.getCurrentRootModel().getModule()'), but '$implementationClass'
|
||||
|has a constructor taking 'Module' as parameter.
|
||||
""".trimMargin(), pluginDescriptor.pluginId))
|
||||
return constructorWithModule.invoke(module) as ModuleConfigurationEditorProvider
|
||||
}
|
||||
synchronized(this) {
|
||||
cachedInstance?.let {
|
||||
return it
|
||||
}
|
||||
val provider = defaultConstructor.invoke() as ModuleConfigurationEditorProvider
|
||||
cachedInstance = provider
|
||||
return provider
|
||||
}
|
||||
}
|
||||
catch (t: Throwable) {
|
||||
throw PluginException("Failed to instantiate ModuleConfigurationEditorProvider ($implementationClass)", t, pluginDescriptor?.pluginId)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val LOG = fileLogger()
|
||||
private val methodLookup = MethodHandles.lookup()
|
||||
private val emptyConstructorMethodType: MethodType = MethodType.methodType(Void.TYPE)
|
||||
private val moduleMethodType = MethodType.methodType(Void.TYPE, Module::class.java)
|
||||
|
||||
@@ -11,6 +11,9 @@ import org.jetbrains.annotations.NotNull;
|
||||
* <moduleConfigurationEditorProvider implementation="qualified-class-name"/>
|
||||
* </extensions>
|
||||
* </pre>
|
||||
*
|
||||
* The implementation must have a no-arg constructor. The {@link com.intellij.openapi.module.Module} instance can be taken from
|
||||
* {@code state} parameter of {@link #createEditors} ({@code state.getCurrentRootModel().getModule()}).
|
||||
*/
|
||||
public interface ModuleConfigurationEditorProvider {
|
||||
@NotNull ModuleConfigurationEditor @NotNull [] createEditors(@NotNull ModuleConfigurationState state);
|
||||
|
||||
@@ -543,8 +543,10 @@
|
||||
<with attribute="implementationClass" implements="com.intellij.testIntegration.TestCreator"/>
|
||||
</extensionPoint>
|
||||
|
||||
<extensionPoint name="moduleConfigurationEditorProvider" interface="com.intellij.openapi.roots.ui.configuration.ModuleConfigurationEditorProvider"
|
||||
area="IDEA_MODULE" dynamic="true"/>
|
||||
<extensionPoint name="moduleConfigurationEditorProvider" beanClass="com.intellij.openapi.roots.ui.configuration.impl.ModuleConfigurationEditorProviderEp"
|
||||
dynamic="true">
|
||||
<with attribute="implementation" implements="com.intellij.openapi.roots.ui.configuration.ModuleConfigurationEditorProvider"/>
|
||||
</extensionPoint>
|
||||
|
||||
<extensionPoint name="callHierarchyProvider" beanClass="com.intellij.lang.LanguageExtensionPoint" dynamic="true">
|
||||
<with attribute="implementationClass" implements="com.intellij.ide.hierarchy.HierarchyProvider"/>
|
||||
|
||||
Reference in New Issue
Block a user