Java tutorial
/* * Copyright (C) 2015 David A. Parry <d.a.parry@leeds.ac.uk> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package com.github.mutationmapper; import static com.github.mutationmapper.MutationMapper.VERSION; import java.io.BufferedOutputStream; import java.io.BufferedWriter; import java.io.File; import java.io.FileOutputStream; import java.io.FileWriter; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.PrintWriter; import java.io.StringWriter; import static java.lang.System.getProperty; import java.net.URL; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.Optional; import java.util.ResourceBundle; import java.util.regex.Matcher; import java.util.regex.Pattern; import javafx.application.Platform; import javafx.beans.binding.Bindings; import javafx.beans.property.BooleanProperty; import javafx.beans.property.SimpleBooleanProperty; import javafx.beans.value.ChangeListener; import javafx.beans.value.ObservableValue; import javafx.collections.FXCollections; import javafx.collections.ObservableList; import javafx.concurrent.Service; import javafx.concurrent.Task; import javafx.concurrent.WorkerStateEvent; import javafx.event.ActionEvent; import javafx.event.Event; import javafx.event.EventHandler; import javafx.fxml.FXML; import javafx.fxml.FXMLLoader; import javafx.fxml.Initializable; import javafx.scene.Scene; import javafx.scene.control.Alert; import javafx.scene.control.Alert.AlertType; import javafx.scene.control.ButtonType; import javafx.scene.control.CheckMenuItem; import javafx.scene.control.ContextMenu; import javafx.scene.control.Hyperlink; import javafx.scene.control.Label; import javafx.scene.control.MenuBar; import javafx.scene.control.MenuItem; import javafx.scene.control.RadioMenuItem; import javafx.scene.control.SelectionMode; import javafx.scene.control.TableColumn; import javafx.scene.control.TablePosition; import javafx.scene.control.TableRow; import javafx.scene.control.TableView; import javafx.scene.control.TextArea; import javafx.scene.control.ToggleGroup; import javafx.scene.control.cell.PropertyValueFactory; import javafx.scene.image.Image; import javafx.scene.input.Clipboard; import javafx.scene.input.ClipboardContent; import javafx.scene.input.KeyCode; import javafx.scene.input.KeyCodeCombination; import javafx.scene.input.KeyCombination; import javafx.scene.layout.AnchorPane; import javafx.scene.layout.GridPane; import javafx.scene.layout.Pane; import javafx.scene.layout.Priority; import javafx.stage.FileChooser; import javafx.stage.Modality; import javafx.stage.Stage; import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.Row; import org.apache.poi.ss.usermodel.Sheet; import org.apache.poi.ss.usermodel.Workbook; import org.apache.poi.xssf.usermodel.XSSFWorkbook; /** * FXML Controller class * * @author david */ public class MutationMapperResultViewController implements Initializable { @FXML AnchorPane resultPane; @FXML MenuBar menuBar; @FXML TableView<MutationMapperResult> resultTable; @FXML TableColumn indexCol; @FXML TableColumn symbolCol; @FXML TableColumn geneCol; @FXML TableColumn transcriptCol; @FXML TableColumn cdsCol; @FXML TableColumn genomicCol; @FXML TableColumn refCol; @FXML TableColumn varCol; @FXML TableColumn consequenceCol; @FXML TableColumn cdsConsequenceCol; @FXML TableColumn proteinConsequenceCol; @FXML TableColumn exonIntronCol; @FXML TableColumn polyphenCol; @FXML TableColumn siftCol; @FXML TableColumn knownVarCol; @FXML TableColumn knownFreqCol; @FXML TableColumn seqInputCol; @FXML Label summaryLabel; @FXML TextArea resultTextArea; @FXML MenuItem saveMenuItem; @FXML MenuItem closeMenuItem; @FXML MenuItem quitMenuItem; @FXML MenuItem clearPreviousMenuItem; @FXML MenuItem clearAndCloseMenuItem; @FXML MenuItem clearMenuItem; @FXML CheckMenuItem canonicalOnlyMenu; @FXML CheckMenuItem mostRecentFirstMenu; @FXML CheckMenuItem codingOnlyMenu; @FXML RadioMenuItem noRefSeqMenu; @FXML RadioMenuItem refSeqMenu; @FXML RadioMenuItem refSeqOnlyMenu; @FXML MenuItem aboutMenuItem; @FXML MenuItem helpMenuItem; private final ObservableList<MutationMapperResult> data = FXCollections.observableArrayList(); private final ObservableList<MutationMapperResult> displayData = FXCollections.observableArrayList(); Integer lastIndex = 0; BooleanProperty refSeq = new SimpleBooleanProperty(); BooleanProperty refSeqOnly = new SimpleBooleanProperty(); BooleanProperty codingOnly = new SimpleBooleanProperty(); BooleanProperty canonicalOnly = new SimpleBooleanProperty(); BooleanProperty mostRecentFirst = new SimpleBooleanProperty(); /** * Initializes the controller class. * @param url * @param rb */ @Override public void initialize(URL url, ResourceBundle rb) { menuBar.setUseSystemMenuBar(true); indexCol.setCellValueFactory(new PropertyValueFactory<>("index")); symbolCol.setCellValueFactory(new PropertyValueFactory<>("geneSymbolLink")); geneCol.setCellValueFactory(new PropertyValueFactory<>("geneIdLink")); transcriptCol.setCellValueFactory(new PropertyValueFactory<>("transcriptLink")); cdsCol.setCellValueFactory(new PropertyValueFactory<>("cdsCoordinate")); genomicCol.setCellValueFactory(new PropertyValueFactory<>("regionLink")); refCol.setCellValueFactory(new PropertyValueFactory<>("refAllele")); varCol.setCellValueFactory(new PropertyValueFactory<>("varAllele")); proteinConsequenceCol.setCellValueFactory(new PropertyValueFactory<>("proteinConsequence")); cdsConsequenceCol.setCellValueFactory(new PropertyValueFactory<>("cdsConsequence")); exonIntronCol.setCellValueFactory(new PropertyValueFactory<>("exonIntronNumber")); consequenceCol.setCellValueFactory(new PropertyValueFactory<>("consequence")); knownVarCol.setCellValueFactory(new PropertyValueFactory<>("snpLink")); knownFreqCol.setCellValueFactory(new PropertyValueFactory<>("knownFreq")); seqInputCol.setCellValueFactory(new PropertyValueFactory<>("seqInput")); polyphenCol.setCellValueFactory(new PropertyValueFactory<>("polyphenResult")); siftCol.setCellValueFactory(new PropertyValueFactory<>("siftResult")); resultTable.setFixedCellSize(-1); resultTable.setRowFactory(param -> { return new TableRow() { @Override public void updateIndex(int i) { super.updateIndex(i); setMinHeight(50);// * i); } }; }); /* cdsConsequenceCol.setCellFactory(new Callback<TableColumn<String,String>, TableCell<String,String>>() { @Override public TableCell<String, String> call( TableColumn<String, String> param) { final TableCell<String, String> cell = new TableCell<String, String>() { private Text text; @Override public void updateItem(String item, boolean empty) { super.updateItem(item, empty); if (item != null && !isEmpty()) { text = new Text(item.toString()); text.wrappingWidthProperty().bind(cdsConsequenceCol.widthProperty()); // Setting the wrapping width to the Text setGraphic(text); } } }; return cell; } }); */ resultTable.setColumnResizePolicy(TableView.UNCONSTRAINED_RESIZE_POLICY); resultTable.getSelectionModel().setCellSelectionEnabled(true); resultTable.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE); MenuItem copyItem = new MenuItem("Copy"); copyItem.setOnAction(new EventHandler<ActionEvent>() { @Override public void handle(ActionEvent event) { ObservableList<TableColumn<MutationMapperResult, ?>> cols = resultTable.getColumns(); ObservableList<TablePosition> posList = resultTable.getSelectionModel().getSelectedCells(); int old_r = -1; StringBuilder clipboardString = new StringBuilder(); for (TablePosition p : posList) { int r = p.getRow(); int c = p.getColumn(); //System.out.println("Pos " + r + "," + c); for (int i = 0; i < cols.size(); i++) { if (i > c) { //System.out.println("Column " + i + " gt " + c + " - breaking"); break; } if (!cols.get(i).isVisible()) { c++; //System.out.println("Column " + i + " invisible - c = " + c); } } Object cell = resultTable.getColumns().get(c).getCellData(r); if (cell instanceof Hyperlink) { Hyperlink link = (Hyperlink) cell; cell = link.getText(); } if (cell == null) cell = ""; if (old_r == r) clipboardString.append('\t'); else if (old_r != -1) clipboardString.append('\n'); //System.out.println("Cell: " + cell); clipboardString.append(cell); old_r = r; //System.out.println("Clipboard: " + clipboardString); } final ClipboardContent content = new ClipboardContent(); content.putString(clipboardString.toString()); Clipboard.getSystemClipboard().setContent(content); } }); ContextMenu menu = new ContextMenu(); menu.getItems().add(copyItem); copyItem.setAccelerator(new KeyCodeCombination(KeyCode.C, KeyCombination.SHORTCUT_DOWN)); resultTable.setContextMenu(menu); closeMenuItem.setOnAction((ActionEvent e) -> { Platform.runLater(() -> { Stage stage = (Stage) resultPane.getScene().getWindow(); stage.close(); }); }); if (System.getProperty("os.name").equals("Mac OS X")) { closeMenuItem.setAccelerator(new KeyCodeCombination(KeyCode.W, KeyCombination.SHORTCUT_DOWN)); } quitMenuItem.setOnAction((ActionEvent e) -> { Platform.runLater(() -> { Platform.exit(); System.exit(0); }); }); saveMenuItem.setOnAction((ActionEvent e) -> { try { writeResultsToFile(); } catch (IOException ex) { Platform.runLater(() -> { Alert error = getExceptionDialog(ex); error.setTitle("Write Failed"); error.setHeaderText("Error writing file."); error.setContentText("Exception encountered when attempting " + "the saved file. See below:"); error.showAndWait(); }); } }); saveMenuItem.disableProperty().bind(Bindings.size(displayData).lessThan(1)); clearAndCloseMenuItem.setOnAction((ActionEvent e) -> { Platform.runLater(() -> { clearTable(); Stage stage = (Stage) resultPane.getScene().getWindow(); stage.close(); }); }); clearMenuItem.setOnAction((ActionEvent e) -> { Platform.runLater(() -> { clearTable(); }); }); clearPreviousMenuItem.setOnAction((ActionEvent e) -> { Platform.runLater(() -> { clearPreviousResults(); }); }); ToggleGroup refseqToggleGroup = new ToggleGroup(); refSeqMenu.setToggleGroup(refseqToggleGroup); refSeqOnlyMenu.setToggleGroup(refseqToggleGroup); noRefSeqMenu.setToggleGroup(refseqToggleGroup); refSeqMenu.selectedProperty().addListener(new ChangeListener<Boolean>() { @Override public void changed(ObservableValue ov, Boolean old_val, Boolean new_val) { redisplayData(); } }); refSeqOnlyMenu.selectedProperty().addListener(new ChangeListener<Boolean>() { @Override public void changed(ObservableValue ov, Boolean old_val, Boolean new_val) { redisplayData(); } }); noRefSeqMenu.selectedProperty().addListener(new ChangeListener<Boolean>() { @Override public void changed(ObservableValue ov, Boolean old_val, Boolean new_val) { redisplayData(); } }); canonicalOnlyMenu.selectedProperty().addListener(new ChangeListener<Boolean>() { @Override public void changed(ObservableValue ov, Boolean old_val, Boolean new_val) { redisplayData(); } }); codingOnlyMenu.selectedProperty().addListener(new ChangeListener<Boolean>() { @Override public void changed(ObservableValue ov, Boolean old_val, Boolean new_val) { redisplayData(); } }); mostRecentFirstMenu.selectedProperty().addListener(new ChangeListener<Boolean>() { @Override public void changed(ObservableValue ov, Boolean old_val, Boolean new_val) { redisplayData(); } }); refSeq.bind(refSeqMenu.selectedProperty()); refSeqOnly.bind(refSeqOnlyMenu.selectedProperty()); canonicalOnly.bind(canonicalOnlyMenu.selectedProperty()); codingOnly.bind(codingOnlyMenu.selectedProperty()); mostRecentFirst.bind(mostRecentFirstMenu.selectedProperty()); aboutMenuItem.setOnAction(new EventHandler() { @Override public void handle(Event ev) { showAbout(); } }); helpMenuItem.setOnAction(new EventHandler() { @Override public void handle(Event ev) { showHelp(); } }); } public void displayData(ArrayList<MutationMapperResult> results) { String index = String.valueOf(lastIndex + 1); char sub = 'A'; for (MutationMapperResult r : results) { if (sub == 'Z') { index = (index + sub); sub = 'A'; } r.setIndex(index + sub); data.add(r); sub++; } lastIndex++; setTableItems(); } private void setTableItems() { displayData.clear(); if (refSeq.getValue() || refSeqOnly.getValue()) { transcriptCol.setCellValueFactory(new PropertyValueFactory<>("refSeqTransciptLink")); } else { transcriptCol.setCellValueFactory(new PropertyValueFactory<>("transciptLink")); } for (MutationMapperResult r : data) { if (canonicalOnly.getValue()) { if (!r.getIsCanonical()) { continue; } } if (codingOnly.getValue()) { if (!r.getBiotype().equals("protein_coding")) { continue; } } if (refSeqOnly.getValue()) { if (r.getRefSeqIds() == null || r.getRefSeqIds().isEmpty()) { continue; } } displayData.add(r); } Collections.sort(displayData, new ResultComparator()); resultTable.setItems(displayData); } public void redisplayData() { resultTable.getItems().clear(); setTableItems(); } public void clearTable() { lastIndex = 0; data.clear(); displayData.clear(); resultTable.getItems().clear(); } public void clearPreviousResults() { ArrayList<MutationMapperResult> lastRun = new ArrayList<>(); for (MutationMapperResult r : data) { if (r.getIndex().matches(lastIndex + "[A-Z]+")) { r.setIndex(null); lastRun.add(r); } } clearTable(); lastIndex = 0; displayData(lastRun); } private void writeResultsToFile() throws IOException { if (displayData.isEmpty()) { Alert alert = new Alert(Alert.AlertType.ERROR); alert.setTitle("Nothing to save"); alert.setHeaderText("No results to save"); alert.setContentText("No results are visible with current filters," + " no file can be saved."); alert.setResizable(true); alert.showAndWait(); return; } FileChooser fileChooser = new FileChooser(); fileChooser.getExtensionFilters().addAll(new FileChooser.ExtensionFilter("Excel (*.xlsx)", "*.xlsx"), new FileChooser.ExtensionFilter("CSV (*.csv)", "*.csv"), new FileChooser.ExtensionFilter("Text (*.txt)", "*.txt")); fileChooser.setTitle("Write results to file..."); fileChooser.setInitialDirectory(new File(getProperty("user.home"))); File wFile = fileChooser.showSaveDialog(resultPane.getScene().getWindow()); if (wFile == null) { return; } else if (!wFile.getName().endsWith(".xlsx") && !wFile.getName().endsWith(".csv") && !wFile.getName().endsWith(".txt")) { String ext = //annoying bug with filechooser means extension might not be appended fileChooser.selectedExtensionFilterProperty().get().getExtensions().get(0).substring(1); wFile = new File(wFile.getAbsolutePath() + ext); } if (wFile.getName().endsWith(".xlsx")) { writeResultsToExcel(wFile); } else if (wFile.getName().endsWith(".csv")) { writeResultsToCsv(wFile); } else { writeResultsToTsv(wFile); } } private void writeResultsToExcel(final File f) throws IOException { final Service<Void> service = new Service<Void>() { @Override protected Task<Void> createTask() { return new Task<Void>() { @Override protected Void call() throws IOException { BufferedOutputStream bo = new BufferedOutputStream(new FileOutputStream(f)); Workbook wb = new XSSFWorkbook(); //CellStyle hlink_style = wb.createCellStyle(); //Font hlink_font = wb.createFont(); //hlink_font.setUnderline(Font.U_SINGLE); //hlink_font.setColor(IndexedColors.BLUE.getIndex()); //hlink_style.setFont(hlink_font); //CreationHelper createHelper = wb.getCreationHelper(); Sheet sheet = wb.createSheet(); Row row = null; int rowNo = 0; row = sheet.createRow(rowNo++); String header[] = { "#", "Symbol", "Gene", "Transcript", "Genomic Coordinate", "Ref", "Var", "CDS", "Consequence", "CDS Consequence", "Protein Consequence", "Exon/Intron", "Colocated Variants", "Polyphen", "Sift", "Seq Input" }; for (int col = 0; col < header.length; col++) { Cell cell = row.createCell(col); cell.setCellValue(header[col]); } updateMessage("Writing results . . ."); updateProgress(0, displayData.size()); int n = 0; for (MutationMapperResult r : displayData) { n++; updateMessage("Writing result " + n + " . . ."); row = sheet.createRow(rowNo++); int col = 0; ArrayList<String> resultsToWrite = getResultArray(r); for (String s : resultsToWrite) { Cell cell = row.createCell(col++); cell.setCellValue(s); } updateProgress(n, displayData.size()); } updateMessage("Wrote " + displayData.size() + " results to file."); wb.write(bo); bo.close(); return null; } }; } }; service.setOnSucceeded(new EventHandler<WorkerStateEvent>() { @Override public void handle(WorkerStateEvent e) { Alert alert = new Alert(AlertType.CONFIRMATION); alert.setTitle("File Saved"); alert.setHeaderText("Displayed results saved to " + f.getName()); alert.setContentText("Open this file now?"); ButtonType yesButton = ButtonType.YES; ButtonType noButton = ButtonType.NO; alert.getButtonTypes().setAll(yesButton, noButton); Optional<ButtonType> response = alert.showAndWait(); if (response.get() == yesButton) { try { openFile(f); } catch (Exception ex) { Alert openError = getExceptionDialog(ex); openError.setTitle("Open Failed"); openError.setHeaderText("Could not open ouput file."); openError.setContentText( "Exception encountered when attempting to open " + "the saved file. See below:"); openError.showAndWait(); } } } }); service.setOnCancelled(new EventHandler<WorkerStateEvent>() { @Override public void handle(WorkerStateEvent e) { Alert writeCancelled = new Alert(AlertType.INFORMATION); writeCancelled.setTitle("Cancelled writing to file"); writeCancelled.setHeaderText("User cancelled writing primers to file."); writeCancelled.showAndWait(); } }); service.setOnFailed(new EventHandler<WorkerStateEvent>() { @Override public void handle(WorkerStateEvent e) { Alert writeFailed = getExceptionDialog(e.getSource().getException()); writeFailed.setTitle("Write Failed"); writeFailed.setHeaderText("Could not write ouput file."); writeFailed.setContentText( "Exception encountered when attempting to write " + "results to file. See below:"); writeFailed.showAndWait(); } }); service.start(); } private void writeResultsToCsv(final File f) throws IOException { writeResultsToText(f, ","); } private void writeResultsToTsv(final File f) throws IOException { writeResultsToText(f, "\t"); } //takes output file and delimiter string as arguments private void writeResultsToText(final File f, final String d) throws IOException { final Service<Void> service = new Service<Void>() { @Override protected Task<Void> createTask() { return new Task<Void>() { @Override protected Void call() throws IOException { FileWriter fw = new FileWriter(f.getAbsoluteFile()); BufferedWriter bw = new BufferedWriter(fw); updateMessage("Writing primers . . ."); updateProgress(0, displayData.size()); String header[] = { "#", "Symbol", "Gene", "Transcript", "Genomic Coordinate", "Ref", "Var", "CDS", "Consequence", "CDS Consequence", "Protein Consequence", "Exon/Intron", "Colocated Variants", "Polyphen", "Sift", "Seq Input" }; bw.write(String.join(d, header)); bw.newLine(); int n = 0; for (MutationMapperResult r : displayData) { n++; updateMessage("Writing result " + n + " . . ."); ArrayList<String> resultsToWrite = getResultArray(r); bw.write(String.join(d, resultsToWrite)); bw.newLine(); updateProgress(n, displayData.size()); } updateMessage("Wrote " + n + " results to file."); bw.close(); return null; } }; } }; service.setOnSucceeded(new EventHandler<WorkerStateEvent>() { @Override public void handle(WorkerStateEvent e) { Alert alert = new Alert(AlertType.CONFIRMATION); alert.setTitle("File Saved"); alert.setHeaderText("Displayed results saved to " + f.getName()); alert.setContentText("Open this file now?"); ButtonType yesButton = ButtonType.YES; ButtonType noButton = ButtonType.NO; alert.getButtonTypes().setAll(yesButton, noButton); Optional<ButtonType> response = alert.showAndWait(); if (response.get() == yesButton) { try { openFile(f); } catch (Exception ex) { Alert openError = getExceptionDialog(ex); openError.setTitle("Open Failed"); openError.setHeaderText("Could not open output file."); openError.setContentText( "Exception encountered when attempting to open " + "the saved file. See below:"); openError.showAndWait(); } } } }); service.setOnCancelled(new EventHandler<WorkerStateEvent>() { @Override public void handle(WorkerStateEvent e) { Alert writeCancelled = new Alert(AlertType.INFORMATION); writeCancelled.setTitle("Cancelled writing to file"); writeCancelled.setHeaderText("User cancelled writing primers to file."); writeCancelled.showAndWait(); } }); service.setOnFailed(new EventHandler<WorkerStateEvent>() { @Override public void handle(WorkerStateEvent e) { Alert writeFailed = getExceptionDialog(e.getSource().getException()); writeFailed.setTitle("Write Failed"); writeFailed.setHeaderText("Could not write output file."); writeFailed.setContentText( "Exception encountered when attempting to write " + "results to file. See below:"); writeFailed.showAndWait(); } }); service.start(); } private ArrayList<String> getResultArray(MutationMapperResult r) { ArrayList<String> values = new ArrayList<>(); values.add(r.getIndex()); values.add(r.getGeneSymbol()); values.add(r.getGeneId()); if (refSeq.getValue() || refSeqOnly.getValue()) { values.add(r.getRefSeqIfAvailable()); } else { values.add(r.getTranscript()); } values.add(r.getGenomicCoordinate()); values.add(r.getRefAllele()); values.add(r.getVarAllele()); values.add(r.getCdsCoordinate()); values.add(r.getConsequence()); values.add(r.getCdsConsequence()); values.add(r.getProteinConsequence()); values.add(r.getExonIntronNumber()); values.add(r.getKnownIds()); values.add(r.getPolyphenResult()); values.add(r.getSiftResult()); values.add(r.getSeqInput()); return values; } private void openFile(File f) throws IOException { String command; //Desktop.getDesktop().open(f); if (System.getProperty("os.name").equals("Linux")) { command = "xdg-open " + f; } else if (System.getProperty("os.name").equals("Mac OS X")) { command = "open " + f; } else if (System.getProperty("os.name").contains("Windows")) { command = "cmd /C start " + f; } else { return; } Runtime.getRuntime().exec(command); } private Alert getExceptionDialog(Throwable ex) { // Create expandable Exception. Alert alert = new Alert(AlertType.ERROR); StringWriter sw = new StringWriter(); PrintWriter pw = new PrintWriter(sw); ex.printStackTrace(pw); String exceptionText = sw.toString(); Label label = new Label("The exception stacktrace was:"); TextArea textArea = new TextArea(exceptionText); textArea.setEditable(false); textArea.setWrapText(true); textArea.setMaxWidth(Double.MAX_VALUE); textArea.setMaxHeight(Double.MAX_VALUE); GridPane.setVgrow(textArea, Priority.ALWAYS); GridPane.setHgrow(textArea, Priority.ALWAYS); GridPane expContent = new GridPane(); expContent.setMaxWidth(Double.MAX_VALUE); expContent.add(label, 0, 0); expContent.add(textArea, 0, 1); // Set expandable Exception into the dialog pane. alert.getDialogPane().setExpandableContent(expContent); alert.setResizable(true); return alert; } public void showAbout() { try { FXMLLoader loader = new FXMLLoader(getClass().getResource("about.fxml")); Pane page = (Pane) loader.load(); Scene scene = new Scene(page); Stage stage = new Stage(); stage.setScene(scene); //scene.getStylesheets().add(AutoPrimer3.class // .getResource("autoprimer3.css").toExternalForm()); AboutController controller = loader.getController(); controller.setVersion(VERSION); stage.initModality(Modality.APPLICATION_MODAL); stage.getIcons().add(new Image(this.getClass().getResourceAsStream("icon.png"))); stage.setTitle("About MutationMapper"); stage.show(); } catch (IOException ex) { Alert alert = new Alert(AlertType.ERROR); alert.setTitle("Mutation Mapper Error"); alert.setHeaderText("Could not display about dialog"); alert.setContentText(ex.getMessage()); ex.printStackTrace(); alert.setResizable(true); alert.showAndWait(); } } public void showHelp() { try { File instructionsPdf = File.createTempFile("MutationMapper_Instructions", ".pdf"); instructionsPdf.deleteOnExit(); InputStream inputStream = this.getClass().getResourceAsStream("instructions.pdf"); OutputStream outputStream = new FileOutputStream(instructionsPdf); int read = 0; byte[] bytes = new byte[1024]; while ((read = inputStream.read(bytes)) != -1) { outputStream.write(bytes, 0, read); } inputStream.close(); outputStream.close(); openFile(instructionsPdf); } catch (IOException ex) { Alert alert = new Alert(AlertType.ERROR); alert.setTitle("Mutation Mapper Error"); alert.setHeaderText("Could not open instructions PDF"); alert.setContentText(ex.getMessage()); System.out.println(alert.getContentText()); alert.setResizable(true); alert.showAndWait(); } } public class ResultComparator implements Comparator<MutationMapperResult> { @Override public int compare(MutationMapperResult r1, MutationMapperResult r2) { Pattern indexPatt = Pattern.compile("(\\d+)(\\w+)"); Matcher m1 = indexPatt.matcher(r1.getIndex()); Matcher m2 = indexPatt.matcher(r2.getIndex()); if (m1.matches()) { if (!m2.matches()) { return -1; } int runNo1 = Integer.parseInt(m1.group(1)); int runNo2 = Integer.parseInt(m2.group(1)); if (runNo1 == runNo2) { String sub1 = m1.group(2); String sub2 = m2.group(2); return sub1.compareTo(sub2); } else { if (mostRecentFirst.getValue()) { return runNo2 - runNo1; } else { return runNo1 - runNo2; } } } else { if (m2.matches()) { return 1; } else { return r1.getIndex().compareTo(r2.getIndex()); } } } } }