/******************************************************************************* * Copyright (c) 2004, 2012 BREDEX GmbH. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * BREDEX GmbH - initial API and implementation and/or initial documentation *******************************************************************************/ package org.eclipse.jubula.rc.common.adaptable; import java.io.IOException; import java.net.URL; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Enumeration; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import org.eclipse.jubula.rc.common.classloader.DefaultUrlLocator; import org.eclipse.jubula.rc.common.classloader.IUrlLocator; import org.eclipse.jubula.tools.internal.utils.ClassPathHacker; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Singleton to register adapter factories */ public class AdapterFactoryRegistry { /** the name of the package to search for adapters */ public static final String ADAPTER_PACKAGE_NAME = "org.eclipse.jubula.rc.common.adapter"; //$NON-NLS-1$ /** the name of the package to search for extension adapters */ public static final String EXT_ADAPTER_PACKAGE_NAME = "org.eclipse.jubula.ext.rc.common.adapter"; //$NON-NLS-1$ /** the logger */ private static Logger log = LoggerFactory .getLogger(AdapterFactoryRegistry.class); /** * Singleton instance of this class */ private static AdapterFactoryRegistry instance = new AdapterFactoryRegistry(); /** * Map that manages the registration. Key is always a class Value is a * collection of IAdapterFactory */ private Map<Class, Collection<IAdapterFactory>> m_registrationMap = new HashMap<Class, Collection<IAdapterFactory>>(); /** * Call Constructor only by using getInstance */ private AdapterFactoryRegistry() { } /** * Return the singleton of this class * * @return singleton */ public static AdapterFactoryRegistry getInstance() { return instance; } /** * Register adapter factory with all its supported classes * * @param factory * adapter factory that should be registered */ public void registerFactory(IAdapterFactory factory) { Class[] supportedClasses = factory.getSupportedClasses(); for (int i = 0; i < supportedClasses.length; i++) { Collection<IAdapterFactory> registeredFactories = m_registrationMap .get(supportedClasses[i]); if (registeredFactories == null) { registeredFactories = new HashSet<IAdapterFactory>(); } registeredFactories.add(factory); m_registrationMap.put(supportedClasses[i], registeredFactories); } } /** * Sign off adapter factory from all its supported classes * * @param factory * adapter factory that should be signed off */ public void signOffFactory(IAdapterFactory factory) { Class[] supportedClasses = factory.getSupportedClasses(); for (int i = 0; i < supportedClasses.length; i++) { final Class supportedClass = supportedClasses[i]; Collection<IAdapterFactory> registeredFactories = m_registrationMap.get(supportedClass); if (registeredFactories == null) { return; } registeredFactories.remove(factory); m_registrationMap.remove(supportedClass); } } /** * @param targetAdapterClass * Type of the adapter * @param objectToAdapt * object that should be adapted * @return Returns an adapter for the objectToAdapt of type * targetAdapterClass. The collection of all supported adapter * factories is iterated. The first value that is not null will be * returned. <code>Null</code> will only be returned if no adapter * can be found for the targetAdapterClass, none of the given * factories can handle the objectToAdapt or the objectToAdapt * itself is <code>null</code>. */ public Object getAdapter(Class targetAdapterClass, Object objectToAdapt) { if (objectToAdapt == null) { return null; } Collection<IAdapterFactory> registeredFactories = null; Class superClass = objectToAdapt.getClass(); while (registeredFactories == null && superClass != Object.class) { registeredFactories = m_registrationMap.get(superClass); superClass = superClass.getSuperclass(); } if (registeredFactories == null) { return null; } for (Iterator<IAdapterFactory> iterator = registeredFactories .iterator(); iterator.hasNext();) { IAdapterFactory adapterFactory = iterator.next(); Object object = adapterFactory.getAdapter(targetAdapterClass, objectToAdapt); if (object != null) { return object; } } return null; } /** * Use this method in eclipse environments. * Must be called to initialize the registration of adapters. * @param urlLocator The URL location converter needed in eclipse environments. */ public static void initRegistration(IUrlLocator urlLocator) { Class[] adapterFactories = findClassesOfType(urlLocator, ADAPTER_PACKAGE_NAME, IAdapterFactory.class); Class[] externalAdapterFactories = findClassesOfType(urlLocator, EXT_ADAPTER_PACKAGE_NAME, IAdapterFactory.class); List<Class> allFactories = new ArrayList<Class>( Arrays.asList(adapterFactories)); allFactories.addAll(Arrays.asList(externalAdapterFactories)); // Register all found factories for (Class c : allFactories) { try { IAdapterFactory factory = (IAdapterFactory) c.newInstance(); getInstance().registerFactory(factory); } catch (IllegalAccessException e) { log.error(e.getLocalizedMessage(), e); } catch (InstantiationException e) { log.error(e.getLocalizedMessage(), e); } } } /** * Use this method outside of eclipse environments. Must be called to * initialize the registration of adapters. This method directly * calls {@link AdapterFactoryRegistry#initRegistration(IUrlLocator)} with * the {@link DefaultUrlLocator}. */ public static void initRegistration() { initRegistration(new DefaultUrlLocator()); } /** * Investigate a package of subclasses of a specific superclass * @param urlLocator * The URL location converter needed in eclipse environments. * @param packageName * name of the package * @param superclass * parent class for found classes * @return found classes */ private static Class[] findClassesOfType(IUrlLocator urlLocator, String packageName, Class<IAdapterFactory> superclass) { try { Class[] allClasses = getClasses(urlLocator, packageName); List<Class> assignableClasses = new ArrayList<Class>(); for (int i = 0; i < allClasses.length; i++) { if (superclass.isAssignableFrom(allClasses[i]) && superclass != allClasses[i]) { assignableClasses.add(allClasses[i]); } } return castListToClassArray(assignableClasses); } catch (ClassNotFoundException e) { return new Class[0]; } catch (IOException e) { return new Class[0]; } } /** * Cast a list of classes to an array of classes * * @param classes * List of classes * @return array of classes */ private static Class[] castListToClassArray(List<Class> classes) { Class[] arrayClasses = new Class[classes.size()]; for (int i = 0; i < arrayClasses.length; i++) { arrayClasses[i] = classes.get(i); } return arrayClasses; } /** * Scans all classes accessible from the context class loader which belong * to the given package and sub packages. * @param urlLocator * The URL location converter needed in eclipse environments. * @param packageName * The base package * @return The classes * @throws ClassNotFoundException * @throws IOException */ private static Class[] getClasses(IUrlLocator urlLocator, String packageName) throws ClassNotFoundException, IOException { ClassLoader classLoader = AdapterFactoryRegistry.class.getClassLoader(); String path = packageName.replace('.', '/'); Enumeration<URL> resources = classLoader.getResources(path); List<URL> dirs = new ArrayList<URL>(); while (resources.hasMoreElements()) { URL resource = resources.nextElement(); try { resource = urlLocator.convertUrl(resource); dirs.add(resource); } catch (IOException e) { log.error(e.getLocalizedMessage(), e); } } List<Class> classes = new ArrayList<Class>(); for (int i = 0; i < dirs.size(); i++) { if (dirs.get(i).toString().startsWith("jar:")) { //$NON-NLS-1$ classes.addAll(ClassPathHacker. findClassesInJar(dirs.get(i), packageName, classLoader)); } else { classes.addAll(ClassPathHacker.findClasses( dirs.get(i), packageName)); } } return castListToClassArray(classes); } /** * Checks if the given factory is already registered * @param factory the factory to check * @return true if the factory is already registered, false otherwise */ public boolean isRegistered(IAdapterFactory factory) { Class[] supportedClasses = factory.getSupportedClasses(); for (int i = 0; i < supportedClasses.length; i++) { final Class supportedClass = supportedClasses[i]; Collection<IAdapterFactory> registeredFactories = m_registrationMap .get(supportedClass); if (registeredFactories == null) { return false; } for (IAdapterFactory iAdapterFactory : registeredFactories) { if (iAdapterFactory.getClass().equals(factory.getClass())) { return true; } } } return false; } }