/*******************************************************************************
* Copyright (c) 2011 Red Hat, Inc.
* Distributed under license by Red Hat, Inc. All rights reserved.
* This program is 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:
* Red Hat, Inc. - initial API and implementation
******************************************************************************/
package org.jboss.tools.cdi.core.extension;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.core.runtime.IExtensionPoint;
import org.eclipse.core.runtime.Platform;
import org.eclipse.jdt.core.IJavaProject;
import org.jboss.tools.cdi.core.CDICorePlugin;
import org.jboss.tools.cdi.core.extension.feature.ICDIFeature;
/**
* Loads Eclipse extension point 'org.jboss.tools.cdi.core.cdiextensions'
* Element:
* 'cdiextension'
* Attributes:
* 'runtime' - Qualified name of CDI runtime extension implementation class.
* 'class' - Qualified name of JBoss Tools CDI model extension implementation class.
* Implements:
* IAdaptable - It is not planned to add regular methods to ICDIExtension,
* all features that are to be provided by implementations, will be available
* through IAdaptable. In that way, adding to core support of a new feature
* will not require to implement new methods in existing extensions.
*
* @author Viacheslav Kabanovich
*
*/
public class CDIExtensionFactory {
static CDIExtensionFactory factory = null;
public static String POINT_ID = "org.jboss.tools.cdi.core.cdiextensions";
public static CDIExtensionFactory getInstance() {
if(factory == null) {
factory = new CDIExtensionFactory();
}
return factory;
}
/**
* Maps CDI runtime fully qualified names of extension implementations to JBoss Tools CDI Model fully
* qualified names of implementations of ICDIExtention.
*/
private Map<String, Set<String>> runtimeToDesign = new HashMap<String, Set<String>>();
/**
* Maps implementation class to its features (IAdaptable cannot be used without a way to give all features)
*/
private Map<Class<? extends ICDIExtension>, Set<Class<?>>> designToFeatures = new HashMap<Class<? extends ICDIExtension>, Set<Class<?>>>();
/**
* Maps fully qualified names of implementations of ICDIExtention to their Class objects.
*/
Map<String, Class<? extends ICDIExtension>> designToClass = new HashMap<String, Class<? extends ICDIExtension>>();
/**
* Maps CDI runtime fully qualified names of extension implementations to
* objects that check if a Java project contains that extension.
*/
Map<String, IExtensionRecognizer> recognizers = new HashMap<String, IExtensionRecognizer>();
private CDIExtensionFactory() {
load();
}
private void load() {
IExtensionPoint point = Platform.getExtensionRegistry().getExtensionPoint(POINT_ID);
IConfigurationElement[] cs = point.getConfigurationElements();
for (IConfigurationElement c: cs) {
String runtime = c.getAttribute("runtime");
String cls = c.getAttribute("class");
String recognizer = c.getAttribute("recognizer");
ICDIExtension extension = null;
try {
Object o = c.createExecutableExtension("class");
if(!(o instanceof ICDIExtension)) {
CDICorePlugin.getDefault().logError("CDI extension " + cls + " should implement ICDIExtension.");
} else {
extension = (ICDIExtension)o;
}
} catch (CoreException e) {
CDICorePlugin.getDefault().logError(e);
continue;
}
if(extension == null) continue;
designToClass.put(cls, extension.getClass());
Set<String> classes = runtimeToDesign.get(runtime);
if(classes == null) {
classes = new HashSet<String>();
runtimeToDesign.put(runtime, classes);
}
classes.add(cls);
if(recognizer != null) {
try {
Object o = c.createExecutableExtension("recognizer");
if(!(o instanceof IExtensionRecognizer)) {
CDICorePlugin.getDefault().logError("CDI extension recognizer " + recognizer + " should implement IExtensionRecognizer.");
} else {
recognizers.put(runtime, (IExtensionRecognizer)o);
}
} catch (CoreException e) {
CDICorePlugin.getDefault().logError(e);
continue;
}
}
}
}
public Set<Class<?>> getFeatures(ICDIExtension extension) {
Set<Class<?>> result = designToFeatures.get(extension.getClass());
if(result == null) {
result = new HashSet<Class<?>>();
getFeatures(extension.getClass(), result);
}
return result;
}
Map<Class<?>, Boolean> featureCheck = new HashMap<Class<?>, Boolean>();
void getFeatures(Class<?> cls, Set<Class<?>> result) {
if(cls == ICDIFeature.class) {
return;
}
if(isFeature(cls)) {
result.add(cls);
}
Class<?>[] is = cls.getInterfaces();
for (Class<?> c: is) {
getFeatures(c, result);
}
if(!cls.isInterface()) {
Class<?> s = cls.getSuperclass();
if(s != null) getFeatures(s, result);
}
}
boolean isFeature(Class<?> cls) {
if(!cls.isInterface()) {
return false;
}
Boolean b = featureCheck.get(cls);
if(b == null) {
Class<?>[] is = cls.getInterfaces();
for (Class<?> c: is) {
if(c == ICDIFeature.class || isFeature(c)) {
b = Boolean.TRUE;
}
}
if(b == null) {
b = Boolean.FALSE;
}
featureCheck.put(cls, b);
}
return b.booleanValue();
}
public Set<String> getExtensionClassesByRuntime(String qualifiedName) {
return runtimeToDesign.get(qualifiedName);
}
public ICDIExtension createExtensionInstance(String qualifiedName) {
Class<? extends ICDIExtension> cls = designToClass.get(qualifiedName);
if(cls != null) {
try {
return cls.newInstance();
} catch (InstantiationException e1) {
CDICorePlugin.getDefault().logError(e1);
} catch (IllegalAccessException e2) {
CDICorePlugin.getDefault().logError(e2);
}
}
return null;
}
/**
* Returns set of CDI extensions, represented by runtime id,
* for which the assigned recognizer determined that
* the given Java project contains that runtime.
* This method is not supposed to check if runtime is
* registered in META-INF/services/javax.enterprise.inject.spi.Extension.
*
* @param javaProject
* @return
*/
public Set<String> getRecognizedRuntimes(IJavaProject javaProject) {
Set<String> result = new HashSet<String>();
for (Map.Entry<String, IExtensionRecognizer> entry: recognizers.entrySet()) {
String runtime = entry.getKey();
IExtensionRecognizer recognizer = entry.getValue();
if(recognizer.containsExtension(runtime, javaProject)) {
result.add(runtime);
}
}
return result;
}
static <F> F adaptTo(ICDIExtension extension, Class<F> feature) {
if(extension == null || feature == null) return null;
Class<?> cls = extension.getClass();
if(feature.isAssignableFrom(cls)) {
return (F)extension;
}
if(extension instanceof IAdaptable) {
return (F)((IAdaptable)extension).getAdapter(feature);
}
return null;
}
}