| /******************************************************************************* |
| * 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; |
| } |
| |
| } |