Source code: org/eclipse/core/internal/runtime/AdapterManager.java
1 /*******************************************************************************
2 * Copyright (c) 2000, 2004 IBM Corporation and others.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Common Public License v1.0
5 * which accompanies this distribution, and is available at
6 * http://www.eclipse.org/legal/cpl-v10.html
7 *
8 * Contributors:
9 * IBM Corporation - initial API and implementation
10 *******************************************************************************/
11 package org.eclipse.core.internal.runtime;
12
13 import java.util.*;
14 import org.eclipse.core.runtime.*;
15
16 /**
17 * This class is the standard implementation of <code>IAdapterManager</code>. It provides
18 * fast lookup of property values with the following semantics:
19 * <ul>
20 * <li>At most one factory will be invoked per property lookup
21 * <li>If multiple installed factories provide the same adapter, only the first found in
22 * the search order will be invoked.
23 * <li>The search order from a class with the definition <br>
24 * <code>class X extends Y implements A, B</code><br> is as follows: <il>
25 * <li>the target's class: X
26 * <li>X's superclasses in order to <code>Object</code>
27 * <li>a breadth-first traversal of the target class's interfaces in the order returned by
28 * <code>getInterfaces</code> (in the example, A and its superinterfaces then B and its
29 * superinterfaces) </il>
30 * </ul>
31 *
32 * @see IAdapterFactory
33 * @see IAdapterManager
34 */
35 public final class AdapterManager implements IAdapterManager, IRegistryChangeListener {
36 /**
37 * Map of factories, keyed by <code>String</code>, fully qualified class name of
38 * the adaptable class that the factory provides adapters for. Value is a <code>List</code>
39 * of <code>IAdapterFactory</code>.
40 */
41 protected final HashMap factories;
42 /**
43 * Cache of adapters for a given adaptable class. Maps String -> Map
44 * (adaptable class name -> (adapter class name -> factory instance))
45 */
46 protected HashMap lookup;
47
48 /**
49 * Constructs a new adapter manager.
50 */
51 public AdapterManager() {
52 factories = new HashMap(5);
53 lookup = null;
54 registerFactoryProxies();
55 Platform.getExtensionRegistry().addRegistryChangeListener(this);
56 }
57
58 /**
59 * Given a type name, add all of the factories that respond to those types into
60 * the given table. Each entry will be keyed by the adapter class name (supplied in
61 * IAdapterFactory.getAdapterList).
62 */
63 private void addFactoriesFor(String typeName, Map table) {
64 List factoryList = (List) factories.get(typeName);
65 if (factoryList == null)
66 return;
67 for (int i = 0, imax = factoryList.size(); i < imax; i++) {
68 IAdapterFactory factory = (IAdapterFactory) factoryList.get(i);
69 if (factory instanceof AdapterFactoryProxy) {
70 String[] adapters = ((AdapterFactoryProxy) factory).getAdapterNames();
71 for (int j = 0; j < adapters.length; j++) {
72 if (table.get(adapters[j]) == null)
73 table.put(adapters[j], factory);
74 }
75 } else {
76 Class[] adapters = factory.getAdapterList();
77 for (int j = 0; j < adapters.length; j++) {
78 String adapterName = adapters[j].getName();
79 if (table.get(adapterName) == null)
80 table.put(adapterName, factory);
81 }
82 }
83 }
84 }
85
86 /**
87 * Returns the class with the given fully qualified name, or null
88 * if that class does not exist or belongs to a plug-in that has not
89 * yet been loaded.
90 */
91 private Class classForName(IAdapterFactory factory, String typeName) {
92 try {
93 if (factory instanceof AdapterFactoryProxy)
94 factory = ((AdapterFactoryProxy) factory).loadFactory(false);
95 if (factory != null)
96 return factory.getClass().getClassLoader().loadClass(typeName);
97 } catch (ClassNotFoundException e) {
98 //class not yet loaded
99 }
100 return null;
101 }
102
103 /**
104 * Builds and returns a table of adapters for the given adaptable type.
105 * The table is keyed by adapter class name. The
106 * value is the <b>sole<b> factory that defines that adapter. Note that
107 * if multiple adapters technically define the same property, only the
108 * first found in the search order is considered.
109 *
110 * Note that it is important to maintain a consistent class and interface
111 * lookup order. See the class comment for more details.
112 */
113 private Map computeClassOrder(Class adaptable) {
114 HashMap table = new HashMap(4);
115 Class clazz = adaptable;
116 Set seen = new HashSet(4);
117 while (clazz != null) {
118 addFactoriesFor(clazz.getName(), table);
119 computeInterfaceOrder(clazz.getInterfaces(), table, seen);
120 clazz = clazz.getSuperclass();
121 }
122 return table;
123 }
124
125 private void computeInterfaceOrder(Class[] interfaces, Map table, Set seen) {
126 List newInterfaces = new ArrayList(interfaces.length);
127 for (int i = 0; i < interfaces.length; i++) {
128 Class interfac = interfaces[i];
129 if (seen.add(interfac)) {
130 addFactoriesFor(interfac.getName(), table);
131 //note we cannot recurse here without changing the resulting interface order
132 newInterfaces.add(interfac);
133 }
134 }
135 for (Iterator it = newInterfaces.iterator(); it.hasNext();)
136 computeInterfaceOrder(((Class) it.next()).getInterfaces(), table, seen);
137 }
138
139 /**
140 * Flushes the cache of adapter search paths. This is generally required whenever an
141 * adapter is added or removed.
142 * <p>
143 * It is likely easier to just toss the whole cache rather than trying to be smart
144 * and remove only those entries affected.
145 * </p>
146 */
147 public synchronized void flushLookup() {
148 lookup = null;
149 }
150
151 /* (non-Javadoc)
152 * @see org.eclipse.core.runtime.IAdapterManager#getAdapter(java.lang.Object, java.lang.Class)
153 */
154 public Object getAdapter(Object adaptable, Class adapterType) {
155 IAdapterFactory factory = getFactory(adaptable.getClass(), adapterType.getName());
156 Object result = null;
157 if (factory != null)
158 result = factory.getAdapter(adaptable, adapterType);
159 if (result == null && adapterType.isInstance(adaptable))
160 return adaptable;
161 return result;
162 }
163
164 /* (non-Javadoc)
165 * @see org.eclipse.core.runtime.IAdapterManager#getAdapter(java.lang.Object, java.lang.Class)
166 */
167 public Object getAdapter(Object adaptable, String adapterType) {
168 return getAdapter(adaptable, adapterType, false);
169 }
170
171 /**
172 * Returns an adapter of the given type for the provided adapter.
173 * @param adaptable the object to adapt
174 * @param adapterType the type to adapt the object to
175 * @param force <code>true</code> if the plug-in providing the
176 * factory should be activated if necessary. <code>false</code>
177 * if no plugin activations are desired.
178 */
179 private Object getAdapter(Object adaptable, String adapterType, boolean force) {
180 IAdapterFactory factory = getFactory(adaptable.getClass(), adapterType);
181 if (force && factory instanceof AdapterFactoryProxy)
182 factory = ((AdapterFactoryProxy) factory).loadFactory(true);
183 Object result = null;
184 if (factory != null) {
185 Class clazz = classForName(factory, adapterType);
186 if (clazz != null)
187 result = factory.getAdapter(adaptable, clazz);
188 }
189 if (result == null && adaptable.getClass().getName().equals(adapterType))
190 return adaptable;
191 return result;
192 }
193
194 /**
195 * Gets the adapter factory installed for objects of class <code>extensibleClass</code>
196 * which defines adapters of type <code>adapter</code>. If no such factories
197 * exists, returns null.
198 */
199 private synchronized IAdapterFactory getFactory(Class adaptable, String adapterName) {
200 Map table;
201 // check the cache first.
202 if (lookup != null) {
203 table = (Map) lookup.get(adaptable.getName());
204 if (table != null)
205 return (IAdapterFactory) table.get(adapterName);
206 }
207 // Its not in the cache so we have to build the adapter table for this class.
208 table = computeClassOrder(adaptable);
209 //cache the table and do the lookup again.
210 if (lookup == null)
211 lookup = new HashMap(30);
212 lookup.put(adaptable.getName(), table);
213 return (IAdapterFactory) table.get(adapterName);
214 }
215
216 public boolean hasAdapter(Object adaptable, String adapterTypeName) {
217 return getFactory(adaptable.getClass(), adapterTypeName) != null;
218 }
219
220 /* (non-Javadoc)
221 * @see org.eclipse.core.runtime.IAdapterManager#loadAdapter(java.lang.Object, java.lang.String)
222 */
223 public Object loadAdapter(Object adaptable, String adapterTypeName) {
224 return getAdapter(adaptable, adapterTypeName, true);
225 }
226
227 /*
228 * @see IAdapterManager#registerAdapters
229 */
230 public synchronized void registerAdapters(IAdapterFactory factory, Class adaptable) {
231 registerFactory(factory, adaptable.getName());
232 flushLookup();
233 }
234
235 private void registerExtension(IExtension extension) {
236 IConfigurationElement[] elements = extension.getConfigurationElements();
237 for (int j = 0; j < elements.length; j++) {
238 AdapterFactoryProxy proxy = AdapterFactoryProxy.createProxy(elements[j]);
239 if (proxy != null)
240 registerFactory(proxy, proxy.getAdaptableType());
241 }
242 }
243
244 /*
245 * @see IAdapterManager#registerAdapters
246 */
247 private void registerFactory(IAdapterFactory factory, String adaptableType) {
248 List list = (List) factories.get(adaptableType);
249 if (list == null) {
250 list = new ArrayList(5);
251 factories.put(adaptableType, list);
252 }
253 list.add(factory);
254 }
255
256 /**
257 * Loads adapters registered with the adapters extension point from
258 * the plug-in registry. Note that the actual factory implementations
259 * are loaded lazily as they are needed.
260 */
261 private void registerFactoryProxies() {
262 IExtensionRegistry registry = Platform.getExtensionRegistry();
263 IExtensionPoint point = registry.getExtensionPoint(Platform.PI_RUNTIME, Platform.PT_ADAPTERS);
264 if (point == null)
265 return;
266 IExtension[] extensions = point.getExtensions();
267 for (int i = 0; i < extensions.length; i++)
268 registerExtension(extensions[i]);
269 }
270 /* (non-Javadoc)
271 * @see org.eclipse.core.runtime.IRegistryChangeListener#registryChanged(org.eclipse.core.runtime.IRegistryChangeEvent)
272 */
273 public synchronized void registryChanged(IRegistryChangeEvent event) {
274 //find the set of changed adapter extensions
275 HashSet toRemove = null;
276 IExtensionDelta[] deltas = event.getExtensionDeltas();
277 String adapterId = Platform.PI_RUNTIME + '.' + Platform.PT_ADAPTERS;
278 boolean found = false;
279 for (int i = 0; i < deltas.length; i++) {
280 //we only care about extensions to the adapters extension point
281 if (!adapterId.equals(deltas[i].getExtensionPoint().getUniqueIdentifier()))
282 continue;
283 found = true;
284 if (deltas[i].getKind() == IExtensionDelta.ADDED)
285 registerExtension(deltas[i].getExtension());
286 else {
287 //create the hash set lazily
288 if (toRemove == null)
289 toRemove = new HashSet();
290 toRemove.add(deltas[i].getExtension());
291 }
292 }
293 //need to discard cached state for the changed extensions
294 if (found)
295 flushLookup();
296 if (toRemove == null)
297 return;
298 //remove any factories belonging to extensions that are going away
299 for (Iterator it = factories.values().iterator(); it.hasNext();) {
300 for (Iterator it2 = ((List) it.next()).iterator(); it2.hasNext();) {
301 IAdapterFactory factory = (IAdapterFactory) it2.next();
302 if (factory instanceof AdapterFactoryProxy) {
303 IExtension ext = ((AdapterFactoryProxy) factory).getExtension();
304 if (toRemove.contains(ext))
305 it2.remove();
306 }
307 }
308 }
309 }
310
311 /*
312 * @see IAdapterManager#unregisterAdapters
313 */
314 public synchronized void unregisterAdapters(IAdapterFactory factory) {
315 for (Iterator it = factories.values().iterator(); it.hasNext();)
316 ((List) it.next()).remove(factory);
317 flushLookup();
318 }
319
320 /*
321 * @see IAdapterManager#unregisterAdapters
322 */
323 public synchronized void unregisterAdapters(IAdapterFactory factory, Class adaptable) {
324 List factoryList = (List) factories.get(adaptable.getName());
325 if (factoryList == null)
326 return;
327 factoryList.remove(factory);
328 flushLookup();
329 }
330
331 /*
332 * Shuts down the adapter manager by removing all factories
333 * and removing the registry change listener. Should only be
334 * invoked during platform shutdown.
335 */
336 public synchronized void unregisterAllAdapters() {
337 factories.clear();
338 flushLookup();
339 Platform.getExtensionRegistry().removeRegistryChangeListener(this);
340 }
341 }