KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > core > internal > runtime > AdapterManager


1 /*******************************************************************************
2  * Copyright (c) 2000, 2006 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Eclipse Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/epl-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.IAdapterFactory;
15 import org.eclipse.core.runtime.IAdapterManager;
16
17 /**
18  * This class is the standard implementation of <code>IAdapterManager</code>. It provides
19  * fast lookup of property values with the following semantics:
20  * <ul>
21  * <li>At most one factory will be invoked per property lookup
22  * <li>If multiple installed factories provide the same adapter, only the first found in
23  * the search order will be invoked.
24  * <li>The search order from a class with the definition <br>
25  * <code>class X extends Y implements A, B</code><br> is as follows: <il>
26  * <li>the target's class: X
27  * <li>X's superclasses in order to <code>Object</code>
28  * <li>a breadth-first traversal of the target class's interfaces in the order returned by
29  * <code>getInterfaces</code> (in the example, A and its superinterfaces then B and its
30  * superinterfaces) </il>
31  * </ul>
32  *
33  * @see IAdapterFactory
34  * @see IAdapterManager
35  */

36 public final class AdapterManager implements IAdapterManager {
37     /**
38      * Cache of adapters for a given adaptable class. Maps String -> Map
39      * (adaptable class name -> (adapter class name -> factory instance))
40      * Thread safety note: The outer map is synchronized using a synchronized
41      * map wrapper class. The inner map is not synchronized, but it is immutable
42      * so synchronization is not necessary.
43      */

44     private Map adapterLookup;
45
46     /**
47      * Cache of classes for a given type name. Avoids too many loadClass calls.
48      * (factory -> (type name -> Class)).
49      * Thread safety note: Since this structure is a nested hash map, and both
50      * the inner and outer maps are mutable, access to this entire structure is
51      * controlled by the classLookupLock field. Note the field can still be
52      * nulled concurrently without holding the lock.
53      */

54     private Map classLookup;
55
56     /**
57      * The lock object controlling access to the classLookup data structure.
58      */

59     private final Object JavaDoc classLookupLock = new Object JavaDoc();
60
61     /**
62      * Cache of class lookup order (Class -> Class[]). This avoids having to compute often, and
63      * provides clients with quick lookup for instanceOf checks based on type name.
64      * Thread safety note: The map is synchronized using a synchronized
65      * map wrapper class. The arrays within the map are immutable.
66      */

67     private Map classSearchOrderLookup;
68
69     /**
70      * Map of factories, keyed by <code>String</code>, fully qualified class name of
71      * the adaptable class that the factory provides adapters for. Value is a <code>List</code>
72      * of <code>IAdapterFactory</code>.
73      */

74     private final HashMap factories;
75
76     private final ArrayList lazyFactoryProviders;
77
78     private static final AdapterManager singleton = new AdapterManager();
79
80     public static AdapterManager getDefault() {
81         return singleton;
82     }
83
84     /**
85      * Private constructor to block instance creation.
86      */

87     private AdapterManager() {
88         factories = new HashMap(5);
89         lazyFactoryProviders = new ArrayList(1);
90     }
91
92     /**
93      * Given a type name, add all of the factories that respond to those types into
94      * the given table. Each entry will be keyed by the adapter class name (supplied in
95      * IAdapterFactory.getAdapterList).
96      */

97     private void addFactoriesFor(String JavaDoc typeName, Map table) {
98         List factoryList = (List) getFactories().get(typeName);
99         if (factoryList == null)
100             return;
101         for (int i = 0, imax = factoryList.size(); i < imax; i++) {
102             IAdapterFactory factory = (IAdapterFactory) factoryList.get(i);
103             if (factory instanceof IAdapterFactoryExt) {
104                 String JavaDoc[] adapters = ((IAdapterFactoryExt) factory).getAdapterNames();
105                 for (int j = 0; j < adapters.length; j++) {
106                     if (table.get(adapters[j]) == null)
107                         table.put(adapters[j], factory);
108                 }
109             } else {
110                 Class JavaDoc[] adapters = factory.getAdapterList();
111                 for (int j = 0; j < adapters.length; j++) {
112                     String JavaDoc adapterName = adapters[j].getName();
113                     if (table.get(adapterName) == null)
114                         table.put(adapterName, factory);
115                 }
116             }
117         }
118     }
119
120     private void cacheClassLookup(IAdapterFactory factory, Class JavaDoc clazz) {
121         synchronized (classLookupLock) {
122             //cache reference to lookup to protect against concurrent flush
123
Map lookup = classLookup;
124             if (lookup == null)
125                 classLookup = lookup = new HashMap(4);
126             HashMap classes = (HashMap) lookup.get(factory);
127             if (classes == null) {
128                 classes = new HashMap(4);
129                 lookup.put(factory, classes);
130             }
131             classes.put(clazz.getName(), clazz);
132         }
133     }
134
135     private Class JavaDoc cachedClassForName(IAdapterFactory factory, String JavaDoc typeName) {
136         synchronized (classLookupLock) {
137             Class JavaDoc clazz = null;
138             //cache reference to lookup to protect against concurrent flush
139
Map lookup = classLookup;
140             if (lookup != null) {
141                 HashMap classes = (HashMap) lookup.get(factory);
142                 if (classes != null) {
143                     clazz = (Class JavaDoc) classes.get(typeName);
144                 }
145             }
146             return clazz;
147         }
148     }
149
150     /**
151      * Returns the class with the given fully qualified name, or null
152      * if that class does not exist or belongs to a plug-in that has not
153      * yet been loaded.
154      */

155     private Class JavaDoc classForName(IAdapterFactory factory, String JavaDoc typeName) {
156         Class JavaDoc clazz = cachedClassForName(factory, typeName);
157         if (clazz == null) {
158             try {
159                 if (factory instanceof IAdapterFactoryExt)
160                     factory = ((IAdapterFactoryExt) factory).loadFactory(false);
161                 if (factory != null) {
162                     clazz = factory.getClass().getClassLoader().loadClass(typeName);
163                     cacheClassLookup(factory, clazz);
164                 }
165             } catch (ClassNotFoundException JavaDoc e) {
166                 // class not yet loaded
167
}
168         }
169         return clazz;
170     }
171
172     /* (non-Javadoc)
173      * @see org.eclipse.core.runtime.IAdapterManager#getAdapterTypes(java.lang.Class)
174      */

175     public String JavaDoc[] computeAdapterTypes(Class JavaDoc adaptable) {
176         Set types = getFactories(adaptable).keySet();
177         return (String JavaDoc[]) types.toArray(new String JavaDoc[types.size()]);
178     }
179
180     /**
181      * Computes the adapters that the provided class can adapt to, along
182      * with the factory object that can perform that transformation. Returns
183      * a table of adapter class name to factory object.
184      * @param adaptable
185      */

186     private Map getFactories(Class JavaDoc adaptable) {
187         //cache reference to lookup to protect against concurrent flush
188
Map lookup = adapterLookup;
189         if (lookup == null)
190             adapterLookup = lookup = Collections.synchronizedMap(new HashMap(30));
191         Map table = (Map) lookup.get(adaptable.getName());
192         if (table == null) {
193             // calculate adapters for the class
194
table = new HashMap(4);
195             Class JavaDoc[] classes = computeClassOrder(adaptable);
196             for (int i = 0; i < classes.length; i++)
197                 addFactoriesFor(classes[i].getName(), table);
198             // cache the table
199
lookup.put(adaptable.getName(), table);
200         }
201         return table;
202     }
203
204     public Class JavaDoc[] computeClassOrder(Class JavaDoc adaptable) {
205         Class JavaDoc[] classes = null;
206         //cache reference to lookup to protect against concurrent flush
207
Map lookup = classSearchOrderLookup;
208         if (lookup == null)
209             classSearchOrderLookup = lookup = Collections.synchronizedMap(new HashMap());
210         else
211             classes = (Class JavaDoc[]) lookup.get(adaptable);
212         // compute class order only if it hasn't been cached before
213
if (classes == null) {
214             ArrayList classList = new ArrayList();
215             computeClassOrder(adaptable, classList);
216             classes = (Class JavaDoc[]) classList.toArray(new Class JavaDoc[classList.size()]);
217             lookup.put(adaptable, classes);
218         }
219         return classes;
220     }
221
222     /**
223      * Builds and returns a table of adapters for the given adaptable type.
224      * The table is keyed by adapter class name. The
225      * value is the <b>sole<b> factory that defines that adapter. Note that
226      * if multiple adapters technically define the same property, only the
227      * first found in the search order is considered.
228      *
229      * Note that it is important to maintain a consistent class and interface
230      * lookup order. See the class comment for more details.
231      */

232     private void computeClassOrder(Class JavaDoc adaptable, Collection classes) {
233         Class JavaDoc clazz = adaptable;
234         Set seen = new HashSet(4);
235         while (clazz != null) {
236             classes.add(clazz);
237             computeInterfaceOrder(clazz.getInterfaces(), classes, seen);
238             clazz = clazz.getSuperclass();
239         }
240     }
241
242     private void computeInterfaceOrder(Class JavaDoc[] interfaces, Collection classes, Set seen) {
243         List newInterfaces = new ArrayList(interfaces.length);
244         for (int i = 0; i < interfaces.length; i++) {
245             Class JavaDoc interfac = interfaces[i];
246             if (seen.add(interfac)) {
247                 //note we cannot recurse here without changing the resulting interface order
248
classes.add(interfac);
249                 newInterfaces.add(interfac);
250             }
251         }
252         for (Iterator it = newInterfaces.iterator(); it.hasNext();)
253             computeInterfaceOrder(((Class JavaDoc) it.next()).getInterfaces(), classes, seen);
254     }
255
256     /**
257      * Flushes the cache of adapter search paths. This is generally required whenever an
258      * adapter is added or removed.
259      * <p>
260      * It is likely easier to just toss the whole cache rather than trying to be smart
261      * and remove only those entries affected.
262      * </p>
263      */

264     public synchronized void flushLookup() {
265         adapterLookup = null;
266         classLookup = null;
267         classSearchOrderLookup = null;
268     }
269
270     /* (non-Javadoc)
271      * @see org.eclipse.core.runtime.IAdapterManager#getAdapter(java.lang.Object, java.lang.Class)
272      */

273     public Object JavaDoc getAdapter(Object JavaDoc adaptable, Class JavaDoc adapterType) {
274         IAdapterFactory factory = (IAdapterFactory) getFactories(adaptable.getClass()).get(adapterType.getName());
275         Object JavaDoc result = null;
276         if (factory != null)
277             result = factory.getAdapter(adaptable, adapterType);
278         if (result == null && adapterType.isInstance(adaptable))
279             return adaptable;
280         return result;
281     }
282
283     /* (non-Javadoc)
284      * @see org.eclipse.core.runtime.IAdapterManager#getAdapter(java.lang.Object, java.lang.Class)
285      */

286     public Object JavaDoc getAdapter(Object JavaDoc adaptable, String JavaDoc adapterType) {
287         return getAdapter(adaptable, adapterType, false);
288     }
289
290     /**
291      * Returns an adapter of the given type for the provided adapter.
292      * @param adaptable the object to adapt
293      * @param adapterType the type to adapt the object to
294      * @param force <code>true</code> if the plug-in providing the
295      * factory should be activated if necessary. <code>false</code>
296      * if no plugin activations are desired.
297      */

298     private Object JavaDoc getAdapter(Object JavaDoc adaptable, String JavaDoc adapterType, boolean force) {
299         IAdapterFactory factory = (IAdapterFactory) getFactories(adaptable.getClass()).get(adapterType);
300         if (force && factory instanceof IAdapterFactoryExt)
301             factory = ((IAdapterFactoryExt) factory).loadFactory(true);
302         Object JavaDoc result = null;
303         if (factory != null) {
304             Class JavaDoc clazz = classForName(factory, adapterType);
305             if (clazz != null)
306                 result = factory.getAdapter(adaptable, clazz);
307         }
308         if (result == null && adaptable.getClass().getName().equals(adapterType))
309             return adaptable;
310         return result;
311     }
312
313     public boolean hasAdapter(Object JavaDoc adaptable, String JavaDoc adapterTypeName) {
314         return getFactories(adaptable.getClass()).get(adapterTypeName) != null;
315     }
316
317     /* (non-Javadoc)
318      * @see org.eclipse.core.runtime.IAdapterManager#queryAdapter(java.lang.Object, java.lang.String)
319      */

320     public int queryAdapter(Object JavaDoc adaptable, String JavaDoc adapterTypeName) {
321         IAdapterFactory factory = (IAdapterFactory) getFactories(adaptable.getClass()).get(adapterTypeName);
322         if (factory == null)
323             return NONE;
324         if (factory instanceof IAdapterFactoryExt) {
325             factory = ((IAdapterFactoryExt) factory).loadFactory(false); // don't force loading
326
if (factory == null)
327                 return NOT_LOADED;
328         }
329         return LOADED;
330     }
331
332     /* (non-Javadoc)
333      * @see org.eclipse.core.runtime.IAdapterManager#loadAdapter(java.lang.Object, java.lang.String)
334      */

335     public Object JavaDoc loadAdapter(Object JavaDoc adaptable, String JavaDoc adapterTypeName) {
336         return getAdapter(adaptable, adapterTypeName, true);
337     }
338
339     /*
340      * @see IAdapterManager#registerAdapters
341      */

342     public synchronized void registerAdapters(IAdapterFactory factory, Class JavaDoc adaptable) {
343         registerFactory(factory, adaptable.getName());
344         flushLookup();
345     }
346
347     /*
348      * @see IAdapterManager#registerAdapters
349      */

350     public void registerFactory(IAdapterFactory factory, String JavaDoc adaptableType) {
351         List list = (List) factories.get(adaptableType);
352         if (list == null) {
353             list = new ArrayList(5);
354             factories.put(adaptableType, list);
355         }
356         list.add(factory);
357     }
358
359     /*
360      * @see IAdapterManager#unregisterAdapters
361      */

362     public synchronized void unregisterAdapters(IAdapterFactory factory) {
363         for (Iterator it = factories.values().iterator(); it.hasNext();)
364             ((List) it.next()).remove(factory);
365         flushLookup();
366     }
367
368     /*
369      * @see IAdapterManager#unregisterAdapters
370      */

371     public synchronized void unregisterAdapters(IAdapterFactory factory, Class JavaDoc adaptable) {
372         List factoryList = (List) factories.get(adaptable.getName());
373         if (factoryList == null)
374             return;
375         factoryList.remove(factory);
376         flushLookup();
377     }
378
379     /*
380      * Shuts down the adapter manager by removing all factories
381      * and removing the registry change listener. Should only be
382      * invoked during platform shutdown.
383      */

384     public synchronized void unregisterAllAdapters() {
385         factories.clear();
386         flushLookup();
387     }
388
389     public void registerLazyFactoryProvider(IAdapterManagerProvider factoryProvider) {
390         synchronized (lazyFactoryProviders) {
391             lazyFactoryProviders.add(factoryProvider);
392         }
393     }
394
395     public boolean unregisterLazyFactoryProvider(IAdapterManagerProvider factoryProvider) {
396         synchronized (lazyFactoryProviders) {
397             return lazyFactoryProviders.remove(factoryProvider);
398         }
399     }
400
401     public HashMap getFactories() {
402         // avoid the synchronize if we don't have to call it
403
if (lazyFactoryProviders.size() == 0)
404             return factories;
405         synchronized (lazyFactoryProviders) {
406             while (lazyFactoryProviders.size() > 0) {
407                 IAdapterManagerProvider provider = (IAdapterManagerProvider) lazyFactoryProviders.remove(0);
408                 if (provider.addFactories(this))
409                     flushLookup();
410             }
411         }
412         return factories;
413     }
414 }
415
Popular Tags