All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.intellij.openapi.util.KeyedExtensionCollector Maven / Gradle / Ivy

Go to download

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);
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy