/* * Copyright (C) 2010 Brockmann Consult GmbH (info@brockmann-consult.de) * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 3 of the License, or (at your option) * any later version. * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, see http://www.gnu.org/licenses/ */ package com.bc.ceres.core; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; /** * The {@code ExtensionManager} is a service used to register and unregister {@link ExtensionFactory}s with a given * type of an extensible object. * * @see Extensible * @see ExtensibleObject */ public abstract class ExtensionManager { private static ExtensionManager instance = new Impl(); private static final TypeCondition IS_EQUAL_TO = new TypeCondition() { @Override public boolean fulfilled(Class<?> a, Class<?> b) { return a.equals(b); } }; private static final TypeCondition IS_SUBTYPE_OF = new TypeCondition() { @Override public boolean fulfilled(Class<?> a, Class<?> b) { return !a.equals(b) && b.isAssignableFrom(a); } }; /** * @return The service instance. */ public static ExtensionManager getInstance() { return instance; } /** * @param instance The service instance. */ public static void setInstance(ExtensionManager instance) { Assert.notNull(instance, "instance"); ExtensionManager.instance = instance; } /** * Registers an extension factory for the given extensible type. * * @param extensibleType The extensible type. * @param factory The factory. */ public abstract void register(Class<?> extensibleType, ExtensionFactory factory); /** * Unregisters an extension factory for the given extensible type. * * @param extensibleType The extensible type. * @param factory The factory. */ public abstract void unregister(Class<?> extensibleType, ExtensionFactory factory); /** * Gets all extension factories registered for the given type. * * @param extensibleType The extensible type. * @return The list of extension factories. May be empty. */ public abstract ExtensionFactory[] getExtensionFactories(Class<?> extensibleType); /** * Gets a dynamic extension for the given (extensible) object. * * @param extensibleObject The (extensible) object. * @param extensionType The type of the requested extension. * @return The extension instance, or {@code null} if the given object is not extensible by this factory * or if the factory fails to provide that extension. * @see ExtensionFactory#getExtension(Object, Class) */ public <E> E getExtension(Object extensibleObject, Class<E> extensionType) { Assert.notNull(extensibleObject, "extensibleObject"); Assert.notNull(extensionType, "extensionType"); return findExtensionDeep(extensibleObject, extensibleObject.getClass(), extensionType); } /** * Finds the extension factory for the given types of the extensible object and the extension. * * @param extensibleType The type of the extensible object. * @param extensionType The type of the extension. * @return The factory, or {@code null} if no such can be found. * @deprecated since Ceres 0.13, because the ExtensionManager shall not only try a single factory * that might fail (e.g. return {@code null}). */ @Deprecated public <E> ExtensionFactory findFactory(Class<?> extensibleType, Class<E> extensionType) { Assert.notNull(extensibleType, "extensibleType"); Assert.notNull(extensionType, "extensionType"); ExtensionFactory factory = findFactoryFlat(extensibleType, extensionType, IS_EQUAL_TO); if (factory == null) { factory = findFactoryFlat(extensibleType, extensionType, IS_SUBTYPE_OF); } if (factory == null) { final Class<?> cls = extensibleType.getSuperclass(); if (cls != null) { factory = findFactory(cls, extensionType); } } if (factory == null) { final Class<?>[] interfaces = extensibleType.getInterfaces(); for (Class<?> ifc : interfaces) { factory = findFactory(ifc, extensionType); if (factory != null) { break; } } } return factory; } private <E> ExtensionFactory findFactoryFlat(Class<?> extensibleType, Class<E> extensionType, TypeCondition condition) { for (ExtensionFactory factory : getExtensionFactories(extensibleType)) { for (Class<?> cls : factory.getExtensionTypes()) { if (condition.fulfilled(cls, extensionType)) { return factory; } } } return null; } private <E> E findExtensionDeep(Object extensibleObject, Class<?> extensibleType, Class<E> extensionType) { Assert.notNull(extensibleObject, "extensibleObject"); Assert.notNull(extensibleType, "extensibleType"); Assert.notNull(extensionType, "extensionType"); E extension; extension = findExtensionFlat(extensibleObject, extensibleType, extensionType, IS_EQUAL_TO); if (extension != null) { return extension; } extension = findExtensionFlat(extensibleObject, extensibleType, extensionType, IS_SUBTYPE_OF); if (extension != null) { return extension; } final Class<?> cls = extensibleType.getSuperclass(); if (cls != null) { extension = findExtensionDeep(extensibleObject, cls, extensionType); if (extension != null) { return extension; } } final Class<?>[] interfaces = extensibleType.getInterfaces(); for (Class<?> ifc : interfaces) { extension = findExtensionDeep(extensibleObject, ifc, extensionType); if (extension != null) { return extension; } } return null; } private <E> E findExtensionFlat(Object extensibleObject, Class<?> extensibleType, Class<E> extensionType, TypeCondition condition) { for (ExtensionFactory factory : getExtensionFactories(extensibleType)) { for (Class<?> cls : factory.getExtensionTypes()) { if (condition.fulfilled(cls, extensionType)) { E extension = getExtensionSafe(factory, extensibleObject, extensionType); if (extension != null) { return extension; } } } } return null; } private <E> E getExtensionSafe(ExtensionFactory factory, Object extensibleObject, Class<E> extensionType) { try { return (E) factory.getExtension(extensibleObject, extensionType); } catch (Throwable thrown) { handleExtensionFactoryFailure(extensibleObject, extensionType, thrown); return null; } } private <E> void handleExtensionFactoryFailure(Object extensibleObject, Class<E> extensionType, Throwable thrown) { Logger logger = Logger.getLogger(System.getProperty("ceres.context", "ceres")); logger.log(Level.FINEST, // it is FINEST, because this problem might occur often String.format("Extension factory %s failed providing an extension for a %s of type %s: %s", this.getClass(), extensibleObject.getClass(), extensionType, thrown.getMessage()), thrown); } private interface TypeCondition { boolean fulfilled(Class<?> a, Class<?> b); } private static class Impl extends ExtensionManager { private HashMap<Class, List<ExtensionFactory>> extensionFactoryListMap = new HashMap<Class, List<ExtensionFactory>>(); private static final ExtensionFactory[] NO_EXTENSION_FACTORIES = new ExtensionFactory[0]; @Override public void register(Class<?> extensibleType, ExtensionFactory factory) { Assert.notNull(extensibleType, "extensibleType"); Assert.notNull(factory, "factory"); List<ExtensionFactory> extensionFactoryList = extensionFactoryListMap.get(extensibleType); if (extensionFactoryList == null) { extensionFactoryList = new ArrayList<ExtensionFactory>(); extensionFactoryListMap.put(extensibleType, extensionFactoryList); } if (!extensionFactoryList.contains(factory)) { extensionFactoryList.add(factory); } } @Override public void unregister(Class<?> extensibleType, ExtensionFactory factory) { Assert.notNull(extensibleType, "extensibleType"); Assert.notNull(factory, "factory"); List<ExtensionFactory> extensionFactoryList = extensionFactoryListMap.get(extensibleType); if (extensionFactoryList != null) { extensionFactoryList.remove(factory); } } @Override public ExtensionFactory[] getExtensionFactories(Class<?> extensibleType) { Assert.notNull(extensibleType, "extensibleType"); List<ExtensionFactory> extensionFactoryList = extensionFactoryListMap.get(extensibleType); if (extensionFactoryList != null) { return extensionFactoryList.toArray(new ExtensionFactory[extensionFactoryList.size()]); } else { return NO_EXTENSION_FACTORIES; } } } }