Java tutorial
/* * BioAssay Ontology Annotator Tools * * (c) 2014-2017 Collaborative Drug Discovery Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License 2.0 * as published by the Free Software Foundation: * * http://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html * * 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. */ package com.cdd.bao.editor; import com.cdd.bao.*; import com.cdd.bao.template.*; import com.cdd.bao.util.*; import com.cdd.bao.editor.endpoint.*; import java.io.*; import java.net.*; import java.util.*; import javafx.event.*; import javafx.geometry.*; import javafx.stage.*; import javafx.scene.*; import javafx.scene.control.*; import javafx.scene.input.*; import javafx.scene.layout.*; import javafx.scene.paint.*; import javafx.application.*; import javafx.beans.value.*; import javafx.util.*; import org.json.*; /* Edit Schema: the primary window for BAO schema editing, which is responsible for taking care of a data instance. */ public class EditSchema { // ------------ private data ------------ private File schemaFile = null; private StackSchema stack = new StackSchema(); private Stage stage; private BorderPane root; private SplitPane splitter; private TreeView<Branch> treeView; private TreeItem<Branch> treeRoot, treeTemplate, treeAssays; private DetailPane detail; private MenuBar menuBar; private Menu menuFile, menuEdit, menuValue, menuView; private CheckMenuItem menuViewSummary; private ProgressBar progBar; private boolean currentlyRebuilding = false; // a "branch" encapsulates a tree item which is a generic heading, or one of the objects used within the schema public static final class Branch { public EditSchema owner; public String heading = null; public Schema.Group group = null; public Schema.Assignment assignment = null; public Schema.Assay assay = null; public String locatorID = null; public Branch(EditSchema owner) { this.owner = owner; } public Branch(EditSchema owner, String heading) { this.owner = owner; this.heading = heading; } public Branch(EditSchema owner, Schema.Group group, String locatorID) { this.owner = owner; this.group = group.clone(); this.locatorID = locatorID; } public Branch(EditSchema owner, Schema.Assignment assignment, String locatorID) { this.owner = owner; this.assignment = assignment.clone(); this.locatorID = locatorID; } public Branch(EditSchema owner, Schema.Assay assay, String locatorID) { this.owner = owner; this.assay = assay; this.locatorID = locatorID; } } // ------------ public methods ------------ public EditSchema(Stage stage) { this.stage = stage; if (MainApplication.icon != null) stage.getIcons().add(MainApplication.icon); menuBar = new MenuBar(); menuBar.setUseSystemMenuBar(true); menuBar.getMenus().add(menuFile = new Menu("_File")); menuBar.getMenus().add(menuEdit = new Menu("_Edit")); menuBar.getMenus().add(menuValue = new Menu("_Value")); menuBar.getMenus().add(menuView = new Menu("Vie_w")); createMenuItems(); treeRoot = new TreeItem<>(new Branch(this)); treeView = new TreeView<>(treeRoot); treeView.setEditable(true); treeView.setCellFactory(p -> new HierarchyTreeCell()); treeView.getSelectionModel().selectedItemProperty().addListener((observable, oldVal, newVal) -> { if (oldVal != null) pullDetail(oldVal); if (newVal != null) pushDetail(newVal); }); treeView.focusedProperty() .addListener((val, oldValue, newValue) -> Platform.runLater(() -> maybeUpdateTree())); detail = new DetailPane(this); StackPane sp1 = new StackPane(), sp2 = new StackPane(); sp1.getChildren().add(treeView); sp2.getChildren().add(detail); splitter = new SplitPane(); splitter.setOrientation(Orientation.HORIZONTAL); splitter.getItems().addAll(sp1, sp2); splitter.setDividerPositions(0.4, 1.0); progBar = new ProgressBar(); progBar.setMaxWidth(Double.MAX_VALUE); root = new BorderPane(); root.setTop(menuBar); root.setCenter(splitter); root.setBottom(progBar); BorderPane.setMargin(progBar, new Insets(2, 2, 2, 2)); Scene scene = new Scene(root, 1000, 800, Color.WHITE); stage.setScene(scene); treeView.setShowRoot(false); treeRoot.getChildren().add(treeTemplate = new TreeItem<>(new Branch(this, "Template"))); treeRoot.getChildren().add(treeAssays = new TreeItem<>(new Branch(this, "Assays"))); treeTemplate.setExpanded(true); treeAssays.setExpanded(true); rebuildTree(); Platform.runLater(() -> treeView.getFocusModel().focus(treeView.getSelectionModel().getSelectedIndex())); // for some reason it defaults to not the first item stage.setOnCloseRequest(event -> { if (!confirmClose()) event.consume(); }); updateTitle(); // loading begins in a background thread, and is updated with a status bar Vocabulary.Listener listener = new Vocabulary.Listener() { public void vocabLoadingProgress(Vocabulary vocab, float progress) { Platform.runLater(() -> { progBar.setProgress(progress); if (vocab.isLoaded()) root.getChildren().remove(progBar); }); } public void vocabLoadingException(Exception ex) { ex.printStackTrace(); } }; Vocabulary.globalInstance(listener); } public TreeView<Branch> getTreeView() { return treeView; } public DetailPane getDetailView() { return detail; } // loads a file and parses the schema public void loadFile(File file) { try { Schema schema = ModelSchema.deserialise(file); loadFile(file, schema); } catch (Exception ex) { ex.printStackTrace(); return; } } // loads a file with an already-parsed schema public void loadFile(File file, Schema schema) { schemaFile = file; stack.setSchema(schema); updateTitle(); rebuildTree(); } // given that a part of the current/indicated branch may have been modified, updates the internal representation of the schema - // and pushes the modified version to the undo stack - then informs the detail view of the modifications; all of this without any // changes in the UI widget states public void updateBranchGroup(Schema.Group modGroup) { if (modGroup == null) return; TreeItem<Branch> item = currentBranch(); if (item == null || item.getValue() == null) return; updateBranchGroup(item.getValue(), modGroup); } public void updateBranchGroup(Branch branch, Schema.Group modGroup) { Schema schema = stack.getSchema(); if (branch.group.parent != null) { // reparent the modified group, then swap it out in the parent's child list Schema.Group replGroup = schema.obtainGroup(branch.locatorID); modGroup.parent = replGroup.parent; int idx = replGroup.parent.subGroups.indexOf(replGroup); replGroup.parent.subGroups.set(idx, modGroup); } else schema.setRoot(modGroup); branch.group = modGroup; stack.changeSchema(schema, true); detail.setGroup(schema, branch.group, false); } public void updateBranchAssignment(Schema.Assignment modAssn) { if (modAssn == null) return; TreeItem<Branch> item = currentBranch(); if (item == null || item.getValue() == null) return; updateBranchAssignment(item.getValue(), modAssn); } public void updateBranchAssignment(Branch branch, Schema.Assignment modAssn) { Schema schema = stack.getSchema(); // reparent the modified assignment, then swap it out in the parent's child list Schema.Assignment replAssn = schema.obtainAssignment(branch.locatorID); modAssn.parent = replAssn.parent; int idx = replAssn.parent.assignments.indexOf(replAssn); replAssn.parent.assignments.set(idx, modAssn); schema.syncAnnotations(replAssn, modAssn); branch.assignment = modAssn; stack.changeSchema(schema, true); detail.setAssignment(schema, branch.assignment, false); } public void updateBranchAssay(Schema.Assay modAssay) { if (modAssay == null) return; TreeItem<Branch> item = currentBranch(); if (item == null) return; Branch branch = item.getValue(); if (branch == null) return; updateBranchAssay(branch, modAssay); item.setValue(new Branch(this)); item.setValue(branch); // triggers redraw } public void updateBranchAssay(Branch branch, Schema.Assay modAssay) { Schema schema = stack.getSchema(); int idx = schema.indexOfAssay(branch.locatorID); schema.setAssay(idx, modAssay); branch.assay = modAssay; stack.changeSchema(schema, true); detail.setAssay(schema, branch.assay, false); } // ------------ private methods ------------ private void updateTitle() { String title = "BioAssay Schema Editor"; if (schemaFile != null) title += " - " + schemaFile.getName(); stage.setTitle(title); } private void createMenuItems() { final KeyCombination.Modifier cmd = KeyCombination.SHORTCUT_DOWN, shift = KeyCombination.SHIFT_DOWN, alt = KeyCombination.ALT_DOWN; addMenu(menuFile, "_New", new KeyCharacterCombination("N", cmd)).setOnAction(event -> actionFileNew()); addMenu(menuFile, "_Open", new KeyCharacterCombination("O", cmd)).setOnAction(event -> actionFileOpen()); addMenu(menuFile, "_Save", new KeyCharacterCombination("S", cmd)) .setOnAction(event -> actionFileSave(false)); addMenu(menuFile, "Save _As", new KeyCharacterCombination("S", cmd, shift)) .setOnAction(event -> actionFileSave(true)); addMenu(menuFile, "_Export Dump", new KeyCharacterCombination("E", cmd)) .setOnAction(event -> actionFileExportDump()); addMenu(menuFile, "_Merge", null).setOnAction(event -> actionFileMerge()); menuFile.getItems().add(new SeparatorMenuItem()); addMenu(menuFile, "Confi_gure", new KeyCharacterCombination(",", cmd)) .setOnAction(event -> actionFileConfigure()); addMenu(menuFile, "_Browse Endpoint", new KeyCharacterCombination("B", cmd, shift)) .setOnAction(event -> actionFileBrowse()); if (false) { addMenu(menuFile, "_Upload Endpoint", new KeyCharacterCombination("U", cmd, shift)) .setOnAction(event -> actionFileUpload()); } Menu menuFileGraphics = new Menu("Graphics"); addMenu(menuFileGraphics, "_Template", null).setOnAction(event -> actionFileGraphicsTemplate()); addMenu(menuFileGraphics, "_Assay", null).setOnAction(event -> actionFileGraphicsAssay()); addMenu(menuFileGraphics, "_Properties", null).setOnAction(event -> actionFileGraphicsProperties()); addMenu(menuFileGraphics, "_Values", null).setOnAction(event -> actionFileGraphicsValues()); menuFile.getItems().add(menuFileGraphics); addMenu(menuFile, "Assay Stats", null).setOnAction(event -> actionFileAssayStats()); menuFile.getItems().add(new SeparatorMenuItem()); addMenu(menuFile, "_Close", new KeyCharacterCombination("W", cmd)).setOnAction(event -> actionFileClose()); addMenu(menuFile, "_Quit", new KeyCharacterCombination("Q", cmd)).setOnAction(event -> actionFileQuit()); addMenu(menuEdit, "Add _Group", new KeyCharacterCombination("G", cmd, shift)) .setOnAction(event -> actionGroupAdd()); addMenu(menuEdit, "Add _Assignment", new KeyCharacterCombination("A", cmd, shift)) .setOnAction(event -> actionAssignmentAdd()); addMenu(menuEdit, "Add Assa_y", new KeyCharacterCombination("Y", cmd, shift)) .setOnAction(event -> actionAssayAdd()); menuEdit.getItems().add(new SeparatorMenuItem()); addMenu(menuEdit, "Cu_t", new KeyCharacterCombination("X", cmd)).setOnAction(event -> actionEditCopy(true)); addMenu(menuEdit, "_Copy", new KeyCharacterCombination("C", cmd)) .setOnAction(event -> actionEditCopy(false)); Menu menuCopyAs = new Menu("Copy As"); menuEdit.getItems().add(menuCopyAs); addMenu(menuEdit, "_Paste", new KeyCharacterCombination("V", cmd)).setOnAction(event -> actionEditPaste()); menuEdit.getItems().add(new SeparatorMenuItem()); addMenu(menuEdit, "_Delete", new KeyCodeCombination(KeyCode.DELETE, cmd, shift)) .setOnAction(event -> actionEditDelete()); addMenu(menuEdit, "_Undo", new KeyCharacterCombination("Z", cmd, shift)) .setOnAction(event -> actionEditUndo()); addMenu(menuEdit, "_Redo", new KeyCharacterCombination("Z", cmd, shift, alt)) .setOnAction(event -> actionEditRedo()); menuEdit.getItems().add(new SeparatorMenuItem()); addMenu(menuEdit, "Move _Up", new KeyCharacterCombination("[", cmd)) .setOnAction(event -> actionEditMove(-1)); addMenu(menuEdit, "Move _Down", new KeyCharacterCombination("]", cmd)) .setOnAction(event -> actionEditMove(1)); addMenu(menuCopyAs, "Layout Tab-Separated", null).setOnAction(event -> actionEditCopyLayoutTSV()); addMenu(menuValue, "_Add Value", new KeyCharacterCombination("V", cmd, shift)) .setOnAction(event -> detail.actionValueAdd()); addMenu(menuValue, "Add _Multiple Values", new KeyCharacterCombination("M", cmd, shift)) .setOnAction(event -> detail.actionValueMultiAdd()); addMenu(menuValue, "_Delete Value", new KeyCodeCombination(KeyCode.DELETE, cmd)) .setOnAction(event -> detail.actionValueDelete()); addMenu(menuValue, "Move _Up", new KeyCodeCombination(KeyCode.UP, cmd)) .setOnAction(event -> detail.actionValueMove(-1)); addMenu(menuValue, "Move _Down", new KeyCodeCombination(KeyCode.DOWN, cmd)) .setOnAction(event -> detail.actionValueMove(1)); menuValue.getItems().add(new SeparatorMenuItem()); addMenu(menuValue, "_Lookup URI", new KeyCharacterCombination("U", cmd)) .setOnAction(event -> detail.actionLookupURI()); addMenu(menuValue, "Lookup _Name", new KeyCharacterCombination("L", cmd)) .setOnAction(event -> detail.actionLookupName()); menuValue.getItems().add(new SeparatorMenuItem()); addMenu(menuValue, "_Sort Values", null).setOnAction(event -> actionValueSort()); addMenu(menuValue, "_Remove Duplicates", null).setOnAction(event -> actionValueDuplicates()); addMenu(menuValue, "Cleanup Values", null).setOnAction(event -> actionValueCleanup()); (menuViewSummary = addCheckMenu(menuView, "_Summary Values", new KeyCharacterCombination("-", cmd))) .setOnAction(event -> actionViewToggleSummary()); addMenu(menuView, "_Template", new KeyCharacterCombination("1", cmd)) .setOnAction(event -> actionViewTemplate()); addMenu(menuView, "_Assays", new KeyCharacterCombination("2", cmd)) .setOnAction(event -> actionViewAssays()); addMenu(menuView, "_Derived Tree", new KeyCharacterCombination("3", cmd)) .setOnAction(event -> detail.actionShowTree()); } private MenuItem addMenu(Menu parent, String title, KeyCombination accel) { MenuItem item = new MenuItem(title); parent.getItems().add(item); if (accel != null) item.setAccelerator(accel); return item; } private CheckMenuItem addCheckMenu(Menu parent, String title, KeyCombination accel) { CheckMenuItem item = new CheckMenuItem(title); parent.getItems().add(item); if (accel != null) item.setAccelerator(accel); return item; } private void rebuildTree() { currentlyRebuilding = true; treeTemplate.getChildren().clear(); treeAssays.getChildren().clear(); Schema schema = stack.getSchema(); Schema.Group root = schema.getRoot(); treeTemplate.setValue(new Branch(this, root, schema.locatorID(root))); fillTreeGroup(schema, root, treeTemplate); for (int n = 0; n < schema.numAssays(); n++) { Schema.Assay assay = schema.getAssay(n); TreeItem<Branch> item = new TreeItem<>(new Branch(this, assay, schema.locatorID(assay))); treeAssays.getChildren().add(item); } currentlyRebuilding = false; } private void fillTreeGroup(Schema schema, Schema.Group group, TreeItem<Branch> parent) { for (Schema.Assignment assn : group.assignments) { TreeItem<Branch> item = new TreeItem<>(new Branch(this, assn, schema.locatorID(assn))); parent.getChildren().add(item); } for (Schema.Group subgrp : group.subGroups) { TreeItem<Branch> item = new TreeItem<>(new Branch(this, subgrp, schema.locatorID(subgrp))); item.setExpanded(true); parent.getChildren().add(item); fillTreeGroup(schema, subgrp, item); } } // convenience for tree selection public TreeItem<Branch> currentBranch() { return treeView.getSelectionModel().getSelectedItem(); } public void setCurrentBranch(TreeItem<Branch> branch) { treeView.getSelectionModel().select(branch); treeView.scrollTo(treeView.getRow(branch)); } public String currentLocatorID() { TreeItem<Branch> branch = currentBranch(); return branch == null ? null : branch.getValue().locatorID; } public TreeItem<Branch> locateBranch(String locatorID) { List<TreeItem<Branch>> stack = new ArrayList<>(); stack.add(treeRoot); while (stack.size() > 0) { TreeItem<Branch> item = stack.remove(0); String lookID = item.getValue().locatorID; if (lookID != null && lookID.equals(locatorID)) return item; for (TreeItem<Branch> sub : item.getChildren()) stack.add(sub); } return null; } public void clearSelection() { detail.clearContent(); treeView.getSelectionModel().clearSelection(); } // for the given branch, pulls out the content: if any changes have been made, pushes the modified schema onto the stack private void pullDetail() { pullDetail(currentBranch()); } private void pullDetail(TreeItem<Branch> item) { if (currentlyRebuilding || item == null) return; Branch branch = item.getValue(); if (branch.group != null) { // if root, check prefix first String prefix = detail.extractPrefix(); if (prefix != null) { prefix = prefix.trim(); if (!prefix.endsWith("#")) prefix += "#"; if (!stack.peekSchema().getSchemaPrefix().equals(prefix)) { try { new URI(prefix); } catch (Exception ex) { //informMessage("Invalid URI", "Prefix is not a valid URI: " + prefix); return; } Schema schema = stack.getSchema(); schema.setSchemaPrefix(prefix); stack.setSchema(schema); } } // then handle the group content Schema.Group modGroup = detail.extractGroup(); if (modGroup == null) return; updateBranchGroup(branch, modGroup); item.setValue(new Branch(this)); item.setValue(branch); // triggers redraw } else if (branch.assignment != null) { Schema.Assignment modAssn = detail.extractAssignment(); if (modAssn == null) return; updateBranchAssignment(branch, modAssn); item.setValue(new Branch(this)); item.setValue(branch); // triggers redraw } else if (branch.assay != null) { Schema.Assay modAssay = detail.extractAssay(); if (modAssay == null) return; updateBranchAssay(branch, modAssay); item.setValue(new Branch(this)); item.setValue(branch); // triggers redraw } } // recreates all the widgets in the detail view, given that the indicated branch has been selected private void pushDetail(TreeItem<Branch> item) { if (currentlyRebuilding || item == null) return; Branch branch = item.getValue(); Schema schema = stack.peekSchema(); if (branch.group != null) { Schema.Group group = schema.obtainGroup(branch.locatorID); detail.setGroup(schema, group, true); } else if (branch.assignment != null) { Schema.Assignment assn = schema.obtainAssignment(branch.locatorID); detail.setAssignment(schema, assn, true); } else if (branch.assay != null) { Schema.Assay assay = schema.obtainAssay(branch.locatorID); detail.setAssay(schema, assay, true); } else detail.clearContent(); } // in case the detail has changed, update the main part of the tree private void maybeUpdateTree() { pullDetail(); } // returns true if the data is already saved, or the user agrees to abandon it private boolean confirmClose() { pullDetail(); if (!stack.isDirty()) return true; Alert alert = new Alert(Alert.AlertType.CONFIRMATION); alert.setTitle("Close Window"); alert.setHeaderText("Abandon changes"); alert.setContentText("Closing this window will cause modifications to be lost."); Optional<ButtonType> result = alert.showAndWait(); return result.get() == ButtonType.OK; } // ------------ action responses ------------ public void actionFileNew() { Stage stage = new Stage(); EditSchema edit = new EditSchema(stage); stage.show(); } public void actionFileSave(boolean promptNew) { pullDetail(); // dialog in case filename is missing or requested as save-to-other if (promptNew || schemaFile == null) { FileChooser chooser = new FileChooser(); chooser.setTitle("Save Schema Template"); if (schemaFile != null) chooser.setInitialDirectory(schemaFile.getParentFile()); File file = chooser.showSaveDialog(stage); if (file == null) return; if (!file.getName().endsWith(".ttl")) file = new File(file.getAbsolutePath() + ".ttl"); schemaFile = file; updateTitle(); } // validity checking if (schemaFile == null) return; if (!schemaFile.getAbsoluteFile().getParentFile().canWrite() || (schemaFile.exists() && !schemaFile.canWrite())) { Util.informMessage("Cannot Save", "Not able to write to file: " + schemaFile.getAbsolutePath()); return; } // serialise-to-file Schema schema = stack.peekSchema(); try { //schema.serialise(System.out); OutputStream ostr = new FileOutputStream(schemaFile); ModelSchema.serialise(schema, ostr); ostr.close(); stack.setDirty(false); } catch (Exception ex) { ex.printStackTrace(); } } public void actionFileOpen() { FileChooser chooser = new FileChooser(); chooser.setTitle("Open Schema Template"); if (schemaFile != null) chooser.setInitialDirectory(schemaFile.getParentFile()); File file = chooser.showOpenDialog(stage); if (file == null) return; try { Schema schema = ModelSchema.deserialise(file); Stage stage = new Stage(); EditSchema edit = new EditSchema(stage); edit.loadFile(file, schema); stage.show(); } catch (IOException ex) { ex.printStackTrace(); Util.informWarning("Open", "Failed to parse file: is it a valid schema?"); } } public void actionFileExportDump() { if (!Vocabulary.globalInstance().isLoaded()) return; pullDetail(); FileChooser chooser = new FileChooser(); chooser.setTitle("Export Schema Dump"); if (schemaFile != null) chooser.setInitialDirectory(schemaFile.getParentFile()); chooser.setInitialFileName("vocab.dump"); File file = chooser.showSaveDialog(stage); if (file == null) return; // when overwriting a file, bring up a preview that shows the differences between before & after, and asks for // confirmation before replacing it if (file.exists()) { try { new CompareVocabTree(file, stack.getSchema()).show(); } catch (Exception ex) { ex.printStackTrace(); } return; } Schema[] schemaList = new Schema[] { stack.getSchema() }; SchemaVocab sv = new SchemaVocab(Vocabulary.globalInstance(), schemaList); Util.writeln("-------------------------"); sv.debugSummary(); Util.writeln("-------------------------"); try { OutputStream ostr = new FileOutputStream(file); sv.serialise(ostr); ostr.close(); } catch (IOException ex) { ex.printStackTrace(); } String msg = "Written to [" + file.getAbsolutePath() + "]. Size: " + file.length(); Util.informWarning("Export", msg); } public void actionFileMerge() { FileChooser chooser = new FileChooser(); chooser.setTitle("Merge Schema"); if (schemaFile != null) chooser.setInitialDirectory(schemaFile.getParentFile()); File file = chooser.showOpenDialog(stage); if (file == null) return; Schema addSchema = null; try { addSchema = ModelSchema.deserialise(file); } catch (IOException ex) { ex.printStackTrace(); Util.informWarning("Merge", "Failed to parse file: is it a valid schema?"); return; } List<String> log = new ArrayList<>(); Schema merged = SchemaUtil.mergeSchema(stack.getSchema(), addSchema, log); if (log.size() == 0) { Util.informMessage("Merge", "The merge file is the same: no action."); return; } String text = String.join("\n", log); Dialog<Boolean> confirm = new Dialog<>(); confirm.setTitle("Confirm Merge Modifications"); TextArea area = new TextArea(text); area.setWrapText(true); area.setPrefWidth(700); area.setPrefHeight(500); confirm.getDialogPane().setContent(area); ButtonType btnApply = new ButtonType("Apply", ButtonBar.ButtonData.OK_DONE); confirm.getDialogPane().getButtonTypes().addAll(new ButtonType("Cancel", ButtonBar.ButtonData.CANCEL_CLOSE), btnApply); confirm.setResultConverter(buttonType -> { if (buttonType == btnApply) return true; return null; }); Optional<Boolean> result = confirm.showAndWait(); if (!result.isPresent() || result.get() != true) return; stack.changeSchema(merged, true); rebuildTree(); } public void actionFileConfigure() { new ConfigPanel().showAndWait(); } public void actionFileBrowse() { String endpoint = EditorPrefs.getSparqlEndpoint(); if (endpoint == null || endpoint.length() == 0) { Util.informWarning("Browse", "You need to setup a SPARQL endpoint first: use the Configuration dialog."); return; } Stage stage = new Stage(); BrowseEndpoint browse = new BrowseEndpoint(stage); stage.show(); } public void actionFileUpload() { Util.writeln("!! upload"); } public void actionFileGraphicsTemplate() { // NOTE: stripped down version; upgrade it to let the user pick the filename, or ideally code up a preview panel if (schemaFile == null) return; RenderSchema render = new RenderSchema(stack.peekSchema()); try { render.createPageTemplate(); String fn = schemaFile.getAbsolutePath(); int i = fn.lastIndexOf('.'); if (i < fn.lastIndexOf('/')) i = -1; if (i >= 0) fn = fn.substring(0, i); fn += "_template.pdf"; render.write(new File(fn)); Util.informMessage("Saved PDF", "Written to:\n" + fn); } catch (Exception ex) { ex.printStackTrace(); } } public void actionFileGraphicsAssay() { // NOTE: stripped down version; upgrade it to let the user pick the filename, or ideally code up a preview panel TreeItem<Branch> item = currentBranch(); Branch branch = item == null ? null : item.getValue(); if (branch == null || branch.assay == null) { Util.informMessage("Graphics for Assay", "Pick an assay first."); return; } if (schemaFile == null) return; RenderSchema render = new RenderSchema(stack.peekSchema()); try { render.createPageAssay(branch.assay); String fn = schemaFile.getAbsolutePath(); int i = fn.lastIndexOf('.'); if (i < fn.lastIndexOf('/')) i = -1; if (i >= 0) fn = fn.substring(0, i); fn += "_assay.pdf"; render.write(new File(fn)); Util.informMessage("Saved PDF", "Written to:\n" + fn); } catch (Exception ex) { ex.printStackTrace(); } } public void actionFileGraphicsProperties() { // NOTE: stripped down version; upgrade it to let the user pick the filename, or ideally code up a preview panel if (schemaFile == null) return; RenderSchema render = new RenderSchema(stack.peekSchema()); try { render.createPageProperties(); String fn = schemaFile.getAbsolutePath(); int i = fn.lastIndexOf('.'); if (i < fn.lastIndexOf('/')) i = -1; if (i >= 0) fn = fn.substring(0, i); fn += "_properties.pdf"; render.write(new File(fn)); Util.informMessage("Saved PDF", "Written to:\n" + fn); } catch (Exception ex) { ex.printStackTrace(); } } public void actionFileGraphicsValues() { // NOTE: stripped down version; upgrade it to let the user pick the filename, or ideally code up a preview panel if (schemaFile == null) return; RenderSchema render = new RenderSchema(stack.peekSchema()); try { render.createPageValues(); String fn = schemaFile.getAbsolutePath(); int i = fn.lastIndexOf('.'); if (i < fn.lastIndexOf('/')) i = -1; if (i >= 0) fn = fn.substring(0, i); fn += "_values.pdf"; render.write(new File(fn)); Util.informMessage("Saved PDF", "Written to:\n" + fn); } catch (Exception ex) { ex.printStackTrace(); } } public void actionFileAssayStats() { List<String> stats = new ArrayList<>(); SchemaUtil.gatherAssayStats(stack.peekSchema(), stats); String text = String.join("\n", stats); Dialog<Boolean> showdlg = new Dialog<>(); showdlg.setTitle("Assay Stats"); TextArea area = new TextArea(text); area.setWrapText(true); area.setPrefWidth(700); area.setPrefHeight(500); showdlg.getDialogPane().setContent(area); showdlg.getDialogPane().getButtonTypes().addAll(new ButtonType("OK", ButtonBar.ButtonData.OK_DONE)); showdlg.setResultConverter(buttonType -> true); showdlg.showAndWait(); } public void actionFileClose() { if (!confirmClose()) return; stage.close(); } public void actionFileQuit() { if (!confirmClose()) return; Platform.exit(); } public void actionGroupAdd() { TreeItem<Branch> item = currentBranch(); if (item == null || (item.getValue().group == null && item.getValue().assignment == null)) { Util.informMessage("Add Group", "Select a group to add to."); return; } pullDetail(); Schema schema = stack.getSchema(); Schema.Group parent = schema.obtainGroup(item.getValue().locatorID); Schema.Group newGroup = schema.appendGroup(parent, new Schema.Group(null, "")); stack.changeSchema(schema); rebuildTree(); setCurrentBranch(locateBranch(schema.locatorID(newGroup))); } public void actionAssignmentAdd() { TreeItem<Branch> item = currentBranch(); if (item == null || (item.getValue().group == null && item.getValue().assignment == null)) { Util.informMessage("Add Assignment", "Select a group to add to."); return; } pullDetail(); Schema schema = stack.getSchema(); Schema.Group parent = schema.obtainGroup(item.getValue().locatorID); Schema.Assignment newAssn = schema.appendAssignment(parent, new Schema.Assignment(null, "", "")); stack.changeSchema(schema); rebuildTree(); setCurrentBranch(locateBranch(schema.locatorID(newAssn))); } public void actionAssayAdd() { pullDetail(); Schema schema = stack.getSchema(); Schema.Assay newAssay = new Schema.Assay(""); schema.appendAssay(newAssay); stack.changeSchema(schema); rebuildTree(); setCurrentBranch(locateBranch(schema.locatorID(newAssay))); } public void actionEditCopy(boolean andCut) { if (!treeView.isFocused()) return; // punt to default action TreeItem<Branch> item = currentBranch(); if (item == null) return; Branch branch = item.getValue(); JSONObject json = null; if (branch.group != null) json = ClipboardSchema.composeGroup(branch.group); else if (branch.assignment != null) json = ClipboardSchema.composeAssignment(branch.assignment); else if (branch.assay != null) json = ClipboardSchema.composeAssay(branch.assay); String serial = null; try { serial = json.toString(2); } catch (JSONException ex) { return; } ClipboardContent content = new ClipboardContent(); content.putString(serial); if (!Clipboard.getSystemClipboard().setContent(content)) { Util.informWarning("Clipboard Copy", "Unable to copy to the clipboard."); return; } if (andCut) actionEditDelete(); } public void actionEditCopyLayoutTSV() { TreeItem<Branch> item = currentBranch(); if (item == null) return; Branch branch = item.getValue(); String tsv = null; try { if (branch.group != null) tsv = ClipboardSchema.composeGroupTSV(branch.group); else if (branch.assignment != null) tsv = ClipboardSchema.composeAssignmentTSV(branch.assignment); if (tsv == null) { Util.informWarning("Clipboard Copy", "Select a branch or assignment to copy."); return; } } catch (Exception ex) { ex.printStackTrace(); return; } ClipboardContent content = new ClipboardContent(); content.putString(tsv); if (!Clipboard.getSystemClipboard().setContent(content)) { Util.informWarning("Clipboard Copy", "Unable to copy to the clipboard."); return; } } public void actionEditPaste() { if (!treeView.isFocused()) return; // punt to default action TreeItem<Branch> item = currentBranch(); if (item == null) { Util.informMessage("Clipboard Paste", "Select a group to paste into."); return; } Branch branch = item.getValue(); Clipboard clipboard = Clipboard.getSystemClipboard(); String serial = clipboard.getString(); if (serial == null) { Util.informWarning("Clipboard Paste", "Content is not parseable."); return; } JSONObject json = null; try { json = new JSONObject(new JSONTokener(serial)); } catch (JSONException ex) { Util.informWarning("Clipboard Paste", "Content is not parseable: it should be a JSON-formatted string."); return; } Schema.Group group = ClipboardSchema.unpackGroup(json); Schema.Assignment assn = ClipboardSchema.unpackAssignment(json); Schema.Assay assay = ClipboardSchema.unpackAssay(json); if (group == null && assn == null && assay == null) { Util.informWarning("Clipboard Paste", "Content does not represent a group, assignment or assay: cannot paste."); return; } pullDetail(); Schema schema = stack.getSchema(); if (group != null) { schema.appendGroup(schema.obtainGroup(branch.locatorID), group); } else if (assn != null) { schema.appendAssignment(schema.obtainGroup(branch.locatorID), assn); } else if (assay != null) { schema.appendAssay(assay); } stack.changeSchema(schema); rebuildTree(); if (group != null) setCurrentBranch(locateBranch(schema.locatorID(group))); else if (assn != null) setCurrentBranch(locateBranch(schema.locatorID(assn))); } public void actionEditDelete() { TreeItem<Branch> item = currentBranch(); Branch branch = item == null ? null : item.getValue(); if (branch == null || (branch.group == null && branch.assignment == null && branch.assay == null)) { Util.informMessage("Delete Branch", "Select a group, assignment or assay to delete."); return; } if (item == treeRoot) { Util.informMessage("Delete Branch", "Can't delete the root branch."); return; } pullDetail(); Schema schema = stack.getSchema(); Schema.Group parent = null; if (branch.group != null) { Schema.Group group = schema.obtainGroup(branch.locatorID); parent = group.parent; schema.deleteGroup(group); } if (branch.assignment != null) { Schema.Assignment assn = schema.obtainAssignment(branch.locatorID); parent = assn.parent; schema.deleteAssignment(assn); } if (branch.assay != null) { Schema.Assay assay = schema.obtainAssay(branch.locatorID); schema.deleteAssay(assay); } stack.changeSchema(schema); rebuildTree(); if (parent != null) setCurrentBranch(locateBranch(schema.locatorID(parent))); else detail.clearContent(); } public void actionEditUndo() { if (!stack.canUndo()) { Util.informMessage("Undo", "Nothing to undo."); return; } stack.performUndo(); rebuildTree(); clearSelection(); } public void actionEditRedo() { if (!stack.canRedo()) { Util.informMessage("Redo", "Nothing to redo."); return; } stack.performRedo(); rebuildTree(); clearSelection(); } public void actionEditMove(int dir) { TreeItem<Branch> item = currentBranch(); Branch branch = item == null ? null : item.getValue(); if (item == treeRoot || branch == null || (branch.group == null && branch.assignment == null && branch.assay == null)) return; pullDetail(); Schema schema = stack.getSchema(); String newLocator = ""; if (branch.group != null) { Schema.Group group = schema.obtainGroup(branch.locatorID); schema.moveGroup(group, dir); newLocator = schema.locatorID(group); } else if (branch.assignment != null) { Schema.Assignment assn = schema.obtainAssignment(branch.locatorID); schema.moveAssignment(assn, dir); newLocator = schema.locatorID(assn); } else if (branch.assay != null) { Schema.Assay assay = schema.obtainAssay(branch.locatorID); schema.moveAssay(assay, dir); newLocator = schema.locatorID(assay); } stack.changeSchema(schema); rebuildTree(); setCurrentBranch(locateBranch(newLocator)); } public void actionValueSort() { TreeItem<Branch> item = currentBranch(); Branch branch = item == null ? null : item.getValue(); if (branch == null || branch.assignment == null) { Util.informMessage("Sort Values", "Select an assignment with values to sort."); return; } pullDetail(); Schema schema = stack.getSchema(); Schema.Assignment assn = schema.obtainAssignment(branch.locatorID); assn.values.sort((v1, v2) -> v1.name.compareToIgnoreCase(v2.name)); if (schema.equals(stack.peekSchema())) { Util.informMessage("Sort", "Values were already sorted."); return; } stack.changeSchema(schema); rebuildTree(); setCurrentBranch(locateBranch(branch.locatorID)); } public void actionValueDuplicates() { TreeItem<Branch> item = currentBranch(); Branch branch = item == null ? null : item.getValue(); if (branch == null || branch.assignment == null) { Util.informMessage("Remove Duplicate Values", "Select an assignment with values to de-duplicate."); return; } pullDetail(); Schema schema = stack.getSchema(); Schema.Assignment assn = schema.obtainAssignment(branch.locatorID); int snippy = 0; for (int i = 1; i < assn.values.size(); i++) { Schema.Value v = assn.values.get(i); for (int j = 0; j < i; j++) if (v.uri.equals(assn.values.get(j).uri)) { snippy++; assn.values.remove(i); i--; break; } } if (snippy == 0) { Util.informMessage("Remove Duplicate Values", "No duplicate URI values were found."); return; } stack.changeSchema(schema); rebuildTree(); setCurrentBranch(locateBranch(branch.locatorID)); Util.informMessage("Remove Duplicate Values", "Number of values removed because of duplicated URI values: " + snippy); } private void actionValueCleanup() { TreeItem<Branch> item = currentBranch(); Branch branch = item == null ? null : item.getValue(); if (branch == null || branch.assignment == null) { Util.informMessage("Cleanup Values", "Select an assignment in order to remove non-URI values."); return; } pullDetail(); Schema schema = stack.getSchema(); Schema.Assignment assn = schema.obtainAssignment(branch.locatorID); int snippy = 0; for (int n = assn.values.size() - 1; n >= 0; n--) { Schema.Value v = assn.values.get(n); if (!v.uri.startsWith("http://") && !v.uri.startsWith("https://")) { snippy++; assn.values.remove(n); } } if (snippy == 0) { Util.informMessage("Cleanup Values", "No values were removed."); return; } stack.changeSchema(schema); rebuildTree(); setCurrentBranch(locateBranch(branch.locatorID)); Util.informMessage("Cleanup Values", "Number of values removed on account of not having a proper URI: " + snippy); } private void actionViewToggleSummary() { pullDetail(); detail.setSummaryView(!detail.getSummaryView()); menuViewSummary.setSelected(detail.getSummaryView()); } public void actionViewTemplate() { treeTemplate.setExpanded(true); treeAssays.setExpanded(false); treeView.getSelectionModel().select(treeTemplate); treeView.getFocusModel().focus(treeView.getSelectionModel().getSelectedIndex()); Platform.runLater(() -> treeView.getFocusModel().focus(treeView.getSelectionModel().getSelectedIndex())); } public void actionViewAssays() { treeTemplate.setExpanded(false); treeAssays.setExpanded(true); treeView.getSelectionModel().select(treeAssays); Platform.runLater(() -> treeView.getFocusModel().focus(treeView.getSelectionModel().getSelectedIndex())); } }