diff --git a/platform/extensions/src/com/intellij/openapi/extensions/AbstractExtensionPointBean.java b/platform/extensions/src/com/intellij/openapi/extensions/AbstractExtensionPointBean.java index ada38444c11d..7264cdb55622 100644 --- a/platform/extensions/src/com/intellij/openapi/extensions/AbstractExtensionPointBean.java +++ b/platform/extensions/src/com/intellij/openapi/extensions/AbstractExtensionPointBean.java @@ -15,6 +15,8 @@ */ package com.intellij.openapi.extensions; +import com.intellij.openapi.diagnostic.Logger; +import org.jetbrains.annotations.Nullable; import org.picocontainer.PicoContainer; import org.picocontainer.defaults.ConstructorInjectionComponentAdapter; import org.jetbrains.annotations.NotNull; @@ -23,6 +25,7 @@ import org.jetbrains.annotations.NotNull; * @author peter */ public abstract class AbstractExtensionPointBean implements PluginAware { + private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.extensions.AbstractExtensionPointBean"); protected PluginDescriptor myPluginDescriptor; public final void setPluginDescriptor(PluginDescriptor pluginDescriptor) { @@ -34,6 +37,17 @@ public abstract class AbstractExtensionPointBean implements PluginAware { return (Class)Class.forName(className, true, getLoaderForClass()); } + @Nullable + public final Class findClassNoExceptions(final String className) { + try { + return findClass(className); + } + catch (ClassNotFoundException e) { + LOG.error("Problem loading class " + className + " from plugin " + myPluginDescriptor.getPluginId().getIdString(), e); + return null; + } + } + public ClassLoader getLoaderForClass() { return myPluginDescriptor == null ? getClass().getClassLoader() : myPluginDescriptor.getPluginClassLoader(); } diff --git a/xml/dom-impl/src/META-INF/DomPlugin.xml b/xml/dom-impl/src/META-INF/DomPlugin.xml index c49db1906d63..9792a2384b48 100644 --- a/xml/dom-impl/src/META-INF/DomPlugin.xml +++ b/xml/dom-impl/src/META-INF/DomPlugin.xml @@ -3,6 +3,9 @@ + + diff --git a/xml/dom-impl/src/com/intellij/util/xml/impl/DomApplicationComponent.java b/xml/dom-impl/src/com/intellij/util/xml/impl/DomApplicationComponent.java index b78efa84713a..838b9ff04279 100644 --- a/xml/dom-impl/src/com/intellij/util/xml/impl/DomApplicationComponent.java +++ b/xml/dom-impl/src/com/intellij/util/xml/impl/DomApplicationComponent.java @@ -108,7 +108,7 @@ public class DomApplicationComponent { } public final void registerImplementation(Class domElementClass, Class implementationClass, - final Disposable parentDisposable) { + @Nullable final Disposable parentDisposable) { myCachedImplementationClasses.registerImplementation(domElementClass, implementationClass, parentDisposable); } diff --git a/xml/dom-impl/src/com/intellij/util/xml/impl/DomImplementationClassEP.java b/xml/dom-impl/src/com/intellij/util/xml/impl/DomImplementationClassEP.java new file mode 100644 index 000000000000..78bef21aac60 --- /dev/null +++ b/xml/dom-impl/src/com/intellij/util/xml/impl/DomImplementationClassEP.java @@ -0,0 +1,30 @@ +package com.intellij.util.xml.impl; + +import com.intellij.openapi.extensions.AbstractExtensionPointBean; +import com.intellij.openapi.extensions.ExtensionPointName; +import com.intellij.util.xmlb.annotations.Attribute; +import org.jetbrains.annotations.Nullable; + +/** + * @author peter + */ +public class DomImplementationClassEP extends AbstractExtensionPointBean { + public static final ExtensionPointName EP_NAME = ExtensionPointName.create("com.intellij.dom.implementationClass"); + + @Attribute("interfaceClass") + public String interfaceName; + + @Attribute("implementationClass") + public String implementationName; + + + @Nullable + public Class getInterfaceClass() { + return findClassNoExceptions(interfaceName); + } + + @Nullable + public Class getImplementationClass() { + return findClassNoExceptions(implementationName); + } +} diff --git a/xml/dom-impl/src/com/intellij/util/xml/impl/ImplementationClassCache.java b/xml/dom-impl/src/com/intellij/util/xml/impl/ImplementationClassCache.java index 90729b5be33e..d07607a608f1 100644 --- a/xml/dom-impl/src/com/intellij/util/xml/impl/ImplementationClassCache.java +++ b/xml/dom-impl/src/com/intellij/util/xml/impl/ImplementationClassCache.java @@ -19,13 +19,13 @@ import com.intellij.openapi.Disposable; import com.intellij.openapi.util.Disposer; import com.intellij.util.ReflectionCache; import com.intellij.util.containers.ConcurrentFactoryMap; +import com.intellij.util.containers.MultiMap; import com.intellij.util.xml.DomReflectionUtil; import com.intellij.util.xml.Implementation; -import gnu.trove.THashMap; import org.jetbrains.annotations.Nullable; +import java.util.Collection; import java.util.Comparator; -import java.util.Map; import java.util.SortedSet; import java.util.TreeSet; @@ -43,7 +43,7 @@ class ImplementationClassCache { }; - private final Map myImplementationClasses = new THashMap(); + private final MultiMap myImplementationClasses = new MultiMap(); private final ConcurrentFactoryMap myCache = new ConcurrentFactoryMap() { @Nullable protected Class create(final Class concreteInterface) { @@ -57,25 +57,44 @@ class ImplementationClassCache { } }; - private void findImplementationClassDFS(final Class concreteInterface, SortedSet results) { - Class aClass = myImplementationClasses.get(concreteInterface); - if (aClass != null) { - results.add(aClass); + ImplementationClassCache() { + for (DomImplementationClassEP ep : DomImplementationClassEP.EP_NAME.getExtensions()) { + myImplementationClasses.putValue(ep.interfaceName, ep); } - else { + } + + private void findImplementationClassDFS(final Class concreteInterface, SortedSet results) { + final Collection values = myImplementationClasses.get(concreteInterface.getName()); + for (DomImplementationClassEP value : values) { + if (value.getInterfaceClass() == concreteInterface) { + results.add(value.getImplementationClass()); + } + } + if (results.isEmpty()) { for (final Class aClass1 : ReflectionCache.getInterfaces(concreteInterface)) { findImplementationClassDFS(aClass1, results); } } } - public final void registerImplementation(final Class domElementClass, Class implementationClass, - final Disposable parentDisposable) { - myImplementationClasses.put(domElementClass, implementationClass); + public final void registerImplementation(final Class domElementClass, final Class implementationClass, + @Nullable final Disposable parentDisposable) { + final DomImplementationClassEP ep = new DomImplementationClassEP() { + @Override + public Class getInterfaceClass() { + return domElementClass; + } + + @Override + public Class getImplementationClass() { + return implementationClass; + } + }; + myImplementationClasses.putValue(domElementClass.getName(), ep); if (parentDisposable != null) { Disposer.register(parentDisposable, new Disposable() { public void dispose() { - myImplementationClasses.remove(domElementClass); + myImplementationClasses.remove(domElementClass.getName()); } }); }