Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Added tst case for returning Java objects from Lua.
- Loading branch information
Showing
7 changed files
with
445 additions
and
1 deletion.
There are no files selected for viewing
89 changes: 89 additions & 0 deletions
89
src/com/skiwi/javaluainteroptest/CallbackFunctionsCompositeObjectContent.fxml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
<?xml version="1.0" encoding="UTF-8"?> | ||
|
||
<?import javafx.geometry.*?> | ||
<?import javafx.scene.text.*?> | ||
<?import javafx.scene.web.*?> | ||
<?import java.lang.*?> | ||
<?import java.util.*?> | ||
<?import javafx.scene.*?> | ||
<?import javafx.scene.control.*?> | ||
<?import javafx.scene.layout.*?> | ||
|
||
<GridPane prefHeight="600.0" prefWidth="800.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="com.skiwi.javaluainteroptest.CallbackFunctionsCompositeObjectContentController"> | ||
<children> | ||
<SplitPane fx:id="splitPane" dividerPositions="0.6" orientation="VERTICAL"> | ||
<items> | ||
<TextArea fx:id="codeTextArea" prefHeight="200.0" prefWidth="200.0" /> | ||
<AnchorPane prefHeight="200.0" prefWidth="200.0"> | ||
<children> | ||
<SplitPane dividerPositions="0.75" prefHeight="160.0" prefWidth="200.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0"> | ||
<items> | ||
<AnchorPane> | ||
<children> | ||
<TableView fx:id="callbackTableView" editable="true" prefHeight="200.0" prefWidth="200.0" AnchorPane.bottomAnchor="30.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0"> | ||
<columns> | ||
<TableColumn fx:id="keyTableColumn" prefWidth="150.0" text="Key" /> | ||
<TableColumn fx:id="valueTableColumn" editable="true" prefWidth="150.0" text="Value" /> | ||
<TableColumn fx:id="resultTableColumn" prefWidth="150.0" text="Result" /> | ||
</columns> | ||
</TableView> | ||
<GridPane layoutX="113.0" layoutY="151.0" prefHeight="40.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0"> | ||
<columnConstraints> | ||
<ColumnConstraints hgrow="NEVER" minWidth="10.0" percentWidth="33.333" prefWidth="100.0" /> | ||
<ColumnConstraints hgrow="NEVER" minWidth="10.0" percentWidth="33.333" prefWidth="100.0" /> | ||
<ColumnConstraints hgrow="NEVER" minWidth="10.0" percentWidth="33.333" prefWidth="100.0" /> | ||
</columnConstraints> | ||
<rowConstraints> | ||
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" /> | ||
</rowConstraints> | ||
<children> | ||
<AnchorPane prefHeight="200.0" prefWidth="200.0"> | ||
<children> | ||
<Label layoutX="14.0" layoutY="16.0" text="Key: " AnchorPane.leftAnchor="14.0"> | ||
<font> | ||
<Font size="15.0" /> | ||
</font> | ||
</Label> | ||
<TextField fx:id="keyTextField" layoutX="46.0" layoutY="14.0" prefWidth="100.0" AnchorPane.leftAnchor="46.0" /> | ||
</children> | ||
</AnchorPane> | ||
<AnchorPane prefHeight="200.0" prefWidth="200.0" GridPane.columnIndex="1"> | ||
<children> | ||
<Label layoutX="14.0" layoutY="16.0" text="Value: " AnchorPane.leftAnchor="14.0"> | ||
<font> | ||
<Font size="15.0" /> | ||
</font> | ||
</Label> | ||
<TextField fx:id="valueTextField" layoutX="59.0" layoutY="14.0" prefWidth="100.0" AnchorPane.leftAnchor="59.0" /> | ||
</children> | ||
</AnchorPane> | ||
<AnchorPane prefHeight="200.0" prefWidth="200.0" GridPane.columnIndex="2"> | ||
<children> | ||
<Button fx:id="addButton" layoutX="132.0" layoutY="14.0" mnemonicParsing="false" onAction="#handleAddButtonAction" prefWidth="75.0" text="Add" AnchorPane.rightAnchor="14.0" /> | ||
</children> | ||
</AnchorPane> | ||
</children> | ||
</GridPane> | ||
</children> | ||
</AnchorPane> | ||
<TextArea fx:id="outputTextArea" prefHeight="200.0" prefWidth="200.0" /> | ||
</items> | ||
</SplitPane> | ||
</children> | ||
</AnchorPane> | ||
</items> | ||
</SplitPane> | ||
<AnchorPane GridPane.rowIndex="1"> | ||
<children> | ||
<Button fx:id="runButton" layoutX="711.0" layoutY="8.0" mnemonicParsing="false" onAction="#handleRunButtonAction" prefWidth="75.0" text="Run" AnchorPane.rightAnchor="14.0" /> | ||
</children> | ||
</AnchorPane> | ||
</children> | ||
<columnConstraints> | ||
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" /> | ||
</columnConstraints> | ||
<rowConstraints> | ||
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="ALWAYS" /> | ||
<RowConstraints minHeight="10.0" prefHeight="40.0" vgrow="SOMETIMES" /> | ||
</rowConstraints> | ||
</GridPane> |
257 changes: 257 additions & 0 deletions
257
src/com/skiwi/javaluainteroptest/CallbackFunctionsCompositeObjectContentController.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,257 @@ | ||
|
||
package com.skiwi.javaluainteroptest; | ||
|
||
import java.io.IOException; | ||
import java.io.OutputStream; | ||
import java.io.PrintStream; | ||
import java.io.StringReader; | ||
import java.net.URL; | ||
import java.util.ResourceBundle; | ||
import java.util.concurrent.ExecutorService; | ||
import java.util.concurrent.Executors; | ||
import java.util.function.Supplier; | ||
import javafx.application.Platform; | ||
import javafx.beans.property.SimpleStringProperty; | ||
import javafx.beans.property.StringProperty; | ||
import javafx.collections.FXCollections; | ||
import javafx.collections.ObservableList; | ||
import javafx.event.ActionEvent; | ||
import javafx.fxml.FXML; | ||
import javafx.fxml.Initializable; | ||
import javafx.scene.control.ContextMenu; | ||
import javafx.scene.control.MenuItem; | ||
import javafx.scene.control.SelectionMode; | ||
import javafx.scene.control.TableColumn; | ||
import javafx.scene.control.TableView; | ||
import javafx.scene.control.TextArea; | ||
import javafx.scene.control.TextField; | ||
import javafx.scene.control.cell.TextFieldTableCell; | ||
import org.controlsfx.dialog.DialogStyle; | ||
import org.controlsfx.dialog.Dialogs; | ||
import org.luaj.vm2.Globals; | ||
import org.luaj.vm2.lib.jse.CoerceJavaToLua; | ||
import org.luaj.vm2.lib.jse.JsePlatform; | ||
import org.luaj.vm2.luajc.LuaJC; | ||
|
||
/** | ||
* FXML Controller class | ||
* | ||
* @author Frank van Heeswijk | ||
*/ | ||
public class CallbackFunctionsCompositeObjectContentController implements Initializable { | ||
@FXML | ||
private TextArea codeTextArea; | ||
|
||
@FXML | ||
private TextArea outputTextArea; | ||
|
||
@FXML | ||
private TableView<TableEntry> callbackTableView; | ||
|
||
@FXML | ||
private TableColumn<TableEntry, String> keyTableColumn; | ||
|
||
@FXML | ||
private TableColumn<TableEntry, String> valueTableColumn; | ||
|
||
@FXML | ||
private TableColumn<TableEntry, String> resultTableColumn; | ||
|
||
@FXML | ||
private TextField keyTextField; | ||
|
||
@FXML | ||
private TextField valueTextField; | ||
|
||
private Globals globals; | ||
|
||
private ExecutorService luaService; | ||
|
||
@Override | ||
public void initialize(final URL url, final ResourceBundle resourceBundle) { | ||
setupLayout(); | ||
setupLua(); | ||
} | ||
|
||
private void setupLayout() { | ||
keyTableColumn.prefWidthProperty().bind(callbackTableView.widthProperty().multiply(0.333d)); | ||
valueTableColumn.prefWidthProperty().bind(callbackTableView.widthProperty().multiply(0.333d)); | ||
resultTableColumn.prefWidthProperty().bind(callbackTableView.widthProperty().multiply(0.333d)); | ||
|
||
keyTableColumn.setCellValueFactory(cellData -> cellData.getValue().keyProperty()); | ||
valueTableColumn.setCellValueFactory(cellData -> { | ||
StringProperty valueProperty = cellData.getValue().valueProperty(); | ||
valueProperty.addListener((observableValue, oldValue, newValue) -> luaService.submit(this::runLuaCode)); | ||
return valueProperty; | ||
}); | ||
resultTableColumn.setCellValueFactory(cellData -> cellData.getValue().resultProperty()); | ||
|
||
valueTableColumn.setCellFactory(TextFieldTableCell.forTableColumn()); | ||
valueTableColumn.setOnEditCommit(cellEditEvent -> { | ||
TableEntry tableEntry = cellEditEvent.getTableView().getItems().get(cellEditEvent.getTablePosition().getRow()); | ||
tableEntry.setValue(cellEditEvent.getNewValue()); | ||
outputTextArea.clear(); | ||
}); | ||
|
||
MenuItem deleteMenuItem = new MenuItem("Delete"); | ||
deleteMenuItem.setOnAction(actionEvent -> { | ||
ObservableList<TableEntry> selectedEntriesInTableView = callbackTableView.getSelectionModel().getSelectedItems(); | ||
ObservableList<TableEntry> selectedEntries = FXCollections.observableArrayList(); | ||
selectedEntriesInTableView.forEach(selectedEntries::add); | ||
FXCollections.copy(selectedEntries, selectedEntriesInTableView); | ||
selectedEntries.forEach(todoEntry -> { | ||
callbackTableView.getItems().remove(todoEntry); | ||
callbackTableView.sort(); | ||
}); | ||
callbackTableView.getSelectionModel().clearSelection(); | ||
}); | ||
ContextMenu contextMenu = new ContextMenu(deleteMenuItem); | ||
callbackTableView.setContextMenu(contextMenu); | ||
|
||
callbackTableView.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE); | ||
|
||
codeTextArea.appendText("-- Always name this function \"initCallbacks\""); | ||
codeTextArea.appendText(System.lineSeparator()); | ||
codeTextArea.appendText("function initCallbacks()"); | ||
codeTextArea.appendText(System.lineSeparator()); | ||
codeTextArea.appendText(" -- Put your callbackStore:addCallback(key:string, function:function(supplier<supplier<string>> -> supplier<string>)) calls here"); | ||
codeTextArea.appendText(System.lineSeparator()); | ||
codeTextArea.appendText("end"); | ||
} | ||
|
||
private void setupLua() { | ||
luaService = Executors.newCachedThreadPool(runnable -> { | ||
Thread thread = new Thread(runnable); | ||
thread.setDaemon(true); | ||
return thread; | ||
}); | ||
|
||
globals = JsePlatform.standardGlobals(); | ||
LuaJC.install(globals); | ||
|
||
globals.STDOUT = new PrintStream(new OutputStream() { | ||
@Override | ||
public void write(int b) throws IOException { | ||
String text = new String(new int[]{b}, 0, 1); | ||
Platform.runLater(() -> outputTextArea.appendText(text)); | ||
} | ||
}); | ||
globals.STDERR = new PrintStream(new OutputStream() { | ||
@Override | ||
public void write(int b) throws IOException { | ||
String text = new String(new int[]{b}, 0, 1); | ||
Platform.runLater(() -> outputTextArea.appendText(text)); | ||
} | ||
}); | ||
} | ||
|
||
@FXML | ||
private void handleRunButtonAction(final ActionEvent actionEvent) { | ||
Platform.runLater(outputTextArea::clear); | ||
luaService.submit(this::runLuaCode); | ||
} | ||
|
||
private void runLuaCode() { | ||
Platform.runLater(() -> { | ||
try { | ||
globals.load(new StringReader(codeTextArea.getText()), "interopTest").call(); | ||
CallbackStore callbackStore = new CallbackStore(); | ||
globals.set("callbackStore", CoerceJavaToLua.coerce(callbackStore)); | ||
globals.get("initCallbacks").call(); | ||
callbackTableView.getItems().forEach(tableEntry -> { | ||
tableEntry.setResult( | ||
callbackStore.<Supplier<Supplier<String>>, Supplier<String>>performCallback( | ||
tableEntry.getKey(), | ||
() -> () -> tableEntry.getValue(), | ||
(Class<Supplier<String>>)(Object)Supplier.class, | ||
Supplier::get) | ||
.toString() | ||
); | ||
}); | ||
} catch (Throwable ex) { | ||
Dialogs.create() | ||
.style(DialogStyle.NATIVE) | ||
.lightweight() | ||
.title("Lua Exception") | ||
.masthead((ex instanceof Error) ? "An error has occured" : "An exception has occured") | ||
.showException(ex); | ||
} | ||
}); | ||
} | ||
|
||
@FXML | ||
private void handleAddButtonAction(final ActionEvent actionEvent) { | ||
String key = keyTextField.getText(); | ||
String value = valueTextField.getText(); | ||
if (key.isEmpty()) { | ||
Dialogs.create() | ||
.style(DialogStyle.NATIVE) | ||
.lightweight() | ||
.title("Invalid Action") | ||
.masthead("Action is not allowed") | ||
.message("You cannot add an empty key.") | ||
.showError(); | ||
return; | ||
} | ||
if (callbackTableView.getItems().stream().anyMatch(tableEntry -> tableEntry.getKey().equals(key))) { | ||
Dialogs.create() | ||
.style(DialogStyle.NATIVE) | ||
.lightweight() | ||
.title("Invalid Action") | ||
.masthead("Action is not allowed") | ||
.message("An entry with key \"" + key + "\" already exists.") | ||
.showError(); | ||
return; | ||
} | ||
TableEntry tableEntry = new TableEntry(key, value); | ||
callbackTableView.getItems().add(tableEntry); | ||
outputTextArea.clear(); | ||
luaService.submit(this::runLuaCode); | ||
keyTextField.clear(); | ||
valueTextField.clear(); | ||
} | ||
|
||
public static class TableEntry { | ||
private final StringProperty keyProperty; | ||
private final StringProperty valueProperty; | ||
private final StringProperty resultProperty; | ||
|
||
private TableEntry(final String key, final String value) { | ||
this.keyProperty = new SimpleStringProperty(key); | ||
this.valueProperty = new SimpleStringProperty(value); | ||
this.resultProperty = new SimpleStringProperty(); | ||
} | ||
|
||
public StringProperty keyProperty() { | ||
return keyProperty; | ||
} | ||
|
||
public String getKey() { | ||
return keyProperty.getValue(); | ||
} | ||
|
||
public StringProperty valueProperty() { | ||
return valueProperty; | ||
} | ||
|
||
public String getValue() { | ||
return valueProperty.getValue(); | ||
} | ||
|
||
public void setValue(final String value) { | ||
valueProperty.setValue(value); | ||
} | ||
|
||
public StringProperty resultProperty() { | ||
return resultProperty; | ||
} | ||
|
||
public void setResult(final String result) { | ||
resultProperty.setValue(result); | ||
} | ||
|
||
public String getResult() { | ||
return resultProperty.getValue(); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.