diff --git a/platform/core-impl/src/com/intellij/util/pico/CachingConstructorInjectionComponentAdapter.java b/platform/core-impl/src/com/intellij/util/pico/CachingConstructorInjectionComponentAdapter.java index 3c6a2719fc0e..f7a5fe195380 100644 --- a/platform/core-impl/src/com/intellij/util/pico/CachingConstructorInjectionComponentAdapter.java +++ b/platform/core-impl/src/com/intellij/util/pico/CachingConstructorInjectionComponentAdapter.java @@ -22,8 +22,8 @@ import java.util.*; final class CachingConstructorInjectionComponentAdapter implements ComponentAdapter { private static final ThreadLocal>> ourGuard = new ThreadLocal<>(); private final PicoContainer container; - private final Object key; - private final Class componentImplementation; + private final @NotNull Object key; + private final @NotNull Class componentImplementation; private Object myInstance; CachingConstructorInjectionComponentAdapter(@NotNull PicoContainer container, @@ -35,12 +35,12 @@ final class CachingConstructorInjectionComponentAdapter implements ComponentAdap } @Override - public Object getComponentKey() { + public @NotNull Object getComponentKey() { return key; } @Override - public Class getComponentImplementation() { + public @NotNull Class getComponentImplementation() { return componentImplementation; } @@ -50,7 +50,7 @@ final class CachingConstructorInjectionComponentAdapter implements ComponentAdap } @Override - public Object getComponentInstance() { + public @NotNull Object getComponentInstance() { Object instance = myInstance; if (instance == null) { instance = instantiateGuarded(this, container, getComponentImplementation()); @@ -59,9 +59,9 @@ final class CachingConstructorInjectionComponentAdapter implements ComponentAdap return instance; } - public static @NotNull Object instantiateGuarded(@Nullable CachingConstructorInjectionComponentAdapter adapter, - @NotNull PicoContainer container, - @NotNull Class componentImplementation) { + private static @NotNull Object instantiateGuarded(@Nullable CachingConstructorInjectionComponentAdapter adapter, + @NotNull PicoContainer container, + @NotNull Class componentImplementation) { Set> currentStack = ourGuard.get(); if (currentStack == null) { currentStack = Collections.newSetFromMap(new IdentityHashMap<>(1)); @@ -91,7 +91,7 @@ final class CachingConstructorInjectionComponentAdapter implements ComponentAdap return container.getComponentAdapter(expectedType); } - ComponentAdapter result = getTargetAdapter(container, expectedType, excludeAdapter.getComponentKey()); + ComponentAdapter result = container.getComponentAdapterOfType(expectedType, excludeAdapter.getComponentKey()); return result == null ? null : expectedType.isAssignableFrom(result.getComponentImplementation()) ? result : null; } @@ -142,8 +142,7 @@ final class CachingConstructorInjectionComponentAdapter implements ComponentAdap private static @NotNull Constructor getGreediestSatisfiableConstructor(@Nullable ComponentAdapter adapter, @NotNull DefaultPicoContainer container, - @NotNull Class componentImplementation) throws - PicoIntrospectionException { + @NotNull Class componentImplementation) throws PicoIntrospectionException { Set> conflicts = new HashSet<>(); Set[]> unsatisfiableDependencyTypes = new HashSet<>(); // filter out all constructors that will definitely not match @@ -221,35 +220,4 @@ final class CachingConstructorInjectionComponentAdapter implements ComponentAdap } return false; } - - private static ComponentAdapter getTargetAdapter(@NotNull DefaultPicoContainer container, - Class expectedType, - @NotNull Object excludeKey) { - ComponentAdapter byKey = container.getComponentAdapter(expectedType); - if (byKey != null && !excludeKey.equals(byKey.getComponentKey())) { - return byKey; - } - - List found = container.getComponentAdaptersOfType(expectedType); - ComponentAdapter exclude = null; - for (ComponentAdapter work : found) { - if (work.getComponentKey().equals(excludeKey)) { - exclude = work; - } - } - found.remove(exclude); - if (found.isEmpty()) { - return container.getParent() == null ? null : container.getParent().getComponentAdapterOfType(expectedType); - } - else if (found.size() == 1) { - return found.get(0); - } - else { - Class[] foundClasses = new Class[found.size()]; - for (int i = 0; i < foundClasses.length; i++) { - foundClasses[i] = found.get(i).getComponentImplementation(); - } - throw new AmbiguousComponentResolutionException(expectedType, foundClasses); - } - } } \ No newline at end of file diff --git a/platform/core-impl/src/com/intellij/util/pico/DefaultPicoContainer.java b/platform/core-impl/src/com/intellij/util/pico/DefaultPicoContainer.java index f990f2e95e1c..293a5513d5c8 100644 --- a/platform/core-impl/src/com/intellij/util/pico/DefaultPicoContainer.java +++ b/platform/core-impl/src/com/intellij/util/pico/DefaultPicoContainer.java @@ -1,9 +1,7 @@ // 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.util.pico; -import org.jetbrains.annotations.ApiStatus; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.*; import org.picocontainer.ComponentAdapter; import org.picocontainer.MutablePicoContainer; @@ -25,7 +23,7 @@ public class DefaultPicoContainer implements MutablePicoContainer { this(null); } - public final @NotNull Collection getComponentAdapters() { + public final @NotNull @Unmodifiable Collection getComponentAdapters() { return componentAdapters.getImmutableSet(); } @@ -38,11 +36,6 @@ public class DefaultPicoContainer implements MutablePicoContainer { return adapter; } - public final void release() { - componentKeyToAdapter.clear(); - componentAdapters.clear(); - } - private @Nullable ComponentAdapter getFromCache(@NotNull Object componentKey) { ComponentAdapter adapter = componentKeyToAdapter.get(componentKey); if (adapter != null) { @@ -51,20 +44,20 @@ public class DefaultPicoContainer implements MutablePicoContainer { return componentKey instanceof Class ? componentKeyToAdapter.get(((Class)componentKey).getName()) : null; } - public final @Nullable ComponentAdapter getComponentAdapterOfType(@NotNull Class componentType) { + final @Nullable ComponentAdapter getComponentAdapterOfType(@NotNull Class componentType, @Nullable Object excludeKey) { // See http://jira.codehaus.org/secure/ViewIssue.jspa?key=PICO-115 ComponentAdapter adapterByKey = getComponentAdapter(componentType); - if (adapterByKey != null) { + if (adapterByKey != null && (excludeKey == null || !excludeKey.equals(adapterByKey.getComponentKey()))) { return adapterByKey; } - List found = getComponentAdaptersOfType(componentType); + List found = getComponentAdaptersOfType(componentType, excludeKey); + if (found.isEmpty()) { + return parent == null ? null : parent.getComponentAdapterOfType(componentType, excludeKey); + } if (found.size() == 1) { return found.get(0); } - if (found.isEmpty()) { - return parent == null ? null : parent.getComponentAdapterOfType(componentType); - } Class[] foundClasses = new Class[found.size()]; for (int i = 0; i < foundClasses.length; i++) { @@ -73,7 +66,8 @@ public class DefaultPicoContainer implements MutablePicoContainer { throw new AmbiguousComponentResolutionException(componentType, foundClasses); } - public final @NotNull List getComponentAdaptersOfType(@NotNull Class componentType) { + private @NotNull @Unmodifiable List getComponentAdaptersOfType(@NotNull Class componentType, + @Nullable Object excludeKey) { if (componentType == String.class) { return Collections.emptyList(); } @@ -87,7 +81,8 @@ public class DefaultPicoContainer implements MutablePicoContainer { for (ComponentAdapter adapter : componentKeyToAdapter.values()) { // exclude services - if (adapter.getComponentKey() instanceof String) { + Object componentKey = adapter.getComponentKey(); + if (componentKey instanceof String || excludeKey != null && excludeKey.equals(componentKey)) { continue; } @@ -99,7 +94,7 @@ public class DefaultPicoContainer implements MutablePicoContainer { return result; } - public final ComponentAdapter registerComponent(@NotNull ComponentAdapter componentAdapter) { + private @NotNull ComponentAdapter registerComponent(@NotNull ComponentAdapter componentAdapter) { if (componentKeyToAdapter.putIfAbsent(componentAdapter.getComponentKey(), componentAdapter) != null) { @NotNull String message = "Key " + componentAdapter.getComponentKey() + " duplicated"; throw new PicoException(message); @@ -140,7 +135,7 @@ public class DefaultPicoContainer implements MutablePicoContainer { @Override public final @Nullable Object getComponentInstanceOfType(@NotNull Class componentType) { - ComponentAdapter componentAdapter = getComponentAdapterOfType(componentType); + ComponentAdapter componentAdapter = getComponentAdapterOfType(componentType, null); return componentAdapter == null ? null : getInstance(componentAdapter); } @@ -165,19 +160,15 @@ public class DefaultPicoContainer implements MutablePicoContainer { return registerComponent(new CachingConstructorInjectionComponentAdapter(this, componentKey, componentImplementation)); } - public final DefaultPicoContainer getParent() { - return parent; - } - /** * A linked hash set that's copied on write operations. */ private static final class LinkedHashSetWrapper { private final Object lock = new Object(); - private volatile Set immutableSet; - private LinkedHashSet synchronizedSet = new LinkedHashSet<>(); + private volatile @Unmodifiable Set immutableSet; + private Set synchronizedSet = new LinkedHashSet<>(); - public void add(@NotNull T element) { + void add(@NotNull T element) { synchronized (lock) { if (!synchronizedSet.contains(element)) { copySyncSetIfExposedAsImmutable().add(element); @@ -185,7 +176,7 @@ public class DefaultPicoContainer implements MutablePicoContainer { } } - private LinkedHashSet copySyncSetIfExposedAsImmutable() { + private Set copySyncSetIfExposedAsImmutable() { if (immutableSet != null) { immutableSet = null; synchronizedSet = new LinkedHashSet<>(synchronizedSet); @@ -193,22 +184,13 @@ public class DefaultPicoContainer implements MutablePicoContainer { return synchronizedSet; } - public void remove(@Nullable T element) { + void remove(@Nullable T element) { synchronized (lock) { copySyncSetIfExposedAsImmutable().remove(element); } } - public void clear() { - synchronized (lock) { - if (immutableSet != null) { - immutableSet = null; - } - synchronizedSet = new LinkedHashSet<>(); - } - } - - public @NotNull Set getImmutableSet() { + @NotNull @Unmodifiable Set getImmutableSet() { Set result = immutableSet; if (result == null) { synchronized (lock) { @@ -232,8 +214,8 @@ public class DefaultPicoContainer implements MutablePicoContainer { } static final class InstanceComponentAdapter implements ComponentAdapter { - private final Object componentKey; - private final Object componentInstance; + private final @NotNull Object componentKey; + private final @NotNull Object componentInstance; InstanceComponentAdapter(@NotNull Object componentKey, @NotNull Object componentInstance) { this.componentKey = componentKey; @@ -241,17 +223,17 @@ public class DefaultPicoContainer implements MutablePicoContainer { } @Override - public Object getComponentInstance() { + public @NotNull Object getComponentInstance() { return componentInstance; } @Override - public Object getComponentKey() { + public @NotNull Object getComponentKey() { return componentKey; } @Override - public Class getComponentImplementation() { + public @NotNull Class getComponentImplementation() { return componentInstance.getClass(); } diff --git a/platform/extensions/src/org/picocontainer/ComponentAdapter.java b/platform/extensions/src/org/picocontainer/ComponentAdapter.java index 3178d746e4c6..a0e530f6816a 100644 --- a/platform/extensions/src/org/picocontainer/ComponentAdapter.java +++ b/platform/extensions/src/org/picocontainer/ComponentAdapter.java @@ -7,6 +7,8 @@ */ package org.picocontainer; +import org.jetbrains.annotations.NotNull; + /** * A component adapter is responsible for providing a specific component instance. An instance of an implementation of * this interface is used inside a {@link PicoContainer} for every registered component or instance. Each @@ -24,6 +26,7 @@ public interface ComponentAdapter { * @return the component's key. Should either be a class type (normally an interface) or an identifier that is * unique (within the scope of the current PicoContainer). */ + @NotNull Object getComponentKey(); /** @@ -32,7 +35,9 @@ public interface ComponentAdapter { * @return the component's implementation class. Should normally be a concrete class (ie, a class that can be * instantiated). */ + @NotNull Class getComponentImplementation(); + @NotNull Object getComponentInstance(); } diff --git a/platform/service-container/src/com/intellij/serviceContainer/ComponentManagerResolver.kt b/platform/service-container/src/com/intellij/serviceContainer/ComponentManagerResolver.kt index 1b0fe5cfb21a..9077702eb9ab 100644 --- a/platform/service-container/src/com/intellij/serviceContainer/ComponentManagerResolver.kt +++ b/platform/service-container/src/com/intellij/serviceContainer/ComponentManagerResolver.kt @@ -24,7 +24,7 @@ internal class ComponentManagerResolver( return !constructor.isAnnotationPresent(NonInjectable::class.java) && !constructor.isAnnotationPresent(Deprecated::class.java) } - override fun isInjectable(parameterType: Class<*>): Boolean = !isNotApplicableClass(parameterType) + override fun isInjectable(parameterType: Class<*>): Boolean = isApplicableClass(parameterType) override fun resolveDependency(parameterType: Class<*>, instanceClass: Class<*>, round: Int): ArgumentSupplier? { if (ComponentManager::class.java === parameterType) { diff --git a/platform/service-container/src/com/intellij/serviceContainer/constructorInjection.kt b/platform/service-container/src/com/intellij/serviceContainer/constructorInjection.kt index b73455848955..3c938887ad51 100644 --- a/platform/service-container/src/com/intellij/serviceContainer/constructorInjection.kt +++ b/platform/service-container/src/com/intellij/serviceContainer/constructorInjection.kt @@ -103,19 +103,20 @@ internal fun instantiateUsingPicoContainer(aClass: Class<*>, } } -internal fun isNotApplicableClass(type: Class<*>): Boolean { - return type.isPrimitive || - type.isAnnotation || - type.isSynthetic || - type.isEnum || - type.isArray || - type === java.lang.String::class.java || - type === Class::class.java || - type === File::class.java || - type === Path::class.java || - java.lang.Number::class.java.isAssignableFrom(type) || - java.util.Collection::class.java.isAssignableFrom(type) || - java.util.Map::class.java.isAssignableFrom(type) +internal fun isApplicableClass(type: Class<*>): Boolean { + @Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN") + return !type.isPrimitive && + !type.isAnnotation && + !type.isSynthetic && + !type.isEnum && + !type.isArray && + type !== java.lang.String::class.java && + type !== Class::class.java && + type !== File::class.java && + type !== Path::class.java && + !java.lang.Number::class.java.isAssignableFrom(type) && + !java.util.Collection::class.java.isAssignableFrom(type) && + !java.util.Map::class.java.isAssignableFrom(type) } private fun getGreediestSatisfiableConstructor(aClass: Class<*>, @@ -147,7 +148,7 @@ private fun getGreediestSatisfiableConstructor(aClass: Class<*>, } // first, perform fast check to ensure that assert about getComponentAdapterOfType is thrown only if the constructor is applicable - if (parameterTypes.any(::isNotApplicableClass)) { + if (!parameterTypes.all(::isApplicableClass)) { continue }