package org.simantics.pythonlink; import java.io.Closeable; import java.util.HashSet; import java.util.Set; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.regex.Pattern; import org.simantics.databoard.Bindings; import org.simantics.databoard.binding.Binding; import org.simantics.databoard.binding.error.BindingException; import org.simantics.databoard.binding.mutable.Variant; public class PythonContext implements Closeable { private long contextID; public interface Listener { void updated(String variableName); void closed(); } static ExecutorService pythonExecutor = Executors.newSingleThreadExecutor(); Set listeners = new HashSet<>(); public enum VariableType { NO_VARIABLE, BOOLEAN, LONG, FLOAT, STRING, BYTEARRAY, DICTIONARY, NDARRAY, SEQUENCE, UNKNOWN } static Pattern namePattern = Pattern.compile("([a-zA-Z_][a-zA-Z_0-9]*)"); PythonContext() { contextID = execute(() -> createContextImpl()); } public void addListener(Listener listener) { listeners.add(listener); } public void removeListener(Listener listener) { listeners.remove(listener); } @Override public void close() { long id = contextID; contextID = 0; if (id != 0) execute(() -> deleteContextImpl(id)); for (Listener l : listeners) { l.closed(); } } public boolean isOpen() { return contextID != 0; } @Override protected void finalize() throws Throwable { super.finalize(); close(); } public void executePythonStatement(String statement) { execute(() -> executePythonStatementImpl( contextID, statement )); for (Listener l : listeners) { l.updated(null); } } // Setters public void setPythonBooleanVariable(String variableName, boolean value) { checkValidName(variableName); execute(() -> setPythonBooleanVariableImpl(contextID, variableName, value)); for (Listener l : listeners) { l.updated(variableName); } } public void setPythonIntegerVariable(String variableName, int value) { checkValidName(variableName); execute(() -> setPythonLongVariableImpl(contextID, variableName, value)); for (Listener l : listeners) { l.updated(variableName); } } public void setPythonLongVariable(String variableName, long value) { checkValidName(variableName); execute(() -> setPythonLongVariableImpl(contextID, variableName, value)); for (Listener l : listeners) { l.updated(variableName); } } public void setPythonDoubleVariable(String variableName, double value) { checkValidName(variableName); execute(() -> setPythonDoubleVariableImpl(contextID, variableName, value)); for (Listener l : listeners) { l.updated(variableName); } } public void setPythonStringVariable(String variableName, String value) { checkValidName(variableName); execute(() -> setPythonStringVariableImpl(contextID, variableName, value)); for (Listener l : listeners) { l.updated(variableName); } } public void setPythonBooleanArrayVariable(String variableName, boolean[] value) { checkValidName(variableName); execute(() -> setPythonBooleanArrayVariableImpl(contextID, variableName, value)); for (Listener l : listeners) { l.updated(variableName); } } public void setPythonIntegerArrayVariable(String variableName, int[] value) { checkValidName(variableName); execute(() -> setPythonIntegerArrayVariableImpl(contextID, variableName, value)); for (Listener l : listeners) { l.updated(variableName); } } public void setPythonLongArrayVariable(String variableName, long[] value) { checkValidName(variableName); execute(() -> setPythonLongArrayVariableImpl(contextID, variableName, value)); for (Listener l : listeners) { l.updated(variableName); } } public void setPythonDoubleArrayVariable(String variableName, double[] value) { checkValidName(variableName); execute(() -> setPythonDoubleArrayVariableImpl(contextID, variableName, value)); for (Listener l : listeners) { l.updated(variableName); } } public void setPythonStringArrayVariable(String variableName, String[] value) { checkValidName(variableName); execute(() -> setPythonStringArrayVariableImpl(contextID, variableName, value)); for (Listener l : listeners) { l.updated(variableName); } } // Getters public boolean getPythonBooleanVariable(String variableName) { checkValidName(variableName); return getPythonBooleanVariableImpl(contextID, variableName); } public int getPythonIntegerVariable(String variableName) { checkValidName(variableName); long value = execute(() -> getPythonLongVariableImpl(contextID, variableName)); if (value > Integer.MAX_VALUE || value < Integer.MIN_VALUE) throw new RuntimeException("Python value not in integer range"); return (int) value; } public long getPythonLongVariable(String variableName) { checkValidName(variableName); return execute(() -> getPythonLongVariableImpl(contextID, variableName)); } public double getPythonDoubleVariable(String variableName) { checkValidName(variableName); return execute(() -> getPythonDoubleVariableImpl(contextID, variableName)); } public String getPythonStringVariable(String variableName) { checkValidName(variableName); return execute(() -> getPythonStringVariableImpl(contextID, variableName)); } public boolean[] getPythonBooleanArrayVariable(String variableName) { checkValidName(variableName); return execute(() -> getPythonBooleanArrayVariableImpl(contextID, variableName)); } public int[] getPythonIntegerArrayVariable(String variableName) { checkValidName(variableName); return execute(() -> getPythonIntegerArrayVariableImpl(contextID, variableName)); } public long[] getPythonLongArrayVariable(String variableName) { checkValidName(variableName); return execute(() -> getPythonLongArrayVariableImpl(contextID, variableName)); } public double[] getPythonDoubleArrayVariable(String variableName) { checkValidName(variableName); return execute(() -> getPythonDoubleArrayVariableImpl(contextID, variableName)); } public String[] getPythonStringArrayVariable(String variableName) { checkValidName(variableName); return execute(() -> getPythonStringArrayVariableImpl(contextID, variableName)); } public void setPythonNDArrayVariable(String variableName, NDArray value) { checkValidName(variableName); execute(() -> setPythonNDArrayVariableImpl(contextID, variableName, value)); } public NDArray getPythonNDArrayVariable(String variableName) { checkValidName(variableName); return execute(() -> getPythonNDArrayVariableImpl(contextID, variableName)); } public Object getPythonVariantVariable(String variableName, Binding binding) { checkValidName(variableName); Object result = execute(() -> getPythonVariantVariableImpl(contextID, variableName)); try { return Bindings.OBJECT.getContent(result, binding); } catch (BindingException e) { throw new RuntimeException(e); } } public Variant getPythonVariantVariable(String variableName) { checkValidName(variableName); return Variant.ofInstance(execute(() -> getPythonVariantVariableImpl(contextID, variableName))); } public void setPythonVariantVariable(String variableName, Variant value) { setPythonVariantVariable(variableName, value.getValue(), value.getBinding()); } public void setPythonVariantVariable(String variableName, Object value, Binding binding) { checkValidName(variableName); if (!binding.isInstance(value)) throw new IllegalArgumentException("Invalid object binding"); execute(() -> setPythonVariantVariableImpl(contextID, variableName, value, binding)); for (Listener l : listeners) { l.updated(variableName); } } public VariableType getPythonVariableType(String variableName) { checkValidName(variableName); int code = execute(() -> getPythonVariableTypeImpl(contextID, variableName)); VariableType[] values = VariableType.values(); if (code < 0 || code >= values.length) return VariableType.UNKNOWN; return values[code]; } public String[] getPythonVariableNames() { return execute(() -> getPythonVariableNamesImpl(contextID)); } private static void checkValidName(String variableName) { if (!namePattern.matcher(variableName).matches()) throw new IllegalArgumentException("Invalid Python variable name " + variableName); } static void execute(Runnable job) { try { pythonExecutor.submit(job).get(); } catch (InterruptedException | ExecutionException e) { throw new RuntimeException(e); } } static V execute(Callable job) { try { return pythonExecutor.submit(job).get(); } catch (InterruptedException | ExecutionException e) { throw new RuntimeException(e); } } // Native function declarations private static native long createContextImpl(); private static native void deleteContextImpl(long contextID); private static native int executePythonStatementImpl(long contextID, String statement); private static native void setPythonBooleanVariableImpl(long contextID, String variableName, boolean value); private static native void setPythonLongVariableImpl(long contextID, String variableName, long value); private static native void setPythonDoubleVariableImpl(long contextID, String variableName, double value); private static native void setPythonStringVariableImpl(long contextID, String variableName, String value); private static native void setPythonBooleanArrayVariableImpl(long contextID, String variableName, boolean[] value); private static native void setPythonIntegerArrayVariableImpl(long contextID, String variableName, int[] value); private static native void setPythonLongArrayVariableImpl(long contextID, String variableName, long[] value); private static native void setPythonDoubleArrayVariableImpl(long contextID, String variableName, double[] value); private static native void setPythonStringArrayVariableImpl(long contextID, String variableName, String[] value); private static native boolean getPythonBooleanVariableImpl(long contextID, String variableName); private static native long getPythonLongVariableImpl(long contextID, String variableName); private static native double getPythonDoubleVariableImpl(long contextID, String variableName); private static native String getPythonStringVariableImpl(long contextID, String variableName); private static native boolean[] getPythonBooleanArrayVariableImpl(long contextID, String variableName); private static native long[] getPythonLongArrayVariableImpl(long contextID, String variableName); private static native int[] getPythonIntegerArrayVariableImpl(long contextID, String variableName); private static native double[] getPythonDoubleArrayVariableImpl(long contextID, String variableName); private static native String[] getPythonStringArrayVariableImpl(long contextID, String variableName); private static native void setPythonNDArrayVariableImpl(long contextID, String variableName, NDArray value); private static native NDArray getPythonNDArrayVariableImpl(long contextID, String variableName); private static native void setPythonVariantVariableImpl(long contextID, String variableName, Object value, Binding binding); private static native Object getPythonVariantVariableImpl(long contextID, String variableName); private static native int getPythonVariableTypeImpl(long contextID, String variableName); private static native String[] getPythonVariableNamesImpl(long contextID); }