/* * Copyright 2000-2016 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * @author max */ package com.intellij.ui; import com.intellij.openapi.project.Project; import com.intellij.openapi.project.impl.ProjectLifecycleListener; import com.intellij.openapi.util.LowMemoryWatcher; import com.intellij.psi.util.PsiModificationTracker; import com.intellij.util.Function; import com.intellij.util.messages.MessageBus; import com.intellij.util.messages.MessageBusConnection; import org.jetbrains.annotations.NotNull; import javax.swing.*; import java.util.LinkedHashMap; import java.util.Map; public class IconDeferrerImpl extends IconDeferrer { private final Object LOCK = new Object(); private final Map<Object, Icon> myIconsCache = new LinkedHashMap<Object, Icon>() { @Override protected boolean removeEldestEntry(Map.Entry<Object, Icon> eldest) { return size() > 100; } }; private long myLastClearTimestamp; protected IconDeferrerImpl() {} public IconDeferrerImpl(MessageBus bus) { final MessageBusConnection connection = bus.connect(); connection.subscribe(PsiModificationTracker.TOPIC, this::clear); connection.subscribe(ProjectLifecycleListener.TOPIC, new ProjectLifecycleListener() { @Override public void afterProjectClosed(@NotNull Project project) { clear(); } }); LowMemoryWatcher.register(this::clear, connection); } protected final void clear() { synchronized (LOCK) { myIconsCache.clear(); myLastClearTimestamp++; } } @Override public <T> Icon defer(final Icon base, final T param, @NotNull final Function<T, Icon> evaluator) { return deferImpl(base, param, evaluator, false); } @Override public <T> Icon deferAutoUpdatable(Icon base, T param, @NotNull Function<T, Icon> evaluator) { return deferImpl(base, param, evaluator, true); } private <T> Icon deferImpl(Icon base, T param, @NotNull Function<T, Icon> evaluator, final boolean autoUpdatable) { if (myEvaluationIsInProgress.get().booleanValue()) { return evaluator.fun(param); } synchronized (LOCK) { Icon result = myIconsCache.get(param); if (result == null) { final long started = myLastClearTimestamp; result = new DeferredIconImpl<>(base, param, evaluator, (DeferredIconImpl<T> source, T key, Icon r) -> { synchronized (LOCK) { // check if our results is not outdated yet if (started == myLastClearTimestamp) { myIconsCache.put(key, autoUpdatable ? source : r); } } }, autoUpdatable); myIconsCache.put(param, result); } return result; } } protected void cacheIcon(Object key,Icon value) { synchronized (LOCK) { myIconsCache.put(key, value); } } private static final ThreadLocal<Boolean> myEvaluationIsInProgress = ThreadLocal.withInitial(() -> Boolean.FALSE); static void evaluateDeferred(@NotNull Runnable runnable) { try { myEvaluationIsInProgress.set(Boolean.TRUE); runnable.run(); } finally { myEvaluationIsInProgress.set(Boolean.FALSE); } } @Override public boolean equalIcons(Icon icon1, Icon icon2) { return DeferredIconImpl.equalIcons(icon1, icon2); } }