blob: e0a12b61bb0ebbe4cc4c6096eec86a9c0d803326 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2014 Bernhard Wedl and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* Contributors:
* Bernhard Wedl - initial API and implementation
*******************************************************************************/
package org.eclipse.ease.modules;
import java.lang.annotation.Annotation;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map.Entry;
import org.eclipse.core.runtime.Path;
import org.eclipse.ease.Activator;
import org.eclipse.ease.ICodeFactory.Parameter;
import org.eclipse.ease.IScriptEngine;
import org.eclipse.ease.Logger;
import org.eclipse.ease.service.IScriptService;
import org.eclipse.ease.service.ScriptService;
public final class ModuleHelper {
@Deprecated
private ModuleHelper() {
}
/**
* Returns a list of exported methods. Any public method marked by a @WrapToScript annotation is exported. If no annotations are found all public methods
* are returned.
*
* @param clazz
* class to be evaluated
* @return list of methods
*/
public static List<Method> getMethods(final Class<?> clazz) {
try {
if ((clazz == null) || (clazz.getMethods().length == 0))
return Collections.emptyList();
} catch (final NoClassDefFoundError e) {
Logger.warning(Activator.PLUGIN_ID, "The class " + clazz.getName() + " is broken", e);
return Collections.emptyList();
}
final List<Method> methods = new ArrayList<>();
final boolean wrapping = ModuleHelper.hasWrapToScript(clazz);
for (final Method method : clazz.getMethods()) {
if ((Modifier.isPublic(method.getModifiers()) && (!wrapping || method.isAnnotationPresent(WrapToScript.class)))) {
// ignore methods from java.lang.Object
if (Object.class.equals(method.getDeclaringClass()))
continue;
methods.add(method);
}
}
return methods;
}
/**
* Returns a List of exported fields. Any public final field marked by a @WrapToScript annotation is exported. If no annotations are found all public final
* fields are returned.
*
* @param clazz
* Class to be evaluated
* @return List of Fields
*/
public static List<Field> getFields(final Class<?> clazz) {
if ((clazz == null) || (clazz.getDeclaredFields().length == 0))
return Collections.emptyList();
final List<Field> fields = new ArrayList<>();
final boolean wrapping = ModuleHelper.hasWrapToScript(clazz);
for (final Field field : clazz.getFields()) {
if ((Modifier.isFinal(field.getModifiers()))
&& (Modifier.isPublic(field.getModifiers()) && (!wrapping || field.isAnnotationPresent(WrapToScript.class))))
fields.add(field);
}
return fields;
}
/**
* Returns if the Class to be evaluated contains {@link WrapToScript} annotations.
*
* @param clazz
* class to be evaluated
* @return <code>true</code> when clazz contains {@link WrapToScript} annotations
*/
private static boolean hasWrapToScript(final Class<?> clazz) {
for (final Field field : clazz.getDeclaredFields()) {
if (field.isAnnotationPresent(WrapToScript.class))
return true;
}
for (final Method method : clazz.getMethods()) {
if (method.isAnnotationPresent(WrapToScript.class))
return true;
}
return false;
}
/**
* Resolve a relative module name to its definition. When only the last part of a module name is provided (without path), this method tries to locate the
* module and returns its definition. If 2 modules with the same name are detected, a {@link RuntimeException} is thrown.
*
* @param identifier
* module identifier
* @return module definition or <code>null</code> in case no matching definition is found
*/
public static ModuleDefinition resolveModuleName(final String identifier) {
final Path searchPath = new Path(identifier);
final IScriptService scriptService = ScriptService.getService();
final Collection<ModuleDefinition> availableModules = scriptService.getAvailableModules();
// check for valid, absolute path
ModuleDefinition candidate = null;
for (final ModuleDefinition definition : availableModules) {
if (definition.getPath().equals(searchPath))
return definition;
if ((searchPath.segmentCount() == 1) && (!searchPath.isAbsolute())) {
if (definition.getPath().lastSegment().equals(searchPath.lastSegment())) {
if (candidate != null)
// we already had one candidate, name is ambiguous
throw new RuntimeException("Module identifier \"" + identifier + "\" is ambiguous. Use full path name to load.");
candidate = definition;
}
}
}
return candidate;
}
/**
* Get all loaded modules for a given script engine.
*
* @param engine
* engine to parse
* @return module definitions for all loaded modules
*/
public static Collection<ModuleDefinition> getLoadedModules(final IScriptEngine engine) {
// FIXME should be recoded as we should better query the environment module directly
final Collection<ModuleDefinition> modules = new HashSet<>();
// statically access service as workbench is not available in headless mode
final IScriptService scriptService = ScriptService.getService();
for (final Entry<String, Object> entry : engine.getVariables().entrySet()) {
if (entry.getKey().startsWith(EnvironmentModule.MODULE_PREFIX)) {
final Class<? extends Object> moduleClass = entry.getValue().getClass();
for (final ModuleDefinition definition : scriptService.getAvailableModules()) {
if (definition.getModuleClass().equals(moduleClass)) {
modules.add(definition);
break;
}
}
}
}
return modules;
}
public static List<Parameter> getParameters(final Method method) {
final ArrayList<Parameter> parameters = new ArrayList<>();
for (int index = 0; index < method.getParameters().length; index++) {
final Parameter parameter = new Parameter();
parameter.setClass(method.getParameters()[index].getType());
parameter.setName(method.getParameters()[index].getName());
final ScriptParameter annotation = getParameterAnnotation(method.getParameters()[index].getAnnotations());
if (annotation != null) {
parameter.setOptional(ScriptParameter.Helper.isOptional(annotation));
parameter.setDefault(annotation.defaultValue());
}
parameters.add(parameter);
}
// post process parameters: find unique names for unnamed parameters
for (final Parameter parameter : parameters) {
if (parameter.getName().isEmpty())
parameter.setName(findName(parameters));
}
return parameters;
}
private static ScriptParameter getParameterAnnotation(final Annotation[] annotations) {
for (final Annotation annotation : annotations) {
if (annotation instanceof ScriptParameter)
return (ScriptParameter) annotation;
}
return null;
}
/**
* Find a unique name that is not used yet.
*
* @param parameters
* list of available parameters
* @return unique unused parameter name
*/
private static String findName(final List<Parameter> parameters) {
String name;
int index = 1;
boolean found;
do {
found = true;
name = "param" + index;
for (final Parameter parameter : parameters) {
if (name.equals(parameter.getName())) {
index++;
found = false;
break;
}
}
} while (!found);
return name;
}
/**
* Check deprecation status of a method/field.
*
* @param element
* method/field to check
* @return <code>true</code> when deprecated
*/
public static boolean isDeprecated(final AccessibleObject element) {
if (element instanceof Method)
return (element.getAnnotation(Deprecated.class) != null) || (((Method) element).getDeclaringClass().getAnnotation(Deprecated.class) != null);
if (element instanceof Field)
return (element.getAnnotation(Deprecated.class) != null) || (((Field) element).getDeclaringClass().getAnnotation(Deprecated.class) != null);
return element.getAnnotation(Deprecated.class) != null;
}
}