package com.github.lindenb.jvarkit.tools.jfx; import java.io.ByteArrayInputStream; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.PrintStream; import java.util.ArrayList; import java.util.List; import com.github.lindenb.jvarkit.tools.bioalcidae.BioAlcidae; import com.github.lindenb.jvarkit.tools.vcffilterjs.VCFFilterJS; import com.github.lindenb.jvarkit.util.jcommander.Launcher; import com.github.lindenb.jvarkit.util.jcommander.Program; import com.github.lindenb.jvarkit.util.log.Logger; import javafx.application.Application; import javafx.application.Platform; import javafx.beans.property.SimpleStringProperty; import javafx.geometry.Insets; import javafx.scene.Scene; import javafx.scene.control.Alert; import javafx.scene.control.Button; import javafx.scene.control.Menu; import javafx.scene.control.MenuBar; import javafx.scene.control.MenuItem; import javafx.scene.control.Tab; import javafx.scene.control.TabPane; import javafx.scene.control.TableColumn; import javafx.scene.control.TableView; import javafx.scene.control.TextArea; import javafx.scene.control.Alert.AlertType; import javafx.scene.control.TabPane.TabClosingPolicy; import javafx.scene.layout.BorderPane; import javafx.scene.layout.FlowPane; import javafx.scene.layout.StackPane; import javafx.scene.layout.VBox; import javafx.stage.FileChooser; import javafx.stage.Stage; public class JvarkitCentral extends Application { private static final Logger LOG = Logger.build(JvarkitCentral.class).make(); @Override public void start(Stage primaryStage) throws Exception { primaryStage.setTitle("Jvarkit-Central"); StackPane root = new StackPane(); root.setPadding(new Insets(2)); final TableView<Class<?>> tableView =new TableView<>(); final TableColumn<Class<?>, String> nameCol = new TableColumn<>("Name"); nameCol.setCellValueFactory(CB-> { final String value; Class<?> clazz = CB.getValue(); final Program program=clazz.getAnnotation(Program.class); if(!Launcher.class.isAssignableFrom(clazz) || program==null) { value=null; } else { value=program.name(); } return new SimpleStringProperty(value); }); final TableColumn<Class<?>, String> descCol = new TableColumn<>("Description"); descCol.setCellValueFactory(CB-> { final String value; Class<?> clazz = CB.getValue(); final Program program=clazz.getAnnotation(Program.class); if(!Launcher.class.isAssignableFrom(clazz) || program==null) { value=null; } else { value=program.description(); } return new SimpleStringProperty(value); }); tableView.getColumns().addAll(nameCol,descCol); final BorderPane borderPane1 = new BorderPane(tableView); borderPane1.setPadding(new Insets(10)); final Button but=new Button("New Instance..."); but.setOnAction(AE->{ final Class<?> clazz=tableView.getSelectionModel().getSelectedItem(); if(clazz==null) return; final Program program=clazz.getAnnotation(Program.class); if(!Launcher.class.isAssignableFrom(clazz) || program==null) return; createNewInstanceOf(clazz); }); FlowPane bottom=new FlowPane(but); borderPane1.setBottom(bottom); tableView.getItems().addAll(String.class,Integer.class, VCFFilterJS.class, BioAlcidae.class ); root.getChildren().add(borderPane1); //root.getChildren().add(btn); primaryStage.setScene(new Scene(root, 300, 250)); primaryStage.show(); } private void createNewInstanceOf(final Class<?> clazz) { final LauncherStage stage = new LauncherStage(clazz); stage.show(); } private static class LaunchAndRun extends Thread { private LauncherStage owner=null; private Launcher instance=null; private String args[]=null; private int returnStatus=0; @Override public void run() { final PrintStream redirectStream=new PrintStream(new ConsolePrintStream() { @Override public void writeToDevice(final String textToPrint) { Platform.runLater(()-> { if(owner.runner != LaunchAndRun.this) return; int L = owner.outputArea.getText().length(); while(L>10000) { owner.outputArea.deleteText(0,10000); L = owner.outputArea.getText().length(); } owner.outputArea.appendText(textToPrint); } ); } }); final InputStream no_stdin= new ByteArrayInputStream(new byte[0]); try { instance.stdin(no_stdin); instance.stdout(redirectStream); instance.stdout().flush(); instance.stderr(redirectStream); int ret= instance.instanceMain(args); this.returnStatus=ret; } catch(Exception err) { LOG.error(err); Platform.runLater(()->{ if(this.owner.runner != LaunchAndRun.this) return; this.owner.runner=null; Alert alert=new Alert(AlertType.ERROR,"An error occured "+err.getMessage()); alert.showAndWait(); }); returnStatus=-1; return; } Platform.runLater(()->{ if(this.owner.runner != LaunchAndRun.this) return; this.owner.runner=null; Alert alert=new Alert(returnStatus==0?AlertType.INFORMATION:AlertType.ERROR, "Program exited with status "+returnStatus ); alert.showAndWait(); }); } } private static class LauncherStage extends Stage { private File lastDir=null; final TabPane tabPane ; private final TextArea outputArea; private final TextArea textArea; private final Class<?> clazz; private volatile LaunchAndRun runner=null; LauncherStage(Class<?> clazz) { this.clazz = clazz; this.setTitle(clazz.getSimpleName()); VBox root = new VBox(); root.setPadding(new Insets(2)); final Menu menuFile=new Menu("File"); MenuItem menuItem=new MenuItem("Close"); menuItem.setOnAction(AE->{doMenuStop();LauncherStage.this.close();}); menuFile.getItems().add(menuItem); menuItem=new MenuItem("Insert Path to Open"); menuItem.setOnAction(AE->{doMenuInsertPath(true);}); menuFile.getItems().add(menuItem); menuItem=new MenuItem("Insert Path to Save"); menuItem.setOnAction(AE->{doMenuInsertPath(false);}); menuFile.getItems().add(menuItem); final MenuBar menuBar=new MenuBar(menuFile); root.getChildren().add(menuBar); this.tabPane = new TabPane(); this.tabPane.setTabClosingPolicy(TabClosingPolicy.UNAVAILABLE); this.tabPane.setPadding(new Insets(10)); this.textArea=new TextArea(""); this.textArea.setPrefColumnCount(80); this.textArea.setPrefRowCount(25); BorderPane borderPane1 = new BorderPane(this.textArea); final Button runButton=new Button("Run"); runButton.setOnAction(AE->doMenuStart()); FlowPane bottom=new FlowPane(runButton); borderPane1.setBottom(bottom); final Tab writeCmdTab=new Tab("Command",borderPane1); tabPane.getTabs().add(writeCmdTab); this.outputArea=new TextArea(""); this.outputArea.setPrefColumnCount(80); this.outputArea.setPrefRowCount(25); this.outputArea.setEditable(false); borderPane1 = new BorderPane(this.outputArea); final Button stopBut=new Button("Stop"); stopBut.setOnAction(AE->doMenuStop()); bottom=new FlowPane(stopBut); borderPane1.setBottom(bottom); final Tab outputTab=new Tab("Output",borderPane1); this.tabPane.getTabs().add(outputTab); root.getChildren().add(tabPane); this.setScene(new Scene(root, 300, 250)); this.setOnCloseRequest(AE->doMenuStop()); } protected void doMenuInsertPath(boolean openDialog) { final FileChooser fc=new FileChooser(); fc.setInitialDirectory(this.lastDir); File f=null; if(openDialog) { f=fc.showOpenDialog(LauncherStage.this); } else { f=fc.showSaveDialog(LauncherStage.this); } if(f==null) return; this.lastDir=f.getParentFile(); this.textArea.insertText(this.textArea.getCaretPosition(),f.getPath()); } private synchronized void doMenuStop() { if(this.runner!=null) { try {this.runner.interrupt(); } catch(Exception err) {err.printStackTrace();} this.runner=null; } this.tabPane.getSelectionModel().select(0); } private synchronized void doMenuStart() { if(this.runner!=null) { final Alert alert=new Alert(AlertType.WARNING, "Program is already running...."); alert.showAndWait(); return; } this.tabPane.getSelectionModel().select(1); final List<String> args; try { args = this.getArguments(); } catch(final IllegalArgumentException err) { LOG.error(err); final Alert alert=new Alert(AlertType.ERROR, String.valueOf(err.getMessage()) ); alert.showAndWait(); return; } LaunchAndRun run =new LaunchAndRun(); //final Launcher instance; try { run.instance = (Launcher)this.clazz.newInstance(); } catch(final Throwable err) { LOG.error(err); final Alert alert=new Alert(AlertType.ERROR, "Error creating a new instance of "+clazz.getName() ); alert.showAndWait(); return; } run.args = args.toArray(new String[args.size()]); this.runner=run; this.runner.owner = this; this.runner.start(); } protected List<String> getArguments() { final List<String> args = new ArrayList<>(); final String s=this.textArea.getText(); int i=0; while(i<s.length()) { if(Character.isWhitespace(s.charAt(i))) {++i;continue;} if(s.charAt(i)=='\"' || s.charAt(i)=='\'') { char quote=s.charAt(i); i++; final StringBuilder sb=new StringBuilder(); while(i< s.length()) { char c = s.charAt(i); ++i; if(c==quote) break; if(c=='\\') { if(i+1>=s.length()) { throw new IllegalArgumentException("Unclosed string after "+sb.toString()); } c= s.charAt(i); switch(c) { case '\'': sb.append('\'');break; case '\"': sb.append('\"');break; case 'n': sb.append('\n');break; case 't': sb.append('\t');break; case '\\': sb.append('\\');break; default: throw new IllegalArgumentException("Unknown escape sequence after: "+sb.toString()); } } else { sb.append(c); } } args.add(sb.toString()); } else { final StringBuilder sb=new StringBuilder(); while(i< s.length() && !Character.isWhitespace(s.charAt(i))) { sb.append(s.charAt(i)); i++; } args.add(sb.toString()); } } LOG.info(this.clazz.getName()+" args are "+args); return args; } } private static class ConsolePrintStream extends OutputStream { private StringBuilder buffer= new StringBuilder(); @Override public void write(int b) throws IOException { if(b==-1) flush(); buffer.append((char)b); if(b=='\n' || buffer.length()>1000) flush(); } @Override public void flush() throws IOException { if(buffer.length()==0) return; writeToDevice(buffer.toString()); buffer.setLength(0); } public void writeToDevice(String s) { } } public static void main(String[] args) { launch(args); } }