diff --git a/extensions/extensions.iml b/extensions/extensions.iml
new file mode 100644
index 000000000000..8aa391e2c744
--- /dev/null
+++ b/extensions/extensions.iml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/extensions/source/com/intellij/openapi/extensions/AreaInstance.java b/extensions/source/com/intellij/openapi/extensions/AreaInstance.java
new file mode 100644
index 000000000000..db6a594d2d90
--- /dev/null
+++ b/extensions/source/com/intellij/openapi/extensions/AreaInstance.java
@@ -0,0 +1,11 @@
+/*
+ * Copyright (c) 2000-2004 by JetBrains s.r.o. All Rights Reserved.
+ * Use is subject to license terms.
+ */
+package com.intellij.openapi.extensions;
+
+/**
+ * @author akireyev
+ */
+public interface AreaInstance {
+}
diff --git a/extensions/source/com/intellij/openapi/extensions/AreaListener.java b/extensions/source/com/intellij/openapi/extensions/AreaListener.java
new file mode 100644
index 000000000000..ded299db81bc
--- /dev/null
+++ b/extensions/source/com/intellij/openapi/extensions/AreaListener.java
@@ -0,0 +1,13 @@
+/*
+ * Copyright (c) 2000-2004 by JetBrains s.r.o. All Rights Reserved.
+ * Use is subject to license terms.
+ */
+package com.intellij.openapi.extensions;
+
+/**
+ * @author akireyev
+ */
+public interface AreaListener {
+ void areaCreated(String areaClass, AreaInstance areaInstance);
+ void areaDisposing(String areaClass, AreaInstance areaInstance);
+}
diff --git a/extensions/source/com/intellij/openapi/extensions/EPAvailabilityListenerExtension.java b/extensions/source/com/intellij/openapi/extensions/EPAvailabilityListenerExtension.java
new file mode 100644
index 000000000000..3cd342f23841
--- /dev/null
+++ b/extensions/source/com/intellij/openapi/extensions/EPAvailabilityListenerExtension.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2000-2004 by JetBrains s.r.o. All Rights Reserved.
+ * Use is subject to license terms.
+ */
+package com.intellij.openapi.extensions;
+
+/**
+ * @author AKireyev
+ */
+public class EPAvailabilityListenerExtension {
+ public static final String EXTENSION_POINT_NAME = "jetbrains.fabrique.framework.epAvailabilityListener";
+
+ private String myExtensionPointName;
+ private String myListenerClass;
+
+ public EPAvailabilityListenerExtension() {
+ }
+
+ public EPAvailabilityListenerExtension(String extensionPointName, String listenerClass) {
+ myExtensionPointName = extensionPointName;
+ myListenerClass = listenerClass;
+ }
+
+ public String getExtensionPointName() {
+ return myExtensionPointName;
+ }
+
+ public void setExtensionPointName(String extensionPointName) {
+ myExtensionPointName = extensionPointName;
+ }
+
+ public String getListenerClass() {
+ return myListenerClass;
+ }
+
+ public void setListenerClass(String listenerClass) {
+ myListenerClass = listenerClass;
+ }
+}
diff --git a/extensions/source/com/intellij/openapi/extensions/Extension.java b/extensions/source/com/intellij/openapi/extensions/Extension.java
new file mode 100644
index 000000000000..9453cb5229e3
--- /dev/null
+++ b/extensions/source/com/intellij/openapi/extensions/Extension.java
@@ -0,0 +1,15 @@
+/*
+ * Copyright (c) 2000-2004 by JetBrains s.r.o. All Rights Reserved.
+ * Use is subject to license terms.
+ */
+package com.intellij.openapi.extensions;
+
+/**
+ * @author kir
+ *
+ * An extension can implement this interface to get notifications when it is added/removed to {@link ExtensionPoint}
+ */
+public interface Extension {
+ void extensionAdded(ExtensionPoint extensionPoint);
+ void extensionRemoved(ExtensionPoint extensionPoint);
+}
diff --git a/extensions/source/com/intellij/openapi/extensions/ExtensionPoint.java b/extensions/source/com/intellij/openapi/extensions/ExtensionPoint.java
new file mode 100644
index 000000000000..915a09dbb80a
--- /dev/null
+++ b/extensions/source/com/intellij/openapi/extensions/ExtensionPoint.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2000-2004 by JetBrains s.r.o. All Rights Reserved.
+ * Use is subject to license terms.
+ */
+package com.intellij.openapi.extensions;
+
+/**
+ * @author AKireyev
+ */
+public interface ExtensionPoint {
+ String getName();
+ AreaInstance getArea();
+
+ String getBeanClassName();
+
+ void registerExtension(Object extension);
+ void registerExtension(Object extension, LoadingOrder order);
+
+ Object[] getExtensions();
+ Object getExtension();
+ boolean hasExtension(Object extension);
+
+ void unregisterExtension(Object extension);
+
+ void addExtensionPointListener(ExtensionPointListener listener);
+ void removeExtensionPointListener(ExtensionPointListener extensionPointListener);
+
+ void reset();
+
+ Class getExtensionClass();
+}
diff --git a/extensions/source/com/intellij/openapi/extensions/ExtensionPointAvailabilityListener.java b/extensions/source/com/intellij/openapi/extensions/ExtensionPointAvailabilityListener.java
new file mode 100644
index 000000000000..6df20949788f
--- /dev/null
+++ b/extensions/source/com/intellij/openapi/extensions/ExtensionPointAvailabilityListener.java
@@ -0,0 +1,13 @@
+/*
+ * Copyright (c) 2000-2004 by JetBrains s.r.o. All Rights Reserved.
+ * Use is subject to license terms.
+ */
+package com.intellij.openapi.extensions;
+
+/**
+ * @author AKireyev
+ */
+public interface ExtensionPointAvailabilityListener {
+ void extensionPointRegistered(ExtensionPoint extensionPoint);
+ void extensionPointRemoved(ExtensionPoint extensionPoint);
+}
diff --git a/extensions/source/com/intellij/openapi/extensions/ExtensionPointListener.java b/extensions/source/com/intellij/openapi/extensions/ExtensionPointListener.java
new file mode 100644
index 000000000000..555c8d38268c
--- /dev/null
+++ b/extensions/source/com/intellij/openapi/extensions/ExtensionPointListener.java
@@ -0,0 +1,10 @@
+/*
+ * Copyright (c) 2000-2004 by JetBrains s.r.o. All Rights Reserved.
+ * Use is subject to license terms.
+ */
+package com.intellij.openapi.extensions;
+
+public interface ExtensionPointListener {
+ void extensionAdded(Object extension);
+ void extensionRemoved(Object extension);
+}
diff --git a/extensions/source/com/intellij/openapi/extensions/Extensions.java b/extensions/source/com/intellij/openapi/extensions/Extensions.java
new file mode 100644
index 000000000000..050198016230
--- /dev/null
+++ b/extensions/source/com/intellij/openapi/extensions/Extensions.java
@@ -0,0 +1,235 @@
+/*
+ * Copyright (c) 2000-2004 by JetBrains s.r.o. All Rights Reserved.
+ * Use is subject to license terms.
+ */
+package com.intellij.openapi.extensions;
+
+import com.intellij.openapi.extensions.impl.ExtensionsAreaImpl;
+import org.apache.commons.collections.MultiHashMap;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+// todo: make default area instance a non-null object
+
+public abstract class Extensions {
+ private static LogProvider ourLogger = new SimpleLogProvider();
+
+ public static final String AREA_LISTENER_EXTENSION_POINT = "jetbrains.fabrique.platform.areaListeners";
+
+ private static Map ourAreaClass2prototypeArea;
+ private static Map ourAreaInstance2area;
+ private static MultiHashMap ourAreaClass2instances;
+ private static Map ourAreaInstance2class;
+ private static Map ourAreaClass2Configuration;
+
+ public static ExtensionsArea getRootArea() {
+ return getArea(null);
+ }
+
+ public static ExtensionsArea getArea(AreaInstance areaInstance) {
+ init();
+ if (!ourAreaInstance2area.containsKey(areaInstance)) {
+ throw new IllegalArgumentException("No area instantiated for: " + areaInstance);
+ }
+ return (ExtensionsArea) ourAreaInstance2area.get(areaInstance);
+ }
+
+ public static Object[] getExtensions(String extensionPointName) {
+ return getExtensions(extensionPointName, null);
+ }
+
+ public static Object[] getExtensions(String extensionPointName, AreaInstance areaInstance) {
+ ExtensionsArea area = getArea(areaInstance);
+ assert area != null: "Unable to get area for " + areaInstance;
+ ExtensionPoint extensionPoint = area.getExtensionPoint(extensionPointName);
+ assert extensionPoint != null: "Unable to get extension point " + extensionPoint + " for " + areaInstance;
+ return extensionPoint.getExtensions();
+ }
+
+ private static void init() {
+ if (ourAreaInstance2area == null) {
+ ourAreaInstance2area = new HashMap();
+ ourAreaClass2prototypeArea = new HashMap();
+ ourAreaClass2instances = new MultiHashMap();
+ ourAreaInstance2class = new HashMap();
+ ourAreaClass2Configuration = new HashMap();
+ ExtensionsAreaImpl rootArea = new ExtensionsAreaImpl(null, null, null, ourLogger);
+ ourAreaInstance2area.put(null, rootArea);
+ ourAreaClass2prototypeArea.put(null, rootArea);
+ rootArea.registerExtensionPoint(AREA_LISTENER_EXTENSION_POINT, AreaListener.class.getName());
+ }
+ }
+
+ static void reset() {
+ ourAreaInstance2area = null;
+ ourAreaClass2instances = null;
+ ourAreaClass2prototypeArea = null;
+ ourAreaInstance2class = null;
+ }
+
+ public static void instantiateArea(String areaClass, AreaInstance areaInstance, AreaInstance parentAreaInstance) {
+ if (areaClass == null) {
+ throw new IllegalArgumentException("Should not try to instantiate the root area");
+ }
+ init();
+ if (!ourAreaClass2Configuration.containsKey(areaClass)) {
+ throw new IllegalArgumentException("Area class is not registered: " + areaClass);
+ }
+ if (ourAreaInstance2area.containsKey(areaInstance)) {
+ throw new IllegalArgumentException("Area already instantiated for: " + areaInstance);
+ }
+ ExtensionsArea parentArea = getArea(parentAreaInstance);
+ AreaClassConfiguration configuration = (AreaClassConfiguration)ourAreaClass2Configuration.get(areaClass);
+ if (!equals(parentArea.getAreaClass(), configuration.getParentClassName())) {
+ throw new IllegalArgumentException("Wrong parent area. Expected class: " + configuration.getParentClassName() + " actual class: " + parentArea.getAreaClass());
+ }
+ ExtensionsAreaImpl area = new ExtensionsAreaImpl(areaClass, areaInstance, parentArea.getPicoContainer(), ourLogger);
+ ourAreaInstance2area.put(areaInstance, area);
+ ourAreaClass2instances.put(areaClass, areaInstance);
+ ourAreaInstance2class.put(areaInstance, areaClass);
+ AreaListener[] listeners = getAreaListeners();
+ for (int i = 0; i < listeners.length; i++) {
+ AreaListener listener = listeners[i];
+ listener.areaCreated(areaClass, areaInstance);
+ }
+ }
+
+ private static AreaListener[] getAreaListeners() {
+ AreaListener[] listeners = (AreaListener[]) getRootArea().getExtensionPoint(AREA_LISTENER_EXTENSION_POINT).getExtensions();
+ return listeners;
+ }
+
+ public static void registerAreaClass(String areaClass, String parentAreaClass) {
+ init();
+ if (ourAreaClass2Configuration.containsKey(areaClass)) {
+ // allow duplicate area class registrations if they are the same - fixing duplicate registration in tests is much more trouble
+ AreaClassConfiguration configuration = (AreaClassConfiguration)ourAreaClass2Configuration.get(areaClass);
+ if (!equals(configuration.getParentClassName(), parentAreaClass)) {
+ throw new RuntimeException("Area class already registered: " + areaClass, ((AreaClassConfiguration)ourAreaClass2Configuration.get(areaClass)).getCreationPoint());
+ }
+ else {
+ return;
+ }
+ }
+ AreaClassConfiguration configuration = new AreaClassConfiguration(areaClass, parentAreaClass);
+ ourAreaClass2Configuration.put(areaClass, configuration);
+ }
+
+ public static void disposeArea(AreaInstance areaInstance) {
+ assert ourAreaInstance2area.containsKey(areaInstance);
+ if (areaInstance == null) {
+ throw new IllegalArgumentException("Cannot dispose root area");
+ }
+
+ AreaListener[] listeners = getAreaListeners();
+ String areaClass = (String) ourAreaInstance2class.get(areaInstance);
+ if (areaClass == null) {
+ throw new IllegalArgumentException("Area class is null (area never instantiated?). Instance: " + areaInstance);
+ }
+ try {
+ for (int i = 0; i < listeners.length; i++) {
+ listeners[i].areaDisposing(areaClass, areaInstance);
+ }
+ } finally {
+ ourAreaInstance2area.remove(areaInstance);
+ ourAreaClass2instances.remove(ourAreaInstance2class.remove(areaInstance), areaInstance);
+ ourAreaInstance2class.remove(areaInstance);
+ }
+ }
+
+ public static AreaInstance[] getAllAreas() {
+ init();
+ final Set keys = ourAreaInstance2area.keySet();
+ return (AreaInstance[]) keys.toArray(new AreaInstance[keys.size()]);
+ }
+
+ public static AreaInstance[] getAllAreas(String areaClass) {
+ Collection instances = (Collection) ourAreaClass2instances.get(areaClass);
+ if (instances != null) {
+ return (AreaInstance[]) instances.toArray(new AreaInstance[instances.size()]);
+ }
+ return new AreaInstance[0];
+ }
+
+ public static Object getAreaClass(AreaInstance areaInstance) {
+ if (areaInstance == null) return null;
+
+ assert ourAreaInstance2class.containsKey(areaInstance);
+ return ourAreaInstance2class.get(areaInstance);
+ }
+
+ public static void unregisterAreaClass(String areaClass) {
+ init();
+ assert ourAreaClass2Configuration.containsKey(areaClass) : "Area class is not registered: " + areaClass;
+ ourAreaClass2Configuration.remove(areaClass);
+ }
+
+ private static boolean equals(Object object1, Object object2) {
+ if (object1 == object2) {
+ return true;
+ }
+ if ((object1 == null) || (object2 == null)) {
+ return false;
+ }
+ return object1.equals(object2);
+ }
+
+ public static void setLogProvider(LogProvider logProvider) {
+ ourLogger = logProvider;
+ }
+
+ private static class AreaClassConfiguration {
+ private String myClassName;
+ private String myParentClassName;
+ private Throwable myCreationPoint;
+
+ AreaClassConfiguration(String className, String parentClassName) {
+ myCreationPoint = new Throwable();
+ myClassName = className;
+ myParentClassName = parentClassName;
+ }
+
+ public Throwable getCreationPoint() {
+ return myCreationPoint;
+ }
+
+ public String getClassName() {
+ return myClassName;
+ }
+
+ public String getParentClassName() {
+ return myParentClassName;
+ }
+ }
+
+ public static class SimpleLogProvider implements LogProvider {
+ public void error(String message) {
+ new Throwable(message).printStackTrace();
+ }
+
+ public void error(String message, Throwable t) {
+ System.err.println(message);
+ t.printStackTrace();
+ }
+
+ public void error(Throwable t) {
+ t.printStackTrace();
+ }
+
+ public void warn(String message) {
+ System.err.println(message);
+ }
+
+ public void warn(String message, Throwable t) {
+ System.err.println(message);
+ t.printStackTrace();
+ }
+
+ public void warn(Throwable t) {
+ t.printStackTrace();
+ }
+ }
+}
diff --git a/extensions/source/com/intellij/openapi/extensions/ExtensionsArea.java b/extensions/source/com/intellij/openapi/extensions/ExtensionsArea.java
new file mode 100644
index 000000000000..2860dace1f8f
--- /dev/null
+++ b/extensions/source/com/intellij/openapi/extensions/ExtensionsArea.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2000-2004 by JetBrains s.r.o. All Rights Reserved.
+ * Use is subject to license terms.
+ */
+package com.intellij.openapi.extensions;
+
+import org.jdom.Element;
+import org.picocontainer.MutablePicoContainer;
+import org.picocontainer.PicoContainer;
+
+/**
+ * @author AKireyev
+ */
+public interface ExtensionsArea {
+
+ void registerExtensionPoint(String extensionPointName, String extensionPointBeanClass);
+ void unregisterExtensionPoint(String extensionPointName);
+
+ boolean hasExtensionPoint(String extensionPointName);
+ ExtensionPoint getExtensionPoint(String extensionPointName);
+
+ ExtensionPoint[] getExtensionPoints();
+
+ void suspendInteractions();
+ void resumeInteractions();
+ void killPendingInteractions();
+
+ void addAvailabilityListener(String epName, ExtensionPointAvailabilityListener listener);
+
+ MutablePicoContainer getPicoContainer();
+
+ void registerExtensionPoint(String pluginName, Element extensionPointElement);
+ void registerExtension(String pluginName, Element extensionElement);
+
+ void unregisterExtensionPoint(String pluginName, Element extensionPointElement);
+
+ void unregisterExtension(String pluginName, Element extensionElement);
+
+ PicoContainer getPluginContainer(String pluginName);
+
+ String getAreaClass();
+}
diff --git a/extensions/source/com/intellij/openapi/extensions/LoadingOrder.java b/extensions/source/com/intellij/openapi/extensions/LoadingOrder.java
new file mode 100644
index 000000000000..558874a50bc6
--- /dev/null
+++ b/extensions/source/com/intellij/openapi/extensions/LoadingOrder.java
@@ -0,0 +1,244 @@
+/*
+ * Copyright (c) 2000-2004 by JetBrains s.r.o. All Rights Reserved.
+ * Use is subject to license terms.
+ */
+package com.intellij.openapi.extensions;
+
+import org.jdom.Element;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author Alexander Kireyev
+ */
+public abstract class LoadingOrder {
+ public static final LoadingOrder ANY = new LoadingOrder("ANY") {
+ int findPlace(Orderable[] orderables, int current) {
+ return DONT_CARE;
+ }
+ };
+ public static final LoadingOrder FIRST = new LoadingOrder("FIRST") {
+ int findPlace(Orderable[] orderables, int current) {
+ return SPECIAL;
+ }
+ };
+ public static final LoadingOrder LAST = new LoadingOrder("LAST") {
+ int findPlace(Orderable[] orderables, int current) {
+ return SPECIAL;
+ }
+ };
+
+ static final int DONT_CARE = -1;
+ static final int ACCEPTABLE = -2;
+ static final int SPECIAL = -3;
+
+ private final String myName; // for debug only
+ private static final String BEFORE_STR = "BEFORE:";
+ private static final String AFTER_STR = "AFTER:";
+
+ private LoadingOrder(String name) {
+ myName = name;
+ }
+
+ public String toString() {
+ return myName;
+ }
+
+ public static LoadingOrder before(final String id) {
+ return new BeforeLoadingOrder(id);
+ }
+
+ public static LoadingOrder after(final String id) {
+ return new AfterLoadingOrder(id);
+ }
+
+ abstract int findPlace(Orderable[] orderables, int current);
+
+ public static void sort(Orderable[] orderables) {
+ Orderable first = null;
+ Orderable last = null;
+ List other = new ArrayList();
+ for (int i = 0; i < orderables.length; i++) {
+ Orderable orderable = orderables[i];
+ if (orderable.getOrder() == FIRST) {
+ if (first != null) {
+ throw new SortingException("More than one 'first' element", new Element[] {first.getDescribingElement(), orderable.getDescribingElement()});
+ }
+ first = orderable;
+ }
+ else if (orderable.getOrder() == LAST) {
+ if (last != null) {
+ throw new SortingException("More than one 'last' element", new Element[] {last.getDescribingElement(), orderable.getDescribingElement()});
+ }
+ last = orderable;
+ }
+ else {
+ other.add(orderable);
+ }
+ }
+ List result = new ArrayList();
+ if (first != null) {
+ result.add(first);
+ }
+ result.addAll(other);
+ if (last != null) {
+ result.add(last);
+ }
+
+ assert result.size() == orderables.length;
+
+ Orderable[] presorted = (Orderable[]) result.toArray(new Orderable[result.size()]);
+
+ int swapCount = 0;
+ int maxSwaps = presorted.length * presorted.length;
+ for (int i = 0; i < presorted.length; i++) {
+ Orderable orderable = presorted[i];
+ LoadingOrder order = orderable.getOrder();
+ int place = order.findPlace(presorted, i);
+ if (place == DONT_CARE || place == ACCEPTABLE || place == SPECIAL) {
+ continue;
+ }
+ if (place == 0 && presorted[0].getOrder() == FIRST) {
+ throw new SortingException("Element attempts to go before the specified first", new Element[] {orderable.getDescribingElement(), presorted[0].getDescribingElement()});
+ }
+ if (place == presorted.length - 1 && presorted[presorted.length - 1].getOrder() == LAST) {
+ throw new SortingException("Element attempts to go after the specified last", new Element[] {orderable.getDescribingElement(), presorted[presorted.length - 1].getDescribingElement()});
+ }
+ moveTo(presorted, i, place);
+ if (i > place) {
+ i = place;
+ }
+ else {
+ i--;
+ }
+ swapCount++;
+ if (swapCount > maxSwaps) {
+ List allElements = new ArrayList();
+ for (int j = 0; j < presorted.length; j++) {
+ allElements.add(presorted[j].getDescribingElement());
+ }
+ throw new SortingException("Could not satisfy sorting requirements", (Element[]) allElements.toArray(new Element[allElements.size()]));
+ }
+ }
+
+ System.arraycopy(presorted, 0, orderables, 0, presorted.length);
+ }
+
+ private static void moveTo(Orderable[] orderables, int from, int to) {
+ if (to == from) return;
+ Orderable movedOrderable = orderables[from];
+ if (to > from) {
+ for (int i = from; i < to; i++) {
+ orderables[i] = orderables[i + 1];
+ }
+ }
+ else {
+ for (int i = from; i > to; i--) {
+ orderables[i] = orderables[i - 1];
+ }
+ }
+ orderables[to] = movedOrderable;
+ }
+
+ public static LoadingOrder readOrder(String orderAttr) {
+ if (orderAttr != null) {
+ if ("FIRST".equalsIgnoreCase(orderAttr)) return FIRST;
+ if ("LAST".equalsIgnoreCase(orderAttr)) return LAST;
+ if ("ANY".equalsIgnoreCase(orderAttr)) return ANY;
+ if (orderAttr.toUpperCase().startsWith(BEFORE_STR)) {
+ return before(orderAttr.substring(BEFORE_STR.length()));
+ }
+ if (orderAttr.toUpperCase().startsWith(AFTER_STR)) {
+ return after(orderAttr.substring(AFTER_STR.length()));
+ }
+ }
+ return ANY;
+ }
+
+ private static class BeforeLoadingOrder extends LoadingOrder {
+ private final String myId;
+
+ public BeforeLoadingOrder(String id) {
+ super(LoadingOrder.BEFORE_STR + id);
+ myId = id;
+ }
+
+ int findPlace(Orderable[] orderables, int current) {
+ for (int i = 0; i < orderables.length; i++) {
+ Orderable orderable = orderables[i];
+ String orderId = orderable.getOrderId();
+ if (myId.equals(orderId)) {
+ if (current < i) {
+ return ACCEPTABLE;
+ }
+ else {
+ return i;
+ }
+ }
+ }
+ return DONT_CARE;
+ }
+
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof BeforeLoadingOrder)) return false;
+
+ final BeforeLoadingOrder beforeLoadingOrder = (BeforeLoadingOrder) o;
+
+ if (!myId.equals(beforeLoadingOrder.myId)) return false;
+
+ return true;
+ }
+
+ public int hashCode() {
+ return myId.hashCode();
+ }
+ }
+
+ private static class AfterLoadingOrder extends LoadingOrder {
+ private final String myId;
+
+ public AfterLoadingOrder(String id) {
+ super(LoadingOrder.AFTER_STR + id);
+ myId = id;
+ }
+
+ int findPlace(Orderable[] orderables, int current) {
+ for (int i = 0; i < orderables.length; i++) {
+ Orderable orderable = orderables[i];
+ String orderId = orderable.getOrderId();
+ if (myId.equals(orderId)) {
+ if (current > i) {
+ return ACCEPTABLE;
+ }
+ else {
+ return i;
+ }
+ }
+ }
+ return DONT_CARE;
+ }
+
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof AfterLoadingOrder)) return false;
+
+ final AfterLoadingOrder afterLoadingOrder = (AfterLoadingOrder) o;
+
+ if (!myId.equals(afterLoadingOrder.myId)) return false;
+
+ return true;
+ }
+
+ public int hashCode() {
+ return myId.hashCode();
+ }
+ }
+
+ public interface Orderable {
+ String getOrderId();
+ LoadingOrder getOrder();
+ Element getDescribingElement();
+ }
+}
diff --git a/extensions/source/com/intellij/openapi/extensions/LogProvider.java b/extensions/source/com/intellij/openapi/extensions/LogProvider.java
new file mode 100644
index 000000000000..755147ec4904
--- /dev/null
+++ b/extensions/source/com/intellij/openapi/extensions/LogProvider.java
@@ -0,0 +1,18 @@
+/*
+ * Copyright (c) 2000-2004 by JetBrains s.r.o. All Rights Reserved.
+ * Use is subject to license terms.
+ */
+package com.intellij.openapi.extensions;
+
+/**
+ * @author Alexander Kireyev
+ */
+public interface LogProvider {
+ void error(String message);
+ void error(String message, Throwable t);
+ void error(Throwable t);
+
+ void warn(String message);
+ void warn(String message, Throwable t);
+ void warn(Throwable t);
+}
diff --git a/extensions/source/com/intellij/openapi/extensions/PluginAware.java b/extensions/source/com/intellij/openapi/extensions/PluginAware.java
new file mode 100644
index 000000000000..ca01c8cd34d7
--- /dev/null
+++ b/extensions/source/com/intellij/openapi/extensions/PluginAware.java
@@ -0,0 +1,12 @@
+/*
+ * Copyright (c) 2000-2004 by JetBrains s.r.o. All Rights Reserved.
+ * Use is subject to license terms.
+ */
+package com.intellij.openapi.extensions;
+
+/**
+ * @author akireyev
+ */
+public interface PluginAware {
+ void setPluginName(String pluginName);
+}
diff --git a/extensions/source/com/intellij/openapi/extensions/ReaderConfigurator.java b/extensions/source/com/intellij/openapi/extensions/ReaderConfigurator.java
new file mode 100644
index 000000000000..04895ab88e42
--- /dev/null
+++ b/extensions/source/com/intellij/openapi/extensions/ReaderConfigurator.java
@@ -0,0 +1,14 @@
+/*
+ * Copyright (c) 2000-2004 by JetBrains s.r.o. All Rights Reserved.
+ * Use is subject to license terms.
+ */
+package com.intellij.openapi.extensions;
+
+import com.thoughtworks.xstream.XStream;
+
+/**
+ * @author AKireyev
+ */
+public interface ReaderConfigurator {
+ void configureReader(XStream xstream);
+}
diff --git a/extensions/source/com/intellij/openapi/extensions/SortingException.java b/extensions/source/com/intellij/openapi/extensions/SortingException.java
new file mode 100644
index 000000000000..4f62fd40a03f
--- /dev/null
+++ b/extensions/source/com/intellij/openapi/extensions/SortingException.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2000-2004 by JetBrains s.r.o. All Rights Reserved.
+ * Use is subject to license terms.
+ */
+package com.intellij.openapi.extensions;
+
+import org.jdom.Element;
+
+/**
+ * @author Alexander Kireyev
+ */
+public class SortingException extends RuntimeException {
+ private Element[] myConflictingElements;
+
+ public SortingException(String message, Element[] conflictingElements) {
+ super(message);
+ myConflictingElements = conflictingElements;
+ }
+
+ public Element[] getConflictingElements() {
+ return myConflictingElements;
+ }
+}
diff --git a/extensions/source/com/intellij/openapi/extensions/impl/AreaDescriptor.java b/extensions/source/com/intellij/openapi/extensions/impl/AreaDescriptor.java
new file mode 100644
index 000000000000..108ed27950d4
--- /dev/null
+++ b/extensions/source/com/intellij/openapi/extensions/impl/AreaDescriptor.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2000-2004 by JetBrains s.r.o. All Rights Reserved.
+ * Use is subject to license terms.
+ */
+package com.intellij.openapi.extensions.impl;
+
+import com.intellij.openapi.extensions.AreaInstance;
+
+/**
+ * @author akireyev
+ */
+class AreaDescriptor {
+ private String myAreaClass;
+ private AreaInstance myInstance;
+
+ public AreaDescriptor(String aClass, AreaInstance instance) {
+ myAreaClass = aClass;
+ myInstance = instance;
+ }
+
+ public String getAreaClass() {
+ return myAreaClass;
+ }
+
+ public AreaInstance getAreaInstance() {
+ return myInstance;
+ }
+
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof AreaDescriptor)) return false;
+
+ final AreaDescriptor areaDescriptor = (AreaDescriptor) o;
+
+ if (myAreaClass != null ? !myAreaClass.equals(areaDescriptor.myAreaClass) : areaDescriptor.myAreaClass != null) return false;
+ if (myInstance != null ? !myInstance.equals(areaDescriptor.myInstance) : areaDescriptor.myInstance != null) return false;
+
+ return true;
+ }
+
+ public int hashCode() {
+ int result;
+ result = (myAreaClass != null ? myAreaClass.hashCode() : 0);
+ result = 29 * result + (myInstance != null ? myInstance.hashCode() : 0);
+ return result;
+ }
+}
diff --git a/extensions/source/com/intellij/openapi/extensions/impl/AreaPicoContainer.java b/extensions/source/com/intellij/openapi/extensions/impl/AreaPicoContainer.java
new file mode 100644
index 000000000000..56ddc69de573
--- /dev/null
+++ b/extensions/source/com/intellij/openapi/extensions/impl/AreaPicoContainer.java
@@ -0,0 +1,175 @@
+/*
+ * Copyright (c) 2000-2004 by JetBrains s.r.o. All Rights Reserved.
+ * Use is subject to license terms.
+ */
+package com.intellij.openapi.extensions.impl;
+
+import org.picocontainer.MutablePicoContainer;
+import org.picocontainer.PicoContainer;
+import org.picocontainer.ComponentAdapter;
+import org.picocontainer.Parameter;
+import org.picocontainer.defaults.DefaultPicoContainer;
+import org.picocontainer.defaults.AmbiguousComponentResolutionException;
+import org.picocontainer.defaults.AbstractPicoVisitor;
+import org.picocontainer.alternatives.AbstractDelegatingMutablePicoContainer;
+
+import java.util.*;
+
+/**
+ * @author Alexander Kireyev
+ */
+public class AreaPicoContainer extends AbstractDelegatingMutablePicoContainer implements MutablePicoContainer {
+ private ExtensionsAreaImpl myArea;
+
+ public AreaPicoContainer(PicoContainer parentPicoContainer, ExtensionsAreaImpl area) {
+ super(new DefaultPicoContainer(parentPicoContainer));
+ myArea = area;
+ }
+
+ public ComponentAdapter getComponentAdapter(final Object componentKey) {
+ final ComponentAdapter[] result = new ComponentAdapter[] { null };
+ accept(new EmptyPicoVisitor() {
+ public void visitComponentAdapter(ComponentAdapter componentAdapter) {
+ if (componentKey.equals(componentAdapter.getComponentKey())) {
+ result[0] = componentAdapter;
+ }
+ }
+ });
+ if (result[0] != null) {
+ return result[0];
+ }
+ else {
+ if (getParent() != null) {
+ return getParent().getComponentAdapter(componentKey);
+ }
+ else {
+ return null;
+ }
+ }
+ }
+
+ public List getComponentInstances() {
+ final List result = new ArrayList();
+ accept(new EmptyPicoVisitor() {
+ public void visitContainer(PicoContainer pico) {
+ result.addAll(pico.getComponentInstances());
+ }
+ });
+ return result;
+ }
+
+ public List getComponentInstancesOfType(final Class type) {
+ final List result = new ArrayList();
+ accept(new EmptyPicoVisitor() {
+ public void visitContainer(PicoContainer pico) {
+ result.addAll(pico.getComponentInstancesOfType(type));
+ }
+ });
+ return result;
+ }
+
+ public Object getComponentInstanceOfType(Class componentType) {
+ List instances = getComponentInstancesOfType(componentType);
+ if (instances.size() == 0) {
+ return null;
+ }
+ else if (instances.size() == 1) {
+ return instances.get(0);
+ }
+ else {
+ throw new AmbiguousComponentResolutionException(componentType, instances.toArray(new Object[instances.size()]));
+ }
+ }
+
+ public Object getComponentInstance(final Object componentKey) {
+ if (getParent() != null) {
+ Object parentInstance = getParent().getComponentInstance(componentKey);
+ if (parentInstance != null) {
+ return parentInstance;
+ }
+ }
+
+ final Object[] result = new Object[] { null };
+ accept(new EmptyPicoVisitor() {
+ public void visitContainer(PicoContainer pico) {
+ final boolean[] found = new boolean[] { false };
+ pico.accept(new EmptyPicoVisitor() {
+ public void visitComponentAdapter(ComponentAdapter componentAdapter) {
+ if (componentKey.equals(componentAdapter.getComponentKey())) {
+ found[0] = true;
+ }
+ }
+ });
+ if (!found[0]) {
+ return;
+ }
+ Object componentInstance = pico.getComponentInstance(componentKey);
+ if (componentInstance != null) {
+ result[0] = componentInstance;
+ }
+ }
+ });
+ return result[0];
+ }
+
+ public ComponentAdapter getComponentAdapterOfType(Class componentType) {
+ List adapters = getComponentAdaptersOfType(componentType);
+ if (adapters.size() == 0) {
+ return null;
+ }
+ else if (adapters.size() == 1) {
+ return (ComponentAdapter) adapters.get(0);
+ }
+ else {
+ Class[] foundClasses = new Class[adapters.size()];
+ for (int i = 0; i < foundClasses.length; i++) {
+ ComponentAdapter componentAdapter = (ComponentAdapter) adapters.get(i);
+ foundClasses[i] = componentAdapter.getComponentImplementation();
+ }
+ throw new AmbiguousComponentResolutionException(componentType, foundClasses);
+ }
+ }
+
+ public Collection getComponentAdapters() {
+ final List result = new ArrayList();
+ if (getParent() != null) {
+ result.addAll(getParent().getComponentAdapters());
+ }
+ accept(new EmptyPicoVisitor() {
+ public void visitComponentAdapter(ComponentAdapter componentAdapter) {
+ result.add(componentAdapter);
+ }
+ });
+ return result;
+ }
+
+ public List getComponentAdaptersOfType(final Class componentType) {
+ final List result = new ArrayList();
+ if (getParent() != null) {
+ result.addAll(getParent().getComponentAdaptersOfType(componentType));
+ }
+ accept(new EmptyPicoVisitor() {
+ public void visitComponentAdapter(ComponentAdapter componentAdapter) {
+ if (componentType.isAssignableFrom(componentAdapter.getComponentImplementation())) {
+ result.add(componentAdapter);
+ }
+ }
+ });
+ return result;
+ }
+
+ public MutablePicoContainer makeChildContainer() {
+ throw new UnsupportedOperationException("Method makeChildContainer() is not implemented");
+ }
+
+ private abstract class EmptyPicoVisitor extends AbstractPicoVisitor {
+ public void visitContainer(PicoContainer pico) {
+ }
+
+ public void visitComponentAdapter(ComponentAdapter componentAdapter) {
+ }
+
+ public void visitParameter(Parameter parameter) {
+ }
+ }
+}
diff --git a/extensions/source/com/intellij/openapi/extensions/impl/ElementConverter.java b/extensions/source/com/intellij/openapi/extensions/impl/ElementConverter.java
new file mode 100644
index 000000000000..23b481b9a35f
--- /dev/null
+++ b/extensions/source/com/intellij/openapi/extensions/impl/ElementConverter.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2000-2004 by JetBrains s.r.o. All Rights Reserved.
+ * Use is subject to license terms.
+ */
+package com.intellij.openapi.extensions.impl;
+
+import com.thoughtworks.xstream.converters.Converter;
+import com.thoughtworks.xstream.converters.MarshallingContext;
+import com.thoughtworks.xstream.converters.UnmarshallingContext;
+import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
+import com.thoughtworks.xstream.io.HierarchicalStreamReader;
+import org.jdom.Element;
+
+/**
+ * @author Alexander Kireyev
+ */
+public class ElementConverter implements Converter {
+ public boolean canConvert(Class aClass) {
+ return Element.class.isAssignableFrom(aClass);
+ }
+
+ public void marshal(Object object, HierarchicalStreamWriter hierarchicalStreamWriter, MarshallingContext marshallingContext) {
+ throw new UnsupportedOperationException("This method is not yet implemented");
+ }
+
+ public Object unmarshal(HierarchicalStreamReader hierarchicalStreamReader, UnmarshallingContext unmarshallingContext) {
+ return hierarchicalStreamReader.peekUnderlyingNode();
+ }
+}
diff --git a/extensions/source/com/intellij/openapi/extensions/impl/ExtensionClassAndAreaInstance.java b/extensions/source/com/intellij/openapi/extensions/impl/ExtensionClassAndAreaInstance.java
new file mode 100644
index 000000000000..66edaf603a24
--- /dev/null
+++ b/extensions/source/com/intellij/openapi/extensions/impl/ExtensionClassAndAreaInstance.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2000-2004 by JetBrains s.r.o. All Rights Reserved.
+ * Use is subject to license terms.
+ */
+package com.intellij.openapi.extensions.impl;
+
+public class ExtensionClassAndAreaInstance {
+ private final Class myExtensionClass;
+ private final Object myAreaInstance;
+
+ public ExtensionClassAndAreaInstance(Class extensionClass, Object areaInstance) {
+ myExtensionClass = extensionClass;
+ myAreaInstance = areaInstance;
+ }
+
+ public Class getExtensionClass() {
+ return myExtensionClass;
+ }
+
+ public Object getAreaInstance() {
+ return myAreaInstance;
+ }
+
+ public boolean equals(Object areaInstance) {
+ if (this == areaInstance) return true;
+ if (!(areaInstance instanceof ExtensionClassAndAreaInstance)) return false;
+
+ final ExtensionClassAndAreaInstance extensionClassAndAreaInstance = (ExtensionClassAndAreaInstance) areaInstance;
+
+ if (myAreaInstance != null ? !myAreaInstance.equals(extensionClassAndAreaInstance.myAreaInstance) : extensionClassAndAreaInstance.myAreaInstance != null) return false;
+ if (!myExtensionClass.equals(extensionClassAndAreaInstance.myExtensionClass)) return false;
+
+ return true;
+ }
+
+ public int hashCode() {
+ int result;
+ result = myExtensionClass.hashCode();
+ result = 29 * result + (myAreaInstance != null ? myAreaInstance.hashCode() : 0);
+ return result;
+ }
+}
diff --git a/extensions/source/com/intellij/openapi/extensions/impl/ExtensionComponentAdapter.java b/extensions/source/com/intellij/openapi/extensions/impl/ExtensionComponentAdapter.java
new file mode 100644
index 000000000000..c75cea3b07c3
--- /dev/null
+++ b/extensions/source/com/intellij/openapi/extensions/impl/ExtensionComponentAdapter.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2000-2004 by JetBrains s.r.o. All Rights Reserved.
+ * Use is subject to license terms.
+ */
+package com.intellij.openapi.extensions.impl;
+
+import com.intellij.openapi.extensions.LoadingOrder;
+import com.intellij.openapi.extensions.PluginAware;
+import com.intellij.openapi.extensions.ReaderConfigurator;
+import com.thoughtworks.xstream.XStream;
+import org.jdom.Element;
+import org.picocontainer.PicoContainer;
+import org.picocontainer.PicoInitializationException;
+import org.picocontainer.PicoIntrospectionException;
+import org.picocontainer.defaults.AssignabilityRegistrationException;
+import org.picocontainer.defaults.ConstructorInjectionComponentAdapter;
+import org.picocontainer.defaults.NotConcreteRegistrationException;
+
+/**
+ * @author Alexander Kireyev
+ */
+public class ExtensionComponentAdapter extends ConstructorInjectionComponentAdapter implements LoadingOrder.Orderable {
+ private Object myComponentInstance;
+ private Element myExtensionElement;
+ private PicoContainer myContainer;
+ private String myPluginName;
+
+ public ExtensionComponentAdapter(Class implementationClass, Element extensionElement, PicoContainer container, String pluginName) {
+ super(new Object(), implementationClass);
+ myExtensionElement = extensionElement;
+ myContainer = container;
+ myPluginName = pluginName;
+ }
+
+ public Object getComponentInstance(final PicoContainer container) throws PicoInitializationException, PicoIntrospectionException, AssignabilityRegistrationException, NotConcreteRegistrationException {
+ assert myContainer == container;
+
+ if (myComponentInstance == null) {
+ if (!Element.class.equals(getComponentImplementation())) {
+ XStream xStream = new XStream(new PropertyReflectionProvider());
+ xStream.registerConverter(new ElementConverter());
+ Object componentInstance = super.getComponentInstance(container);
+ if (componentInstance instanceof ReaderConfigurator) {
+ ReaderConfigurator readerConfigurator = (ReaderConfigurator) componentInstance;
+ readerConfigurator.configureReader(xStream);
+ }
+ xStream.alias(myExtensionElement.getName(), componentInstance.getClass());
+ myComponentInstance = xStream.unmarshal(new JDomReader(myExtensionElement), componentInstance);
+ }
+ else {
+ myComponentInstance = myExtensionElement;
+ }
+ if (myComponentInstance instanceof PluginAware) {
+ PluginAware pluginAware = (PluginAware) myComponentInstance;
+ pluginAware.setPluginName(myPluginName);
+ }
+ }
+
+ return myComponentInstance;
+ }
+
+ public Object getExtension() {
+ return getComponentInstance(myContainer);
+ }
+
+ public LoadingOrder getOrder() {
+ String orderAttr = myExtensionElement.getAttributeValue("order");
+ return LoadingOrder.readOrder(orderAttr);
+ }
+
+ public String getOrderId() {
+ return myExtensionElement.getAttributeValue("id");
+ }
+
+ public Element getExtensionElement() {
+ return myExtensionElement;
+ }
+
+ public Element getDescribingElement() {
+ return getExtensionElement();
+ }
+}
diff --git a/extensions/source/com/intellij/openapi/extensions/impl/ExtensionPointImpl.java b/extensions/source/com/intellij/openapi/extensions/impl/ExtensionPointImpl.java
new file mode 100644
index 000000000000..d5984388d98a
--- /dev/null
+++ b/extensions/source/com/intellij/openapi/extensions/impl/ExtensionPointImpl.java
@@ -0,0 +1,286 @@
+/*
+ * Copyright (c) 2000-2004 by JetBrains s.r.o. All Rights Reserved.
+ * Use is subject to license terms.
+ */
+package com.intellij.openapi.extensions.impl;
+
+import com.intellij.openapi.extensions.*;
+
+import java.lang.ref.SoftReference;
+import java.lang.reflect.Array;
+import java.util.*;
+
+import org.jdom.Element;
+
+/**
+ * @author AKireyev
+ */
+public class ExtensionPointImpl implements ExtensionPoint {
+ private final LogProvider myLogger;
+
+ private String myName;
+ private String myBeanClassName;
+ private List myExtensions = new ArrayList();
+ private List myLoadedAdapters = new ArrayList();
+ private Set myExtensionAdapters = new LinkedHashSet();
+ private Set myEPListeners = new LinkedHashSet();
+ private SoftReference myExtensionsCache;
+ private ExtensionsAreaImpl myOwner;
+ private final AreaInstance myArea;
+ private Class myExtensionClass;
+
+ public ExtensionPointImpl(String name, String beanClassName, ExtensionsAreaImpl owner, AreaInstance area, LogProvider logger) {
+ myName = name;
+ myBeanClassName = beanClassName;
+ myOwner = owner;
+ myArea = area;
+ myLogger = logger;
+ }
+
+ public String getName() {
+ return myName;
+ }
+
+ public AreaInstance getArea() {
+ return myArea;
+ }
+
+ public String getBeanClassName() {
+ return myBeanClassName;
+ }
+
+ public void registerExtension(Object extension) {
+ registerExtension(extension, LoadingOrder.ANY);
+ }
+
+ public void registerExtension(Object extension, LoadingOrder order) {
+ assert (extension != null) : "Extension cannot be null";
+
+ myOwner.getMutablePicoContainer().registerComponentInstance(new Object(), extension);
+
+ assert myExtensions.size() == myLoadedAdapters.size();
+
+ if (LoadingOrder.ANY == order) {
+ int index = myLoadedAdapters.size();
+ if (myLoadedAdapters.size() > 0) {
+ ExtensionComponentAdapter lastAdapter = (ExtensionComponentAdapter) myLoadedAdapters.get(myLoadedAdapters.size() - 1);
+ if (lastAdapter.getOrder() == LoadingOrder.LAST) {
+ index--;
+ }
+ }
+ internalRegisterExtension(extension, new ObjectComponentAdapter(extension, order), index, true);
+ }
+ else {
+ myExtensionAdapters.add(new ObjectComponentAdapter(extension, order));
+ processAdapters();
+ }
+ }
+
+ private void internalRegisterExtension(Object extension, ExtensionComponentAdapter adapter, int index, boolean runNotifications) {
+ myExtensionsCache = null;
+
+ if (myExtensions.contains(extension)) {
+ myLogger.error("Extension was already added: " + extension);
+ }
+ else {
+ myExtensions.add(index, extension);
+ myLoadedAdapters.add(index, adapter);
+ if (runNotifications) {
+ if (extension instanceof Extension) {
+ Extension o = (Extension) extension;
+ try {
+ o.extensionAdded(this);
+ } catch (Throwable e) {
+ myLogger.error(e);
+ }
+ }
+
+ notifyListenersOnAdd(extension);
+ }
+ }
+ }
+
+ private void notifyListenersOnAdd(Object extension) {
+ ExtensionPointListener[] listeners = (ExtensionPointListener[]) myEPListeners.toArray(new ExtensionPointListener[myEPListeners.size()]);
+ for (int i = 0; i < listeners.length; i++) {
+ ExtensionPointListener listener = listeners[i];
+ try {
+ listener.extensionAdded(extension);
+ } catch (Throwable e) {
+ myLogger.error(e);
+ }
+ }
+ }
+
+ public Object[] getExtensions() {
+ Object[] result = null;
+
+ processAdapters();
+
+ if (myExtensionsCache != null) {
+ result = (Object[]) myExtensionsCache.get();
+ }
+ if (result == null) {
+ result = myExtensions.toArray((Object[])Array.newInstance(getExtensionClass(), myExtensions.size()));
+ myExtensionsCache = new SoftReference(result);
+ }
+
+ return result;
+ }
+
+ private void processAdapters() {
+ if (myExtensionAdapters.size() > 0) {
+ List allAdapters = new ArrayList(myExtensionAdapters.size() + myLoadedAdapters.size());
+ allAdapters.addAll(myExtensionAdapters);
+ allAdapters.addAll(myLoadedAdapters);
+ myExtensions.clear();
+ List loadedAdapters = myLoadedAdapters;
+ myLoadedAdapters = new ArrayList();
+ ExtensionComponentAdapter[] adapters = (ExtensionComponentAdapter[]) allAdapters.toArray(new ExtensionComponentAdapter[myExtensionAdapters.size()]);
+ LoadingOrder.sort(adapters);
+ for (int i = 0; i < adapters.length; i++) {
+ ExtensionComponentAdapter adapter = adapters[i];
+ Object extension = adapter.getExtension();
+ internalRegisterExtension(extension, adapter, i, !loadedAdapters.contains(adapter));
+ }
+ myExtensionAdapters.clear();
+ }
+ }
+
+ public Object getExtension() {
+ Object[] extensions = getExtensions();
+ if (extensions.length == 0) return null;
+
+ return extensions[0];
+ }
+
+ public boolean hasExtension(Object extension) {
+ processAdapters();
+
+ return myExtensions.contains(extension);
+ }
+
+ public void unregisterExtension(Object extension) {
+ assert (extension != null) : "Extension cannot be null";
+
+ myOwner.getMutablePicoContainer().unregisterComponentByInstance(extension);
+
+ processAdapters();
+
+ internalUnregisterExtension(extension);
+ }
+
+ private void internalUnregisterExtension(Object extension) {
+ myExtensionsCache = null;
+
+ if (!myExtensions.contains(extension)) {
+ throw new IllegalArgumentException("Extension to be removed not found: " + extension);
+ }
+ int index = myExtensions.indexOf(extension);
+ myExtensions.remove(index);
+ myLoadedAdapters.remove(index);
+
+ notifyListenersOnRemove(extension);
+
+ if (extension instanceof Extension) {
+ Extension o = (Extension) extension;
+ try {
+ o.extensionRemoved(this);
+ } catch (Throwable e) {
+ myLogger.error(e);
+ }
+ }
+ }
+
+ private void notifyListenersOnRemove(Object extensionObject) {
+ for (Iterator iterator = myEPListeners.iterator(); iterator.hasNext();) {
+ ExtensionPointListener listener = (ExtensionPointListener)iterator.next();
+ try {
+ listener.extensionRemoved(extensionObject);
+ } catch (Throwable e) {
+ myLogger.error(e);
+ }
+ }
+ }
+
+ public void addExtensionPointListener(ExtensionPointListener listener) {
+ if (myEPListeners.add(listener)) {
+ for (Iterator iterator = myExtensions.iterator(); iterator.hasNext();) {
+ try {
+ listener.extensionAdded(iterator.next());
+ } catch (Throwable e) {
+ myLogger.error(e);
+ }
+ }
+ }
+ }
+
+ public void removeExtensionPointListener(ExtensionPointListener listener) {
+ if (myEPListeners.contains(listener)) {
+ for (Iterator iterator = myExtensions.iterator(); iterator.hasNext();) {
+ try {
+ listener.extensionRemoved(iterator.next());
+ } catch (Throwable e) {
+ myLogger.error(e);
+ }
+ }
+
+ myEPListeners.remove(listener);
+ }
+ }
+
+ public void reset() {
+ Object[] extensions = getExtensions();
+ for (int i = 0; i < extensions.length; i++) {
+ Object extension = extensions[i];
+ unregisterExtension(extension);
+ }
+ }
+
+ public Class getExtensionClass() {
+ if (myExtensionClass == null) {
+ try {
+ myExtensionClass = Class.forName(myBeanClassName);
+ }
+ catch (ClassNotFoundException e) {
+ myExtensionClass = Object.class;
+ }
+ }
+ return myExtensionClass;
+ }
+
+ public String toString() {
+ return getName();
+ }
+
+ void registerExtensionAdapter(ExtensionComponentAdapter adapter) {
+ myExtensionAdapters.add(adapter);
+ }
+
+ private static class ObjectComponentAdapter extends ExtensionComponentAdapter {
+ private Object myExtension;
+ private LoadingOrder myLoadingOrder;
+
+ public ObjectComponentAdapter(Object extension, LoadingOrder loadingOrder) {
+ super(Object.class, null, null, null);
+ myExtension = extension;
+ myLoadingOrder = loadingOrder;
+ }
+
+ public Object getExtension() {
+ return myExtension;
+ }
+
+ public LoadingOrder getOrder() {
+ return myLoadingOrder;
+ }
+
+ public String getOrderId() {
+ return null;
+ }
+
+ public Element getDescribingElement() {
+ return new Element("RuntimeExtension: " + myExtension);
+ }
+ }
+}
diff --git a/extensions/source/com/intellij/openapi/extensions/impl/ExtensionsAreaImpl.java b/extensions/source/com/intellij/openapi/extensions/impl/ExtensionsAreaImpl.java
new file mode 100644
index 000000000000..fed9f828ff64
--- /dev/null
+++ b/extensions/source/com/intellij/openapi/extensions/impl/ExtensionsAreaImpl.java
@@ -0,0 +1,379 @@
+/*
+ * Copyright (c) 2000-2004 by JetBrains s.r.o. All Rights Reserved.
+ * Use is subject to license terms.
+ */
+package com.intellij.openapi.extensions.impl;
+
+import com.intellij.openapi.extensions.*;
+import org.apache.commons.collections.MultiHashMap;
+import org.apache.commons.collections.MultiMap;
+import org.jdom.Element;
+import org.jdom.Namespace;
+import org.jdom.output.XMLOutputter;
+import org.picocontainer.MutablePicoContainer;
+import org.picocontainer.PicoContainer;
+import org.picocontainer.defaults.ConstructorInjectionComponentAdapter;
+import org.picocontainer.defaults.DefaultPicoContainer;
+
+import java.io.IOException;
+import java.io.StringWriter;
+import java.lang.reflect.Modifier;
+import java.util.*;
+
+public class ExtensionsAreaImpl implements ExtensionsArea {
+ private final LogProvider myLogger;
+
+ static Map ourDefaultEPs = new HashMap();
+
+ static {
+ ourDefaultEPs.put(EPAvailabilityListenerExtension.EXTENSION_POINT_NAME, EPAvailabilityListenerExtension.class.getName());
+ }
+
+ private static boolean DEBUG_REGISTRATION = true;
+
+ private MutablePicoContainer myPicoContainer;
+// private Map myPluginName2picoContainer = new HashMap();
+ private Throwable myCreationTrace = null;
+ private Map myExtensionPoints = new HashMap();
+ private Map myEPTraces = new HashMap();
+ private MultiMap myAvailabilityListeners = new MultiHashMap();
+ private List mySuspendedListenerActions = new ArrayList();
+ private boolean myAvailabilityNotificationsActive = true;
+
+ private final AreaInstance myAreaInstance;
+ private MultiMap myServiceRegistry = new MultiHashMap();
+ private final String myAreaClass;
+ private Map myServiceClass2ServicesCache = new HashMap();
+ private Map myExtensionElement2extension = new HashMap();
+ private Map myPluginName2picoContainer = new HashMap();
+
+ public ExtensionsAreaImpl(String areaClass, AreaInstance areaInstance, PicoContainer parentPicoContainer, LogProvider logger) {
+ if (DEBUG_REGISTRATION) {
+ myCreationTrace = new Throwable("Area creation trace");
+ }
+ myAreaClass = areaClass;
+ myAreaInstance = areaInstance;
+ myPicoContainer = new AreaPicoContainer(parentPicoContainer, this);
+ if (areaInstance != null) {
+ myPicoContainer.registerComponentInstance(areaInstance);
+ }
+ initialize();
+ myLogger = logger;
+ }
+
+ public ExtensionsAreaImpl(MutablePicoContainer picoContainer, LogProvider logger) {
+ this(null, null, picoContainer, logger);
+ }
+
+ public MutablePicoContainer getPicoContainer() {
+ return myPicoContainer;
+ }
+
+ MutablePicoContainer getMutablePicoContainer() {
+ return myPicoContainer;
+ }
+
+ public String getAreaClass() {
+ return myAreaClass;
+ }
+
+ public void registerExtensionPoint(String pluginName, Element extensionPointElement) {
+ assert pluginName != null;
+ String epName = pluginName + '.' + extensionPointElement.getAttributeValue("name");
+ String className = extensionPointElement.getAttributeValue("beanClass");
+ if (className == null) {
+ className = extensionPointElement.getAttributeValue("interface");
+ }
+ if (className == null) {
+ throw new RuntimeException("No class specified for extension point: " + epName);
+ }
+ registerExtensionPoint(epName, className);
+ }
+
+ public void registerExtension(final String pluginName, final Element extensionElement) {
+ String epName = extractEPName(extensionElement);
+ ExtensionComponentAdapter adapter;
+ String implClass = null;
+ Class extensionClass = getExtensionPoint(epName).getExtensionClass();
+ implClass = extensionElement.getAttributeValue("implementation");
+ if (extensionClass.isInterface() || Modifier.isAbstract(extensionClass.getModifiers())) {
+ if (implClass == null) {
+ throw new RuntimeException("Expected implementation for extension declaration (ep = " + epName + ")");
+ }
+ }
+ if (implClass != null) {
+ try {
+ Class implementationClass = Class.forName(implClass);
+ adapter = new ExtensionComponentAdapter(implementationClass, extensionElement, getPluginContainer(pluginName), pluginName);
+ }
+ catch (ClassNotFoundException e) {
+ myLogger.warn("Extension implementation class not found: " + implClass);
+ myExtensionElement2extension.put(extensionElement, null);
+ return;
+ }
+ }
+ else {
+ final ExtensionPoint extensionPoint = getExtensionPoint(epName);
+ adapter = new ExtensionComponentAdapter(extensionPoint.getExtensionClass(), extensionElement, getPluginContainer(pluginName), pluginName);
+ }
+ myExtensionElement2extension.put(extensionElement, adapter);
+ internalGetPluginContainer(pluginName).registerComponent(adapter);
+ getExtensionPointImpl(epName).registerExtensionAdapter(adapter);
+ }
+
+ private String extractEPName(final Element extensionElement) {
+ String epName = extensionElement.getAttributeValue("point");
+ if (epName == null) {
+ Namespace namespace = extensionElement.getNamespace();
+ epName = namespace.getURI() + '.' + extensionElement.getName();
+ }
+ return epName;
+ }
+
+ public PicoContainer getPluginContainer(String pluginName) {
+ return internalGetPluginContainer(pluginName);
+ }
+
+ private MutablePicoContainer internalGetPluginContainer(String pluginName) {
+// return myPicoContainer;
+ DefaultPicoContainer pluginContainer = (DefaultPicoContainer) myPluginName2picoContainer.get(pluginName);
+ if (pluginContainer == null) {
+ pluginContainer = new DefaultPicoContainer(myPicoContainer);
+ myPicoContainer.addChildContainer(pluginContainer);
+ myPluginName2picoContainer.put(pluginName, pluginContainer);
+ }
+ return pluginContainer;
+ }
+
+ private void disposePluginContainer(String pluginName) {
+ DefaultPicoContainer pluginContainer = (DefaultPicoContainer) myPluginName2picoContainer.remove(pluginName);
+ if (pluginContainer != null) {
+ myPicoContainer.removeChildContainer(pluginContainer);
+ }
+ }
+
+ public void unregisterExtensionPoint(String pluginName, Element extensionPointElement) {
+ assert pluginName != null;
+ String epName = pluginName + '.' + extensionPointElement.getAttributeValue("name");
+ unregisterExtensionPoint(epName);
+ }
+
+ public void unregisterExtension(String pluginName, Element extensionElement) {
+ String epName = extractEPName(extensionElement);
+ if (!myExtensionElement2extension.containsKey(extensionElement)) {
+ XMLOutputter xmlOutputter = new XMLOutputter();
+ xmlOutputter.setIndent(" ");
+ xmlOutputter.setTextNormalize(true);
+ xmlOutputter.setNewlines(true);
+ StringWriter stringWriter = new StringWriter();
+ try {
+ xmlOutputter.output(extensionElement, stringWriter);
+ }
+ catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ myLogger.warn(stringWriter.toString());
+ throw new IllegalArgumentException("Trying to unregister extension element that was never registered");
+ }
+ ExtensionComponentAdapter adapter = (ExtensionComponentAdapter) myExtensionElement2extension.remove(extensionElement);
+ if (adapter == null) return;
+ if (getExtensionPoint(epName).hasExtension(adapter.getExtension())) {
+ getExtensionPoint(epName).unregisterExtension(adapter.getExtension());
+ MutablePicoContainer pluginContainer = internalGetPluginContainer(pluginName);
+ pluginContainer.unregisterComponent(adapter.getComponentKey());
+ if (pluginContainer.getComponentAdapters().size() == 0) {
+ disposePluginContainer(pluginName);
+ }
+ }
+ }
+
+ public void initialize() {
+ for (Iterator iterator = ourDefaultEPs.keySet().iterator(); iterator.hasNext();) {
+ String epName = (String) iterator.next();
+ registerExtensionPoint(epName, (String) ourDefaultEPs.get(epName));
+ }
+ getExtensionPoint(EPAvailabilityListenerExtension.EXTENSION_POINT_NAME).addExtensionPointListener(new ExtensionPointListener() {
+ public void extensionRemoved(Object extension) {
+ EPAvailabilityListenerExtension epListenerExtension = (EPAvailabilityListenerExtension) extension;
+ List listeners = (List) myAvailabilityListeners.get(epListenerExtension.getExtensionPointName());
+ for (Iterator iterator = listeners.iterator(); iterator.hasNext();) {
+ Object listener = iterator.next();
+ if (listener.getClass().getName().equals(epListenerExtension.getListenerClass())) {
+ iterator.remove();
+ return;
+ }
+ }
+ myLogger.warn("Failed to find EP availability listener: " + epListenerExtension.getListenerClass());
+ }
+
+ public void extensionAdded(Object extension) {
+ EPAvailabilityListenerExtension epListenerExtension = (EPAvailabilityListenerExtension) extension;
+ try {
+ String epName = epListenerExtension.getExtensionPointName();
+
+ ExtensionPointAvailabilityListener listener = (ExtensionPointAvailabilityListener) instantiate(Class.forName(epListenerExtension.getListenerClass()));
+ addAvailabilityListener(epName, listener);
+ }
+ catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+ });
+ }
+
+ public Object instantiate(Class clazz) {
+ ConstructorInjectionComponentAdapter adapter = new ConstructorInjectionComponentAdapter(System.identityHashCode(new Object()) + "", clazz);
+ return adapter.getComponentInstance(getPicoContainer());
+ }
+
+ public Throwable getCreationTrace() {
+ return myCreationTrace;
+ }
+
+ public void addAvailabilityListener(String epName, ExtensionPointAvailabilityListener listener) {
+ myAvailabilityListeners.put(epName, listener);
+ if (hasExtensionPoint(epName)) {
+ notifyAvailableListener(listener, (ExtensionPoint) myExtensionPoints.get(epName));
+ }
+ }
+
+ public void registerExtensionPoint(final String extensionPointName, String extensionPointBeanClass) {
+ if (hasExtensionPoint(extensionPointName)) {
+ if (DEBUG_REGISTRATION) {
+ myLogger.error((Throwable) myEPTraces.get(extensionPointName));
+ }
+ throw new RuntimeException("Duplicate registration for EP: " + extensionPointName);
+ }
+
+ ExtensionPointImpl extensionPoint = new ExtensionPointImpl(extensionPointName, extensionPointBeanClass, this, myAreaInstance, myLogger);
+ myExtensionPoints.put(extensionPointName, extensionPoint);
+ notifyEPRegistered(extensionPoint);
+ if (DEBUG_REGISTRATION) {
+ myEPTraces.put(extensionPointName, new Throwable("Original registration for " + extensionPointName));
+ }
+ }
+
+ private void notifyEPRegistered(final ExtensionPoint extensionPoint) {
+ List listeners = (List) myAvailabilityListeners.get(extensionPoint.getName());
+ if (listeners != null) {
+ for (Iterator i = listeners.iterator(); i.hasNext();) {
+ final ExtensionPointAvailabilityListener listener = (ExtensionPointAvailabilityListener) i.next();
+ notifyAvailableListener(listener, extensionPoint);
+ }
+ }
+ }
+
+ private void notifyAvailableListener(final ExtensionPointAvailabilityListener listener, final ExtensionPoint extensionPoint) {
+ Runnable action = new Runnable() {
+ public void run() {
+ listener.extensionPointRegistered(extensionPoint);
+ }
+ };
+ if (myAvailabilityNotificationsActive) {
+ action.run();
+ }
+ else {
+ mySuspendedListenerActions.add(action);
+ }
+ }
+
+ public ExtensionPoint getExtensionPoint(String extensionPointName) {
+ return getExtensionPointImpl(extensionPointName);
+ }
+
+ private ExtensionPointImpl getExtensionPointImpl(String extensionPointName) {
+ if (!hasExtensionPoint(extensionPointName)) {
+ throw new IllegalArgumentException("Missing extension point: " + extensionPointName +
+ " in area " + myAreaInstance );
+ }
+ return (ExtensionPointImpl) myExtensionPoints.get(extensionPointName);
+ }
+
+ public ExtensionPoint[] getExtensionPoints() {
+ return (ExtensionPoint[]) myExtensionPoints.values().toArray(new ExtensionPoint[myExtensionPoints.size()]);
+ }
+
+ public void unregisterExtensionPoint(final String extensionPointName) {
+ ExtensionPoint extensionPoint = (ExtensionPoint) myExtensionPoints.get(extensionPointName);
+ if (extensionPoint != null) {
+ extensionPoint.reset();
+ myExtensionPoints.remove(extensionPointName);
+ notifyEPRemoved(extensionPoint);
+ }
+ }
+
+ private void notifyEPRemoved(final ExtensionPoint extensionPoint) {
+ List listeners = (List) myAvailabilityListeners.get(extensionPoint.getName());
+ if (listeners != null) {
+ for (Iterator i = listeners.iterator(); i.hasNext();) {
+ final ExtensionPointAvailabilityListener listener = (ExtensionPointAvailabilityListener) i.next();
+ Runnable action = new Runnable() {
+ public void run() {
+ listener.extensionPointRemoved(extensionPoint);
+ }
+ };
+ if (myAvailabilityNotificationsActive) {
+ action.run();
+ }
+ else {
+ mySuspendedListenerActions.add(action);
+ }
+ }
+ }
+ }
+
+ public boolean hasExtensionPoint(String extensionPointName) {
+ return myExtensionPoints.containsKey(extensionPointName);
+ }
+
+ public void suspendInteractions() {
+ myAvailabilityNotificationsActive = false;
+ }
+
+ public void resumeInteractions() {
+ myAvailabilityNotificationsActive = true;
+ ExtensionPoint[] extensionPoints = getExtensionPoints();
+ for (int i = 0; i < extensionPoints.length; i++) {
+ ExtensionPoint extensionPoint = extensionPoints[i];
+ extensionPoint.getExtensions(); // creates extensions from ComponentAdapters
+ }
+ for (Iterator i = mySuspendedListenerActions.iterator(); i.hasNext();) {
+ Runnable action = (Runnable) i.next();
+ try {
+ action.run();
+ }
+ catch (Exception e) {
+ myLogger.error(e);
+ }
+ }
+ mySuspendedListenerActions.clear();
+ }
+
+ public void killPendingInteractions() {
+ mySuspendedListenerActions.clear();
+ }
+
+ private Set collectInterfaces(String className) {
+ try {
+ Class serviceClass = Class.forName(className);
+ Set classes = new HashSet();
+ classes.add(serviceClass.getName());
+ collectInterfaces(serviceClass, classes);
+ return classes;
+ }
+ catch (ClassNotFoundException e) {
+ return Collections.EMPTY_SET;
+ }
+ }
+
+ private void collectInterfaces(Class serviceClass, Set classes) {
+ Class[] interfaces = serviceClass.getInterfaces();
+ for (int i = 0; i < interfaces.length; i++) {
+ Class anInterface = interfaces[i];
+ if (!classes.contains(anInterface.getName())) {
+ classes.add(anInterface.getName());
+ collectInterfaces(anInterface, classes);
+ }
+ }
+ }
+}
diff --git a/extensions/testSource/com/intellij/openapi/extensions/impl/ExtensionComponentAdapterTest.java b/extensions/testSource/com/intellij/openapi/extensions/impl/ExtensionComponentAdapterTest.java
new file mode 100644
index 000000000000..d8089ece2e5f
--- /dev/null
+++ b/extensions/testSource/com/intellij/openapi/extensions/impl/ExtensionComponentAdapterTest.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2000-2004 by JetBrains s.r.o. All Rights Reserved.
+ * Use is subject to license terms.
+ */
+package com.intellij.openapi.extensions.impl;
+
+import org.jdom.Element;
+import org.jdom.JDOMException;
+import org.jdom.input.SAXBuilder;
+import org.jmock.cglib.MockObjectTestCase;
+import org.picocontainer.defaults.DefaultPicoContainer;
+
+import java.io.IOException;
+import java.io.StringReader;
+
+import com.intellij.openapi.extensions.LoadingOrder;
+
+/**
+ * @author Alexander Kireyev
+ */
+public class ExtensionComponentAdapterTest extends MockObjectTestCase {
+ public void testLoadingOrderReading() {
+ assertEquals(LoadingOrder.ANY, createAdapter("").getOrder());
+ assertEquals(LoadingOrder.FIRST, createAdapter("").getOrder());
+ assertEquals(LoadingOrder.LAST, createAdapter("").getOrder());
+ assertEquals(LoadingOrder.before("test"), createAdapter("").getOrder());
+ assertEquals(LoadingOrder.after("test"), createAdapter("").getOrder());
+ }
+
+ private ExtensionComponentAdapter createAdapter(String text) {
+ Element extensionElement = readElement(text);
+
+ ExtensionComponentAdapter adapter = new ExtensionComponentAdapter(Object.class, extensionElement, new DefaultPicoContainer(), "");
+ return adapter;
+ }
+
+ static Element readElement(String text) {
+ Element extensionElement1 = null;
+ try {
+ extensionElement1 = new SAXBuilder().build(new StringReader(text)).getRootElement();
+ }
+ catch (JDOMException e) {
+ throw new RuntimeException(e);
+ }
+ catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ Element extensionElement = extensionElement1;
+ return extensionElement;
+ }
+
+}
diff --git a/extensions/testSource/com/intellij/openapi/extensions/impl/ExtensionPointImplTest.java b/extensions/testSource/com/intellij/openapi/extensions/impl/ExtensionPointImplTest.java
new file mode 100644
index 000000000000..b5e57b861c41
--- /dev/null
+++ b/extensions/testSource/com/intellij/openapi/extensions/impl/ExtensionPointImplTest.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright (c) 2000-2004 by JetBrains s.r.o. All Rights Reserved.
+ * Use is subject to license terms.
+ */
+package com.intellij.openapi.extensions.impl;
+
+import com.intellij.openapi.extensions.*;
+import junit.framework.TestCase;
+import org.picocontainer.defaults.DefaultPicoContainer;
+
+/**
+ * @author AKireyev
+ */
+public class ExtensionPointImplTest extends TestCase {
+ public void testCreate() {
+ ExtensionPointImpl extensionPoint = buildExtensionPoint();
+ assertEquals(ExtensionsImplTest.EXTENSION_POINT_NAME_1, extensionPoint.getName());
+ assertEquals(Integer.class.getName(), extensionPoint.getBeanClassName());
+ }
+
+ private ExtensionPointImpl buildExtensionPoint() {
+ return new ExtensionPointImpl(ExtensionsImplTest.EXTENSION_POINT_NAME_1, Integer.class.getName(), buildExtensionArea(), null, new Extensions.SimpleLogProvider());
+ }
+
+ private ExtensionsAreaImpl buildExtensionArea() {
+ return new ExtensionsAreaImpl(new DefaultPicoContainer(), new Extensions.SimpleLogProvider());
+ }
+
+ public void testUnregisterObject() {
+ ExtensionPointImpl extensionPoint = buildExtensionPoint();
+ extensionPoint.registerExtension(new Integer(123));
+ Object[] extensions = extensionPoint.getExtensions();
+ assertEquals(1, extensions.length);
+ extensionPoint.unregisterExtension(new Integer(123));
+ extensions = extensionPoint.getExtensions();
+ assertEquals(0, extensions.length);
+ }
+
+ public void testRegisterUnregister_Extension() {
+
+ final AreaInstance area = new AreaInstance() {};
+ final ExtensionPointImpl extensionPoint = new ExtensionPointImpl("an.extension.point", Object.class.getName(), buildExtensionArea(), area, new Extensions.SimpleLogProvider());
+
+ final boolean[] flags = new boolean[2];
+ Extension extension = new Extension() {
+ public void extensionAdded(ExtensionPoint extensionPoint1) {
+ assertSame(extensionPoint, extensionPoint1);
+ assertSame(area, extensionPoint1.getArea());
+ flags[0] = true;
+ }
+
+ public void extensionRemoved(ExtensionPoint extensionPoint1) {
+ assertSame(extensionPoint, extensionPoint1);
+ assertSame(area, extensionPoint1.getArea());
+ flags[1] = true;
+ }
+ };
+
+ extensionPoint.registerExtension(extension);
+ assertTrue("Registratioon call is missed", flags[0]);
+ assertFalse(flags[1]);
+
+ extensionPoint.unregisterExtension(extension);
+ assertTrue("UnRegistratioon call is missed", flags[1]);
+ }
+
+ public void testRegisterObject() {
+ ExtensionPointImpl extensionPoint = buildExtensionPoint();
+ extensionPoint.registerExtension(new Integer(123));
+ Object[] extensions = extensionPoint.getExtensions();
+ assertEquals("One extension", 1, extensions.length);
+ assertEquals("Correct type", Integer[].class, extensions.getClass());
+ assertEquals("Correct object", new Integer(123), extensions[0]);
+ }
+
+ public void testRegistrationOrder() {
+ ExtensionPointImpl extensionPoint = buildExtensionPoint();
+ extensionPoint.registerExtension(new Integer(123));
+ extensionPoint.registerExtension(new Integer(321), LoadingOrder.FIRST);
+ Object[] extensions = extensionPoint.getExtensions();
+ assertEquals("One extension", 2, extensions.length);
+ assertEquals("Correct object", new Integer(321), extensions[0]);
+ }
+
+ public void testListener() {
+ ExtensionPointImpl extensionPoint = buildExtensionPoint();
+ final boolean added[] = new boolean[1];
+ final boolean removed[] = new boolean[1];
+ extensionPoint.addExtensionPointListener(new ExtensionPointListener() {
+ public void extensionAdded(Object extension) {
+ added[0] = true;
+ }
+
+ public void extensionRemoved(Object extension) {
+ removed[0] = true;
+ }
+ });
+ assertFalse(added[0]);
+ assertFalse(removed[0]);
+ extensionPoint.registerExtension(new Integer(123));
+ assertTrue(added[0]);
+ assertFalse(removed[0]);
+ added[0] = false;
+ extensionPoint.unregisterExtension(new Integer(123));
+ assertFalse(added[0]);
+ assertTrue(removed[0]);
+ }
+
+ public void testLateListener() {
+ ExtensionPointImpl extensionPoint = buildExtensionPoint();
+ final boolean added[] = new boolean[1];
+ extensionPoint.registerExtension(new Integer(123));
+ assertFalse(added[0]);
+ extensionPoint.addExtensionPointListener(new ExtensionPointListener() {
+ public void extensionAdded(Object extension) {
+ added[0] = true;
+ }
+
+ public void extensionRemoved(Object extension) {
+ }
+ });
+ assertTrue(added[0]);
+ }
+}
diff --git a/extensions/testSource/com/intellij/openapi/extensions/impl/ExtensionsImplTest.java b/extensions/testSource/com/intellij/openapi/extensions/impl/ExtensionsImplTest.java
new file mode 100644
index 000000000000..46210eb52d4f
--- /dev/null
+++ b/extensions/testSource/com/intellij/openapi/extensions/impl/ExtensionsImplTest.java
@@ -0,0 +1,352 @@
+/*
+ * Copyright (c) 2000-2004 by JetBrains s.r.o. All Rights Reserved.
+ * Use is subject to license terms.
+ */
+package com.intellij.openapi.extensions.impl;
+
+import junit.framework.TestCase;
+import com.intellij.openapi.extensions.*;
+import java.util.Arrays;
+
+import org.picocontainer.defaults.DefaultPicoContainer;
+import org.picocontainer.MutablePicoContainer;
+
+/**
+ * @author AKireyev
+ */
+public class ExtensionsImplTest extends TestCase {
+ public static final String EXTENSION_POINT_NAME_1 = "ext.point.one";
+ public static final String AREA_1 = "the.area.one";
+
+ protected void tearDown() throws Exception {
+ ExtensionsTestUtils.hardReset();
+ super.tearDown(); //To change body of overriden methods use Options | File Templates.
+ }
+
+ public void testCreateAndAccess() {
+ ExtensionsAreaImpl extensionsArea = new ExtensionsAreaImpl(null, new Extensions.SimpleLogProvider());
+ int numEP = extensionsArea.getExtensionPoints().length;
+ extensionsArea.registerExtensionPoint(EXTENSION_POINT_NAME_1, Integer.class.getName());
+ assertEquals("Additional EP available", numEP + 1, extensionsArea.getExtensionPoints().length);
+ assertNotNull("EP by name available", extensionsArea.getExtensionPoint(EXTENSION_POINT_NAME_1));
+ }
+
+ public void testInvalidActions() {
+ ExtensionsAreaImpl extensionsArea = new ExtensionsAreaImpl(null, new Extensions.SimpleLogProvider());
+ extensionsArea.registerExtensionPoint(EXTENSION_POINT_NAME_1, Integer.class.getName());
+ try {
+ extensionsArea.registerExtensionPoint(EXTENSION_POINT_NAME_1, Boolean.class.getName());
+ fail("Should not allow duplicate registration");
+ } catch (RuntimeException e) {
+ }
+ }
+
+ public void testUnregisterEP() {
+ ExtensionsAreaImpl extensionsArea = new ExtensionsAreaImpl(null, new Extensions.SimpleLogProvider());
+ int numEP = extensionsArea.getExtensionPoints().length;
+ extensionsArea.registerExtensionPoint(EXTENSION_POINT_NAME_1, Integer.class.getName());
+ final boolean removed[] = new boolean[1];
+ removed[0] = false;
+ extensionsArea.getExtensionPoint(EXTENSION_POINT_NAME_1).addExtensionPointListener(new ExtensionPointListener() {
+ public void extensionAdded(Object extension) {
+ }
+
+ public void extensionRemoved(Object extension) {
+ removed[0] = true;
+ }
+ });
+ extensionsArea.getExtensionPoint(EXTENSION_POINT_NAME_1).registerExtension(new Integer(123));
+ extensionsArea.unregisterExtensionPoint(EXTENSION_POINT_NAME_1);
+ assertTrue("Extension point should be removed", extensionsArea.getExtensionPoints().length == numEP);
+ assertTrue("Extension point disposed", removed[0]);
+ }
+
+ public void testAvailabilityListener() {
+ ExtensionsAreaImpl extensionsArea = new ExtensionsAreaImpl(null, new Extensions.SimpleLogProvider());
+ MyListener.reset();
+ extensionsArea.getExtensionPoint(EPAvailabilityListenerExtension.EXTENSION_POINT_NAME).registerExtension(
+ new EPAvailabilityListenerExtension(EXTENSION_POINT_NAME_1, MyListener.class.getName()));
+ assertEquals(0, MyListener.regcount);
+ assertEquals(0, MyListener.remcount);
+ extensionsArea.registerExtensionPoint(EXTENSION_POINT_NAME_1, Integer.class.getName());
+ assertEquals(1, MyListener.regcount);
+ assertEquals(0, MyListener.remcount);
+ MyListener.reset();
+ extensionsArea.unregisterExtensionPoint(EXTENSION_POINT_NAME_1);
+ assertEquals(1, MyListener.remcount);
+ assertEquals(0, MyListener.regcount);
+ }
+
+ public void testAvailability2Listeners() {
+ ExtensionsAreaImpl extensionsArea = new ExtensionsAreaImpl(null, new Extensions.SimpleLogProvider());
+ MyListener.reset();
+ extensionsArea.registerExtensionPoint(EXTENSION_POINT_NAME_1, Integer.class.getName());
+ extensionsArea.getExtensionPoint(EPAvailabilityListenerExtension.EXTENSION_POINT_NAME).registerExtension(
+ new EPAvailabilityListenerExtension(EXTENSION_POINT_NAME_1, MyListener.class.getName()));
+ extensionsArea.getExtensionPoint(EPAvailabilityListenerExtension.EXTENSION_POINT_NAME).registerExtension(
+ new EPAvailabilityListenerExtension(EXTENSION_POINT_NAME_1, MyListener.class.getName()));
+ assertEquals(2, MyListener.regcount);
+ assertEquals(0, MyListener.remcount);
+ MyListener.reset();
+ extensionsArea.unregisterExtensionPoint(EXTENSION_POINT_NAME_1);
+ assertEquals(2, MyListener.remcount);
+ assertEquals(0, MyListener.regcount);
+ }
+
+ public void testAvailabilityListenerAfter() {
+ ExtensionsAreaImpl extensionsArea = new ExtensionsAreaImpl(null, new Extensions.SimpleLogProvider());
+ extensionsArea.registerExtensionPoint(EXTENSION_POINT_NAME_1, Integer.class.getName());
+ MyListener.reset();
+ extensionsArea.getExtensionPoint(EPAvailabilityListenerExtension.EXTENSION_POINT_NAME).registerExtension(
+ new EPAvailabilityListenerExtension(EXTENSION_POINT_NAME_1, MyListener.class.getName()));
+ assertEquals(1, MyListener.regcount);
+ assertEquals(0, MyListener.remcount);
+ }
+
+ public void testAvailabilityListenerDelay() {
+ ExtensionsAreaImpl extensionsArea = new ExtensionsAreaImpl(null, new Extensions.SimpleLogProvider());
+ MyListener.reset();
+ extensionsArea.suspendInteractions();
+ extensionsArea.getExtensionPoint(EPAvailabilityListenerExtension.EXTENSION_POINT_NAME).registerExtension(
+ new EPAvailabilityListenerExtension(EXTENSION_POINT_NAME_1, MyListener.class.getName()));
+ assertEquals(0, MyListener.regcount);
+ assertEquals(0, MyListener.remcount);
+ extensionsArea.registerExtensionPoint(EXTENSION_POINT_NAME_1, Integer.class.getName());
+ assertEquals(0, MyListener.regcount);
+ assertEquals(0, MyListener.remcount);
+ extensionsArea.resumeInteractions();
+ assertEquals(1, MyListener.regcount);
+ assertEquals(0, MyListener.remcount);
+ MyListener.reset();
+ extensionsArea.suspendInteractions();
+ assertEquals(0, MyListener.regcount);
+ assertEquals(0, MyListener.remcount);
+ extensionsArea.unregisterExtensionPoint(EXTENSION_POINT_NAME_1);
+ assertEquals(0, MyListener.regcount);
+ assertEquals(0, MyListener.remcount);
+ extensionsArea.resumeInteractions();
+ assertEquals(1, MyListener.remcount);
+ assertEquals(0, MyListener.regcount);
+ }
+
+ public void testKillAvailabilityNotifications() {
+ ExtensionsAreaImpl extensionsArea = new ExtensionsAreaImpl(null, new Extensions.SimpleLogProvider());
+ MyListener.reset();
+ extensionsArea.suspendInteractions();
+ extensionsArea.getExtensionPoint(EPAvailabilityListenerExtension.EXTENSION_POINT_NAME).registerExtension(
+ new EPAvailabilityListenerExtension(EXTENSION_POINT_NAME_1, MyListener.class.getName()));
+ assertEquals(0, MyListener.regcount);
+ assertEquals(0, MyListener.remcount);
+ extensionsArea.registerExtensionPoint(EXTENSION_POINT_NAME_1, Integer.class.getName());
+ assertEquals(0, MyListener.regcount);
+ assertEquals(0, MyListener.remcount);
+ extensionsArea.killPendingInteractions();
+ extensionsArea.resumeInteractions();
+ assertEquals(0, MyListener.regcount);
+ assertEquals(0, MyListener.remcount);
+ }
+
+ public void testListenerAfterResume() {
+ ExtensionsAreaImpl extensionsArea = new ExtensionsAreaImpl(null, new Extensions.SimpleLogProvider());
+ extensionsArea.suspendInteractions();
+ extensionsArea.resumeInteractions();
+ MyListener.reset();
+ extensionsArea.getExtensionPoint(EPAvailabilityListenerExtension.EXTENSION_POINT_NAME).registerExtension(
+ new EPAvailabilityListenerExtension(EXTENSION_POINT_NAME_1, MyListener.class.getName()));
+ assertEquals(0, MyListener.regcount);
+ assertEquals(0, MyListener.remcount);
+ extensionsArea.registerExtensionPoint(EXTENSION_POINT_NAME_1, Integer.class.getName());
+ assertEquals(1, MyListener.regcount);
+ assertEquals(0, MyListener.remcount);
+ MyListener.reset();
+ extensionsArea.unregisterExtensionPoint(EXTENSION_POINT_NAME_1);
+ assertEquals(1, MyListener.remcount);
+ assertEquals(0, MyListener.regcount);
+ }
+
+ public void testTryPicoContainer() {
+ DefaultPicoContainer rootContainer = new DefaultPicoContainer();
+ rootContainer.registerComponentInstance("plugin1", new DefaultPicoContainer(rootContainer));
+ rootContainer.registerComponentInstance("plugin2", new DefaultPicoContainer(rootContainer));
+// rootContainer.registerComponentImplementation("plugin2", DefaultPicoContainer.class);
+ MutablePicoContainer container1 = (MutablePicoContainer)rootContainer.getComponentInstance("plugin1");
+ MutablePicoContainer container2 = (MutablePicoContainer)rootContainer.getComponentInstance("plugin2");
+ container1.registerComponentImplementation("component1", MyComponent1.class);
+ container1.registerComponentImplementation("component1.1", MyComponent1.class);
+ container2.registerComponentImplementation("component2", MyComponent2.class);
+ MyInterface1 testInstance = new MyInterface1() {
+ public void run() {
+ }
+ };
+ rootContainer.registerComponentInstance(testInstance);
+ MyComponent1 component1 = (MyComponent1)container1.getComponentInstance("component1");
+ assertEquals(testInstance, component1.testObject);
+ rootContainer.registerComponentInstance("component1", component1);
+ MyComponent1 component11 = (MyComponent1)container1.getComponentInstance("component1.1");
+ rootContainer.registerComponentInstance("component11", component11);
+ MyComponent2 component2 = (MyComponent2)container2.getComponentInstance("component2");
+ assertEquals(testInstance, component2.testObject);
+ assertTrue(Arrays.asList(component2.comp1).contains(component1));
+ assertTrue(Arrays.asList(component2.comp1).contains(component11));
+ rootContainer.registerComponentInstance("component2", component2);
+ rootContainer.registerComponentImplementation(MyTestComponent.class);
+ MyTestComponent testComponent = (MyTestComponent)rootContainer.getComponentInstance(MyTestComponent.class);
+ assertTrue(Arrays.asList(testComponent.comp1).contains(component1));
+ assertTrue(Arrays.asList(testComponent.comp1).contains(component11));
+ assertEquals(component2, testComponent.comp2);
+ }
+
+ public void testTryPicoContainer2() {
+ DefaultPicoContainer rootContainer = new DefaultPicoContainer();
+ rootContainer.registerComponentImplementation("component1", MyComponent1.class);
+ rootContainer.registerComponentImplementation("component1.1", MyComponent1.class);
+ rootContainer.registerComponentImplementation("component2", MyComponent2.class);
+ rootContainer.registerComponentImplementation(MyTestComponent.class);
+ MyInterface1 testInstance = new MyInterface1() {
+ public void run() {
+ }
+ };
+ rootContainer.registerComponentInstance(testInstance);
+ MyTestComponent testComponent = (MyTestComponent)rootContainer.getComponentInstance(MyTestComponent.class);
+ MyComponent2 component2 = (MyComponent2)rootContainer.getComponentInstance("component2");
+ MyComponent1 component11 = (MyComponent1)rootContainer.getComponentInstance("component1.1");
+ MyComponent1 component1 = (MyComponent1)rootContainer.getComponentInstance("component1");
+ assertEquals(testInstance, component1.testObject);
+ assertEquals(testInstance, component2.testObject);
+ assertTrue(Arrays.asList(component2.comp1).contains(component1));
+ assertTrue(Arrays.asList(component2.comp1).contains(component11));
+ assertTrue(Arrays.asList(testComponent.comp1).contains(component1));
+ assertTrue(Arrays.asList(testComponent.comp1).contains(component11));
+ assertEquals(component2, testComponent.comp2);
+ }
+
+ public void testExtensionsNamespaces() {
+ ExtensionsAreaImpl extensionsArea = new ExtensionsAreaImpl(new DefaultPicoContainer(), new Extensions.SimpleLogProvider());
+ extensionsArea.registerExtensionPoint("plugin.ep1", TestExtensionClassOne.class.getName());
+ extensionsArea.registerExtension("plugin", ExtensionComponentAdapterTest.readElement(
+ "3"));
+ extensionsArea.registerExtension("plugin", ExtensionComponentAdapterTest.readElement(
+ "1"));
+ extensionsArea.registerExtension("plugin", ExtensionComponentAdapterTest.readElement(
+ "2"));
+ ExtensionPoint extensionPoint = extensionsArea.getExtensionPoint("plugin.ep1");
+ TestExtensionClassOne[] extensions = (TestExtensionClassOne[]) extensionPoint.getExtensions();
+ assertEquals(3, extensions.length);
+ assertEquals("1", extensions[0].getText());
+ assertEquals("2", extensions[1].getText());
+ assertEquals("3", extensions[2].getText());
+ }
+
+ public void testExtensionsWithOrdering() {
+ ExtensionsAreaImpl extensionsArea = new ExtensionsAreaImpl(new DefaultPicoContainer(), new Extensions.SimpleLogProvider());
+ extensionsArea.registerExtensionPoint("ep1", TestExtensionClassOne.class.getName());
+ extensionsArea.registerExtension("", ExtensionComponentAdapterTest.readElement(
+ "3"));
+ extensionsArea.registerExtension("", ExtensionComponentAdapterTest.readElement(
+ "1"));
+ extensionsArea.registerExtension("", ExtensionComponentAdapterTest.readElement(
+ "2"));
+ ExtensionPoint extensionPoint = extensionsArea.getExtensionPoint("ep1");
+ TestExtensionClassOne[] extensions = (TestExtensionClassOne[]) extensionPoint.getExtensions();
+ assertEquals(3, extensions.length);
+ assertEquals("1", extensions[0].getText());
+ assertEquals("2", extensions[1].getText());
+ assertEquals("3", extensions[2].getText());
+ }
+
+ public void testExtensionsWithOrderingUpdate() {
+ ExtensionsAreaImpl extensionsArea = new ExtensionsAreaImpl(new DefaultPicoContainer(), new Extensions.SimpleLogProvider());
+ extensionsArea.registerExtensionPoint("ep1", TestExtensionClassOne.class.getName());
+ extensionsArea.registerExtension("", ExtensionComponentAdapterTest.readElement(
+ "6"));
+ extensionsArea.registerExtension("", ExtensionComponentAdapterTest.readElement(
+ "1"));
+ extensionsArea.registerExtension("", ExtensionComponentAdapterTest.readElement(
+ "3"));
+ ExtensionPoint extensionPoint = extensionsArea.getExtensionPoint("ep1");
+ TestExtensionClassOne[] extensions = (TestExtensionClassOne[]) extensionPoint.getExtensions();
+ assertEquals(3, extensions.length);
+ assertEquals("1", extensions[0].getText());
+ assertEquals("3", extensions[1].getText());
+ assertEquals("6", extensions[2].getText());
+ TestExtensionClassOne extension = new TestExtensionClassOne("xxx");
+ extensionPoint.registerExtension(extension);
+ extensionPoint.unregisterExtension(extension);
+ extensionsArea.registerExtension("", ExtensionComponentAdapterTest.readElement(
+ "2"));
+ extensionsArea.registerExtension("", ExtensionComponentAdapterTest.readElement(
+ "4"));
+ extensionPoint.registerExtension(new TestExtensionClassOne("5"));
+ extensions = (TestExtensionClassOne[]) extensionPoint.getExtensions();
+ assertEquals(6, extensions.length);
+ assertEquals("1", extensions[0].getText());
+ assertEquals("2", extensions[1].getText());
+ assertEquals("3", extensions[2].getText());
+ assertEquals("4", extensions[3].getText());
+ assertEquals("5", extensions[4].getText());
+ assertEquals("6", extensions[5].getText());
+ }
+
+ public interface MyInterface1 extends Runnable {
+ }
+
+ public interface MyInterface2 {
+ }
+
+ public interface MyInterface3 extends Runnable {
+ }
+
+ public interface MyInterface4 extends MyInterface1, MyInterface2, MyInterface3 {
+ }
+
+ public static class MyClass implements MyInterface4 {
+ public void run() {
+ }
+ }
+
+ public static class MyComponent1 {
+ public MyInterface1 testObject;
+
+ public MyComponent1(MyInterface1 testObject) {
+ this.testObject = testObject;
+ }
+ }
+
+ public static class MyComponent2 {
+ public MyInterface1 testObject;
+ public MyComponent1[] comp1;
+
+ public MyComponent2(MyComponent1[] comp1, MyInterface1 testObject) {
+ this.comp1 = comp1;
+ this.testObject = testObject;
+ }
+ }
+
+ public static class MyTestComponent {
+ public MyComponent1[] comp1;
+ public MyComponent2 comp2;
+
+ public MyTestComponent(MyComponent1[] comp1, MyComponent2 comp2) {
+ this.comp1 = comp1;
+ this.comp2 = comp2;
+ }
+ }
+
+ public static class MyListener implements ExtensionPointAvailabilityListener {
+ public static int regcount = 0;
+ public static int remcount = 0;
+
+ public void extensionPointRegistered(ExtensionPoint extensionPoint) {
+ regcount++;
+ }
+
+ public void extensionPointRemoved(ExtensionPoint extensionPoint) {
+ remcount++;
+ }
+
+ public static void reset() {
+ regcount = 0;
+ remcount = 0;
+ }
+ }
+}
diff --git a/extensions/testSource/com/intellij/openapi/extensions/impl/LoadingOrderTest.java b/extensions/testSource/com/intellij/openapi/extensions/impl/LoadingOrderTest.java
new file mode 100644
index 000000000000..bdc238bc3b5f
--- /dev/null
+++ b/extensions/testSource/com/intellij/openapi/extensions/impl/LoadingOrderTest.java
@@ -0,0 +1,158 @@
+/*
+ * Copyright (c) 2000-2004 by JetBrains s.r.o. All Rights Reserved.
+ * Use is subject to license terms.
+ */
+package com.intellij.openapi.extensions.impl;
+
+import org.jmock.cglib.MockObjectTestCase;
+import org.jmock.cglib.Mock;
+import org.jmock.core.stub.ReturnStub;
+import org.jdom.Element;
+import org.picocontainer.PicoContainer;
+import org.picocontainer.defaults.AssignabilityRegistrationException;
+
+import java.util.ArrayList;
+
+import com.intellij.openapi.extensions.LoadingOrder;
+import com.intellij.openapi.extensions.SortingException;
+
+/**
+ * @author Alexander Kireyev
+ */
+public class LoadingOrderTest extends MockObjectTestCase {
+ public void testSimpleSorting() {
+ ArrayList target = new ArrayList();
+ target.add(createElement(LoadingOrder.ANY, null, "any"));
+ target.add(createElement(LoadingOrder.FIRST, null, "1"));
+ target.add(createElement(LoadingOrder.LAST, null, "2"));
+ target.add(createElement(LoadingOrder.ANY, null, "any"));
+ LoadingOrder.Orderable[] array = (LoadingOrder.Orderable[]) target.toArray(new LoadingOrder.Orderable[target.size()]);
+ assertSequence(array, "1anyany2");
+ }
+
+ public void testComplexSorting() {
+ ArrayList target = new ArrayList();
+ String idOne = "idone";
+ String idTwo = "idTwo";
+ target.add(createElement(LoadingOrder.before(idTwo), idOne, "2"));
+ target.add(createElement(LoadingOrder.FIRST, null, "0"));
+ target.add(createElement(LoadingOrder.LAST, null, "5"));
+ target.add(createElement(LoadingOrder.after(idTwo), null, "4"));
+ target.add(createElement(LoadingOrder.ANY, idTwo, "3"));
+ target.add(createElement(LoadingOrder.before(idOne), null, "1"));
+ LoadingOrder.Orderable[] array = (LoadingOrder.Orderable[]) target.toArray(new LoadingOrder.Orderable[target.size()]);
+ assertSequence(array, "012345");
+ }
+
+ public void testComplexSorting2() {
+ ArrayList target = new ArrayList();
+ String idOne = "idone";
+ target.add(createElement(LoadingOrder.before(idOne), null, "2"));
+ target.add(createElement(LoadingOrder.after(idOne), null, "4"));
+ target.add(createElement(LoadingOrder.FIRST, null, "1"));
+ target.add(createElement(LoadingOrder.ANY, idOne, "3"));
+ target.add(createElement(LoadingOrder.ANY, null, "5"));
+ target.add(createElement(LoadingOrder.LAST, null, "6"));
+ LoadingOrder.Orderable[] array = (LoadingOrder.Orderable[]) target.toArray(new LoadingOrder.Orderable[target.size()]);
+ assertSequence(array, "123456");
+ }
+
+ private void assertSequence(LoadingOrder.Orderable[] array, String expected) {
+ LoadingOrder.sort(array);
+ StringBuffer sequence = buildSequence(array);
+ assertEquals(expected, sequence.toString());
+ }
+
+ private StringBuffer buildSequence(LoadingOrder.Orderable[] array) {
+ StringBuffer sequence = new StringBuffer();
+ for (int i = 0; i < array.length; i++) {
+ LoadingOrder.Orderable adapter = array[i];
+ sequence.append(((MyElement)adapter.getDescribingElement()).getID());
+ }
+ return sequence;
+ }
+
+ public void testFailingSortingBeforeFirst() {
+ ArrayList target = new ArrayList();
+ target.add(createElement(LoadingOrder.ANY, null, "good"));
+ target.add(createElement(LoadingOrder.FIRST, "first", "bad"));
+ target.add(createElement(LoadingOrder.LAST, null, "good"));
+ target.add(createElement(LoadingOrder.before("first"), null, "bad"));
+ LoadingOrder.Orderable[] array = (LoadingOrder.Orderable[]) target.toArray(new LoadingOrder.Orderable[target.size()]);
+ checkSortingFailure(array, 2);
+ }
+
+ public void testFailingSortingFirst() {
+ ArrayList target = new ArrayList();
+ target.add(createElement(LoadingOrder.ANY, null, "good"));
+ target.add(createElement(LoadingOrder.FIRST, "first", "bad"));
+ target.add(createElement(LoadingOrder.LAST, null, "good"));
+ target.add(createElement(LoadingOrder.FIRST, null, "bad"));
+ LoadingOrder.Orderable[] array = (LoadingOrder.Orderable[]) target.toArray(new LoadingOrder.Orderable[target.size()]);
+ checkSortingFailure(array, 2);
+ }
+
+ private void checkSortingFailure(LoadingOrder.Orderable[] array, int expectedCount) {
+ try {
+ LoadingOrder.sort(array);
+ fail("Should have failed");
+ }
+ catch (SortingException e) {
+ Element[] conflictingElements = e.getConflictingElements();
+ assertEquals(expectedCount, conflictingElements.length);
+ for (int i = 0; i < conflictingElements.length; i++) {
+ MyElement conflictingElement = (MyElement) conflictingElements[i];
+ assertEquals("bad", conflictingElement.getID());
+ }
+ }
+ }
+
+ public void testFailingSortingAfterLast() {
+ ArrayList target = new ArrayList();
+ target.add(createElement(LoadingOrder.after("last"), null, "bad"));
+ target.add(createElement(LoadingOrder.FIRST, null, "good"));
+ target.add(createElement(LoadingOrder.LAST, "last", "bad"));
+ target.add(createElement(LoadingOrder.ANY, null, "good"));
+ LoadingOrder.Orderable[] array = (LoadingOrder.Orderable[]) target.toArray(new LoadingOrder.Orderable[target.size()]);
+ checkSortingFailure(array, 2);
+ }
+
+ public void testFailingSortingLast() {
+ ArrayList target = new ArrayList();
+ target.add(createElement(LoadingOrder.LAST, null, "bad"));
+ target.add(createElement(LoadingOrder.FIRST, null, "good"));
+ target.add(createElement(LoadingOrder.LAST, "last", "bad"));
+ target.add(createElement(LoadingOrder.ANY, null, "good"));
+ LoadingOrder.Orderable[] array = (LoadingOrder.Orderable[]) target.toArray(new LoadingOrder.Orderable[target.size()]);
+ checkSortingFailure(array, 2);
+ }
+
+ public void testFailingSortingComplex() {
+ ArrayList target = new ArrayList();
+ target.add(createElement(LoadingOrder.after("2"), "1", "bad"));
+ target.add(createElement(LoadingOrder.after("3"), "2", "bad"));
+ target.add(createElement(LoadingOrder.after("1"), "3", "bad"));
+ LoadingOrder.Orderable[] array = (LoadingOrder.Orderable[]) target.toArray(new LoadingOrder.Orderable[target.size()]);
+ checkSortingFailure(array, 3);
+ }
+
+ private LoadingOrder.Orderable createElement(LoadingOrder order, String idString, String elementId) {
+ Mock mock = new Mock(LoadingOrder.Orderable.class);
+ mock.stubs().method("getOrder").withNoArguments().will(new ReturnStub(order));
+ mock.stubs().method("getOrderId").withNoArguments().will(returnValue(idString));
+ mock.stubs().method("getDescribingElement").withNoArguments().will(new ReturnStub(new MyElement(elementId)));
+ return (LoadingOrder.Orderable) mock.proxy();
+ }
+
+ private static class MyElement extends Element {
+ private String myID;
+
+ public MyElement(String ID) {
+ myID = ID;
+ }
+
+ public String getID() {
+ return myID;
+ }
+ }
+}
diff --git a/extensions/testSource/com/intellij/openapi/extensions/impl/TestExtensionClassOne.java b/extensions/testSource/com/intellij/openapi/extensions/impl/TestExtensionClassOne.java
new file mode 100644
index 000000000000..17e75d38e220
--- /dev/null
+++ b/extensions/testSource/com/intellij/openapi/extensions/impl/TestExtensionClassOne.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2000-2004 by JetBrains s.r.o. All Rights Reserved.
+ * Use is subject to license terms.
+ */
+package com.intellij.openapi.extensions.impl;
+
+/**
+ * @author Alexander Kireyev
+ */
+public class TestExtensionClassOne {
+ private String myText;
+
+ public TestExtensionClassOne() {
+ }
+
+ public TestExtensionClassOne(String text) {
+ myText = text;
+ }
+
+ public String getText() {
+ return myText;
+ }
+
+ public void setText(String text) {
+ myText = text;
+ }
+}
diff --git a/lib/dev/jmock-1.0.1.jar b/lib/dev/jmock-1.0.1.jar
new file mode 100644
index 000000000000..e774359fc19b
Binary files /dev/null and b/lib/dev/jmock-1.0.1.jar differ
diff --git a/lib/dev/jmock-cglib-1.0.1.jar b/lib/dev/jmock-cglib-1.0.1.jar
new file mode 100644
index 000000000000..494fa645b0ea
Binary files /dev/null and b/lib/dev/jmock-cglib-1.0.1.jar differ
diff --git a/lib/xstream.jar b/lib/xstream.jar
new file mode 100644
index 000000000000..d657fa0a35e4
Binary files /dev/null and b/lib/xstream.jar differ