/*
* 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.lang.reflect.Constructor;
/**
* An implementation of a {@link com.bc.ceres.core.ExtensionFactory} for a single extension type.
*
* @author Norman Fomferra
* @version $Revision$ $Date$
* @since Ceres 0.9
*/
public class SingleTypeExtensionFactory<T, E> implements ExtensionFactory {
private final Class<E> extensionType;
private final Class<? extends E> extensionSubType;
/**
* Constructs a {@code SingleTypeExtensionFactory} for extensions of the given type.
* The {@link #getExtensionSubType() extensionSubType} will be the same as the given type.
*
* @param extensionType The extension type.
*/
public SingleTypeExtensionFactory(Class<E> extensionType) {
this(extensionType, extensionType);
}
/**
* Constructs a {@code SingleTypeExtensionFactory} for extensions of the given sub-type which implement the given type.
*
* @param extensionType The extension type. Must be {@link Class#isAssignableFrom(Class) assignable from} {@code extensionSubType}.
* @param extensionSubType The specific extension sub-type.
*/
public SingleTypeExtensionFactory(Class<E> extensionType, Class<? extends E> extensionSubType) {
Assert.argument(extensionType.isAssignableFrom(extensionSubType), "extensionType.isAssignableFrom(extensionSubType)");
this.extensionType = extensionType;
this.extensionSubType = extensionSubType;
}
/**
* @return The extension type.
*/
public final Class<E> getExtensionType() {
return extensionType;
}
/**
* @return The specific extension sub-type.
*/
public final Class<? extends E> getExtensionSubType() {
return extensionSubType;
}
/**
* Gets an instance of an extension type for the specified object of type T.
* If this factory's {@code extensionType} is assignable from the given {@code extensionType}, the method
* calls {@link #getExtensionImpl(Object, Class)}. Otherwise {@code null} is returned.
*
* @param object The object to be extended.
* @param extensionType The type of the requested extension.
* @return The extension object, or {@code null} if the given object is not extensible by this factory or if an error occurs during the call to {@link #getExtensionImpl(Object, Class)}.
*/
@SuppressWarnings({"unchecked"})
@Override
public final E getExtension(Object object, Class<?> extensionType) {
if (this.extensionType.isAssignableFrom(extensionType)) {
try {
return getExtensionImpl((T) object, (Class<E>) extensionType);
} catch (Throwable throwable) {
throw new RuntimeException(throwable);
}
}
return null;
}
/**
* Creates an extension object for the given {@code object}.
* The new extension object must be an instance of the {@link #getExtensionSubType() extensionSubType} passed to the constructor.
* Called if, and only if this factory's {@link #getExtensionType() extensionType} is assignable from the given {@code extensionType}.
* <p>The default implementation returns a new instance of {@code extensionSubType}, either
* created from its public no-arg constructor or its public 1-arg constructor which can take the given {@code object}. Clients may subclass and
* override this method in order to implement a more sophisticated instance creation.</p>
*
* @param object The object to be extended.
* @param extensionType The type of the requested extension.
* @return The extension object.
* @throws Throwable If an error occurs.
*/
protected E getExtensionImpl(T object, Class<E> extensionType) throws Throwable {
Class<? extends E> subType = getExtensionSubType();
try {
Constructor<? extends E> constructor = subType.getConstructor(object.getClass());
return constructor.newInstance(object);
} catch (Exception e) {
return subType.newInstance();
}
}
/**
* @return The array containing the {@link #getExtensionType() extensionType} and optionally
* the {@link #getExtensionSubType() extensionSubType} supported by this factory.
*/
@Override
public final Class<?>[] getExtensionTypes() {
if (extensionType.equals(extensionSubType)) {
return new Class<?>[]{getExtensionSubType()};
}
return new Class<?>[]{getExtensionSubType(), getExtensionType()};
}
}