blob: b3043a7d715a20dc09f8d4f9b553701f9d6d076b [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2009 IBM Corporation and others.
* 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:
* IBM Corporation - initial API and implementation
******************************************************************************/
package org.eclipse.e4.workbench.ui.internal;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.core.runtime.IExtensionRegistry;
import org.eclipse.core.runtime.InvalidRegistryObjectException;
import org.eclipse.e4.core.services.IContributionFactory;
import org.eclipse.e4.core.services.IContributionFactorySpi;
import org.eclipse.e4.core.services.context.IEclipseContext;
import org.eclipse.e4.core.services.context.spi.ContextInjectionFactory;
import org.eclipse.emf.common.util.URI;
import org.osgi.framework.Bundle;
import org.osgi.service.log.LogService;
/**
* Create the contribution factory.
*/
public class ReflectionContributionFactory implements IContributionFactory {
private IExtensionRegistry registry;
private Map<String, Object> languages;
/**
* Create a reflection factory.
*
* @param registry
* to read languages.
*/
public ReflectionContributionFactory(IExtensionRegistry registry) {
this.registry = registry;
processLanguages();
}
/*
* (non-Javadoc)
*
* @see org.eclipse.e4.core.services.IContributionFactory#call(java.lang.Object,
* java.lang.String, java.lang.String, org.eclipse.e4.core.services.context.IEclipseContext,
* java.lang.Object)
*/
public Object call(Object object, String uriString, String methodName, IEclipseContext context,
Object defaultValue) {
if (uriString != null) {
URI uri = URI.createURI(uriString);
if (uri.segmentCount() > 3) {
String prefix = uri.segment(2);
IContributionFactorySpi factory = (IContributionFactorySpi) languages.get(prefix);
return factory.call(object, methodName, context, defaultValue);
}
}
Method targetMethod = null;
Method[] methods = object.getClass().getMethods();
// Optimization: if there's only one method, use it.
if (methods.length == 1) {
targetMethod = methods[0];
} else {
ArrayList<Method> toSort = new ArrayList<Method>();
for (int i = 0; i < methods.length; i++) {
Method method = methods[i];
// Filter out non-public constructors
if ((method.getModifiers() & Modifier.PUBLIC) != 0
&& method.getName().equals(methodName)) {
toSort.add(method);
}
}
// Sort the methods by descending number of method
// arguments
Collections.sort(toSort, new Comparator<Method>() {
/*
* (non-Javadoc)
*
* @see java.util.Comparator#compare(java.lang.Object, java.lang.Object)
*/
public int compare(Method m1, Method m2) {
int l1 = m1.getParameterTypes().length;
int l2 = m2.getParameterTypes().length;
return l1 - l2;
}
});
// Find the first satisfiable method
for (Iterator<Method> iter = toSort.iterator(); iter.hasNext() && targetMethod == null;) {
Method next = iter.next();
boolean satisfiable = true;
Class<?>[] params = next.getParameterTypes();
for (int i = 0; i < params.length && satisfiable; i++) {
Class<?> clazz = params[i];
if (!context.containsKey(clazz.getName())
&& !IEclipseContext.class.equals(clazz)) {
satisfiable = false;
}
}
if (satisfiable) {
targetMethod = next;
}
}
}
if (targetMethod == null) {
if (defaultValue != null) {
return defaultValue;
}
throw new RuntimeException(
"could not find satisfiable method " + methodName + " in class " + object.getClass()); //$NON-NLS-1$//$NON-NLS-2$
}
Class<?>[] paramKeys = targetMethod.getParameterTypes();
try {
Activator.trace(Policy.DEBUG_CMDS, "calling: " + methodName, null); //$NON-NLS-1$
Object[] params = new Object[paramKeys.length];
for (int i = 0; i < params.length; i++) {
if (IEclipseContext.class.equals(paramKeys[i])) {
params[i] = context;
} else {
params[i] = context.get(paramKeys[i].getName());
}
}
return targetMethod.invoke(object, params);
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
/*
* (non-Javadoc)
*
* @see org.eclipse.e4.core.services.IContributionFactory#create(java.lang.String ,
* org.eclipse.e4.core.services.context.IEclipseContext)
*/
public Object create(String uriString, IEclipseContext context) {
if (uriString == null) {
return null;
}
URI uri = URI.createURI(uriString);
Bundle bundle = getBundle(uri);
Object contribution;
if (bundle != null) {
contribution = createFromBundle(bundle, context, uri);
} else {
contribution = null;
Activator.log(LogService.LOG_ERROR, "Unable to retrive the bundle from the URI: " //$NON-NLS-1$
+ uriString);
}
return contribution;
}
protected Object createFromBundle(Bundle bundle, IEclipseContext context, URI uri) {
Object contribution;
if (uri.segmentCount() > 3) {
String prefix = uri.segment(2);
IContributionFactorySpi factory = (IContributionFactorySpi) languages.get(prefix);
StringBuffer resource = new StringBuffer(uri.segment(3));
for (int i = 4; i < uri.segmentCount(); i++) {
resource.append('/');
resource.append(uri.segment(i));
}
contribution = factory.create(bundle, resource.toString(), context);
} else {
String clazz = uri.segment(2);
try {
Class<?> targetClass = bundle.loadClass(clazz);
contribution = ContextInjectionFactory.make(targetClass, context);
} catch (ClassNotFoundException e) {
contribution = null;
String message = "Unable to load class '" + clazz + "' from bundle '" //$NON-NLS-1$ //$NON-NLS-2$
+ bundle.getBundleId() + "'"; //$NON-NLS-1$
Activator.log(LogService.LOG_ERROR, message, e);
} catch (InvocationTargetException e) {
contribution = null;
String message = "Unable to instantiate class '" + clazz + "' from bundle '" //$NON-NLS-1$ //$NON-NLS-2$
+ bundle.getBundleId() + "'"; //$NON-NLS-1$
Activator.log(LogService.LOG_ERROR, message, e);
} catch (InstantiationException e) {
contribution = null;
String message = "Unable to instantiate class '" + clazz + "' from bundle '" //$NON-NLS-1$ //$NON-NLS-2$
+ bundle.getBundleId() + "'"; //$NON-NLS-1$
Activator.log(LogService.LOG_ERROR, message, e);
}
}
return contribution;
}
protected void processLanguages() {
languages = new HashMap<String, Object>();
String extId = "org.eclipse.e4.languages"; //$NON-NLS-1$
IConfigurationElement[] languageElements = registry.getConfigurationElementsFor(extId);
for (int i = 0; i < languageElements.length; i++) {
IConfigurationElement languageElement = languageElements[i];
try {
languages.put(languageElement.getAttribute("name"), //$NON-NLS-1$
languageElement.createExecutableExtension("contributionFactory")); //$NON-NLS-1$
} catch (InvalidRegistryObjectException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (CoreException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
protected Bundle getBundle(URI platformURI) {
return Activator.getDefault().getBundleForName(platformURI.segment(1));
}
}