blob: 7f2cf33332ebbd51693fe993cfa816168d376361 [file] [log] [blame]
/*
* Copyright 2000-2009 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 = 0;
@SuppressWarnings("UnusedDeclaration")
private final LowMemoryWatcher myLowMemoryWatcher = LowMemoryWatcher.register(new Runnable() {
@Override
public void run() {
clear();
}
});
public IconDeferrerImpl(MessageBus bus) {
final MessageBusConnection connection = bus.connect();
connection.subscribe(PsiModificationTracker.TOPIC, new PsiModificationTracker.Listener() {
@Override
public void modificationCountChanged() {
clear();
}
});
connection.subscribe(ProjectLifecycleListener.TOPIC, new ProjectLifecycleListener.Adapter() {
@Override
public void afterProjectClosed(@NotNull Project project) {
clear();
}
});
}
private void clear() {
synchronized (LOCK) {
myIconsCache.clear();
myLastClearTimestamp++;
}
}
@Override
public <T> Icon defer(final Icon base, final T param, @NotNull final Function<T, Icon> f) {
return deferImpl(base, param, f, false);
}
@Override
public <T> Icon deferAutoUpdatable(Icon base, T param, @NotNull Function<T, Icon> f) {
return deferImpl(base, param, f, true);
}
private <T> Icon deferImpl(Icon base, T param, @NotNull Function<T, Icon> f, final boolean autoupdatable) {
if (myEvaluationIsInProgress.get().booleanValue()) {
return f.fun(param);
}
synchronized (LOCK) {
Icon result = myIconsCache.get(param);
if (result == null) {
final long started = myLastClearTimestamp;
result = new DeferredIconImpl<T>(base, param, f, new DeferredIconImpl.IconListener<T>() {
@Override
public void evalDone(DeferredIconImpl<T> source, T key, @NotNull 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;
}
}
private static final ThreadLocal<Boolean> myEvaluationIsInProgress = new ThreadLocal<Boolean>() {
@Override
protected Boolean initialValue() {
return Boolean.FALSE;
}
};
static void evaluateDeferred(@NotNull Runnable runnable) {
try {
myEvaluationIsInProgress.set(Boolean.TRUE);
runnable.run();
}
finally {
myEvaluationIsInProgress.set(Boolean.FALSE);
}
}
}