/*******************************************************************************
* Copyright (c) 2008, 2014 Stuart McCulloch
* 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:
* Stuart McCulloch - initial API and implementation
*******************************************************************************/
package org.eclipse.sisu.peaberry.eclipse;
import static java.lang.reflect.Proxy.newProxyInstance;
import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Method;
import org.eclipse.core.runtime.ContributorFactoryOSGi;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.sisu.peaberry.ServiceException;
import org.osgi.framework.Bundle;
/**
* A factory that attempts to create bean instances from Eclipse Extensions.
*
* @author mcculls@gmail.com (Stuart McCulloch)
*/
final class ExtensionBeanFactory {
private static final String CONTENT_KEY = "text()";
// instances not allowed
private ExtensionBeanFactory() {}
static Object newInstance(final Class<?> clazz, final IConfigurationElement config) {
// client just wants the actual configuration object
if (Object.class == clazz || IConfigurationElement.class == clazz) {
return config;
}
try {
// first try to instantiate direct class
return newExtensionImpl(clazz, config);
} catch (final RuntimeException re) {/* fall back to proxy */} // NOPMD
// create proxy based on the supplied interface
final ClassLoader loader = clazz.getClassLoader();
final Class<?>[] api = new Class[]{clazz};
return newProxyInstance(loader, api, new ExtensionBeanHandler(config));
}
static Object newExtensionImpl(final Class<?> clazz, final IConfigurationElement config) {
// assume name is kept under "class" (unless mapped)
final String clazzKey = mapName(clazz, "class");
final String clazzName = mapContent(config, clazzKey);
// make sure implementation is compatible with the required interface
if (!clazz.isAssignableFrom(loadExtensionClass(config, clazzName))) {
throw new ClassCastException(clazz + " is not assignable from: " + clazzName);
}
try {
return config.createExecutableExtension(clazzKey);
} catch (final CoreException e) {
throw new ServiceException(e);
}
}
static String mapName(final AnnotatedElement type, final String name) {
if (null != findAnnotation(type, "MapContent")) {
return CONTENT_KEY;
}
final String mapName = getAnnotationValue(findAnnotation(type, "MapName"), String.class);
return null == mapName || mapName.length() == 0 ? name : mapName;
}
static String mapContent(final IConfigurationElement config, final String elementKey) {
return CONTENT_KEY.equals(elementKey) ? config.getValue() : config.getAttribute(elementKey);
}
static Annotation findAnnotation(final AnnotatedElement element, final String simpleName) {
for (final Annotation a : element.getAnnotations()) {
if (simpleName.equals(a.annotationType().getSimpleName())) {
return a;
}
}
return null;
}
@SuppressWarnings("unchecked")
static <T> T getAnnotationValue(final Annotation element, final Class<? extends T> clazz) {
try {
final Method m = element.annotationType().getDeclaredMethod("value");
if (clazz.isAssignableFrom(m.getReturnType())) {
return (T) m.invoke(element);
}
} catch (final Exception e) {/* no such setting */} // NOPMD
return null;
}
static Class<?> loadExtensionClass(final IConfigurationElement config, final String clazzName) {
final Bundle bundle = ContributorFactoryOSGi.resolve(config.getContributor());
String value = clazzName;
int n = value.indexOf(':');
// unravel the factory indirection to get the real class
if (value.startsWith(GuiceExtensionFactory.class.getName())) {
value = n < 0 ? config.getAttribute("id") : value.substring(n + 1);
n = value.indexOf(':');
}
try {
return bundle.loadClass(n < 0 ? value : value.substring(0, n));
} catch (final ClassNotFoundException e) {
throw new ServiceException(e);
}
}
}