com.intellij.openapi.util.KeyedExtensionCollector Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of core-api Show documentation
Show all versions of core-api Show documentation
A packaging of the IntelliJ Community Edition core-api library.
This is release number 1 of trunk branch 142.
The newest version!
/*
* Copyright 2000-2014 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.openapi.util;
import com.intellij.diagnostic.PluginException;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.extensions.*;
import com.intellij.openapi.progress.ProcessCanceledException;
import com.intellij.util.ConcurrencyUtil;
import com.intellij.util.KeyedLazyInstance;
import com.intellij.util.SmartList;
import com.intellij.util.containers.ContainerUtil;
import gnu.trove.THashMap;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
import java.util.concurrent.ConcurrentMap;
public class KeyedExtensionCollector {
private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.util.KeyedExtensionCollector");
private final Map> myExplicitExtensions = new THashMap>();
private final ConcurrentMap> myCache = ContainerUtil.newConcurrentMap();
@NonNls private final String lock;
private ExtensionPoint> myPoint;
private final String myEpName;
private ExtensionPointAndAreaListener> myListener;
private final List> myListeners = ContainerUtil.createLockFreeCopyOnWriteList();
public KeyedExtensionCollector(@NonNls @NotNull String epName) {
myEpName = epName;
lock = "lock for KeyedExtensionCollector " + epName;
resetAreaListener();
}
private void resetAreaListener() {
synchronized (lock) {
myCache.clear();
if (myPoint != null) {
myPoint.removeExtensionPointListener(myListener);
myPoint = null;
myListener = null;
}
}
}
public void addExplicitExtension(@NotNull KeyT key, @NotNull T t) {
synchronized (lock) {
final String skey = keyToString(key);
List list = myExplicitExtensions.get(skey);
if (list == null) {
list = new ArrayList();
myExplicitExtensions.put(skey, list);
}
list.add(t);
myCache.remove(skey);
for (ExtensionPointListener listener : myListeners) {
listener.extensionAdded(t, null);
}
}
}
public void removeExplicitExtension(@NotNull KeyT key, @NotNull T t) {
synchronized (lock) {
final String skey = keyToString(key);
List list = myExplicitExtensions.get(skey);
if (list != null) {
list.remove(t);
myCache.remove(skey);
}
for (ExtensionPointListener listener : myListeners) {
listener.extensionRemoved(t, null);
}
}
}
@NotNull
protected String keyToString(@NotNull KeyT key) {
return key.toString();
}
/**
* @see #findSingle(Object)
*/
@NotNull
public List forKey(@NotNull KeyT key) {
final String stringKey = keyToString(key);
boolean rebuild = myPoint == null && Extensions.getRootArea().hasExtensionPoint(myEpName);
List cached = rebuild ? null : myCache.get(stringKey);
if (cached != null) return cached;
cached = buildExtensions(stringKey, key);
cached = ConcurrencyUtil.cacheOrGet(myCache, stringKey, cached);
return cached;
}
public T findSingle(@NotNull KeyT key) {
List list = forKey(key);
return list.isEmpty() ? null : list.get(0);
}
@NotNull
protected List buildExtensions(@NotNull String stringKey, @NotNull KeyT key) {
return buildExtensions(Collections.singleton(stringKey));
}
@NotNull
protected final List buildExtensions(@NotNull Set keys) {
synchronized (lock) {
List result = null;
for (Map.Entry> entry : myExplicitExtensions.entrySet()) {
String key = entry.getKey();
if (keys.contains(key)) {
List list = entry.getValue();
if (result == null) {
result = new ArrayList(list);
}
else {
result.addAll(list);
}
}
}
final ExtensionPoint> point = getPoint();
if (point != null) {
final KeyedLazyInstance[] beans = point.getExtensions();
for (KeyedLazyInstance bean : beans) {
if (keys.contains(bean.getKey())) {
final T instance;
try {
instance = bean.getInstance();
}
catch (ProcessCanceledException e) {
throw e;
}
catch (Exception e) {
LOG.error(e);
continue;
}
catch (LinkageError e) {
LOG.error(e);
continue;
}
if (result == null) result = new SmartList();
result.add(instance);
}
}
}
return result == null ? Collections.emptyList() : result;
}
}
@Nullable
private ExtensionPoint> getPoint() {
ExtensionPoint> point = myPoint;
if (point == null && Extensions.getRootArea().hasExtensionPoint(myEpName)) {
ExtensionPointName> typesafe = ExtensionPointName.create(myEpName);
myPoint = point = Extensions.getRootArea().getExtensionPoint(typesafe);
myListener = new ExtensionPointAndAreaListener>() {
@Override
public void extensionAdded(@NotNull final KeyedLazyInstance bean, @Nullable final PluginDescriptor pluginDescriptor) {
synchronized (lock) {
if (bean.getKey() == null) {
if (pluginDescriptor != null) {
throw new PluginException("No key specified for extension of class " + bean.getInstance().getClass(),
pluginDescriptor.getPluginId());
}
LOG.error("No key specified for extension of class " + bean.getInstance().getClass());
return;
}
myCache.remove(bean.getKey());
for (ExtensionPointListener listener : myListeners) {
listener.extensionAdded(bean.getInstance(), null);
}
}
}
@Override
public void extensionRemoved(@NotNull final KeyedLazyInstance bean, @Nullable final PluginDescriptor pluginDescriptor) {
synchronized (lock) {
myCache.remove(bean.getKey());
for (ExtensionPointListener listener : myListeners) {
listener.extensionRemoved(bean.getInstance(), null);
}
}
}
@Override
public void areaReplaced(final ExtensionsArea area) {
resetAreaListener();
}
};
point.addExtensionPointListener(myListener);
}
return point;
}
public boolean hasAnyExtensions() {
synchronized (lock) {
if (!myExplicitExtensions.isEmpty()) return true;
final ExtensionPoint> point = getPoint();
return point != null && point.hasAnyExtensions();
}
}
public void addListener(@NotNull ExtensionPointListener listener) {
myListeners.add(listener);
}
public void removeListener(@NotNull ExtensionPointListener listener) {
myListeners.remove(listener);
}
}