001package jmri.jmrit.logixng.util.parser.functions; 002 003import java.lang.reflect.InvocationTargetException; 004import java.lang.reflect.Constructor; 005import java.util.ArrayList; 006import java.util.HashSet; 007import java.util.List; 008import java.util.Set; 009 010import jmri.JmriException; 011import jmri.jmrit.logixng.SymbolTable; 012import jmri.jmrit.logixng.util.parser.*; 013import jmri.util.TypeConversionUtil; 014 015import org.openide.util.lookup.ServiceProvider; 016 017/** 018 * Implementation of string functions. 019 * 020 * @author Daniel Bergqvist 2020 021 */ 022@ServiceProvider(service = FunctionFactory.class) 023public class JavaFunctions implements FunctionFactory { 024 025 @Override 026 public String getModule() { 027 return "Java"; 028 } 029 030 @Override 031 public Set<Function> getFunctions() { 032 Set<Function> functionClasses = new HashSet<>(); 033 034 addNewFunction(functionClasses); 035 036 return functionClasses; 037 } 038 039 @Override 040 public Set<Constant> getConstants() { 041 Set<Constant> constantClasses = new HashSet<>(); 042 constantClasses.add(new Constant(getModule(), "null", null)); 043 constantClasses.add(new Constant(getModule(), "None", null)); 044 return constantClasses; 045 } 046 047 @Override 048 public String getConstantDescription() { 049 return Bundle.getMessage("Java.ConstantDescriptions"); 050 } 051 052 private void addNewFunction(Set<Function> functionClasses) { 053 functionClasses.add(new AbstractFunction(this, "new", Bundle.getMessage("Java.new_Descr")) { 054 private boolean isAssignableFrom(Class<?> type, Object param) { 055 if (param == null) return true; 056 if (type.isAssignableFrom(param.getClass())) return true; 057 if ((type == Byte.TYPE) && (param instanceof Long)) return true; 058 if ((type == Short.TYPE) && (param instanceof Long)) return true; 059 if ((type == Integer.TYPE) && (param instanceof Long)) return true; 060 return ((type == Float.TYPE) && (param instanceof Double)); 061 } 062 063 private boolean canCall(Constructor<?> constructor, Object[] params) { 064 Class<?>[] paramTypes = constructor.getParameterTypes(); 065 if (paramTypes.length != params.length) return false; 066 for (int i=0; i < paramTypes.length; i++) { 067 if (!isAssignableFrom(paramTypes[i], params[i])) return false; 068 } 069 return true; 070 } 071 072 private Object callConstructor(Constructor<?> constructor, Object[] params) 073 throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, InstantiationException { 074 075 Class<?>[] paramTypes = constructor.getParameterTypes(); 076 Object[] newParams = new Object[params.length]; 077 for (int i=0; i < params.length; i++) { 078 Object newParam; 079 if ((params[i] == null) || (paramTypes[i].isAssignableFrom(params[i].getClass()))) newParam = params[i]; 080 else if ((paramTypes[i] == Byte.TYPE) && (params[i] instanceof Long)) newParam = (byte)(long)params[i]; 081 else if ((paramTypes[i] == Short.TYPE) && (params[i] instanceof Long)) newParam = (short)(long)params[i]; 082 else if ((paramTypes[i] == Integer.TYPE) && (params[i] instanceof Long)) newParam = (int)(long)params[i]; 083 else if ((paramTypes[i] == Float.TYPE) && (params[i] instanceof Double)) newParam = (float)(double)params[i]; 084 else throw new RuntimeException(String.format("%s cannot be assigned to %s", params[i].getClass().getName(), paramTypes[i].getName())); 085 newParams[i] = newParam; 086 } 087 return constructor.newInstance(newParams); 088 } 089 090 public Object createInstance(String className, List<Object> parameters) 091 throws JmriException, ClassNotFoundException, InstantiationException { 092 093 if (className == null) throw new NullPointerException("Class name is null"); 094 095 Class<?> clazz = Class.forName(className); 096 Constructor<?>[] constructors = clazz.getConstructors(); 097 Object[] params = parameters.toArray(); 098 099 Exception exception = null; 100 for (Constructor<?> c : constructors) { 101 try { 102 if (canCall(c, params)) return callConstructor(c, params); 103 } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) { 104 exception = ex; 105 } 106 } 107 108 if (exception != null) throw new ReflectionException("Reflection exception", exception); 109 110 List<String> paramList = new ArrayList<>(); 111 for (Object o : params) paramList.add(String.format("%s:%s", o, o != null ? o.getClass().getName() : "null")); 112 throw new CannotCreateInstanceException(String.format("Can not create new instance of class %s with parameters %s", clazz.getName(), String.join(", ", paramList)), clazz.getName()); 113 } 114 115 @Override 116 public Object calculate(SymbolTable symbolTable, List<ExpressionNode> parameterList) 117 throws CalculateException, JmriException { 118 if (parameterList.isEmpty()) { 119 throw new WrongNumberOfParametersException(Bundle.getMessage("WrongNumberOfParameters1", getName(), 1)); 120 } 121 122 String className = TypeConversionUtil.convertToString( 123 parameterList.get(0).calculate(symbolTable), false); 124 125 List<Object> list = new ArrayList<>(); 126 for (int i=1; i < parameterList.size(); i++) { 127 list.add(parameterList.get(i).calculate(symbolTable)); 128 } 129 130 try { 131 return createInstance(className, list); 132 } catch (ClassNotFoundException e) { 133 throw new ClassIsNotFoundException(String.format("The class %s is not found", className), className); 134 } catch (InstantiationException e) { 135 throw new ReflectionException("Reflection exception", e); 136 } 137 } 138 }); 139 } 140 141}