blob: e79a338036f63c004d09eca52c4f8a55c21783a0 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2013 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.stem.model.ctdl.functions;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.core.runtime.Platform;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl;
public class FunctionDefinitionLoader {
public static final String EXTENSION_POINT_NAME = "org.eclipse.stem.model.ctdl.functions.functions";
private static FunctionDefinitionLoader INSTANCE;
private Map<String,List<ExternalFunctionDefinition>> functionDefinitions = new HashMap<String,List<ExternalFunctionDefinition>>();
private ResourceSet resourceSet;
private Resource defaultResource;
private FunctionDefinitionLoader()
{
load();
}
public static synchronized FunctionDefinitionLoader getInstance()
{
//if (INSTANCE == null) {
INSTANCE = new FunctionDefinitionLoader();
//}
return INSTANCE;
}
public Map<String,List<ExternalFunctionDefinition>> getDefinitions()
{
return Collections.unmodifiableMap(functionDefinitions);
}
private void load()
{
resourceSet = new ResourceSetImpl();
defaultResource = resourceSet.createResource(URI
.createURI("empty.extFunc"));
IConfigurationElement[] extensions = Platform.getExtensionRegistry().getConfigurationElementsFor(EXTENSION_POINT_NAME);
for (IConfigurationElement extension : extensions) {
ExternalFunctionDefinition functionDef = getDefinitionForExtension(extension);
if (functionDef != null) {
List<ExternalFunctionDefinition> definitionsByName = functionDefinitions.get(functionDef.getName());
if (definitionsByName == null) {
definitionsByName = new ArrayList<ExternalFunctionDefinition>();
functionDefinitions.put(functionDef.getName(), definitionsByName);
}
definitionsByName.add(functionDef);
}
}
}
private ExternalFunctionDefinition getDefinitionForExtension(
IConfigurationElement functionDef)
{
ExternalFunctionDefinition functionDefinition = ExternalFunctionsFactory.eINSTANCE.createExternalFunctionDefinition();
functionDefinition.setName(functionDef.getAttribute("name"));
functionDefinition.setClassName(functionDef.getAttribute("className"));
functionDefinition.setMethodName(functionDef.getAttribute("methodName"));
functionDefinition.setExtPointDefinition(functionDef);
try {
populateFunctionArguments(functionDefinition);
populateMethodArguments(functionDefinition);
validateClassAndMethodSignature(functionDefinition);
} catch (FunctionDefinitionException fde) {
fde.printStackTrace();
return null;
}
defaultResource.getContents().add(functionDefinition);
return functionDefinition;
}
private void populateFunctionArguments(ExternalFunctionDefinition definition)
{
IConfigurationElement[] functionParamBlocks = definition.getExtPointDefinition()
.getChildren("functionParams");
if (functionParamBlocks.length > 0) {
IConfigurationElement[] args = functionParamBlocks[0]
.getChildren("functionParam");
for (IConfigurationElement arg : args) {
FunctionArgument fa = ExternalFunctionsFactory.eINSTANCE.createFunctionArgument();
fa.setName(arg.getAttribute("name"));
fa.setType(arg.getAttribute("type"));
definition.getFunctionArguments().add(fa);
}
}
}
private void populateMethodArguments(ExternalFunctionDefinition definition) throws FunctionDefinitionException
{
IConfigurationElement[] generatedParamBlocks = definition.getExtPointDefinition()
.getChildren("generatedParams");
if (generatedParamBlocks.length > 0) {
IConfigurationElement[] args = generatedParamBlocks[0]
.getChildren("generatedParam");
for (IConfigurationElement arg : args) {
String mapsFrom = arg.getAttribute("mapsFrom");
FunctionArgument fa = getParameterByName(mapsFrom, definition);
if (fa != null) {
FunctionArgumentReference ref = ExternalFunctionsFactory.eINSTANCE.createFunctionArgumentReference();
ref.setArgIndex(definition.getFunctionArguments().indexOf(fa));
ref.setRef(fa);
ref.setType(fa.getType());
definition.getJavaMethodArguments().add(ref);
} else {
Class<?> globalType = STEMDSLUtils.getGlobalSystemVariables().get(mapsFrom);
if (globalType == null) {
globalType = STEMDSLUtils.getGlobalUserVariables().get(mapsFrom);
}
if (globalType == null) {
throw new FunctionDefinitionException("Unable to find parameter mapping for method parameter key "+ mapsFrom, definition);
}
SystemArgumentReference ref = ExternalFunctionsFactory.eINSTANCE.createSystemArgumentReference();
ref.setMapsFrom(mapsFrom);
ref.setType(globalType.getName());
definition.getJavaMethodArguments().add(ref);
}
}
}
}
private void validateClassAndMethodSignature(
ExternalFunctionDefinition definition) throws FunctionDefinitionException
{
Throwable parent = null;
Class<?> functionClass = null;
// Attempt 1: Try to grab it reflectively by class name
try {
functionClass = Class.forName(definition.getClassName());
} catch (ClassNotFoundException e) { } // continue
// Attempt 2: Use the definition available from OSGi/plugin registry
if (functionClass == null) {
try {
Object classInstance = definition.getExtPointDefinition().createExecutableExtension("className");
if (classInstance != null) {
functionClass = classInstance.getClass();
}
} catch (CoreException e) {
// It may be useful to hold on to this
parent = e;
}
}
definition.setClass(functionClass);
// If we still can't find it, then bail out.
// TODO Decide if we can still proceed without the class instance
if (definition.getClass_() == null) {
throw new FunctionDefinitionException("Could not instantiate instance of function definition class for function "+ definition.getName(), definition, parent);
}
String[] declaredParams = getJavaMethodArgTypes(definition);
Method foundMethod = null;
for (Method method : functionClass.getMethods()) {
if (method.getName().equals(definition.getMethodName()) // Check method name
/*&& method.getReturnType() == double.class*/ // Check return type
&& Modifier.isPublic(method.getModifiers()) // Check is public
&& Modifier.isStatic(method.getModifiers())) // Check is static
{
Class<?>[] methodParams = method.getParameterTypes();
if (methodParams.length == declaredParams.length) {
boolean match = true;
for (int idx=0; idx<methodParams.length; idx++) {
if (!methodParameterMatch(methodParams[idx], declaredParams[idx])) {
match = false;
break;
} else {
definition.getJavaMethodArguments().get(idx).setJavaType(methodParams[idx]);
}
}
if (match) {
foundMethod = method;
break;
}
}
}
}
definition.setMethod(foundMethod);
if (definition.getMethod() == null) {
throw new FunctionDefinitionException("Could not find an appropriate class method for definition "+ definition.getName(), definition);
}
// for () {
// Class<?> asdf = m.getReturnType();
//
// // if (asdf.getName())
// System.out.println(m);
// }
}
private boolean methodParameterMatch(Class<?> methodParam, String declaredParam) {
if (methodParam.getName().equals(declaredParam)) {
return true;
}
if (declaredParam.equals("compartment")) {
if (methodParam == double.class || (methodParam == org.eclipse.emf.ecore.EAttribute.class)) {
return true;
}
}
return false;
}
private String[] getJavaMethodArgTypes(ExternalFunctionDefinition definition) throws FunctionDefinitionException
{
String[] types = new String[definition.getJavaMethodArguments().size()];
int idx = 0;
for (JavaMethodArgument arg : definition.getJavaMethodArguments()) {
types[idx++] = arg.getType();
}
return types;
}
private FunctionArgument getParameterByName(String name, ExternalFunctionDefinition def)
{
for (FunctionArgument param : def.getFunctionArguments()) {
if (param.getName().equals(name)) {
return param;
}
}
return null;
}
}