Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

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 }