use concurrent maps in Extensions (CR-IC-7245)
[idea/community.git] / platform / extensions / src / com / intellij / openapi / extensions / Extensions.java
1 /*
2  * Copyright 2000-2009 JetBrains s.r.o.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.intellij.openapi.extensions;
17
18 import com.intellij.openapi.Disposable;
19 import com.intellij.openapi.extensions.impl.ExtensionsAreaImpl;
20 import com.intellij.openapi.util.Disposer;
21 import com.intellij.util.containers.ContainerUtil;
22 import org.jetbrains.annotations.NonNls;
23 import org.jetbrains.annotations.NotNull;
24 import org.jetbrains.annotations.Nullable;
25 import org.jetbrains.annotations.TestOnly;
26
27 import java.util.Map;
28
29 public class Extensions {
30   public static final ExtensionPointName<AreaListener> AREA_LISTENER_EXTENSION_POINT = new ExtensionPointName<AreaListener>("com.intellij.arealistener");
31   private static LogProvider ourLogger = new SimpleLogProvider();
32   private static final Map<AreaInstance, ExtensionsAreaImpl> ourAreaInstance2area = ContainerUtil.newConcurrentMap();
33   private static final Map<String, AreaClassConfiguration> ourAreaClass2Configuration = ContainerUtil.newConcurrentMap();
34
35   @NotNull private static ExtensionsAreaImpl ourRootArea = createRootArea();
36
37   private Extensions() {
38   }
39
40   @NotNull
41   private static ExtensionsAreaImpl createRootArea() {
42     ExtensionsAreaImpl rootArea = new ExtensionsAreaImpl(null, null, null, ourLogger);
43     rootArea.registerExtensionPoint(AREA_LISTENER_EXTENSION_POINT.getName(), AreaListener.class.getName());
44     return rootArea;
45   }
46
47   @NotNull
48   public static ExtensionsArea getRootArea() {
49     return ourRootArea;
50   }
51
52   @NotNull
53   public static ExtensionsArea getArea(@Nullable AreaInstance areaInstance) {
54     if (areaInstance == null) {
55       return ourRootArea;
56     }
57     ExtensionsAreaImpl area = ourAreaInstance2area.get(areaInstance);
58     if (area == null) {
59       throw new IllegalArgumentException("No area instantiated for: " + areaInstance);
60     }
61     return area;
62   }
63
64   @TestOnly
65   public static void cleanRootArea(@NotNull Disposable parentDisposable) {
66     final ExtensionsAreaImpl oldRootArea = (ExtensionsAreaImpl)getRootArea();
67     final ExtensionsAreaImpl newArea = createRootArea();
68     ourRootArea = newArea;
69     oldRootArea.notifyAreaReplaced();
70     Disposer.register(parentDisposable, new Disposable() {
71       @Override
72       public void dispose() {
73         ourRootArea = oldRootArea;
74         newArea.notifyAreaReplaced();
75       }
76     });
77   }
78
79   @NotNull
80   public static Object[] getExtensions(@NonNls String extensionPointName) {
81     return getExtensions(extensionPointName, null);
82   }
83
84   @NotNull
85   @SuppressWarnings({"unchecked"})
86   public static <T> T[] getExtensions(@NotNull ExtensionPointName<T> extensionPointName) {
87     return (T[])getExtensions(extensionPointName.getName(), null);
88   }
89
90   @NotNull
91   @SuppressWarnings({"unchecked"})
92   public static <T> T[] getExtensions(@NotNull ExtensionPointName<T> extensionPointName, AreaInstance areaInstance) {
93     // keep it until 1.7 JDK
94     return Extensions.<T>getExtensions(extensionPointName.getName(), areaInstance);
95   }
96
97   @NotNull
98   public static <T> T[] getExtensions(String extensionPointName, @Nullable AreaInstance areaInstance) {
99     ExtensionsArea area = getArea(areaInstance);
100     ExtensionPoint<T> extensionPoint = area.getExtensionPoint(extensionPointName);
101     return extensionPoint.getExtensions();
102   }
103
104   @NotNull
105   public static <T, U extends T> U findExtension(@NotNull ExtensionPointName<T> extensionPointName, @NotNull Class<U> extClass) {
106     for (T t : getExtensions(extensionPointName)) {
107       if (extClass.isInstance(t)) {
108         //noinspection unchecked
109         return (U) t;
110       }
111     }
112     throw new IllegalArgumentException("could not find extension implementation " + extClass);
113   }
114
115   @NotNull
116   public static <T, U extends T> U findExtension(@NotNull ExtensionPointName<T> extensionPointName, AreaInstance areaInstance, @NotNull Class<U> extClass) {
117     for (T t : getExtensions(extensionPointName, areaInstance)) {
118       if (extClass.isInstance(t)) {
119         //noinspection unchecked
120         return (U) t;
121       }
122     }
123     throw new IllegalArgumentException("could not find extension implementation " + extClass);
124   }
125
126   public static void instantiateArea(@NonNls @NotNull String areaClass, @NotNull AreaInstance areaInstance, @Nullable AreaInstance parentAreaInstance) {
127     AreaClassConfiguration configuration = ourAreaClass2Configuration.get(areaClass);
128     if (configuration == null) {
129       throw new IllegalArgumentException("Area class is not registered: " + areaClass);
130     }
131     ExtensionsArea parentArea = getArea(parentAreaInstance);
132     if (!equals(parentArea.getAreaClass(), configuration.getParentClassName())) {
133       throw new IllegalArgumentException("Wrong parent area. Expected class: " + configuration.getParentClassName() + " actual class: " + parentArea.getAreaClass());
134     }
135     ExtensionsAreaImpl area = new ExtensionsAreaImpl(areaClass, areaInstance, parentArea.getPicoContainer(), ourLogger);
136     if (ourAreaInstance2area.put(areaInstance, area) != null) {
137       throw new IllegalArgumentException("Area already instantiated for: " + areaInstance);
138     }
139     for (AreaListener listener : getAreaListeners()) {
140       listener.areaCreated(areaClass, areaInstance);
141     }
142   }
143
144   @NotNull
145   private static AreaListener[] getAreaListeners() {
146     return getRootArea().getExtensionPoint(AREA_LISTENER_EXTENSION_POINT).getExtensions();
147   }
148
149   public static void registerAreaClass(@NonNls @NotNull String areaClass, @Nullable @NonNls String parentAreaClass) {
150     if (ourAreaClass2Configuration.containsKey(areaClass)) {
151       // allow duplicate area class registrations if they are the same - fixing duplicate registration in tests is much more trouble
152       AreaClassConfiguration configuration = ourAreaClass2Configuration.get(areaClass);
153       if (!equals(configuration.getParentClassName(), parentAreaClass)) {
154         throw new RuntimeException("Area class already registered: " + areaClass + ", "+ ourAreaClass2Configuration.get(areaClass));
155       }
156       else {
157         return;
158       }
159     }
160     AreaClassConfiguration configuration = new AreaClassConfiguration(areaClass, parentAreaClass);
161     ourAreaClass2Configuration.put(areaClass, configuration);
162   }
163
164   public static void disposeArea(@NotNull AreaInstance areaInstance) {
165     assert ourAreaInstance2area.containsKey(areaInstance);
166
167     String areaClass = ourAreaInstance2area.get(areaInstance).getAreaClass();
168     if (areaClass == null) {
169       throw new IllegalArgumentException("Area class is null (area never instantiated?). Instance: " + areaInstance);
170     }
171     try {
172       for (AreaListener listener : getAreaListeners()) {
173         listener.areaDisposing(areaClass, areaInstance);
174       }
175     }
176     finally {
177       ourAreaInstance2area.remove(areaInstance);
178     }
179   }
180
181   private static boolean equals(@Nullable Object object1, @Nullable Object object2) {
182     return object1 == object2 || object1 != null && object2 != null && object1.equals(object2);
183   }
184
185   public static void setLogProvider(@NotNull LogProvider logProvider) {
186     ourLogger = logProvider;
187   }
188
189   private static class AreaClassConfiguration {
190     private final String myClassName;
191     private final String myParentClassName;
192
193     AreaClassConfiguration(@NotNull String className, String parentClassName) {
194       myClassName = className;
195       myParentClassName = parentClassName;
196     }
197
198     @NotNull
199     public String getClassName() {
200       return myClassName;
201     }
202
203     public String getParentClassName() {
204       return myParentClassName;
205     }
206   }
207
208   @SuppressWarnings("CallToPrintStackTrace")
209   public static class SimpleLogProvider implements LogProvider {
210     @Override
211     public void error(String message) {
212       new Throwable(message).printStackTrace();
213     }
214
215     @Override
216     public void error(String message, @NotNull Throwable t) {
217       System.err.println(message);
218       t.printStackTrace();
219     }
220
221     @Override
222     public void error(@NotNull Throwable t) {
223       t.printStackTrace();
224     }
225
226     @Override
227     public void warn(String message) {
228       System.err.println(message);
229     }
230
231     @Override
232     public void warn(String message, @NotNull Throwable t) {
233       System.err.println(message);
234       t.printStackTrace();
235     }
236
237     @Override
238     public void warn(@NotNull Throwable t) {
239       t.printStackTrace();
240     }
241   }
242 }