fix findIcon(path, ClassLoader)

GitOrigin-RevId: fd35f17f9ecb137afa677228244693abc35e74c0
This commit is contained in:
Vladimir Krivosheev
2020-10-10 15:03:07 +00:00
committed by intellij-monorepo-bot
parent 2efd1b4175
commit ffc64b112b
5 changed files with 189 additions and 122 deletions
@@ -32,12 +32,14 @@ public final class PropertyGroup implements Group, ColoredItemPresentation, Acce
private SmartPsiElementPointer<?> myGetterPointer;
private SmartPsiElementPointer<?> mySetterPointer;
private boolean myIsStatic;
public static final Icon PROPERTY_READ_ICON = loadIcon("/nodes/propertyRead.png");
public static final Icon PROPERTY_READ_STATIC_ICON = loadIcon("/nodes/propertyReadStatic.png");
public static final Icon PROPERTY_WRITE_ICON = loadIcon("/nodes/propertyWrite.png");
public static final Icon PROPERTY_WRITE_STATIC_ICON = loadIcon("/nodes/propertyWriteStatic.png");
public static final Icon PROPERTY_READ_WRITE_ICON = loadIcon("/nodes/propertyReadWrite.png");
public static final Icon PROPERTY_READ_WRITE_STATIC_ICON = loadIcon("/nodes/propertyReadWriteStatic.png");
private final Project myProject;
private final Collection<TreeElement> myChildren = new ArrayList<>();
@@ -202,7 +204,7 @@ public final class PropertyGroup implements Group, ColoredItemPresentation, Acce
}
private static Icon loadIcon(@NonNls String resourceName) {
Icon icon = IconLoader.findIcon(resourceName);
Icon icon = IconLoader.findIcon(resourceName, PropertyGroup.class, PropertyGroup.class.getClassLoader(), null, true);
Application application = ApplicationManager.getApplication();
if (icon == null && application != null && application.isUnitTestMode()) {
return new ImageIcon();
@@ -31,7 +31,7 @@ import java.util.zip.ZipInputStream;
/**
* @author Dmitry Avdeev
*/
public class LocalArchivedTemplate extends ArchivedProjectTemplate {
public final class LocalArchivedTemplate extends ArchivedProjectTemplate {
public static final @NonNls String DESCRIPTION_PATH = Project.DIRECTORY_STORE_FOLDER + "/description.html";
static final @NonNls String TEMPLATE_DESCRIPTOR = Project.DIRECTORY_STORE_FOLDER + "/project-template.xml";
static final @NonNls String TEMPLATE_META_XML = "template-meta.xml";
@@ -40,13 +40,12 @@ public class LocalArchivedTemplate extends ArchivedProjectTemplate {
static final @NonNls String ROOT_FILE_NAME = "root";
private final URL myArchivePath;
private final ModuleType myModuleType;
private final ModuleType<?> myModuleType;
@Nullable private final List<RootDescription> myModuleDescriptions;
private boolean myEscaped = true;
private Icon myIcon;
public LocalArchivedTemplate(@NotNull URL archivePath,
@NotNull ClassLoader classLoader) {
public LocalArchivedTemplate(@NotNull URL archivePath, @NotNull ClassLoader classLoader) {
super(getTemplateName(archivePath), null);
myArchivePath = archivePath;
@@ -135,7 +134,7 @@ public class LocalArchivedTemplate extends ArchivedProjectTemplate {
@Nullable
String readEntry(@NotNull final String endsWith) {
try {
return processStream(new StreamProcessor<String>() {
return processStream(new StreamProcessor<>() {
@Override
public String consume(@NotNull ZipInputStream stream) throws IOException {
ZipEntry entry;
@@ -1,20 +1,20 @@
// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.ide;
import com.intellij.openapi.extensions.AbstractExtensionPointBean;
import com.intellij.openapi.extensions.ExtensionPointName;
import com.intellij.openapi.extensions.PluginAware;
import com.intellij.openapi.extensions.PluginDescriptor;
import com.intellij.openapi.extensions.RequiredElement;
import com.intellij.openapi.util.IconLoader;
import com.intellij.openapi.util.NullableLazyValue;
import com.intellij.util.xmlb.annotations.Attribute;
import com.intellij.util.xmlb.annotations.Transient;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
/**
* @author yole
*/
public class TypeIconEP extends AbstractExtensionPointBean {
public static final ExtensionPointName<TypeIconEP> EP_NAME = ExtensionPointName.create("com.intellij.typeIcon");
final class TypeIconEP implements PluginAware {
@Transient
private PluginDescriptor pluginDescriptor;
@Attribute("className")
@RequiredElement
@@ -24,14 +24,12 @@ public class TypeIconEP extends AbstractExtensionPointBean {
@RequiredElement
public String icon;
private final NullableLazyValue<Icon> myIcon = new NullableLazyValue<Icon>() {
@Override
protected Icon compute() {
return IconLoader.findIcon(icon, getLoaderForClass());
}
};
@Transient final NullableLazyValue<Icon> lazyIcon = NullableLazyValue.createValue(() -> {
return IconLoader.findIcon(icon, pluginDescriptor == null ? getClass().getClassLoader() : pluginDescriptor.getPluginClassLoader());
});
public NullableLazyValue<Icon> getIcon() {
return myIcon;
@Override
public final void setPluginDescriptor(@NotNull PluginDescriptor pluginDescriptor) {
this.pluginDescriptor = pluginDescriptor;
}
}
@@ -25,7 +25,9 @@ import java.util.Map;
import java.util.Set;
import java.util.function.Function;
public class TypePresentationServiceImpl extends TypePresentationService {
public final class TypePresentationServiceImpl extends TypePresentationService {
private static final ExtensionPointName<TypeIconEP> TYPE_ICON_EP_NAME = new ExtensionPointName<>("com.intellij.typeIcon");
private static final ExtensionPointName<PresentationProvider<?>> PROVIDER_EP = new ExtensionPointName<>("com.intellij.presentationProvider");
private static final ClassExtension<PresentationProvider<?>> PROVIDERS = new ClassExtension<>(PROVIDER_EP.getName());
@@ -45,9 +47,8 @@ public class TypePresentationServiceImpl extends TypePresentationService {
return findFirst(type, template -> template.getIcon(o, 0));
}
@Nullable
@Override
public String getTypePresentableName(Class type) {
public @NotNull String getTypePresentableName(Class type) {
String typeName = findFirst(type, template -> template.getTypeName());
return typeName != null ? typeName : getDefaultTypeName(type);
}
@@ -77,13 +78,13 @@ public class TypePresentationServiceImpl extends TypePresentationService {
}
public TypePresentationServiceImpl() {
for (TypeIconEP ep : TypeIconEP.EP_NAME.getExtensionList()) {
myIcons.put(ep.className, ep.getIcon());
for (TypeIconEP ep : TYPE_ICON_EP_NAME.getExtensionList()) {
myIcons.put(ep.className, ep.lazyIcon);
}
TypeIconEP.EP_NAME.addExtensionPointListener(new ExtensionPointListener<TypeIconEP>() {
TYPE_ICON_EP_NAME.addExtensionPointListener(new ExtensionPointListener<>() {
@Override
public void extensionAdded(@NotNull TypeIconEP extension, @NotNull PluginDescriptor pluginDescriptor) {
myIcons.put(extension.className, extension.getIcon());
myIcons.put(extension.className, extension.lazyIcon);
}
@Override
@@ -95,7 +96,7 @@ public class TypePresentationServiceImpl extends TypePresentationService {
for (TypeNameEP ep : TypeNameEP.EP_NAME.getExtensionList()) {
myNames.put(ep.className, ep.getTypeName());
}
TypeNameEP.EP_NAME.addExtensionPointListener(new ExtensionPointListener<TypeNameEP>() {
TypeNameEP.EP_NAME.addExtensionPointListener(new ExtensionPointListener<>() {
@Override
public void extensionAdded(@NotNull TypeNameEP extension, @NotNull PluginDescriptor pluginDescriptor) {
myNames.put(extension.className, extension.getTypeName());
@@ -115,46 +116,48 @@ public class TypePresentationServiceImpl extends TypePresentationService {
});
}
@Nullable
private PresentationTemplate createPresentationTemplate(Class<?> type) {
private @Nullable PresentationTemplate createPresentationTemplate(Class<?> type) {
Presentation presentation = type.getAnnotation(Presentation.class);
if (presentation != null) {
return new AnnotationBasedTemplate(presentation, type);
}
PresentationProvider<?> provider = PROVIDERS.forClass(type);
if (provider != null) {
return new ProviderBasedTemplate(provider);
}
final NullableLazyValue<Icon> icon = myIcons.get(type.getName());
final NullableLazyValue<String> typeName = myNames.get(type.getName());
if (icon != null || typeName != null) {
return new PresentationTemplate() {
@Nullable
@Override
public Icon getIcon(Object o, int flags) {
return icon == null ? null : icon.getValue();
}
@Nullable
@Override
public String getName(Object o) {
return null;
}
@Nullable
@Override
public String getTypeName() {
return typeName == null ? null : typeName.getValue();
}
@Nullable
@Override
public String getTypeName(Object o) {
return getTypeName();
}
};
NullableLazyValue<Icon> icon = myIcons.get(type.getName());
NullableLazyValue<String> typeName = myNames.get(type.getName());
if (icon == null && typeName == null) {
return null;
}
return null;
return new PresentationTemplate() {
@Nullable
@Override
public Icon getIcon(Object o, int flags) {
return icon == null ? null : icon.getValue();
}
@Nullable
@Override
public String getName(Object o) {
return null;
}
@Nullable
@Override
public String getTypeName() {
return typeName == null ? null : typeName.getValue();
}
@Nullable
@Override
public String getTypeName(Object o) {
return getTypeName();
}
};
}
private final Map<String, NullableLazyValue<Icon>> myIcons = new HashMap<>();
@@ -223,7 +226,7 @@ public class TypePresentationServiceImpl extends TypePresentationService {
}
@SuppressWarnings("unchecked")
private static class AnnotationBasedTemplate extends PresentationProvider<Object> implements PresentationTemplate {
private static final class AnnotationBasedTemplate extends PresentationProvider<Object> implements PresentationTemplate {
private final Presentation myPresentation;
private final Class<?> myClass;
@@ -241,7 +244,9 @@ public class TypePresentationServiceImpl extends TypePresentationService {
@Nullable
@Override
public Icon getIcon(Object o, int flags) {
if (o == null) return myIcon.getValue();
if (o == null) {
return myIcon.getValue();
}
PresentationProvider provider = myPresentationProvider.getValue();
if (provider == null) {
return myIcon.getValue();
@@ -277,7 +282,7 @@ public class TypePresentationServiceImpl extends TypePresentationService {
return namer == null ? null : namer.getName(o);
}
private final NullableLazyValue<Icon> myIcon = new NullableLazyValue<Icon>() {
private final NullableLazyValue<Icon> myIcon = new NullableLazyValue<>() {
@Override
protected Icon compute() {
if (StringUtil.isEmpty(myPresentation.icon())) return null;
@@ -285,12 +290,12 @@ public class TypePresentationServiceImpl extends TypePresentationService {
}
};
private final NullableLazyValue<PresentationProvider<?>> myPresentationProvider = new NullableLazyValue<PresentationProvider<?>>() {
private final NullableLazyValue<PresentationProvider<?>> myPresentationProvider = new NullableLazyValue<>() {
@Override
protected PresentationProvider<?> compute() {
Class<? extends PresentationProvider> aClass = myPresentation.provider();
try {
return aClass == PresentationProvider.class ? null : aClass.newInstance();
return aClass == PresentationProvider.class ? null : aClass.getDeclaredConstructor().newInstance();
}
catch (Exception e) {
return null;
@@ -281,7 +281,7 @@ public final class IconLoader {
return path.regionMatches(dotIndex - suffixLength, "Icons", 0, suffixLength);
}
public static @Nullable Icon findIcon(URL url) {
public static @Nullable Icon findIcon(@Nullable URL url) {
return findIcon(url, true);
}
@@ -324,9 +324,16 @@ public final class IconLoader {
cachedIcon = iconCache.computeIfAbsent(key, k -> {
@SuppressWarnings("unchecked")
ClassLoader classLoader1 = (ClassLoader)((Pair<String, Object>)k).getSecond();
ImageDataResolverImpl resolver = new ImageDataResolverImpl(path, clazz, classLoader1, handleNotFound, /* useCacheOnLoad = */ true);
if (!deferUrlResolve && resolver.getURL() == null) {
return null;
ImageDataLoader resolver;
if (deferUrlResolve) {
resolver = new ImageDataResolverImpl(path, clazz, classLoader1, handleNotFound, /* useCacheOnLoad = */ true);
}
else {
URL url = doResolve(originalPath, classLoader1, null, HandleNotFound.IGNORE);
if (url == null) {
return null;
}
resolver = new ResolvedImageDataResolver(url, classLoader1);
}
return new CachedImageIcon(originalPath, resolver, null, null);
});
@@ -348,8 +355,7 @@ public final class IconLoader {
return icon;
}
@Nullable
public static Icon findIcon(@NotNull String path, @NotNull ClassLoader classLoader) {
public static @Nullable Icon findIcon(@NotNull String path, @NotNull ClassLoader classLoader) {
return findIcon(path, null, classLoader, HandleNotFound.IGNORE, false);
}
@@ -1003,12 +1009,69 @@ public final class IconLoader {
@Nullable IconLoader.ImageDataLoader patch(@NotNull String originalPath, @NotNull IconTransform transform);
boolean isMyClassLoader(@NotNull ClassLoader loader);
boolean isMyClassLoader(@NotNull ClassLoader classLoader);
}
private static final class ResolvedImageDataResolver implements ImageDataLoader {
private final URL url;
private final ClassLoader classLoader;
ResolvedImageDataResolver(@NotNull URL url, @Nullable ClassLoader classLoader) {
this.classLoader = classLoader;
this.url = url;
}
@Override
public @Nullable Image loadImage(@Nullable List<ImageFilter> filters, @NotNull ScaleContext scaleContext, boolean isDark) {
int flags = ImageLoader.USE_SVG | ImageLoader.ALLOW_FLOAT_SCALING | ImageLoader.USE_CACHE;
if (isDark) {
flags |= ImageLoader.USE_DARK;
}
String path = url.toString();
return ImageLoader.load(path, filters, null, null, flags, scaleContext, !path.endsWith(".svg"));
}
@Override
public @NotNull URL getURL() {
return this.url;
}
@Override
public final @Nullable IconLoader.ImageDataLoader patch(@NotNull String originalPath, @NotNull IconTransform transform) {
Pair<String, ClassLoader> patchedPath = transform.patchPath(originalPath, classLoader);
if (patchedPath == null) {
return null;
}
ClassLoader classLoader = patchedPath.second == null ? null : patchedPath.second;
String path = patchedPath.first;
// This use case for temp themes only. Here we want immediately replace existing icon to a local one
if (path != null && path.startsWith("file:/")) {
try {
ImageDataResolverImpl resolver = new ImageDataResolverImpl(new URL(path), path.substring(1), classLoader, true);
resolver.resolve();
return resolver;
}
catch (MalformedURLException ignore) {
}
}
return null;
}
@Override
public final boolean isMyClassLoader(@NotNull ClassLoader classLoader) {
return classLoader == this.classLoader;
}
@Override
public final String toString() {
return "ResolvedImageDataResolver{" +
", url=" + url +
'}';
}
}
/**
* Used to defer URL resolve.
*/
private static class ImageDataResolverImpl implements ImageDataLoader {
private static final URL UNRESOLVED_URL;
@@ -1092,29 +1155,6 @@ public final class IconLoader {
getURL();
}
private static @Nullable URL doResolve(@Nullable String overriddenPath,
@Nullable ClassLoader classLoader,
@Nullable Class<?> ownerClass,
@NotNull HandleNotFound handleNotFound) {
String path = overriddenPath;
URL url = null;
if (path != null) {
if (classLoader != null) {
// paths in ClassLoader getResource must not start with "/"
path = path.charAt(0) == '/' ? path.substring(1) : path;
url = findUrl(path, classLoader::getResource);
}
if (url == null && ownerClass != null) {
// some plugins use findIcon("icon.png",IconContainer.class)
url = findUrl(path, ownerClass::getResource);
}
}
if (url == null) {
handleNotFound.handle("Can't find icon in '" + path + "' near " + classLoader);
}
return url;
}
@Override
public final @Nullable URL getURL() {
URL result = this.url;
@@ -1159,32 +1199,9 @@ public final class IconLoader {
return null;
}
@SuppressWarnings("DuplicateExpressions")
private static @Nullable URL findUrl(@NotNull String path, @NotNull Function<? super String, URL> urlProvider) {
URL url = urlProvider.apply(path);
if (url != null) {
return url;
}
// Find either PNG or SVG icon. The icon will then be wrapped into CachedImageIcon
// which will load proper icon version depending on the context - UI theme, DPI.
// SVG version, when present, has more priority than PNG.
// See for details: com.intellij.util.ImageLoader.ImageDescList#create
if (path.endsWith(".png")) {
path = path.substring(0, path.length() - 4) + ".svg";
}
else if (path.endsWith(".svg")) {
path = path.substring(0, path.length() - 4) + ".png";
}
else {
LOG.debug("unexpected path: ", path);
}
return urlProvider.apply(path);
}
@Override
public final boolean isMyClassLoader(@NotNull ClassLoader loader) {
return classLoader == loader;
public final boolean isMyClassLoader(@NotNull ClassLoader classLoader) {
return this.classLoader == classLoader;
}
@Override
@@ -1199,6 +1216,52 @@ public final class IconLoader {
}
}
static @Nullable URL doResolve(@Nullable String overriddenPath,
@Nullable ClassLoader classLoader,
@Nullable Class<?> ownerClass,
@NotNull HandleNotFound handleNotFound) {
String path = overriddenPath;
URL url = null;
if (path != null) {
if (classLoader != null) {
// paths in ClassLoader getResource must not start with "/"
path = path.charAt(0) == '/' ? path.substring(1) : path;
url = findUrl(path, classLoader::getResource);
}
if (url == null && ownerClass != null) {
// some plugins use findIcon("icon.png",IconContainer.class)
url = findUrl(path, ownerClass::getResource);
}
}
if (url == null) {
handleNotFound.handle("Can't find icon in '" + path + "' near " + classLoader);
}
return url;
}
@SuppressWarnings("DuplicateExpressions")
private static @Nullable URL findUrl(@NotNull String path, @NotNull Function<? super String, URL> urlProvider) {
URL url = urlProvider.apply(path);
if (url != null) {
return url;
}
// Find either PNG or SVG icon. The icon will then be wrapped into CachedImageIcon
// which will load proper icon version depending on the context - UI theme, DPI.
// SVG version, when present, has more priority than PNG.
// See for details: com.intellij.util.ImageLoader.ImageDescList#create
if (path.endsWith(".png")) {
path = path.substring(0, path.length() - 4) + ".svg";
}
else if (path.endsWith(".svg")) {
path = path.substring(0, path.length() - 4) + ".png";
}
else {
LOG.debug("unexpected path: ", path);
}
return urlProvider.apply(path);
}
@NotNull
public static Icon createLazy(@NotNull Supplier<? extends @NotNull Icon> producer) {
return new LazyIcon() {