diff --git a/build.gradle b/build.gradle index 9f1370ae..146f361b 100644 --- a/build.gradle +++ b/build.gradle @@ -3,7 +3,7 @@ import java.util.regex.Matcher plugins { id 'application' id 'org.openjfx.javafxplugin' version '0.0.14' - id 'com.diffplug.spotless' version '6.23.3' + id 'com.diffplug.spotless' version '6.25.0' } group 'de.unijena.cheminf' diff --git a/src/main/java/de/unijena/cheminf/mortar/configuration/Configuration.java b/src/main/java/de/unijena/cheminf/mortar/configuration/Configuration.java new file mode 100644 index 00000000..494994e1 --- /dev/null +++ b/src/main/java/de/unijena/cheminf/mortar/configuration/Configuration.java @@ -0,0 +1,96 @@ +/* + * MORTAR - MOlecule fRagmenTAtion fRamework + * Copyright (C) 2024 Felix Baensch, Jonas Schaub (felix.baensch@w-hs.de, jonas.schaub@uni-jena.de) + * + * Source code is available at + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package de.unijena.cheminf.mortar.configuration; + +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.util.MissingResourceException; +import java.util.Properties; + +/** + * Thread-safe singleton class for reading configuration properties file, e.g. for paths to resource folders. + * + * @author Jonas Schaub + * @version 1.0.0.0 + */ +public class Configuration implements IConfiguration { + /** + * Single instance of this class. + */ + private static Configuration instance; + // + /** + * Properties imported and cached from the configuration properties file. + */ + private final Properties properties; + // + /** + * Path to the configuration properties file to read. + */ + public static final String PROPERTIES_FILE_PATH = "de/unijena/cheminf/mortar/configuration/MORTAR_configuration.properties"; + // + /** + * Private constructor that creates the single instance. + * + * @throws IOException if the properties file is not found or if an error occurs when reading from the input stream + */ + private Configuration() throws IOException { + this.properties = new Properties(); + try (InputStream tmpInputStream = this.getClass().getClassLoader().getResourceAsStream(Configuration.PROPERTIES_FILE_PATH)) { + if (tmpInputStream != null) { + //throws IOException if an error occurs when reading from the input stream + this.properties.load(tmpInputStream); + } else { + //extends IOException + throw new FileNotFoundException(String.format("property file '%s' not found in the classpath", Configuration.PROPERTIES_FILE_PATH)); + } + } + } + // + /** + * Returns the single instance of this class. + * + * @return instance of this class + * @throws IOException if the properties file is not found or if an error occurs when reading from the input stream + */ + public static synchronized Configuration getInstance() throws IOException { + if (Configuration.instance == null) { + Configuration.instance = new Configuration(); + } + return Configuration.instance; + } + // + @Override + public String getProperty(String aKey) throws MissingResourceException { + String tmpProperty = this.properties.getProperty(aKey).trim(); + if (tmpProperty == null) { + throw new MissingResourceException(String.format("Property '%s' not found", aKey), Configuration.class.getName(), aKey); + } else { + return tmpProperty; + } + } +} diff --git a/src/main/java/de/unijena/cheminf/mortar/configuration/IConfiguration.java b/src/main/java/de/unijena/cheminf/mortar/configuration/IConfiguration.java new file mode 100644 index 00000000..6c009e6b --- /dev/null +++ b/src/main/java/de/unijena/cheminf/mortar/configuration/IConfiguration.java @@ -0,0 +1,45 @@ +/* + * MORTAR - MOlecule fRagmenTAtion fRamework + * Copyright (C) 2024 Felix Baensch, Jonas Schaub (felix.baensch@w-hs.de, jonas.schaub@uni-jena.de) + * + * Source code is available at + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package de.unijena.cheminf.mortar.configuration; + +import java.util.MissingResourceException; + +/** + * Interface for classes reading configuration properties files, e.g. for paths to resource folders. + * + * @author Jonas Schaub + * @version 1.0.0.0 + */ +public interface IConfiguration { + /** + * Returns the string value associated with the key in the configurations properties file. + * + * @param aKey key defined in the configuration properties file + * @return property value associated with the given key + * @throws MissingResourceException if no associated value could be found for the given key + */ + public String getProperty(String aKey) throws MissingResourceException; +} diff --git a/src/main/java/de/unijena/cheminf/mortar/controller/AboutViewController.java b/src/main/java/de/unijena/cheminf/mortar/controller/AboutViewController.java index 712e0c96..411a9507 100644 --- a/src/main/java/de/unijena/cheminf/mortar/controller/AboutViewController.java +++ b/src/main/java/de/unijena/cheminf/mortar/controller/AboutViewController.java @@ -25,12 +25,13 @@ package de.unijena.cheminf.mortar.controller; +import de.unijena.cheminf.mortar.configuration.IConfiguration; import de.unijena.cheminf.mortar.gui.util.ExternalTool; import de.unijena.cheminf.mortar.gui.util.GuiDefinitions; import de.unijena.cheminf.mortar.gui.util.GuiUtil; import de.unijena.cheminf.mortar.gui.views.AboutView; import de.unijena.cheminf.mortar.message.Message; -import de.unijena.cheminf.mortar.model.util.BasicDefinitions; +import de.unijena.cheminf.mortar.model.util.FileUtil; import de.unijena.cheminf.mortar.model.util.LogUtil; import javafx.application.Platform; @@ -39,7 +40,6 @@ import javafx.scene.Scene; import javafx.scene.control.Alert; import javafx.scene.control.Hyperlink; -import javafx.scene.control.Tooltip; import javafx.scene.image.Image; import javafx.stage.Modality; import javafx.stage.Stage; @@ -59,42 +59,39 @@ import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; -import java.io.InputStream; import java.net.URI; import java.net.URISyntaxException; -import java.util.Objects; import java.util.logging.Level; import java.util.logging.Logger; /** - * Controller class for AboutView + * Controller class for AboutView. * * @author Felix Baensch * @version 1.0.0.0 */ public class AboutViewController { - - // + // /** - * Main Stage / parent Stage + * Main Stage / parent Stage. */ - private Stage mainStage; + private final Stage mainStage; /** - * View to controll + * View to control. */ private AboutView aboutView; /** - * Stage to show AboutView + * Stage to show AboutView. */ private Stage aboutViewStage; /** - * path to xml file which contains information about external tools + * ObservableList to show properties of ExternalTools. */ - private String toolsXmlFileName = "de/unijena/cheminf/mortar/descriptions/tools_description.xml"; + private final ObservableList toolObservableList; /** - * ObservableList to show properties of ExternalTools + * Configuration class to read resource file paths from. */ - private ObservableList toolObservableList; + private final IConfiguration configuration; // // // @@ -105,23 +102,25 @@ public class AboutViewController { // // /** - * Constructor + * Constructor, shows about view on the given stage. * * @param aStage Stage + * @param aConfiguration configuration class reading from properties file */ - public AboutViewController(Stage aStage){ + public AboutViewController(Stage aStage, IConfiguration aConfiguration) { this.mainStage = aStage; this.toolObservableList = FXCollections.observableArrayList(); + this.configuration = aConfiguration; this.showAboutView(); } // // /** - * Sets stage, scene and gui properties and shows view + * Sets stage, scene, and gui properties and shows view. */ - private void showAboutView(){ - if(this.aboutView == null){ - this.aboutView = new AboutView(); + private void showAboutView() { + if (this.aboutView == null) { + this.aboutView = new AboutView(this.configuration); } this.aboutViewStage = new Stage(); Scene tmpScene = new Scene(this.aboutView, GuiDefinitions.GUI_MAIN_VIEW_WIDTH_VALUE, GuiDefinitions.GUI_MAIN_VIEW_HEIGHT_VALUE); @@ -131,92 +130,63 @@ private void showAboutView(){ this.aboutViewStage.setTitle(Message.get("AboutView.title.text")); this.aboutViewStage.setMinHeight(GuiDefinitions.GUI_MAIN_VIEW_HEIGHT_VALUE); this.aboutViewStage.setMinWidth(GuiDefinitions.GUI_MAIN_VIEW_WIDTH_VALUE); - InputStream tmpImageInputStream = AboutViewController.class.getResourceAsStream("/de/unijena/cheminf/mortar/images/Mortar_Logo_Icon1.png"); - this.aboutViewStage.getIcons().add(new Image(tmpImageInputStream)); + String tmpIconURL = this.getClass().getClassLoader().getResource( + this.configuration.getProperty("mortar.imagesFolder") + this.configuration.getProperty("mortar.logo.icon.name")).toExternalForm(); + this.aboutViewStage.getIcons().add(new Image(tmpIconURL)); Platform.runLater(()->{ this.addListeners(); - this.getExternalToolInfosFromXml(); + this.getExternalToolInfoFromXml(); this.aboutView.getTableView().setItems(this.toolObservableList); }); this.aboutViewStage.showAndWait(); } // /** - * Adds event handlers and listeners + * Adds event handlers and listeners. */ - private void addListeners(){ + private void addListeners() { //close button - this.aboutView.getCloseButton().setOnAction(actionEvent -> { - this.aboutViewStage.close(); - }); + this.aboutView.getCloseButton().setOnAction(actionEvent -> this.aboutViewStage.close()); //log file button - this.aboutView.getLogFileButton().setOnAction(actionEvent -> { - this.openFilePathInExplorer(LogUtil.getLogFileDirectoryPath()); - }); + this.aboutView.getLogFileButton().setOnAction(actionEvent -> FileUtil.openFilePathInExplorer(LogUtil.getLogFileDirectoryPath())); //gitHUb button - this.aboutView.getGitHubButton().setOnAction(actionEvent -> { - this.openGitHubRepositoryInDefaultBrowser(); - }); + this.aboutView.getGitHubButton().setOnAction(actionEvent -> this.openGitHubRepositoryInDefaultBrowser()); //tutorial button - this.aboutView.getTutorialButton().setOnAction(actionEvent -> { - this.openTutorialInDefaultPdfViewer(); - }); + this.aboutView.getTutorialButton().setOnAction(actionEvent -> this.openTutorialInDefaultPdfViewer()); } // /** - * Opens given path in OS depending explorer equivalent + * Opens GitHub repository website in system default browser. Developers note: Does not really fit in FileUtil. * - * @param aPath path to open + * @throws SecurityException if URL could not be opened */ - private void openFilePathInExplorer(String aPath){ - if (Objects.isNull(aPath) || aPath.isEmpty() || aPath.isBlank()) - throw new IllegalArgumentException("Given file path is null or empty."); - String tmpOS = System.getProperty("os.name").toUpperCase(); + private void openGitHubRepositoryInDefaultBrowser() throws SecurityException { try{ - if (tmpOS.contains("WIN")) - Runtime.getRuntime().exec("explorer /open," + aPath); - else if (tmpOS.contains("MAC")) - Runtime.getRuntime().exec("open -R " + aPath); - else if (tmpOS.contains("NUX") || tmpOS.contains("NIX") || tmpOS.contains("AIX")) - Runtime.getRuntime().exec("gio open " + aPath); - else - throw new SecurityException("OS name " + tmpOS + " unknown."); - } catch (IOException anException) { - LOGGER.log(Level.SEVERE, anException.toString(), anException); - throw new SecurityException("Could not open directory path"); - } - } - // - /** - * Opens GitHub repository website in system default browser - * - * Note: Does not really fit in FileUtil - */ - private void openGitHubRepositoryInDefaultBrowser(){ - try{ - Desktop.getDesktop().browse(new URI(BasicDefinitions.GITHUB_REPOSITORY_URL)); + Desktop.getDesktop().browse(new URI(this.configuration.getProperty("mortar.github.repository.url"))); } catch (IOException | URISyntaxException anException) { - LOGGER.log(Level.SEVERE, anException.toString(), anException); - throw new SecurityException("Could not open directory path"); + AboutViewController.LOGGER.log(Level.SEVERE, anException.toString(), anException); + throw new SecurityException("Could not open repository URL"); } } // /** - * Opens the MORTAR tutorial in system default browser + * Opens the MORTAR tutorial in system default browser. + * + * @throws SecurityException if URL could not be opened */ private void openTutorialInDefaultPdfViewer() { //Note: Does not work when started from IDE, only in built version started from JAR try { - Desktop.getDesktop().open(new File(BasicDefinitions.MORTAR_TUTORIAL_RELATIVE_FILE_PATH)); + Desktop.getDesktop().open(new File(this.configuration.getProperty("mortar.tutorial.relativeFilePath"))); } catch (IOException | IllegalArgumentException anException) { - LOGGER.log(Level.SEVERE, anException.toString(), anException); + AboutViewController.LOGGER.log(Level.SEVERE, anException.toString(), anException); Hyperlink tmpLinkToTutorial = new Hyperlink(Message.get("AboutView.tutorialButton.alert.hyperlink.text")); - tmpLinkToTutorial.setTooltip(new Tooltip(BasicDefinitions.MORTAR_TUTORIAL_URL)); + tmpLinkToTutorial.setTooltip(GuiUtil.createTooltip(this.configuration.getProperty("mortar.tutorial.url"))); tmpLinkToTutorial.setOnAction(event -> { try { - Desktop.getDesktop().browse(new URI(BasicDefinitions.MORTAR_TUTORIAL_URL)); + Desktop.getDesktop().browse(new URI(this.configuration.getProperty("mortar.tutorial.url"))); } catch (IOException | URISyntaxException e) { - LOGGER.log(Level.SEVERE, anException.toString(), anException); + AboutViewController.LOGGER.log(Level.SEVERE, anException.toString(), anException); throw new SecurityException("Could not open URI"); } }); @@ -226,26 +196,34 @@ private void openTutorialInDefaultPdfViewer() { } // /** - * Reads xml file (tools_description.xml in resources) which contains information about the used external tools + * Reads xml file (tools_description.xml in resources) which contains information about the used external tools. */ - private void getExternalToolInfosFromXml(){ - DocumentBuilderFactory tmpDocumentBuilderFactory = DocumentBuilderFactory.newInstance(); - try{ + private void getExternalToolInfoFromXml() { + try { + DocumentBuilderFactory tmpDocumentBuilderFactory = DocumentBuilderFactory.newInstance(); // optional, but recommended // process XML securely, avoid attacks like XML External Entities (XXE) tmpDocumentBuilderFactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true); + tmpDocumentBuilderFactory.setExpandEntityReferences(false); + tmpDocumentBuilderFactory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); + tmpDocumentBuilderFactory.setFeature("http://xml.org/sax/features/external-general-entities", false); + tmpDocumentBuilderFactory.setFeature("http://xml.org/sax/features/external-parameter-entities", false); + tmpDocumentBuilderFactory.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, ""); + tmpDocumentBuilderFactory.setAttribute(XMLConstants.ACCESS_EXTERNAL_SCHEMA, ""); DocumentBuilder tmpDocBuilder = tmpDocumentBuilderFactory.newDocumentBuilder(); - Document tmpDoc = tmpDocBuilder.parse(getClass().getClassLoader().getResourceAsStream(this.toolsXmlFileName)); - if(tmpDoc == null){ - throw new FileNotFoundException("File not found " + this.toolsXmlFileName); + Document tmpDoc = tmpDocBuilder.parse(this.getClass().getClassLoader().getResource( + this.configuration.getProperty("mortar.descriptionsFolder") + + this.configuration.getProperty("mortar.tools.description.name")).toExternalForm()); + if (tmpDoc == null) { + throw new FileNotFoundException("Tools description XML file not found."); } // optional, but recommended // http://stackoverflow.com/questions/13786607/normalization-in-dom-parsing-with-java-how-does-it-work tmpDoc.getDocumentElement().normalize(); NodeList tmpList = tmpDoc.getElementsByTagName("externalTool"); - for(int i = 0; i < tmpList.getLength(); i++){ + for (int i = 0; i < tmpList.getLength(); i++) { Node tmpNode = tmpList.item(i); - if(tmpNode.getNodeType() == Node.ELEMENT_NODE){ + if (tmpNode.getNodeType() == Node.ELEMENT_NODE) { Element tmpElem = (Element) tmpNode; String tmpName = tmpElem.getElementsByTagName("name").item(0).getTextContent(); String tmpVersion = tmpElem.getElementsByTagName("version").item(0).getTextContent(); @@ -255,8 +233,13 @@ private void getExternalToolInfosFromXml(){ this.toolObservableList.add(tmpTool); } } - } catch (ParserConfigurationException | IOException | SAXException anException) { - LOGGER.log(Level.SEVERE, anException.toString(), anException); + } catch (ParserConfigurationException | IOException | SAXException | NullPointerException anException) { + AboutViewController.LOGGER.log(Level.SEVERE, anException.toString(), anException); + ExternalTool tmpTool = new ExternalTool(Message.get("AboutViewController.Error.XMLParsing.Name"), + Message.get("AboutViewController.Error.XMLParsing.Version"), + Message.get("AboutViewController.Error.XMLParsing.Author"), + Message.get("AboutViewController.Error.XMLParsing.License")); + this.toolObservableList.add(tmpTool); } } // diff --git a/src/main/java/de/unijena/cheminf/mortar/controller/FragmentationSettingsViewController.java b/src/main/java/de/unijena/cheminf/mortar/controller/FragmentationSettingsViewController.java index ca587b24..36d081fd 100644 --- a/src/main/java/de/unijena/cheminf/mortar/controller/FragmentationSettingsViewController.java +++ b/src/main/java/de/unijena/cheminf/mortar/controller/FragmentationSettingsViewController.java @@ -25,6 +25,7 @@ package de.unijena.cheminf.mortar.controller; +import de.unijena.cheminf.mortar.configuration.IConfiguration; import de.unijena.cheminf.mortar.gui.util.GuiDefinitions; import de.unijena.cheminf.mortar.gui.views.SettingsView; import de.unijena.cheminf.mortar.message.Message; @@ -38,72 +39,75 @@ import javafx.stage.Modality; import javafx.stage.Stage; -import java.io.InputStream; import java.util.HashMap; import java.util.Map; import java.util.logging.Logger; /** - * SettingsViewController - * controls {@link SettingsView} for fragmentation settings + * SettingsViewController controls {@link SettingsView} for fragmentation settings. * * @author Felix Baensch * @version 1.0.0.0 */ public class FragmentationSettingsViewController { - // /** - * Main stage object of the application + * Main stage object of the application. */ private final Stage mainStage; /** - * Stage for the SettingsView + * Stage for the SettingsView. */ private Stage fragmentationSettingsViewStage; /** - * SettingsView + * SettingsView. */ private SettingsView settingsView; /** - * Map of maps to hold initial settings properties for each algorithm + * Map of maps to hold initial settings properties for each algorithm. + */ + private final Map> recentProperties; + /** + * Array of {@link IMoleculeFragmenter} objects. */ - private Map> recentProperties; + private final IMoleculeFragmenter[] fragmenters; /** - * Array of {@link IMoleculeFragmenter} objects + * Display name of the selected fragmentation algorithm. */ - private IMoleculeFragmenter[] fragmenters; + private final String selectedFragmenterDisplayName; /** - * Name of the selected fragmentation algorithm + * Configuration class to read resource file paths from. */ - private String selectedFragmenterName; + private final IConfiguration configuration; /** - * Logger + * Logger. */ private static final Logger LOGGER = Logger.getLogger(FragmentationSettingsViewController.class.getName()); // - /** - * Constructor + * Constructor. * * @param aStage Stage * @param anArrayOfFragmenters IMoleculeFragmenter[] - * @param aSelectedFragmenterAlgorithmName String + * @param aSelectedFragmenterAlgorithmDisplayName display name of selected fragmenter (display name, not internal name!) + * @param aConfiguration configuration instance to read resource file paths from */ - public FragmentationSettingsViewController(Stage aStage, IMoleculeFragmenter[] anArrayOfFragmenters, String aSelectedFragmenterAlgorithmName){ + public FragmentationSettingsViewController(Stage aStage, IMoleculeFragmenter[] anArrayOfFragmenters, String aSelectedFragmenterAlgorithmDisplayName, IConfiguration aConfiguration) { this.mainStage = aStage; this.recentProperties = new HashMap<>(CollectionUtil.calculateInitialHashCollectionCapacity(anArrayOfFragmenters.length)); this.fragmenters = anArrayOfFragmenters; - this.selectedFragmenterName = aSelectedFragmenterAlgorithmName; + this.selectedFragmenterDisplayName = aSelectedFragmenterAlgorithmDisplayName; + this.configuration = aConfiguration; this.openFragmentationSettingsView(); } // /** - * Initialises and opens a settings view for fragmentationSettings + * Initialises and opens a settings view for fragmentationSettings. */ - private void openFragmentationSettingsView(){ - if(this.settingsView == null) + private void openFragmentationSettingsView() { + if (this.settingsView == null) { this.settingsView = new SettingsView(); + } this.fragmentationSettingsViewStage = new Stage(); Scene tmpScene = new Scene(this.settingsView, GuiDefinitions.GUI_MAIN_VIEW_WIDTH_VALUE, GuiDefinitions.GUI_MAIN_VIEW_HEIGHT_VALUE); this.fragmentationSettingsViewStage.setScene(tmpScene); @@ -113,65 +117,66 @@ private void openFragmentationSettingsView(){ this.fragmentationSettingsViewStage.setTitle(Message.get("FragmentationSettingsView.title.text")); this.fragmentationSettingsViewStage.setMinHeight(GuiDefinitions.GUI_MAIN_VIEW_HEIGHT_VALUE); this.fragmentationSettingsViewStage.setMinWidth(GuiDefinitions.GUI_MAIN_VIEW_WIDTH_VALUE); - InputStream tmpImageInputStream = FragmentationSettingsViewController.class.getResourceAsStream("/de/unijena/cheminf/mortar/images/Mortar_Logo_Icon1.png"); - this.fragmentationSettingsViewStage.getIcons().add(new Image(tmpImageInputStream)); + String tmpIconURL = this.getClass().getClassLoader().getResource( + this.configuration.getProperty("mortar.imagesFolder") + + this.configuration.getProperty("mortar.logo.icon.name")).toExternalForm(); + this.fragmentationSettingsViewStage.getIcons().add(new Image(tmpIconURL)); // this.addListener(); for (IMoleculeFragmenter tmpFragmenter : this.fragmenters) { HashMap tmpRecentProperties = new HashMap<>(CollectionUtil.calculateInitialHashCollectionCapacity(tmpFragmenter.settingsProperties().size())); - this.recentProperties.put(tmpFragmenter.getFragmentationAlgorithmName(), tmpRecentProperties); - Tab tmpTab = this.settingsView.addTab(this.fragmentationSettingsViewStage, - tmpFragmenter.getFragmentationAlgorithmName(), tmpFragmenter.settingsProperties(), + this.recentProperties.put(tmpFragmenter.getFragmentationAlgorithmDisplayName(), tmpRecentProperties); + Tab tmpTab = this.settingsView.addTab( + tmpFragmenter.getFragmentationAlgorithmDisplayName(), tmpFragmenter.settingsProperties(), + tmpFragmenter.getSettingNameToDisplayNameMap(), tmpFragmenter.getSettingNameToTooltipTextMap(), tmpRecentProperties); - if(tmpFragmenter.getFragmentationAlgorithmName().equals(this.selectedFragmenterName)){ + if (tmpFragmenter.getFragmentationAlgorithmDisplayName().equals(this.selectedFragmenterDisplayName)) { this.settingsView.getSelectionModel().select(tmpTab); } } } // /** - * Adds listeners + * Adds listeners. */ - private void addListener(){ + private void addListener() { //fragmentationSettingsViewStage close request this.fragmentationSettingsViewStage.setOnCloseRequest(event -> { - for(int i = 0; i < this.fragmenters.length; i++){ - if(this.fragmenters[i].getFragmentationAlgorithmName().equals(this.settingsView.getTabPane().getSelectionModel().getSelectedItem().getId())){ - this.setRecentProperties(this.fragmenters[i], this.recentProperties.get(this.settingsView.getTabPane().getSelectionModel().getSelectedItem().getId())); + for (IMoleculeFragmenter fragmenter : this.fragmenters) { + if (fragmenter.getFragmentationAlgorithmDisplayName().equals(this.settingsView.getTabPane().getSelectionModel().getSelectedItem().getId())) { + this.setRecentProperties(fragmenter, this.recentProperties.get(this.settingsView.getTabPane().getSelectionModel().getSelectedItem().getId())); } } this.fragmentationSettingsViewStage.close(); }); //applyButton - this.settingsView.getApplyButton().setOnAction(event -> { - this.fragmentationSettingsViewStage.close(); - }); + this.settingsView.getApplyButton().setOnAction(event -> this.fragmentationSettingsViewStage.close()); //cancelButton this.settingsView.getCancelButton().setOnAction(event -> { - for(int i = 0; i < this.fragmenters.length; i++){ - this.setRecentProperties(this.fragmenters[i], this.recentProperties.get(this.fragmenters[i].getFragmentationAlgorithmName())); + for (IMoleculeFragmenter fragmenter : this.fragmenters) { + this.setRecentProperties(fragmenter, this.recentProperties.get(fragmenter.getFragmentationAlgorithmDisplayName())); } this.fragmentationSettingsViewStage.close(); }); //defaultButton this.settingsView.getDefaultButton().setOnAction(event -> { - for(int i = 0; i < this.fragmenters.length; i++){ - if(this.fragmenters[i].getFragmentationAlgorithmName().equals(this.settingsView.getTabPane().getSelectionModel().getSelectedItem().getId())){ - this.fragmenters[i].restoreDefaultSettings(); + for (IMoleculeFragmenter fragmenter : this.fragmenters) { + if (fragmenter.getFragmentationAlgorithmDisplayName().equals(this.settingsView.getTabPane().getSelectionModel().getSelectedItem().getId())) { + fragmenter.restoreDefaultSettings(); } } }); } - + // /** - * Sets the properties of the given fragmenter to the values of the 'recentPropertiesMap' + * Sets the properties of the given fragmenter to the values of the 'recentPropertiesMap'. * * @param aFragmenter IMoleculeFragmenter * @param aRecentPropertiesMap Map */ - private void setRecentProperties(IMoleculeFragmenter aFragmenter, Map aRecentPropertiesMap){ + private void setRecentProperties(IMoleculeFragmenter aFragmenter, Map aRecentPropertiesMap){ for (Property tmpProperty : aFragmenter.settingsProperties()) { - if(aRecentPropertiesMap.containsKey(tmpProperty.getName())){ + if (aRecentPropertiesMap.containsKey(tmpProperty.getName())) { tmpProperty.setValue(aRecentPropertiesMap.get(tmpProperty.getName())); } } diff --git a/src/main/java/de/unijena/cheminf/mortar/controller/HistogramViewController.java b/src/main/java/de/unijena/cheminf/mortar/controller/HistogramViewController.java index 5ef7dc55..f5922e34 100644 --- a/src/main/java/de/unijena/cheminf/mortar/controller/HistogramViewController.java +++ b/src/main/java/de/unijena/cheminf/mortar/controller/HistogramViewController.java @@ -25,11 +25,7 @@ package de.unijena.cheminf.mortar.controller; -/** - * TODO: - * - add default button to histogram view - */ - +import de.unijena.cheminf.mortar.configuration.IConfiguration; import de.unijena.cheminf.mortar.gui.util.GuiDefinitions; import de.unijena.cheminf.mortar.gui.util.GuiUtil; import de.unijena.cheminf.mortar.gui.views.HistogramView; @@ -38,7 +34,8 @@ import de.unijena.cheminf.mortar.model.depict.DepictionUtil; import de.unijena.cheminf.mortar.model.util.ChemUtil; import de.unijena.cheminf.mortar.model.util.CollectionUtil; -import de.unijena.cheminf.mortar.model.util.SimpleEnumConstantNameProperty; +import de.unijena.cheminf.mortar.model.util.IDisplayEnum; +import de.unijena.cheminf.mortar.model.util.SimpleIDisplayEnumConstantProperty; import javafx.beans.binding.Bindings; import javafx.beans.property.Property; @@ -76,7 +73,6 @@ import org.openscience.cdk.exception.CDKException; import org.openscience.cdk.interfaces.IAtomContainer; -import java.io.InputStream; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -95,74 +91,91 @@ public class HistogramViewController implements IViewToolController { /** * Enum for the available bar spacing width options. */ - public static enum BarWidthOption { + public static enum BarWidthOption implements IDisplayEnum { /** - * Small bar width + * Small bar width. */ - SMALL(Message.get("HistogramView.barWidths.small")), + SMALL(Message.get("HistogramView.barWidths.small.displayName"), Message.get("HistogramView.barWidths.small.tooltip")), /** - * Medium bar width + * Medium bar width. */ - MEDIUM(Message.get("HistogramView.barWidths.medium")), + MEDIUM(Message.get("HistogramView.barWidths.medium.displayName"), Message.get("HistogramView.barWidths.medium.tooltip")), /** - * Large bar width + * Large bar width. */ - LARGE(Message.get("HistogramView.barWidths.large")); + LARGE(Message.get("HistogramView.barWidths.large.displayName"), Message.get("HistogramView.barWidths.large.tooltip")); /** * A name for the respective constant that is meant for display, i.e. taken from the Message file. */ private final String displayName; /** - * Constructor that sets the display name. - * - * @param aDisplayName a name for the respective constant that is meant for display, i.e. taken from the Message file. + * Language-specific tooltip text for display in GUI. */ - BarWidthOption(String aDisplayName) { - this.displayName = aDisplayName; - } + private final String tooltip; /** - * Returns a name for the respective constant that is meant for display, i.e. taken from the Message file. + * Constructor setting the display name and tooltip. * - * @return display name of the constant + * @param aDisplayName display name + * @param aTooltip tooltip text */ + private BarWidthOption(String aDisplayName, String aTooltip) { + this.displayName = aDisplayName; + this.tooltip = aTooltip; + } + // + @Override public String getDisplayName() { return this.displayName; } + // + @Override + public String getTooltipText() { + return this.tooltip; + } } // /** * Enum for the available frequency options, i.e. which frequency of the fragments to display, the absolute frequency * or the molecule frequency. */ - public static enum FrequencyOption { + public static enum FrequencyOption implements IDisplayEnum { /** * Display the molecule frequencies of the fragments in the histogram. */ - MOLECULE_FREQUENCY(Message.get("HistogramView.chooseDataComboBoxMoleculeFrequency.text")), + MOLECULE_FREQUENCY(Message.get("HistogramView.frequencyOption.moleculeFrequency.displayName"), + Message.get("HistogramView.frequencyOption.moleculeFrequency.tooltip")), /** * Display the absolute fragment frequencies in the histogram. */ - ABSOLUTE_FREQUENCY(Message.get("HistogramView.chooseDataComboBoxFragmentFrequency.text")); + ABSOLUTE_FREQUENCY(Message.get("HistogramView.frequencyOption.absoluteFrequency.displayName"), + Message.get("HistogramView.frequencyOption.absoluteFrequency.tooltip")); /** * A name for the respective constant that is meant for display, i.e. taken from the Message file. */ private final String displayName; /** - * Constructor that sets the display name. - * - * @param aDisplayName a name for the respective constant that is meant for display, i.e. taken from the Message file. + * Language-specific tooltip text for display in GUI. */ - FrequencyOption(String aDisplayName) { - this.displayName = aDisplayName; - } + private final String tooltip; /** - * Returns a name for the respective constant that is meant for display, i.e. taken from the Message file. + * Constructor setting the display name and tooltip. * - * @return display name of the constant + * @param aDisplayName display name + * @param aTooltip tooltip text */ + private FrequencyOption(String aDisplayName, String aTooltip) { + this.displayName = aDisplayName; + this.tooltip = aTooltip; + } + @Override public String getDisplayName() { return this.displayName; } + // + @Override + public String getTooltipText() { + return this.tooltip; + } } // // @@ -178,11 +191,11 @@ public String getDisplayName() { /** * Default of bar spacing width. */ - public static final BarWidthOption DEFAULT_BAR_WIDTH = BarWidthOption.LARGE; + public static final HistogramViewController.BarWidthOption DEFAULT_BAR_WIDTH = HistogramViewController.BarWidthOption.LARGE; /** * Default fragment frequency to display. */ - public static final FrequencyOption DEFAULT_DISPLAY_FREQUENCY = FrequencyOption.ABSOLUTE_FREQUENCY; + public static final HistogramViewController.FrequencyOption DEFAULT_DISPLAY_FREQUENCY = HistogramViewController.FrequencyOption.ABSOLUTE_FREQUENCY; /** * Default for whether bar labels (the fragment frequencies) should be displayed. */ @@ -212,75 +225,79 @@ public String getDisplayName() { */ public static final String HISTOGRAM_BARS_SELECTED_COLOR_HEX_VALUE = "#00008b"; //dark blue /** - * Value for the width of the image corresponding to the structure of the fragments + * Value for the width of the image corresponding to the structure of the fragments. */ public static final double STRUCTURE_DEPICTION_IMAGE_INITIAL_WIDTH = 250.0; /** - * Value for the height of the image corresponding to the structure of the fragments + * Value for the height of the image corresponding to the structure of the fragments. */ public static final double STRUCTURE_DEPICTION_IMAGE_INITIAL_HEIGHT = 150.0; /** - * Image zoom factor value + * Image zoom factor value. */ public static final double STRUCTURE_DEPICTION_IMAGE_INITIAL_ZOOM_FACTOR = 3.0; /** - * Value for the small bar gap + * Value for the small bar gap. */ public static final double GUI_HISTOGRAM_SMALL_BAR_GAP_CONST = 3.5416; /** - * Value fpr the medium bar gap + * Value fpr the medium bar gap. */ public static final double GUI_HISTOGRAM_MEDIUM_BAR_GAP_CONST = 5.0; /** - * Value for the large bar gap + * Value for the large bar gap. */ public static final double GUI_HISTOGRAM_LARGE_BAR_GAP_CONST = 6.5384; /** - * Value for the small bar width + * Value for the small bar width. */ public static final double GUI_HISTOGRAM_SMALL_BAR_WIDTH = 15.0; /** - * Value for the medium bar width + * Value for the medium bar width. */ public static final double GUI_HISTOGRAM_MEDIUM_BAR_WIDTH = 20.0; /*** - * Value for the large bar width + * Value for the large bar width. */ public static final double GUI_HISTOGRAM_LARGE_BAR_WIDTH = 30.0; /** - * Value for the small histogram growth factor + * Value for the small histogram growth factor. */ public static final double GUI_HISTOGRAM_SMALL_HISTOGRAM_HEIGHT_VALUE = 27.0; /** - * Value for the medium histogram growth factor + * Value for the medium histogram growth factor. */ public static final double GUI_HISTOGRAM_MEDIUM_HISTOGRAM_HEIGHT_VALUE = 37.0; /** - * Value for the large histogram growth factor + * Value for the large histogram growth factor. */ public static final double GUI_HISTOGRAM_LARGE_HISTOGRAM_HEIGHT_VALUE = 50.0; /** - * Value of the bar label sizes + * Value of the bar label sizes. */ public static final double GUI_BAR_LABEL_SIZE = 10.0; /** - * Value of tickLabel length + * Value of tickLabel length. */ public static final double HISTOGRAM_TICK_LABEL_LENGTH = 15.0; /** - * Value of tickLabel gap + * Value of tickLabel gap. */ public static final double HISTOGRAM_TICK_LABEL_GAP = 10.0; // // // /** - * Logger of this class + * Logger of this class. */ private static final Logger LOGGER = Logger.getLogger(HistogramViewController.class.getName()); // // // + /** + * Configuration class to read resource file paths from. + */ + private final IConfiguration configuration; /** * Setting for number of displayed fragments. */ @@ -288,11 +305,11 @@ public String getDisplayName() { /** * Setting for width of spaces between the histogram bars. */ - private final SimpleEnumConstantNameProperty barWidthSetting; + private final SimpleIDisplayEnumConstantProperty barWidthSetting; /** * Setting for which fragment frequency to display. */ - private final SimpleEnumConstantNameProperty displayFrequencySetting; + private final SimpleIDisplayEnumConstantProperty displayFrequencySetting; /** * Setting for maximum SMILES length to display. */ @@ -316,24 +333,24 @@ public String getDisplayName() { /** * All settings of this view tool, encapsulated in JavaFX properties for binding in GUI and persistence. */ - private final List settings; + private final List> settings; // // // /** - * Stage of the main application view + * Stage of the main application view. */ private Stage mainStage; /** - * HistogramView to display + * HistogramView to display. */ private HistogramView histogramView; /** - * Stage of the HistogramView + * Stage of the HistogramView. */ private Stage histogramStage; /** - * Scene of the histogram stage + * Scene of the histogram stage. */ private Scene histogramScene; /** @@ -353,19 +370,19 @@ public String getDisplayName() { */ private double imageZoomFactor; /** - * Y-axis of the histogram + * Y-axis of the histogram. */ private CategoryAxis categoryAxis; /** - * X-axis of the histogram + * X-axis of the histogram. */ private NumberAxis numberAxis; /** - * Histogram chart + * Histogram chart. */ - private BarChart histogramChart; + private BarChart histogramChart; /** - * Atom container to depict the respective structure when the cursor hovers over a bar + * Atom container to depict the respective structure when the cursor hovers over a bar. */ private IAtomContainer atomContainerForDisplayCache; // @@ -373,8 +390,11 @@ public String getDisplayName() { // /** * Constructor, initialises all settings with their default values. Does *not* open the view. + * + * @param aConfiguration configuration instance to read resource file paths from */ - public HistogramViewController() { + public HistogramViewController(IConfiguration aConfiguration) { + this.configuration = aConfiguration; this.settings = new ArrayList<>(8); this.displayedFragmentsNumberSetting = new SimpleIntegerProperty(this, //the name could be displayed but is not used for that currently @@ -388,26 +408,24 @@ public void set(int newValue) throws NullPointerException, IllegalArgumentExcept } }; this.settings.add(this.displayedFragmentsNumberSetting); - this.barWidthSetting = new SimpleEnumConstantNameProperty(this, - //the name could be displayed but is not used for that currently - Message.get("HistogramView.barWidthSetting.name"), - HistogramViewController.DEFAULT_BAR_WIDTH.name(), + this.barWidthSetting = new SimpleIDisplayEnumConstantProperty(this, + "Bar width setting", + HistogramViewController.DEFAULT_BAR_WIDTH, HistogramViewController.BarWidthOption.class) { @Override - public void set(String newValue) throws NullPointerException, IllegalArgumentException { + public void set(IDisplayEnum newValue) throws NullPointerException, IllegalArgumentException { super.set(newValue); //value transferred to GUI in openHistogramView() //value updated in addListenersToHistogramView(), listener of apply-button } }; this.settings.add(this.barWidthSetting); - this.displayFrequencySetting = new SimpleEnumConstantNameProperty(this, - //the name could be displayed but is not used for that currently - Message.get("HistogramView.displayFrequencySetting.name"), - HistogramViewController.DEFAULT_DISPLAY_FREQUENCY.name(), + this.displayFrequencySetting = new SimpleIDisplayEnumConstantProperty(this, + "Displayed frequency setting", + HistogramViewController.DEFAULT_DISPLAY_FREQUENCY, HistogramViewController.FrequencyOption.class) { @Override - public void set(String newValue) throws NullPointerException, IllegalArgumentException { + public void set(IDisplayEnum newValue) throws NullPointerException, IllegalArgumentException { super.set(newValue); //value transferred to GUI in openHistogramView() //value updated in addListenersToHistogramView(), listener of apply-button @@ -488,14 +506,11 @@ public void set(boolean newValue) { *

*/ @Override - public List settingsProperties() { + public List> settingsProperties() { //note: see comments in constructor for how the setting values are transferred in both directions (GUI <-> Properties) return this.settings; } // - /** - * {@inheritDoc} - */ @Override public String getViewToolNameForDisplay() { return HistogramViewController.VIEW_TOOL_NAME_FOR_DISPLAY; @@ -510,8 +525,8 @@ public String getViewToolNameForDisplay() { */ @Override public void restoreDefaultSettings() { - this.barWidthSetting.set(HistogramViewController.DEFAULT_BAR_WIDTH.name()); - this.displayFrequencySetting.set(HistogramViewController.DEFAULT_DISPLAY_FREQUENCY.name()); + this.barWidthSetting.set(HistogramViewController.DEFAULT_BAR_WIDTH); + this.displayFrequencySetting.set(HistogramViewController.DEFAULT_DISPLAY_FREQUENCY); this.maximumSMILESLengthSetting.set(HistogramViewController.DEFAULT_MAX_SMILES_LENGTH); this.displayedFragmentsNumberSetting.set((HistogramViewController.DEFAULT_NUMBER_OF_DISPLAYED_FRAGMENTS)); this.displayBarLabelsSetting.set(HistogramViewController.DEFAULT_DISPLAY_BAR_LABELS_SETTING); @@ -520,9 +535,6 @@ public void restoreDefaultSettings() { this.displaySMILESSetting.set(HistogramViewController.DEFAULT_DISPLAY_SMILES_SETTING); } // - /** - * {@inheritDoc} - */ @Override public boolean canBeUsedOnTab(TabNames aTabNameEnumConstant) { return switch (aTabNameEnumConstant) { @@ -559,12 +571,10 @@ public void openHistogramView(Stage aMainStage, List aFragmen this.histogramStage.setTitle(Message.get("HistogramView.title")); this.histogramStage.setMinHeight(GuiDefinitions.GUI_MAIN_VIEW_HEIGHT_VALUE); this.histogramStage.setMinWidth(GuiDefinitions.GUI_MAIN_VIEW_WIDTH_VALUE); - InputStream tmpImageInputStream = HistogramViewController.class.getResourceAsStream("/de/unijena/cheminf/mortar/images/Mortar_Logo_Icon1.png"); - if (!Objects.isNull(tmpImageInputStream)) { - this.histogramStage.getIcons().add(new Image(tmpImageInputStream)); - } else { - HistogramViewController.LOGGER.log(Level.WARNING, "Mortar_Logo_Icon1.png could not be imported."); - } + String tmpIconURL = this.getClass().getClassLoader().getResource( + this.configuration.getProperty("mortar.imagesFolder") + + this.configuration.getProperty("mortar.logo.icon.name")).toExternalForm(); + this.histogramStage.getIcons().add(new Image(tmpIconURL)); this.histogramView = new HistogramView(this.fragmentListCopy.size()); //
// @@ -573,24 +583,24 @@ public void openHistogramView(Stage aMainStage, List aFragmen // when the "apply" button is clicked this.histogramView.getMaximumSMILESLengthTextField().setText(Integer.toString(this.maximumSMILESLengthSetting.get())); String tmpCurrentlySetBarWidthOptionDisplayName = null; - for (BarWidthOption tmpBarWidthOption : BarWidthOption.values()) { - if (tmpBarWidthOption.name().equals(this.barWidthSetting.get())) { + for (HistogramViewController.BarWidthOption tmpBarWidthOption : HistogramViewController.BarWidthOption.values()) { + if (tmpBarWidthOption.equals(this.barWidthSetting.get())) { tmpCurrentlySetBarWidthOptionDisplayName = tmpBarWidthOption.getDisplayName(); } } if (Objects.isNull(tmpCurrentlySetBarWidthOptionDisplayName)) { - this.barWidthSetting.set(HistogramViewController.DEFAULT_BAR_WIDTH.name()); + this.barWidthSetting.set(HistogramViewController.DEFAULT_BAR_WIDTH); tmpCurrentlySetBarWidthOptionDisplayName = HistogramViewController.DEFAULT_BAR_WIDTH.getDisplayName(); } this.histogramView.getBarWidthsComboBox().setValue(tmpCurrentlySetBarWidthOptionDisplayName); String tmpCurrentlySetFrequencyOptionDisplayName = null; - for (FrequencyOption tmpFrequencyOption : FrequencyOption.values()) { - if (tmpFrequencyOption.name().equals(this.displayFrequencySetting.get())) { + for (HistogramViewController.FrequencyOption tmpFrequencyOption : HistogramViewController.FrequencyOption.values()) { + if (tmpFrequencyOption.equals(this.displayFrequencySetting.get())) { tmpCurrentlySetFrequencyOptionDisplayName = tmpFrequencyOption.getDisplayName(); } } if (Objects.isNull(tmpCurrentlySetFrequencyOptionDisplayName)) { - this.displayFrequencySetting.set(HistogramViewController.DEFAULT_DISPLAY_FREQUENCY.name()); + this.displayFrequencySetting.set(HistogramViewController.DEFAULT_DISPLAY_FREQUENCY); tmpCurrentlySetFrequencyOptionDisplayName = HistogramViewController.DEFAULT_DISPLAY_FREQUENCY.getDisplayName(); } this.histogramView.getFrequencyComboBox().setValue(tmpCurrentlySetFrequencyOptionDisplayName); @@ -608,7 +618,7 @@ public void openHistogramView(Stage aMainStage, List aFragmen // Double[] tmpHistogramHeightFactorAndCategoryGap = this.calculateBarSpacing( this.displayedFragmentsNumberSetting.get(), - this.getBarWidthOptionEnumConstantFromDisplayName((String)this.histogramView.getBarWidthsComboBox().getValue())); + this.getBarWidthOptionEnumConstantFromDisplayName(this.histogramView.getBarWidthsComboBox().getValue())); this.histogramChart = this.createHistogram( this.displayedFragmentsNumberSetting.get(), this.histogramView, @@ -652,7 +662,7 @@ public void openHistogramView(Stage aMainStage, List aFragmen * @throws IllegalArgumentException if given number of fragments to display is bigger than number of available fragments * @return a BarChart to display in the view */ - private BarChart createHistogram(int aFragmentNumber, + private BarChart createHistogram(int aFragmentNumber, HistogramView aHistogramView, int aSmilesLength, CheckBox aBarLabelCheckBox, @@ -679,7 +689,7 @@ private BarChart createHistogram(int aFragmentNumber, this.numberAxis.setForceZeroInRange(true); this.numberAxis.setTickLabelFill(Color.BLACK); this.numberAxis.setLabel(Message.get("HistogramViewController.XAxisLabel.text")); - BarChart tmpHistogramBarChart = new BarChart(this.numberAxis, this.categoryAxis); + BarChart tmpHistogramBarChart = new BarChart<>(this.numberAxis, this.categoryAxis); tmpHistogramBarChart.setCategoryGap(0.0); tmpHistogramBarChart.setBarGap(0.0); ScrollPane tmpScrollPane = aHistogramView.getHistogramScrollPane(); @@ -693,9 +703,8 @@ private BarChart createHistogram(int aFragmentNumber, int tmpFragmentListIndexDecreasing = this.fragmentListCopy.size(); ArrayList tmpFullSmilesLength = new ArrayList<>(); //if false, the "molecule frequency" is used for sorting instead - boolean tmpSortByFragmentFrequency = this.displayFrequencySetting.get().equals(FrequencyOption.ABSOLUTE_FREQUENCY.name()); + boolean tmpSortByFragmentFrequency = this.displayFrequencySetting.get().equals(HistogramViewController.FrequencyOption.ABSOLUTE_FREQUENCY); String tmpSortProperty = (tmpSortByFragmentFrequency ? "absoluteFrequency" : "moleculeFrequency"); - //TODO: rework method and usage CollectionUtil.sortGivenFragmentListByPropertyAndSortType(this.fragmentListCopy, tmpSortProperty, true); // true to sort the list in ascending order for (FragmentDataModel tmpFragmentDataModel : this.fragmentListCopy) { if (tmpFragmentDataModel.getUniqueSmiles().length() > aSmilesLength) { @@ -757,7 +766,7 @@ private BarChart createHistogram(int aFragmentNumber, tmpFrequencyList.size()); List tmpSmilesToDepict = tmpFullSmilesLength.subList(tmpFullSmilesLength.size()- aFragmentNumber, tmpFullSmilesLength.size()); - XYChart.Series tmpSeries = new XYChart.Series(); + XYChart.Series tmpSeries = new XYChart.Series<>(); if (tmpSublistSmiles.size() != tmpSublistFrequency.size() || tmpSublistSmiles.size() != tmpSmilesToDepict.size()) { throw new IllegalArgumentException("SMILES code and frequency sublists for display are of unequal size."); } @@ -765,7 +774,7 @@ private BarChart createHistogram(int aFragmentNumber, Integer tmpCurrentFrequency = tmpSublistFrequency.get(i); String tmpCurrentDisplaySmiles = tmpSublistSmiles.get(i); String tmpCurrentFullSmilesForParsing = tmpSmilesToDepict.get(i); - XYChart.Data tmpStringNumberData = new XYChart.Data(tmpCurrentFrequency, tmpCurrentDisplaySmiles); + XYChart.Data tmpStringNumberData = new XYChart.Data<>(tmpCurrentFrequency, tmpCurrentDisplaySmiles); StackPane tmpHistogramBarStackPane = this.createStackPaneWithContextMenuAndStructureDisplayForBar( aHistogramView.getStructureDisplayImageView(), tmpCurrentFullSmilesForParsing); @@ -809,12 +818,12 @@ private void addListenersToHistogramView() { this.histogramView.getDisplayedFragmentsNumberTextField().setTextFormatter( new TextFormatter<>(GuiUtil.getStringToIntegerConverter(), this.displayedFragmentsNumberSetting.get(), //default value - GuiUtil.getPositiveIntegerWithoutZeroFilter()) + GuiUtil.getPositiveIntegerFilter(false)) ); this.histogramView.getMaximumSMILESLengthTextField().setTextFormatter( new TextFormatter<>(GuiUtil.getStringToIntegerConverter(), this.maximumSMILESLengthSetting.get(), //default value - GuiUtil.getPositiveIntegerWithoutZeroFilter()) + GuiUtil.getPositiveIntegerFilter(false)) ); //disable apply button if both(!) text fields are empty this.histogramView.getApplyButton().disableProperty().bind( @@ -842,11 +851,7 @@ private void addListenersToHistogramView() { //displayed fragments nr text field is empty -> reset this setting to default and parse maximum SMILES length // setting from text field this.maximumSMILESLengthSetting.set(Integer.parseInt(this.histogramView.getMaximumSMILESLengthTextFieldContent())); - if (this.fragmentListCopy.size() >= HistogramViewController.DEFAULT_NUMBER_OF_DISPLAYED_FRAGMENTS) { - this.displayedFragmentsNumberSetting.set(HistogramViewController.DEFAULT_NUMBER_OF_DISPLAYED_FRAGMENTS); - } else { - this.displayedFragmentsNumberSetting.set(this.fragmentListCopy.size()); - } + this.displayedFragmentsNumberSetting.set(Math.min(this.fragmentListCopy.size(), HistogramViewController.DEFAULT_NUMBER_OF_DISPLAYED_FRAGMENTS)); this.histogramView.getDisplayedFragmentsNumberTextField().setText(String.valueOf(this.displayedFragmentsNumberSetting.get())); } else { //both text fields have values -> parse and check @@ -860,13 +865,13 @@ private void addListenersToHistogramView() { return; } } - BarWidthOption tmpBarWidthSettingEnumValue = this.getBarWidthOptionEnumConstantFromDisplayName( - (String)this.histogramView.getBarWidthsComboBox().getValue()); - this.barWidthSetting.set(tmpBarWidthSettingEnumValue.name()); + HistogramViewController.BarWidthOption tmpBarWidthSettingEnumValue = this.getBarWidthOptionEnumConstantFromDisplayName( + this.histogramView.getBarWidthsComboBox().getValue()); + this.barWidthSetting.set(tmpBarWidthSettingEnumValue); Double[] tmpHistogramSizeGap = this.calculateBarSpacing( this.displayedFragmentsNumberSetting.get(), tmpBarWidthSettingEnumValue); - this.displayFrequencySetting.set(this.getFrequencyOptionEnumConstantFromDisplayName((String)this.histogramView.getFrequencyComboBox().getValue()).name()); + this.displayFrequencySetting.set(this.getFrequencyOptionEnumConstantFromDisplayName(this.histogramView.getFrequencyComboBox().getValue())); this.histogramChart = this.createHistogram( this.displayedFragmentsNumberSetting.get(), this.histogramView, @@ -883,18 +888,18 @@ private void addListenersToHistogramView() { this.categoryAxis.setTickLabelsVisible(tmpDisplaySMILES); }); this.histogramView.getDisplayGridLinesCheckBox().selectedProperty() - .addListener((ObservableValue ov, Boolean old_val, Boolean new_val) -> { - this.histogramChart.setVerticalGridLinesVisible(new_val); - this.histogramChart.setHorizontalGridLinesVisible(new_val); + .addListener((ObservableValue ov, Boolean oldVal, Boolean newVal) -> { + this.histogramChart.setVerticalGridLinesVisible(newVal); + this.histogramChart.setHorizontalGridLinesVisible(newVal); //update setting for persistence - this.displayGridLinesSetting.set(new_val); + this.displayGridLinesSetting.set(newVal); }); this.histogramView.getDisplaySmilesOnYAxisCheckBox().selectedProperty() - .addListener((ObservableValue ov, Boolean old_val, Boolean new_val) -> { - this.categoryAxis.setTickMarkVisible(new_val); - this.categoryAxis.setTickLabelsVisible(new_val); + .addListener((ObservableValue ov, Boolean oldVal, Boolean newVal) -> { + this.categoryAxis.setTickMarkVisible(newVal); + this.categoryAxis.setTickLabelsVisible(newVal); //update setting for persistence - this.displaySMILESSetting.set(new_val); + this.displaySMILESSetting.set(newVal); }); this.histogramScene.widthProperty().addListener((observable, oldValue, newValue) -> { double tmpWidthChange = ((this.histogramScene.getWidth() - GuiDefinitions.GUI_MAIN_VIEW_WIDTH_VALUE) / GuiDefinitions.GUI_MAIN_VIEW_WIDTH_VALUE) * 100.0; @@ -939,8 +944,11 @@ private StackPane createStackPaneWithContextMenuAndStructureDisplayForBar(ImageV ContextMenu tmpContextMenu = new ContextMenu(); tmpContextMenu.getItems().addAll(tmpCopySmilesMenuItem, tmpCopyStructureMenuItem); try { - tmpCopySmilesMenuItem.setGraphic(new ImageView(new Image("de/unijena/cheminf/mortar/images/copy_icon_16x16.png"))); - tmpCopyStructureMenuItem.setGraphic(new ImageView(new Image("de/unijena/cheminf/mortar/images/copy_icon_16x16.png"))); + String tmpCopyIconURL = this.getClass().getClassLoader().getResource( + this.configuration.getProperty("mortar.imagesFolder") + + this.configuration.getProperty("mortar.icon.copy.name")).toExternalForm(); + tmpCopySmilesMenuItem.setGraphic(new ImageView(new Image(tmpCopyIconURL))); + tmpCopyStructureMenuItem.setGraphic(new ImageView(new Image(tmpCopyIconURL))); } catch(NullPointerException | IllegalArgumentException anException) { HistogramViewController.LOGGER.log(Level.WARNING, "Copy icon for context menus could not be imported."); } @@ -974,9 +982,7 @@ private StackPane createStackPaneWithContextMenuAndStructureDisplayForBar(ImageV /* Event to open context menu (right click) to copy SMILES string or structure. Context menu also opens, if a right click on the frequency label is detected. */ - EventHandler tmpContextMenuEventHandler = event -> { - tmpContextMenu.show(tmpNodePane, event.getScreenX(), event.getScreenY()); - }; + EventHandler tmpContextMenuEventHandler = event -> tmpContextMenu.show(tmpNodePane, event.getScreenX(), event.getScreenY()); tmpNodePane.addEventHandler(MouseEvent.MOUSE_ENTERED, tmpMouseHoverEventHandler); tmpNodePane.addEventHandler(ContextMenuEvent.CONTEXT_MENU_REQUESTED, tmpContextMenuEventHandler); // Listener ContextMenuItems @@ -1025,33 +1031,33 @@ private void addFrequencyBarLabelToBarAndAddListenersToBarCheckBoxes( Label tmpBarLabel = new Label(); tmpBarLabel.setTranslateY(0.0); tmpBarLabel.setAlignment(Pos.CENTER_RIGHT); - tmpBarLabel.setPrefWidth(HistogramViewController.GUI_BAR_LABEL_SIZE * (double) tmpDigitLength); - tmpBarLabel.setMinWidth(HistogramViewController.GUI_BAR_LABEL_SIZE * (double) tmpDigitLength); - tmpBarLabel.setMaxWidth(HistogramViewController.GUI_BAR_LABEL_SIZE * (double) tmpDigitLength); + tmpBarLabel.setPrefWidth(HistogramViewController.GUI_BAR_LABEL_SIZE * tmpDigitLength); + tmpBarLabel.setMinWidth(HistogramViewController.GUI_BAR_LABEL_SIZE * tmpDigitLength); + tmpBarLabel.setMaxWidth(HistogramViewController.GUI_BAR_LABEL_SIZE * tmpDigitLength); tmpBarLabel.setTranslateX(tmpDigitLength * HistogramViewController.GUI_BAR_LABEL_SIZE + 5.0); tmpBarLabel.setStyle(null); tmpBarLabel.setText(String.valueOf(aFrequency)); - if(this.displayBarLabelsSetting.get()) { + if (this.displayBarLabelsSetting.get()) { aStackPane.getChildren().add(tmpBarLabel); } - aLabelCheckBox.selectedProperty().addListener((ObservableValue ov, Boolean old_val, Boolean new_val) -> { - if (new_val) { + aLabelCheckBox.selectedProperty().addListener((ObservableValue ov, Boolean oldVal, Boolean newVal) -> { + if (Boolean.TRUE.equals(newVal)) { aStackPane.getChildren().add(tmpBarLabel); } else { aStackPane.getChildren().remove(tmpBarLabel); } - this.displayBarLabelsSetting.set(new_val); + this.displayBarLabelsSetting.set(newVal); }); - if(this.displayBarShadowsSetting.get()) { + if (this.displayBarShadowsSetting.get()) { aStackPane.setEffect(new DropShadow(10,2,3, Color.BLACK)); } - aBarStylingCheckBox.selectedProperty().addListener((ObservableValue ov, Boolean old_val, Boolean new_val) -> { - if(new_val) { + aBarStylingCheckBox.selectedProperty().addListener((ObservableValue ov, Boolean oldVal, Boolean newVal) -> { + if (Boolean.TRUE.equals(newVal)) { aStackPane.setEffect(new DropShadow(10, 2, 3, Color.BLACK)); } else { aStackPane.setEffect(null); } - this.displayBarShadowsSetting.set(new_val); + this.displayBarShadowsSetting.set(newVal); }); } // @@ -1092,7 +1098,7 @@ private void closeWindowEvent(WindowEvent anEvent) { * @param aBarWidthOptionConstant enum constant from BarWidthOption to set the bar width value * @return double array which contains both, a value for the histogram height factor [0] and a value for the category gap [1]. */ - private Double[] calculateBarSpacing(int aNumberOfDisplayedFragments, BarWidthOption aBarWidthOptionConstant) { + private Double[] calculateBarSpacing(int aNumberOfDisplayedFragments, HistogramViewController.BarWidthOption aBarWidthOptionConstant) { Double[] tmpHistogramHeightFactorAndCategoryGap = new Double[2]; double tmpCurrentHistogramHeight; double tmpGapDeviation; @@ -1101,46 +1107,46 @@ private Double[] calculateBarSpacing(int aNumberOfDisplayedFragments, BarWidthOp double tmpFinalHistogramHeight = 0.0; //return value is initialised here with a default value double tmpFinalGapSpacing; switch (aBarWidthOptionConstant) { - case SMALL: + case HistogramViewController.BarWidthOption.SMALL: if (aNumberOfDisplayedFragments <= 24) { //magic number - tmpCurrentHistogramHeight = GuiDefinitions.GUI_NOT_SCROLLABLE_HEIGHT / (double) aNumberOfDisplayedFragments; + tmpCurrentHistogramHeight = GuiDefinitions.GUI_NOT_SCROLLABLE_HEIGHT / aNumberOfDisplayedFragments; tmpGapDeviation = tmpCurrentHistogramHeight / (GuiDefinitions.GUI_NOT_SCROLLABLE_HEIGHT / 24.0); tmpGapSpacing = HistogramViewController.GUI_HISTOGRAM_SMALL_BAR_GAP_CONST * tmpGapDeviation; tmpFinalGapSpacing = tmpCurrentHistogramHeight - tmpGapSpacing; tmpCategoryGap = tmpFinalGapSpacing - HistogramViewController.GUI_HISTOGRAM_SMALL_BAR_WIDTH; } else { tmpFinalHistogramHeight = HistogramViewController.GUI_HISTOGRAM_SMALL_HISTOGRAM_HEIGHT_VALUE; - tmpCurrentHistogramHeight = tmpFinalHistogramHeight * (double) aNumberOfDisplayedFragments - 85.0; + tmpCurrentHistogramHeight = tmpFinalHistogramHeight * aNumberOfDisplayedFragments - 85.0; tmpGapSpacing = tmpCurrentHistogramHeight / aNumberOfDisplayedFragments; tmpCategoryGap = tmpGapSpacing - HistogramViewController.GUI_HISTOGRAM_SMALL_BAR_WIDTH; } break; - case MEDIUM: + case HistogramViewController.BarWidthOption.MEDIUM: if (aNumberOfDisplayedFragments <= 17) { //magic number - tmpCurrentHistogramHeight = GuiDefinitions.GUI_NOT_SCROLLABLE_HEIGHT / (double) aNumberOfDisplayedFragments; + tmpCurrentHistogramHeight = GuiDefinitions.GUI_NOT_SCROLLABLE_HEIGHT / aNumberOfDisplayedFragments; tmpGapDeviation = tmpCurrentHistogramHeight / (GuiDefinitions.GUI_NOT_SCROLLABLE_HEIGHT / 17.0); tmpGapSpacing = HistogramViewController.GUI_HISTOGRAM_MEDIUM_BAR_GAP_CONST * tmpGapDeviation; tmpFinalGapSpacing = tmpCurrentHistogramHeight - tmpGapSpacing; tmpCategoryGap = tmpFinalGapSpacing - HistogramViewController.GUI_HISTOGRAM_MEDIUM_BAR_WIDTH; } else { tmpFinalHistogramHeight = HistogramViewController.GUI_HISTOGRAM_MEDIUM_HISTOGRAM_HEIGHT_VALUE; - tmpCurrentHistogramHeight = tmpFinalHistogramHeight * (double) aNumberOfDisplayedFragments - 85.0; - tmpGapSpacing = tmpCurrentHistogramHeight / (double) aNumberOfDisplayedFragments; + tmpCurrentHistogramHeight = tmpFinalHistogramHeight * aNumberOfDisplayedFragments - 85.0; + tmpGapSpacing = tmpCurrentHistogramHeight / aNumberOfDisplayedFragments; tmpCategoryGap = tmpGapSpacing - HistogramViewController.GUI_HISTOGRAM_MEDIUM_BAR_WIDTH ; } break; - case LARGE: + case HistogramViewController.BarWidthOption.LARGE: default: if (aNumberOfDisplayedFragments <= 13) { //magic number - tmpCurrentHistogramHeight = GuiDefinitions.GUI_NOT_SCROLLABLE_HEIGHT / (double) aNumberOfDisplayedFragments; + tmpCurrentHistogramHeight = GuiDefinitions.GUI_NOT_SCROLLABLE_HEIGHT / aNumberOfDisplayedFragments; tmpGapDeviation = tmpCurrentHistogramHeight / (GuiDefinitions.GUI_NOT_SCROLLABLE_HEIGHT / 13.0); tmpGapSpacing = HistogramViewController.GUI_HISTOGRAM_LARGE_BAR_GAP_CONST * tmpGapDeviation; tmpFinalGapSpacing = tmpCurrentHistogramHeight - tmpGapSpacing; tmpCategoryGap = tmpFinalGapSpacing - HistogramViewController.GUI_HISTOGRAM_LARGE_BAR_WIDTH; } else { tmpFinalHistogramHeight = HistogramViewController.GUI_HISTOGRAM_LARGE_HISTOGRAM_HEIGHT_VALUE; - tmpCurrentHistogramHeight = tmpFinalHistogramHeight * (double) aNumberOfDisplayedFragments - 85.0; - tmpGapSpacing = tmpCurrentHistogramHeight / (double) aNumberOfDisplayedFragments; + tmpCurrentHistogramHeight = tmpFinalHistogramHeight * aNumberOfDisplayedFragments - 85.0; + tmpGapSpacing = tmpCurrentHistogramHeight / aNumberOfDisplayedFragments; tmpCategoryGap = tmpGapSpacing - HistogramViewController.GUI_HISTOGRAM_LARGE_BAR_WIDTH; } break; @@ -1158,7 +1164,7 @@ private Double[] calculateBarSpacing(int aNumberOfDisplayedFragments, BarWidthOp * @return an upper limit for the x-axis that leaves enough room for the frequency labels */ private int calculateXAxisUpperBoundWithSpaceForLabels(int aMaxValue, int aTickValue) { - int tmpTickNumber = (int) Math.round(aMaxValue / aTickValue); + int tmpTickNumber = Math.round((float) aMaxValue / aTickValue); int tmpXAxisExtensionValue; if ((aTickValue * tmpTickNumber) > aMaxValue) { tmpXAxisExtensionValue = (aTickValue * tmpTickNumber) + aTickValue; @@ -1176,21 +1182,20 @@ private int calculateXAxisUpperBoundWithSpaceForLabels(int aMaxValue, int aTickV * @param aDisplayName the displayed bar width option name * @return enum constant associated with the display name or default value */ - private BarWidthOption getBarWidthOptionEnumConstantFromDisplayName(String aDisplayName) { + private HistogramViewController.BarWidthOption getBarWidthOptionEnumConstantFromDisplayName(String aDisplayName) { if(Objects.isNull(aDisplayName) || aDisplayName.isBlank()) { HistogramViewController.LOGGER.log(Level.WARNING, "Given string is null or empty, default bar width" + "option is returned."); return HistogramViewController.DEFAULT_BAR_WIDTH; } - BarWidthOption tmpEnumConstantBarWidth = null; - for (BarWidthOption tmpOption : BarWidthOption.values()) { + HistogramViewController.BarWidthOption tmpEnumConstantBarWidth = null; + for (HistogramViewController.BarWidthOption tmpOption : HistogramViewController.BarWidthOption.values()) { if (tmpOption.getDisplayName().equals(aDisplayName)) { tmpEnumConstantBarWidth = tmpOption; } } if (Objects.isNull(tmpEnumConstantBarWidth)) { - HistogramViewController.LOGGER.log(Level.WARNING, "Output of histogram view bar spacing combo box \"" - + aDisplayName + "\"did not equal any of the pre-set enum values and was reset to default."); + HistogramViewController.LOGGER.log(Level.WARNING, "Output of histogram view bar spacing combo box \"{0}\"did not equal any of the pre-set enum values and was reset to default.", aDisplayName); tmpEnumConstantBarWidth = HistogramViewController.DEFAULT_BAR_WIDTH; } return tmpEnumConstantBarWidth; @@ -1203,21 +1208,20 @@ private BarWidthOption getBarWidthOptionEnumConstantFromDisplayName(String aDisp * @param aDisplayName the displayed frequency option name * @return enum constant associated with the display name or default value */ - private FrequencyOption getFrequencyOptionEnumConstantFromDisplayName(String aDisplayName) { + private HistogramViewController.FrequencyOption getFrequencyOptionEnumConstantFromDisplayName(String aDisplayName) { if(Objects.isNull(aDisplayName) || aDisplayName.isBlank()) { HistogramViewController.LOGGER.log(Level.WARNING, "Given string is null or empty, default frequency " + "option is returned."); return HistogramViewController.DEFAULT_DISPLAY_FREQUENCY; } - FrequencyOption tmpEnumConstantFrequencyOption = null; - for (FrequencyOption tmpOption : FrequencyOption.values()) { + HistogramViewController.FrequencyOption tmpEnumConstantFrequencyOption = null; + for (HistogramViewController.FrequencyOption tmpOption : HistogramViewController.FrequencyOption.values()) { if (tmpOption.getDisplayName().equals(aDisplayName)) { tmpEnumConstantFrequencyOption = tmpOption; } } if (Objects.isNull(tmpEnumConstantFrequencyOption)) { - HistogramViewController.LOGGER.log(Level.WARNING, "Output of histogram view frequency combo box \"" - + aDisplayName + "\"did not equal any of the pre-set enum values and was reset to default."); + HistogramViewController.LOGGER.log(Level.WARNING, "Output of histogram view frequency combo box \"{0}\"did not equal any of the pre-set enum values and was reset to default.", aDisplayName); tmpEnumConstantFrequencyOption = HistogramViewController.DEFAULT_DISPLAY_FREQUENCY; } return tmpEnumConstantFrequencyOption; diff --git a/src/main/java/de/unijena/cheminf/mortar/controller/IViewToolController.java b/src/main/java/de/unijena/cheminf/mortar/controller/IViewToolController.java index d8c1cd0f..dc5939f6 100644 --- a/src/main/java/de/unijena/cheminf/mortar/controller/IViewToolController.java +++ b/src/main/java/de/unijena/cheminf/mortar/controller/IViewToolController.java @@ -44,7 +44,7 @@ public interface IViewToolController { * * @return list of settings represented by properties */ - public List settingsProperties(); + public List> settingsProperties(); /** * Returns a view tool name that can be displayed in the GUI, e.g. in the views menu. * @@ -63,8 +63,5 @@ public interface IViewToolController { * @return true if the view tool can be opened when this specific tab is active */ public boolean canBeUsedOnTab(TabNames aTabNameEnumConstant); - - //TODO how to add a getInstance method? - //TODO add open view tool method? // } diff --git a/src/main/java/de/unijena/cheminf/mortar/controller/MainViewController.java b/src/main/java/de/unijena/cheminf/mortar/controller/MainViewController.java index f349277c..c0f69825 100644 --- a/src/main/java/de/unijena/cheminf/mortar/controller/MainViewController.java +++ b/src/main/java/de/unijena/cheminf/mortar/controller/MainViewController.java @@ -25,6 +25,7 @@ package de.unijena.cheminf.mortar.controller; +import de.unijena.cheminf.mortar.configuration.IConfiguration; import de.unijena.cheminf.mortar.gui.controls.CustomPaginationSkin; import de.unijena.cheminf.mortar.gui.controls.GridTabForTableView; import de.unijena.cheminf.mortar.gui.util.GuiDefinitions; @@ -46,7 +47,6 @@ import de.unijena.cheminf.mortar.model.util.BasicDefinitions; import de.unijena.cheminf.mortar.model.util.ChemUtil; import de.unijena.cheminf.mortar.model.util.CollectionUtil; -import de.unijena.cheminf.mortar.model.util.FileUtil; import de.unijena.cheminf.mortar.model.util.LogUtil; import javafx.application.Platform; @@ -87,7 +87,6 @@ import org.openscience.cdk.interfaces.IAtomContainerSet; import java.io.File; -import java.io.InputStream; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -97,105 +96,111 @@ import java.util.concurrent.ExecutionException; import java.util.logging.Level; import java.util.logging.Logger; -import java.util.stream.Collectors; /** - * MainViewController - * controls {@link MainView}. + * MainViewController controls {@link MainView}. * * @author Felix Baensch, Jonas Schaub * @version 1.0.0.0 */ public class MainViewController { - // + // /** - * Primary Stage + * Primary Stage. */ - private Stage primaryStage; + private final Stage primaryStage; /** - * MainView + * MainView. */ - private MainView mainView; + private final MainView mainView; /** - * Scene + * Scene. */ - private Scene scene; + private final Scene scene; /** - * TabPane which holds the different tabs + * TabPane which holds the different tabs. */ - private TabPane mainTabPane; + private final TabPane mainTabPane; /** - * ObservableList to hold MoleculeDataModels for visualisation in MoleculesDataTableView + * ObservableList to hold MoleculeDataModels for visualisation in MoleculesDataTableView. */ - private ObservableList moleculeDataModelList; + private final ObservableList moleculeDataModelList; /** - * MoleculesDataTableView to show imported molecules + * MoleculesDataTableView to show imported molecules. */ private MoleculesDataTableView moleculesDataTableView; /** - * SettingsContainer + * SettingsContainer. */ - private SettingsContainer settingsContainer; + private final SettingsContainer settingsContainer; /** - * FragmentationService + * FragmentationService. */ - private FragmentationService fragmentationService; + private final FragmentationService fragmentationService; /** - * ViewToolsManager + * ViewToolsManager. */ - private ViewToolsManager viewToolsManager; + private final ViewToolsManager viewToolsManager; /** - * Button to start single algorithm fragmentation + * Button to start single algorithm fragmentation. */ private Button fragmentationButton; /** - * Button to cancel running fragmentation + * Button to cancel running fragmentation. */ private Button cancelFragmentationButton; /** - * HashMap to hold Lists of FragmentDataModels for each fragmentation + * HashMap to hold Lists of FragmentDataModels for each fragmentation. */ - private HashMap> mapOfFragmentDataModelLists; + private final HashMap> mapOfFragmentDataModelLists; /** - * Boolean value whether fragmentation is running + * Boolean value whether fragmentation is running. */ private boolean isFragmentationRunning; /** - * Task for parallel fragmentation + * Task for parallel fragmentation. */ private Task parallelFragmentationMainTask; /** - * Thread for task for parallel fragmentation + * Thread for task for parallel fragmentation. */ private Thread fragmentationThread; /** - * Thread for molecule imports, so GUI thread is always responsive + * Thread for molecule imports, so GUI thread is always responsive. */ private Thread importerThread; /** - * Task for molecule file import + * Task for molecule file import. */ private Task importTask; /** - * Thread for molecule exports, so GUI thread is always responsive + * Storing the name of the last imported file. + */ + private String importedFileName; + /** + * Thread for molecule exports, so GUI thread is always responsive. */ private Thread exporterThread; /** - * Task for molecule file export + * Task for molecule file export. */ private Task> exportTask; /** - * BooleanProperty whether import is running + * BooleanProperty whether import is running. + */ + private final BooleanProperty isImportRunningProperty; + /** + * BooleanProperty whether export is running. */ - private BooleanProperty isImportRunningProperty; + private final BooleanProperty isExportRunningProperty; /** - * BooleanProperty whether export is running + * Thread safe list to hold running threads to update StatusBar. */ - private BooleanProperty isExportRunningProperty; + private final CopyOnWriteArrayList threadList; /** - * Thread safe list to hold running threads to update StatusBar + * Configuration class to read resource file paths from. */ - private CopyOnWriteArrayList threadList; + private final IConfiguration configuration; // // // @@ -203,30 +208,31 @@ public class MainViewController { * Logger of this class. */ private static final Logger LOGGER = Logger.getLogger(MainViewController.class.getName()); - /** - * Path to css style sheet - */ - private static final String STYLE_SHEET_PATH = "/de/unijena/cheminf/mortar/style/StyleSheet.css"; // // - /** - * Constructor + * Constructor. Starts the application. * * @param aStage Stage * @param aMainView MainView * @param anAppDir String path to app dir + * @param aConfiguration configuration class reading from properties file + * @throws IllegalArgumentException given application directory is either no directory or does not exist + * @throws NullPointerException if one param is null */ - public MainViewController(Stage aStage, MainView aMainView, String anAppDir) { + public MainViewController(Stage aStage, MainView aMainView, String anAppDir, IConfiguration aConfiguration) + throws IllegalArgumentException, NullPointerException { // Objects.requireNonNull(aStage, "aStage (instance of Stage) is null"); Objects.requireNonNull(aMainView, "aMainView (instance of MainView) is null"); Objects.requireNonNull(aMainView, "anAppDir (instance of String) is null"); + Objects.requireNonNull(aConfiguration, "aConfiguration (instance of IConfiguration) is null"); File tmpAppDirFile = new File(anAppDir); if (!tmpAppDirFile.isDirectory() || !tmpAppDirFile.exists()) { - throw new IllegalArgumentException("The given application directory is neither no directory or does not exist"); + throw new IllegalArgumentException("The given application directory is either no directory or does not exist"); } // + this.configuration = aConfiguration; this.moleculeDataModelList = FXCollections.observableArrayList(param -> new Observable[]{param.selectionProperty()}); this.primaryStage = aStage; this.mainView = aMainView; @@ -235,26 +241,30 @@ public MainViewController(Stage aStage, MainView aMainView, String anAppDir) { this.fragmentationService = new FragmentationService(this.settingsContainer); this.fragmentationService.reloadFragmenterSettings(); this.fragmentationService.reloadActiveFragmenterAndPipeline(); - this.viewToolsManager = new ViewToolsManager(); + this.viewToolsManager = new ViewToolsManager(this.configuration); this.viewToolsManager.reloadViewToolsSettings(); - // + // this.mainTabPane = new TabPane(); this.mainView.getMainCenterPane().getChildren().add(this.mainTabPane); GuiUtil.guiBindControlSizeToParentPane(this.mainView.getMainCenterPane(), this.mainTabPane); this.scene = new Scene(this.mainView, GuiDefinitions.GUI_MAIN_VIEW_WIDTH_VALUE, GuiDefinitions.GUI_MAIN_VIEW_HEIGHT_VALUE); - this.scene.getStylesheets().add(this.getClass().getResource(this.STYLE_SHEET_PATH).toExternalForm()); + String tmpStyleSheetURL = this.getClass().getClassLoader().getResource( + this.configuration.getProperty("mortar.styleFolder") + + this.configuration.getProperty("mortar.stylesheet.name")).toExternalForm(); + this.scene.getStylesheets().add(tmpStyleSheetURL); this.primaryStage.setTitle(Message.get("Title.text")); this.primaryStage.setScene(this.scene); this.primaryStage.show(); this.primaryStage.setMinHeight(GuiDefinitions.GUI_MAIN_VIEW_HEIGHT_VALUE); this.primaryStage.setMinWidth(GuiDefinitions.GUI_MAIN_VIEW_WIDTH_VALUE); - InputStream tmpImageInputStream = MainViewController.class.getResourceAsStream("/de/unijena/cheminf/mortar/images/Mortar_Logo_Icon1.png"); - this.primaryStage.getIcons().add(new Image(tmpImageInputStream)); + String tmpIconURL = this.getClass().getClassLoader().getResource( + this.configuration.getProperty("mortar.imagesFolder") + this.configuration.getProperty("mortar.logo.icon.name")).toExternalForm(); + this.primaryStage.getIcons().add(new Image(tmpIconURL)); // this.isImportRunningProperty = new SimpleBooleanProperty(false); this.isExportRunningProperty = new SimpleBooleanProperty(false); this.mapOfFragmentDataModelLists = new HashMap<>(CollectionUtil.calculateInitialHashCollectionCapacity(5)); - this.threadList = new CopyOnWriteArrayList(); + this.threadList = new CopyOnWriteArrayList<>(); this.addListener(); this.addFragmentationAlgorithmCheckMenuItems(); } @@ -290,7 +300,7 @@ private void addListener() { //fragments export to PDB this.mainView.getMainMenuBar().getFragmentsExportToPDBMenuItem().addEventHandler( EventType.ROOT, - anEvent -> this.exportFile(Exporter.ExportTypes.PDB_FILE)); + anEvent -> this.exportFile(Exporter.ExportTypes.FRAGMENT_PDB_FILE)); //fragments export to PDF this.mainView.getMainMenuBar().getFragmentsExportToPDFMenuItem().addEventHandler( EventType.ROOT, @@ -298,11 +308,11 @@ private void addListener() { //fragments export to single SDF this.mainView.getMainMenuBar().getFragmentsExportToSingleSDFMenuItem().addEventHandler( EventType.ROOT, - anEvent -> this.exportFile(Exporter.ExportTypes.SINGLE_SD_FILE)); + anEvent -> this.exportFile(Exporter.ExportTypes.FRAGMENT_SINGLE_SD_FILE)); //fragments export to separate SDFs this.mainView.getMainMenuBar().getFragmentsExportToSeparateSDFsMenuItem().addEventHandler( EventType.ROOT, - anEvent -> this.exportFile(Exporter.ExportTypes.SD_FILE)); + anEvent -> this.exportFile(Exporter.ExportTypes.FRAGMENT_MULTIPLE_SD_FILES)); //items export to CSV this.mainView.getMainMenuBar().getItemsExportToCSVMenuItem().addEventHandler( EventType.ROOT, @@ -342,7 +352,7 @@ private void addListener() { } ); this.primaryStage.addEventFilter(WindowEvent.WINDOW_CLOSE_REQUEST, (this::closeWindowEvent)); - this.mainView.getMainMenuBar().getAboutViewMenuItem().setOnAction(actionEvent -> new AboutViewController(this.primaryStage)); + this.mainView.getMainMenuBar().getAboutViewMenuItem().setOnAction(actionEvent -> new AboutViewController(this.primaryStage, this.configuration)); this.scene.addEventFilter(KeyEvent.KEY_PRESSED, keyEvent -> { GridTabForTableView tmpGrid = ((GridTabForTableView) this.mainTabPane.getSelectionModel().getSelectedItem()); if (tmpGrid == null) { @@ -366,34 +376,23 @@ else if (keyEvent.getCode() == KeyCode.LEFT || keyEvent.getCode() == KeyCode.PAG keyEvent.consume(); } }); - this.mainTabPane.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> { - Platform.runLater(() -> { - if (newValue == null) { - return; - } - if (newValue.getId().equals(TabNames.MOLECULES.toString())) { - this.mainView.getMainMenuBar().getHistogramViewerMenuItem().setDisable(true); - } else { - this.mainView.getMainMenuBar().getHistogramViewerMenuItem().setDisable(false); - } - if (newValue.getId().equals(TabNames.ITEMIZATION.toString())) { - this.mainView.getMainMenuBar().getOverviewViewMenuItem().setDisable(true); - } else { - this.mainView.getMainMenuBar().getOverviewViewMenuItem().setDisable(false); - } - }); - }); + this.mainTabPane.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> Platform.runLater(() -> { + if (newValue == null) { + return; + } + this.mainView.getMainMenuBar().getHistogramViewerMenuItem().setDisable(newValue.getId().equals(TabNames.MOLECULES.toString())); + this.mainView.getMainMenuBar().getOverviewViewMenuItem().setDisable(newValue.getId().equals(TabNames.ITEMIZATION.toString())); + })); } // - /** - * Closes application + * Closes application. + * + * @param aStatus the status to use for calling System.exit(); a nonzero status code indicates abnormal termination */ private void closeApplication(int aStatus) { - if (moleculeDataModelList.size() > 0) { - if (!this.isFragmentationStopAndDataLossConfirmed()) { - return; - } + if (!moleculeDataModelList.isEmpty() && (!this.isFragmentationStopAndDataLossConfirmed())) { + return; } this.settingsContainer.preserveSettings(); this.viewToolsManager.persistViewToolsSettings(); @@ -407,7 +406,6 @@ private void closeApplication(int aStatus) { System.exit(aStatus); } // - /** * Opens a dialog to warn the user of possible data loss and stopping a running fragmentation, e.g. when a new * molecule set should be imported or the application shut down. Returns true if "OK" was clicked, "false" for cancel @@ -431,9 +429,8 @@ private boolean isFragmentationStopAndDataLossConfirmed() { return tmpConfirmationResult == ButtonType.OK; } // - /** - * Closes the application via closeApplication method when close window event was fired + * Closes the application via closeApplication method when close window event was fired. * * @param anEvent WindowEvent */ @@ -442,14 +439,13 @@ private void closeWindowEvent(WindowEvent anEvent) { anEvent.consume(); } // - /** - * Loads molecule file and opens molecules tab + * Loads molecule file and opens molecules tab. * - * @param aParentStage Stage + * @param aParentStage Stage where to open the file chooser dialog */ private void importMoleculeFile(Stage aParentStage) { - if (this.moleculeDataModelList.size() > 0) { + if (!this.moleculeDataModelList.isEmpty()) { if (!this.isFragmentationStopAndDataLossConfirmed()) { return; } @@ -477,18 +473,18 @@ protected IAtomContainerSet call() throws Exception { return tmpSet; } }; - this.importTask.setOnSucceeded(event -> { + this.importTask.setOnSucceeded(event -> //note: setOnSucceeded() takes place in the JavaFX GUI thread again but still runLater() is necessary to wait // for the thread to be free for the update Platform.runLater(() -> { IAtomContainerSet tmpAtomContainerSet = null; try { - tmpAtomContainerSet = importTask.get(); + tmpAtomContainerSet = this.importTask.get(); } catch (InterruptedException | ExecutionException anException) { MainViewController.LOGGER.log(Level.SEVERE, anException.toString(), anException); GuiUtil.guiExceptionAlert(Message.get("Error.ExceptionAlert.Title"), Message.get("Importer.FileImportExceptionAlert.Header"), - Message.get("Importer.FileImportExceptionAlert.Text") + "\n" + FileUtil.getAppDirPath() + File.separator + BasicDefinitions.LOG_FILES_DIRECTORY + File.separator, + Message.get("Importer.FileImportExceptionAlert.Text") + "\n" + LogUtil.getLogFileDirectoryPath(), anException); this.updateStatusBar(this.importerThread, Message.get("Status.importFailed")); } @@ -519,16 +515,17 @@ protected IAtomContainerSet call() throws Exception { tmpMoleculeDataModel.setName(tmpAtomContainer.getProperty(Importer.MOLECULE_NAME_PROPERTY_KEY)); this.moleculeDataModelList.add(tmpMoleculeDataModel); } - MainViewController.LOGGER.log(Level.INFO, "Successfully imported " + tmpAtomContainerSet.getAtomContainerCount() - + " molecules from file: " + tmpImporter.getFileName() + "; " + tmpExceptionCount - + " molecules could not be parsed into the internal data model (SMILES code generation failed). " + - "See above how many molecules could not be read from the input file at all or produced exceptions while preprocessing."); + this.importedFileName = tmpImporter.getFileName(); + MainViewController.LOGGER.log(Level.INFO, String.format("Successfully imported %d molecules from file: %s; " + + "%d molecules could not be parsed into the internal data model (SMILES code generation failed). " + + "See above how many molecules could not be read from the input file at all or produced exceptions while preprocessing.", + tmpAtomContainerSet.getAtomContainerCount(), tmpImporter.getFileName(), tmpExceptionCount)); this.updateStatusBar(this.importerThread, Message.get("Status.imported")); this.isImportRunningProperty.setValue(false); this.mainView.getMainCenterPane().setStyle("-fx-background-image: none"); this.openMoleculesTab(); - }); - }); + }) + ); this.importTask.setOnCancelled(event -> { this.updateStatusBar(this.importerThread, Message.get("Status.canceled")); this.isImportRunningProperty.setValue(false); @@ -548,9 +545,8 @@ protected IAtomContainerSet call() throws Exception { this.importerThread.start(); } // - /** - * Exports the given type of file + * Exports the given type of file. * * @param anExportType Enum to specify what type of file to export */ @@ -562,13 +558,9 @@ private void exportFile(Exporter.ExportTypes anExportType) { return; } switch (anExportType) { - case FRAGMENT_CSV_FILE: - case PDB_FILE: - case FRAGMENT_PDF_FILE: - case SINGLE_SD_FILE: - case SD_FILE: - if (this.getItemsListOfSelectedFragmenterByTabId(TabNames.FRAGMENTS) == null || - this.getItemsListOfSelectedFragmenterByTabId(TabNames.FRAGMENTS).size() == 0 || + case Exporter.ExportTypes.FRAGMENT_CSV_FILE, Exporter.ExportTypes.FRAGMENT_PDB_FILE, Exporter.ExportTypes.FRAGMENT_PDF_FILE, Exporter.ExportTypes.FRAGMENT_SINGLE_SD_FILE, FRAGMENT_MULTIPLE_SD_FILES: + if (this.getItemsListOfSelectedFragmentationByTabId(TabNames.FRAGMENTS) == null || + this.getItemsListOfSelectedFragmentationByTabId(TabNames.FRAGMENTS).isEmpty() || ((GridTabForTableView) mainTabPane.getSelectionModel().getSelectedItem()).getFragmentationNameOutOfTitle() == null) { GuiUtil.guiMessageAlert( Alert.AlertType.INFORMATION, @@ -579,11 +571,10 @@ private void exportFile(Exporter.ExportTypes anExportType) { return; } break; - case ITEM_CSV_FILE: - case ITEM_PDF_FILE: - if (this.getItemsListOfSelectedFragmenterByTabId(TabNames.ITEMIZATION) == null || - this.getItemsListOfSelectedFragmenterByTabId(TabNames.ITEMIZATION).size() == 0 || - this.moleculeDataModelList == null || this.moleculeDataModelList.size() == 0 || + case Exporter.ExportTypes.ITEM_CSV_FILE, Exporter.ExportTypes.ITEM_PDF_FILE: + if (this.getItemsListOfSelectedFragmentationByTabId(TabNames.ITEMIZATION) == null || + this.getItemsListOfSelectedFragmentationByTabId(TabNames.ITEMIZATION).isEmpty() || + this.moleculeDataModelList == null || this.moleculeDataModelList.isEmpty() || ((GridTabForTableView) mainTabPane.getSelectionModel().getSelectedItem()).getFragmentationNameOutOfTitle() == null) { GuiUtil.guiMessageAlert( Alert.AlertType.INFORMATION, @@ -599,81 +590,81 @@ private void exportFile(Exporter.ExportTypes anExportType) { if (this.isExportRunningProperty.get()) { this.interruptExport(); } - tmpExporter.saveFile(this.primaryStage, anExportType, ((GridTabForTableView) mainTabPane.getSelectionModel().getSelectedItem()).getFragmentationNameOutOfTitle()); - if(tmpExporter.getFile() == null){ + //returns null if file chooser dialog was cancelled + File tmpExportFile = tmpExporter.openFileChooserForExportFileOrDir(this.primaryStage, anExportType, + ((GridTabForTableView) this.mainTabPane.getSelectionModel().getSelectedItem()).getFragmentationNameOutOfTitle()); + if (tmpExportFile == null) { return; } boolean tmpGenerate2dAtomCoordinates = false; - switch (anExportType) { - case PDB_FILE: - case SINGLE_SD_FILE: - if (!ChemUtil.checkMoleculeListForCoordinates(getItemsListOfSelectedFragmenterByTabId(TabNames.FRAGMENTS))) { - ButtonType tmpConfirmationResult = GuiUtil.guiConfirmationAlert( - Message.get("Exporter.FragmentsTab.ConfirmationAlert.No3dInformationAvailable.title"), - Message.get("Exporter.FragmentsTab.ConfirmationAlert.No3dInformationAvailable.header"), - Message.get("Exporter.FragmentsTab.ConfirmationAlert.No3dInformationAvailable.text") - ); - tmpGenerate2dAtomCoordinates = tmpConfirmationResult == ButtonType.OK; - } - break; + if (anExportType.equals(Exporter.ExportTypes.FRAGMENT_PDB_FILE) || anExportType.equals(Exporter.ExportTypes.FRAGMENT_SINGLE_SD_FILE) + && (!ChemUtil.checkMoleculeListForCoordinates(this.getItemsListOfSelectedFragmentationByTabId(TabNames.FRAGMENTS)))) { + ButtonType tmpConfirmationResult = GuiUtil.guiConfirmationAlert( + Message.get("Exporter.FragmentsTab.ConfirmationAlert.No3dInformationAvailable.title"), + Message.get("Exporter.FragmentsTab.ConfirmationAlert.No3dInformationAvailable.header"), + Message.get("Exporter.FragmentsTab.ConfirmationAlert.No3dInformationAvailable.text") + ); + tmpGenerate2dAtomCoordinates = tmpConfirmationResult == ButtonType.OK; } boolean tmpGenerate2dAtomCoordinatesFinal = tmpGenerate2dAtomCoordinates; this.exportTask = new Task<>() { @Override protected List call() throws Exception { - switch (anExportType) { - case FRAGMENT_CSV_FILE: - return tmpExporter.exportCsvFile( - getItemsListOfSelectedFragmenterByTabId(TabNames.FRAGMENTS), - ((GridTabForTableView) mainTabPane.getSelectionModel().getSelectedItem()).getFragmentationNameOutOfTitle(), - settingsContainer.getCsvExportSeparatorSetting(), - TabNames.FRAGMENTS - ); - case PDB_FILE: - return tmpExporter.exportFragmentsAsChemicalFile( - getItemsListOfSelectedFragmenterByTabId(TabNames.FRAGMENTS), - ((GridTabForTableView) mainTabPane.getSelectionModel().getSelectedItem()).getFragmentationNameOutOfTitle(), - ChemFileTypes.PDB, - tmpGenerate2dAtomCoordinatesFinal - ); - case FRAGMENT_PDF_FILE: - return tmpExporter.exportPdfFile( - getItemsListOfSelectedFragmenterByTabId(TabNames.FRAGMENTS), - moleculeDataModelList, - ((GridTabForTableView) mainTabPane.getSelectionModel().getSelectedItem()).getFragmentationNameOutOfTitle(), - TabNames.FRAGMENTS - ); - case SINGLE_SD_FILE: - return tmpExporter.exportFragmentsAsChemicalFile( - getItemsListOfSelectedFragmenterByTabId(TabNames.FRAGMENTS), - ((GridTabForTableView) mainTabPane.getSelectionModel().getSelectedItem()).getFragmentationNameOutOfTitle(), - ChemFileTypes.SDF, - tmpGenerate2dAtomCoordinatesFinal, - true - ); - case SD_FILE: - return tmpExporter.exportFragmentsAsChemicalFile( - getItemsListOfSelectedFragmenterByTabId(TabNames.FRAGMENTS), - ((GridTabForTableView) mainTabPane.getSelectionModel().getSelectedItem()).getFragmentationNameOutOfTitle(), - ChemFileTypes.SDF, - false - ); - case ITEM_CSV_FILE: - return tmpExporter.exportCsvFile( - moleculeDataModelList, - ((GridTabForTableView) mainTabPane.getSelectionModel().getSelectedItem()).getFragmentationNameOutOfTitle(), - settingsContainer.getCsvExportSeparatorSetting(), - TabNames.ITEMIZATION - ); - case ITEM_PDF_FILE: - return tmpExporter.exportPdfFile( - getItemsListOfSelectedFragmenterByTabId(TabNames.ITEMIZATION), - moleculeDataModelList, - ((GridTabForTableView) mainTabPane.getSelectionModel().getSelectedItem()).getFragmentationNameOutOfTitle(), - TabNames.ITEMIZATION - ); - } - return null; + return switch (anExportType) { + case Exporter.ExportTypes.FRAGMENT_CSV_FILE -> tmpExporter.exportCsvFile( + tmpExportFile, + MainViewController.this.getItemsListOfSelectedFragmentationByTabId(TabNames.FRAGMENTS), + ((GridTabForTableView) MainViewController.this.mainTabPane.getSelectionModel().getSelectedItem()).getFragmentationNameOutOfTitle(), + MainViewController.this.settingsContainer.getCsvExportSeparatorSettingCharacter(), + TabNames.FRAGMENTS + ); + case Exporter.ExportTypes.FRAGMENT_PDB_FILE -> + tmpExporter.exportFragmentsAsChemicalFile( + tmpExportFile, + MainViewController.this.getItemsListOfSelectedFragmentationByTabId(TabNames.FRAGMENTS), + ChemFileTypes.PDB, + tmpGenerate2dAtomCoordinatesFinal + ); + case Exporter.ExportTypes.FRAGMENT_PDF_FILE -> tmpExporter.exportPdfFile( + tmpExportFile, + MainViewController.this.getItemsListOfSelectedFragmentationByTabId(TabNames.FRAGMENTS), + MainViewController.this.moleculeDataModelList, + ((GridTabForTableView) MainViewController.this.mainTabPane.getSelectionModel().getSelectedItem()).getFragmentationNameOutOfTitle(), + MainViewController.this.importedFileName, + TabNames.FRAGMENTS + ); + case Exporter.ExportTypes.FRAGMENT_SINGLE_SD_FILE -> + tmpExporter.exportFragmentsAsChemicalFile( + tmpExportFile, + MainViewController.this.getItemsListOfSelectedFragmentationByTabId(TabNames.FRAGMENTS), + ChemFileTypes.SDF, + tmpGenerate2dAtomCoordinatesFinal, + true + ); + case Exporter.ExportTypes.FRAGMENT_MULTIPLE_SD_FILES -> + tmpExporter.exportFragmentsAsChemicalFile( + tmpExportFile, + MainViewController.this.getItemsListOfSelectedFragmentationByTabId(TabNames.FRAGMENTS), + ChemFileTypes.SDF, + false + ); + case Exporter.ExportTypes.ITEM_CSV_FILE -> tmpExporter.exportCsvFile( + tmpExportFile, + MainViewController.this.moleculeDataModelList, + ((GridTabForTableView) MainViewController.this.mainTabPane.getSelectionModel().getSelectedItem()).getFragmentationNameOutOfTitle(), + MainViewController.this.settingsContainer.getCsvExportSeparatorSettingCharacter(), + TabNames.ITEMIZATION + ); + case Exporter.ExportTypes.ITEM_PDF_FILE -> tmpExporter.exportPdfFile( + tmpExportFile, + MainViewController.this.getItemsListOfSelectedFragmentationByTabId(TabNames.FRAGMENTS), + MainViewController.this.moleculeDataModelList, + ((GridTabForTableView) MainViewController.this.mainTabPane.getSelectionModel().getSelectedItem()).getFragmentationNameOutOfTitle(), + MainViewController.this.importedFileName, + TabNames.ITEMIZATION + ); + default -> throw new UnsupportedOperationException("Unknown export type."); + }; } }; this.exportTask.setOnSucceeded(event -> { @@ -685,11 +676,12 @@ protected List call() throws Exception { Message.get("Exporter.FragmentsTab.ExportNotPossible.title"), Message.get("Exporter.FragmentsTab.ExportNotPossible.header"), null); + return; } - if (tmpFailedExportFragments.size() > 0) { + if (!tmpFailedExportFragments.isEmpty()) { StringBuilder tmpStringBuilder = new StringBuilder(); for (String tmpFragmentName : tmpFailedExportFragments) { - tmpStringBuilder.append(tmpFragmentName + "\n"); + tmpStringBuilder.append(tmpFragmentName).append("\n"); } GuiUtil.guiExpandableAlert( Alert.AlertType.WARNING.toString(), @@ -726,33 +718,32 @@ protected List call() throws Exception { this.exporterThread.start(); } // - /** - * Opens settings view for fragmentationSettings + * Opens settings view for fragmentation settings. */ private void openFragmentationSettingsView() { - FragmentationSettingsViewController tmpFragmentationSettingsViewController = - new FragmentationSettingsViewController(this.primaryStage, this.fragmentationService.getFragmenters(), this.fragmentationService.getSelectedFragmenter().getFragmentationAlgorithmName()); + new FragmentationSettingsViewController(this.primaryStage, + this.fragmentationService.getFragmenters(), + this.fragmentationService.getSelectedFragmenter().getFragmentationAlgorithmDisplayName(), + this.configuration); } // - /** - * Opens PipelineSettingsView + * Opens PipelineSettingsView. */ private void openPipelineSettingsView() { PipelineSettingsViewController tmpPipelineSettingsViewController = - new PipelineSettingsViewController(this.primaryStage, this.fragmentationService, this.moleculeDataModelList.size() > 0, this.isFragmentationRunning); + new PipelineSettingsViewController(this.primaryStage, this.fragmentationService, !this.moleculeDataModelList.isEmpty(), this.isFragmentationRunning, this.configuration); if (tmpPipelineSettingsViewController.isFragmentationStarted()) { this.startFragmentation(tmpPipelineSettingsViewController.isFragmentationStarted()); } } // - /** - * Opens HistogramView + * Opens HistogramView. */ private void openHistogramView() { - List tmpMoleculesList = this.getItemsListOfSelectedFragmenterByTabId(TabNames.FRAGMENTS); + List tmpMoleculesList = this.getItemsListOfSelectedFragmentationByTabId(TabNames.FRAGMENTS); List tmpFragmentsList = new ArrayList<>(tmpMoleculesList.size()); for (MoleculeDataModel tmpMolecule : tmpMoleculesList) { tmpFragmentsList.add((FragmentDataModel) tmpMolecule); @@ -760,40 +751,40 @@ private void openHistogramView() { this.viewToolsManager.openHistogramView(this.primaryStage, tmpFragmentsList); } // - /** - * Adds CheckMenuItems for fragmentation algorithms to MainMenuBar + * Adds CheckMenuItems for fragmentation algorithms to MainMenuBar. */ private void addFragmentationAlgorithmCheckMenuItems() { ToggleGroup tmpToggleGroup = new ToggleGroup(); for (IMoleculeFragmenter tmpFragmenter : this.fragmentationService.getFragmenters()) { - RadioMenuItem tmpRadioMenuItem = new RadioMenuItem(tmpFragmenter.getFragmentationAlgorithmName()); + RadioMenuItem tmpRadioMenuItem = new RadioMenuItem(tmpFragmenter.getFragmentationAlgorithmDisplayName()); tmpRadioMenuItem.setToggleGroup(tmpToggleGroup); this.mainView.getMainMenuBar().getFragmentationAlgorithmMenu().getItems().add(tmpRadioMenuItem); - if (!Objects.isNull(this.fragmentationService.getSelectedFragmenter()) && tmpFragmenter.getFragmentationAlgorithmName().equals(this.fragmentationService.getSelectedFragmenter().getFragmentationAlgorithmName())) { + if (!Objects.isNull(this.fragmentationService.getSelectedFragmenter()) + && tmpFragmenter.getFragmentationAlgorithmDisplayName() + .equals(this.fragmentationService.getSelectedFragmenter().getFragmentationAlgorithmDisplayName())) { tmpToggleGroup.selectToggle(tmpRadioMenuItem); } } tmpToggleGroup.selectedToggleProperty().addListener((observableValue, oldValue, newValue) -> { if (tmpToggleGroup.getSelectedToggle() != null) { this.fragmentationService.setSelectedFragmenter(((RadioMenuItem) newValue).getText()); - this.fragmentationService.setSelectedFragmenterNameProperty(((RadioMenuItem) newValue).getText()); + this.fragmentationService.setSelectedFragmenterDisplayName(((RadioMenuItem) newValue).getText()); } }); } // - /** - * Opens settings view for global settings + * Opens settings view for global settings. */ private void openGlobalSettingsView() { - SettingsViewController tmpSettingsViewController = new SettingsViewController(this.primaryStage, this.settingsContainer); + SettingsViewController tmpSettingsViewController = new SettingsViewController(this.primaryStage, this.settingsContainer, this.configuration); Platform.runLater(() -> { if (tmpSettingsViewController.hasRowsPerPageChanged()) { for (Tab tmpTab : this.mainTabPane.getTabs()) { - TableView tmpTableView = ((GridTabForTableView) tmpTab).getTableView(); - int tmpListSize = 0; - tmpListSize = ((IDataTableView) tmpTableView).getItemsList().size(); + // type of generic not given because it does not matter here, only the size of the items list + TableView tmpTableView = ((GridTabForTableView) tmpTab).getTableView(); + int tmpListSize = ((IDataTableView) tmpTableView).getItemsList().size(); int tmpPageIndex = ((GridTabForTableView) tmpTab).getPagination().getCurrentPageIndex(); int tmpRowsPerPage = this.settingsContainer.getRowsPerPageSetting(); int tmpPageCount = tmpListSize / tmpRowsPerPage; @@ -803,10 +794,16 @@ private void openGlobalSettingsView() { if (tmpPageIndex > tmpPageCount) { tmpPageIndex = tmpPageCount; } + /* + the following might cause "javafx.scene.control.skin.VirtualFlow addTrailingCells + INFO: index exceeds maxCellCount. Check size calculations for class javafx.scene.control.TableRow" + when the new rows per page value is smaller than the older one, but it is not a real problem; + the refreshed GUI just needs to "scroll" to a different position + */ ((GridTabForTableView) tmpTab).getPagination().setPageCount(tmpPageCount); ((GridTabForTableView) tmpTab).getPagination().setCurrentPageIndex(tmpPageIndex); ((GridTabForTableView) tmpTab).getTableView().refresh(); - GuiUtil.setImageStructureHeight(((GridTabForTableView) tmpTab).getTableView(), ((GridTabForTableView) tmpTab).getTableView().getHeight(), this.settingsContainer); + GuiUtil.setImageStructureHeight(((GridTabForTableView) tmpTab).getTableView(), ((GridTabForTableView) tmpTab).getTableView().getHeight(), this.settingsContainer.getRowsPerPageSetting()); ((GridTabForTableView) tmpTab).getTableView().refresh(); } } @@ -823,16 +820,15 @@ private void openGlobalSettingsView() { }); } // - /** - * Opens OverviewView + * Opens OverviewView. * * @param aDataSource Source of the data to be shown in the overview view */ private void openOverviewView(OverviewViewController.DataSources aDataSource) { try { switch (aDataSource) { - case MOLECULES_TAB -> { + case OverviewViewController.DataSources.MOLECULES_TAB -> { if (!(this.mainTabPane.getSelectionModel().getSelectedItem().getId().equals(TabNames.MOLECULES.toString()))) //should not happen throw new IllegalStateException(); @@ -840,10 +836,10 @@ private void openOverviewView(OverviewViewController.DataSources aDataSource) { this.primaryStage, aDataSource, ((GridTabForTableView) mainTabPane.getSelectionModel().getSelectedItem()).getTitle(), - getItemsListOfSelectedFragmenterByTabId(TabNames.MOLECULES) + getItemsListOfSelectedFragmentationByTabId(TabNames.MOLECULES) ); } - case FRAGMENTS_TAB -> { + case OverviewViewController.DataSources.FRAGMENTS_TAB -> { if (!(this.mainTabPane.getSelectionModel().getSelectedItem().getId().equals(TabNames.FRAGMENTS.toString()))) //should not happen throw new IllegalStateException(); @@ -851,10 +847,10 @@ private void openOverviewView(OverviewViewController.DataSources aDataSource) { this.primaryStage, aDataSource, ((GridTabForTableView) mainTabPane.getSelectionModel().getSelectedItem()).getTitle(), - this.getItemsListOfSelectedFragmenterByTabId(TabNames.FRAGMENTS) + this.getItemsListOfSelectedFragmentationByTabId(TabNames.FRAGMENTS) ); } - case PARENT_MOLECULES_SAMPLE -> { + case OverviewViewController.DataSources.PARENT_MOLECULES_SAMPLE -> { if (!(this.mainTabPane.getSelectionModel().getSelectedItem().getId().equals(TabNames.FRAGMENTS.toString()))) //should not happen throw new IllegalStateException(); @@ -867,7 +863,7 @@ private void openOverviewView(OverviewViewController.DataSources aDataSource) { } //getting the data for the overview view List tmpDataForOverviewView = new ArrayList<>(); - int tmpSelectedRowIndex = ((TableView) tmpSelectedTab.getTableView()).getSelectionModel().getSelectedCells().get(0).getRow(); + int tmpSelectedRowIndex = ((TableView) tmpSelectedTab.getTableView()).getSelectionModel().getSelectedCells().getFirst().getRow(); int tmpIndexInDataList = tmpSelectedTab.getPagination().getCurrentPageIndex() * this.settingsContainer.getRowsPerPageSetting() + tmpSelectedRowIndex; //adding the fragment itself tmpDataForOverviewView.add(((IDataTableView) tmpSelectedTab.getTableView()).getItemsList().get(tmpIndexInDataList)); @@ -880,7 +876,7 @@ private void openOverviewView(OverviewViewController.DataSources aDataSource) { tmpDataForOverviewView ); } - case ITEM_WITH_FRAGMENTS_SAMPLE -> { + case OverviewViewController.DataSources.ITEM_WITH_FRAGMENTS_SAMPLE -> { if (!(this.mainTabPane.getSelectionModel().getSelectedItem().getId().equals(TabNames.ITEMIZATION.toString()))) //should not happen throw new IllegalStateException(); @@ -893,12 +889,12 @@ private void openOverviewView(OverviewViewController.DataSources aDataSource) { } //getting the data for the overview view List tmpDataForOverviewView = new ArrayList<>(); - int tmpSelectedRowIndex = ((TableView) tmpSelectedTab.getTableView()).getSelectionModel().getSelectedCells().get(0).getRow(); + int tmpSelectedRowIndex = ((TableView) tmpSelectedTab.getTableView()).getSelectionModel().getSelectedCells().getFirst().getRow(); int tmpIndexInDataList = tmpSelectedTab.getPagination().getCurrentPageIndex() * this.settingsContainer.getRowsPerPageSetting() + tmpSelectedRowIndex; //adding the item itself tmpDataForOverviewView.add(((IDataTableView) tmpSelectedTab.getTableView()).getItemsList().get(tmpIndexInDataList)); //adding the sample of fragments - tmpDataForOverviewView.addAll(((IDataTableView) tmpSelectedTab.getTableView()).getItemsList().get(tmpIndexInDataList).getFragmentsOfSpecificAlgorithm(tmpSelectedTab.getFragmentationNameOutOfTitle())); + tmpDataForOverviewView.addAll(((IDataTableView) tmpSelectedTab.getTableView()).getItemsList().get(tmpIndexInDataList).getFragmentsOfSpecificFragmentation(tmpSelectedTab.getFragmentationNameOutOfTitle())); this.viewToolsManager.openOverviewView( this.primaryStage, OverviewViewController.DataSources.ITEM_WITH_FRAGMENTS_SAMPLE, @@ -923,6 +919,7 @@ private void openOverviewView(OverviewViewController.DataSources aDataSource) { int tmpNewPageIndex = tmpIndexOfMoleculeDataModelToReturnTo / this.settingsContainer.getRowsPerPageSetting(); ((GridTabForTableView) this.mainTabPane.getSelectionModel().getSelectedItem()).getPagination() .setCurrentPageIndex(tmpNewPageIndex); + // unnecessary to provide generic type TableView tmpSelectedTabTableView = ((GridTabForTableView) this.mainTabPane.getSelectionModel() .getSelectedItem()).getTableView(); if (tmpSelectedTabTableView.getClass() == MoleculesDataTableView.class) { @@ -944,72 +941,52 @@ private void openOverviewView(OverviewViewController.DataSources aDataSource) { this.viewToolsManager.resetCachedIndexOfStructureInMoleculeDataModelList(); } // - /** - * Opens molecules tab + * Opens molecules tab. */ private void openMoleculesTab() { - this.moleculesDataTableView = new MoleculesDataTableView(); + this.moleculesDataTableView = new MoleculesDataTableView(this.configuration); this.moleculesDataTableView.setItemsList(this.moleculeDataModelList); GridTabForTableView tmpMoleculesTab = new GridTabForTableView(Message.get("MainTabPane.moleculesTab.title"), TabNames.MOLECULES.name(), this.moleculesDataTableView); this.mainTabPane.getTabs().add(tmpMoleculesTab); - int tmpRowsPerPage = this.settingsContainer.getRowsPerPageSetting(); - int tmpPageCount = this.moleculeDataModelList.size() / tmpRowsPerPage; - if (this.moleculeDataModelList.size() % tmpRowsPerPage > 0) { - tmpPageCount++; - } - if(this.moleculeDataModelList.size() == 0){ - tmpPageCount = 1; - } - Pagination tmpPagination = new Pagination(tmpPageCount, 0); - tmpPagination.setSkin(new CustomPaginationSkin(tmpPagination)); - tmpPagination.setPageFactory((pageIndex) -> this.moleculesDataTableView.createMoleculeTableViewPage(pageIndex, this.settingsContainer)); - VBox.setVgrow(tmpPagination, Priority.ALWAYS); - HBox.setHgrow(tmpPagination, Priority.ALWAYS); + Pagination tmpPagination = this.createPaginationWithSuitablePageCount(this.moleculeDataModelList.size()); + tmpPagination.setPageFactory(pageIndex -> this.moleculesDataTableView.createMoleculeTableViewPage(pageIndex, this.settingsContainer)); tmpMoleculesTab.addPaginationToGridPane(tmpPagination); HBox tmpFragmentationButtonsHBox = new HBox(); tmpFragmentationButtonsHBox.setPadding(new Insets(GuiDefinitions.GUI_INSETS_VALUE, GuiDefinitions.GUI_INSETS_VALUE, GuiDefinitions.GUI_INSETS_VALUE, GuiDefinitions.GUI_INSETS_VALUE)); tmpFragmentationButtonsHBox.setSpacing(GuiDefinitions.GUI_SPACING_VALUE); tmpFragmentationButtonsHBox.setAlignment(Pos.CENTER_LEFT); this.fragmentationButton = new Button(); - this.fragmentationButton.textProperty().bind(this.fragmentationService.selectedFragmenterNamePropertyProperty()); - Tooltip tmpTooltip = new Tooltip(); - tmpTooltip.textProperty().bind(Bindings.format(Message.get("MainTabPane.moleculesTab.fragmentButton.text"), this.fragmentationService.selectedFragmenterNamePropertyProperty())); + this.fragmentationButton.textProperty().bind(this.fragmentationService.selectedFragmenterDisplayNameProperty()); + Tooltip tmpTooltip = GuiUtil.createTooltip(""); + tmpTooltip.textProperty().bind(Bindings.format(Message.get("MainTabPane.moleculesTab.fragmentButton.text"), this.fragmentationService.selectedFragmenterDisplayNameProperty())); this.fragmentationButton.setTooltip(tmpTooltip); - double tmpTextWidth = new Text(this.fragmentationService.getSelectedFragmenterNameProperty()).getLayoutBounds().getWidth() + 20; + double tmpTextWidth = new Text(this.fragmentationService.getSelectedFragmenterDisplayName()).getLayoutBounds().getWidth() + 20; this.fragmentationButton.setPrefWidth(tmpTextWidth); this.fragmentationButton.setMinWidth(tmpTextWidth); this.fragmentationButton.setMaxWidth(tmpTextWidth); this.fragmentationButton.setPrefHeight(GuiDefinitions.GUI_BUTTON_HEIGHT_VALUE); - this.fragmentationService.selectedFragmenterNamePropertyProperty().addListener((observable, oldValue, newValue) -> { - double tmpTextWidthChange = new Text(this.fragmentationService.getSelectedFragmenterNameProperty()).getLayoutBounds().getWidth() + 20; + this.fragmentationService.selectedFragmenterDisplayNameProperty().addListener((observable, oldValue, newValue) -> { + double tmpTextWidthChange = new Text(newValue).getLayoutBounds().getWidth() + 20; this.fragmentationButton.setPrefWidth(tmpTextWidthChange); this.fragmentationButton.setMinWidth(tmpTextWidthChange); this.fragmentationButton.setMaxWidth(tmpTextWidthChange); }); tmpFragmentationButtonsHBox.getChildren().add(this.fragmentationButton); - this.cancelFragmentationButton = new Button(Message.get("MainTabPane.moleculesTab.cancelFragmentationButton.text")); - this.cancelFragmentationButton.setTooltip(new Tooltip(Message.get("MainTabPane.moleculesTab.cancelFragmentationButton.tooltip"))); - this.cancelFragmentationButton.setPrefWidth(GuiDefinitions.GUI_BUTTON_WIDTH_VALUE); - this.cancelFragmentationButton.setMinWidth(GuiDefinitions.GUI_BUTTON_WIDTH_VALUE); - this.cancelFragmentationButton.setMaxWidth(GuiDefinitions.GUI_BUTTON_WIDTH_VALUE); - this.cancelFragmentationButton.setPrefHeight(GuiDefinitions.GUI_BUTTON_HEIGHT_VALUE); - this.cancelFragmentationButton.setVisible(false);; + this.cancelFragmentationButton = GuiUtil.getButtonOfStandardSize(Message.get("MainTabPane.moleculesTab.cancelFragmentationButton.text")); + this.cancelFragmentationButton.setTooltip(GuiUtil.createTooltip(Message.get("MainTabPane.moleculesTab.cancelFragmentationButton.tooltip"))); + this.cancelFragmentationButton.setVisible(false); tmpFragmentationButtonsHBox.getChildren().add(this.cancelFragmentationButton); tmpMoleculesTab.addNodeToGridPane(tmpFragmentationButtonsHBox, 0, 1, 1, 1); - this.fragmentationButton.setOnAction(event -> { - this.startFragmentation(); - }); - this.cancelFragmentationButton.setOnAction(event -> { - this.interruptFragmentation(); - }); + this.fragmentationButton.setOnAction(event -> this.startFragmentation()); + this.cancelFragmentationButton.setOnAction(event -> this.interruptFragmentation()); HBox tmpViewButtonsHBox = new HBox(); tmpViewButtonsHBox.setPadding(new Insets(GuiDefinitions.GUI_INSETS_VALUE, GuiDefinitions.GUI_INSETS_VALUE, GuiDefinitions.GUI_INSETS_VALUE, GuiDefinitions.GUI_INSETS_VALUE)); tmpViewButtonsHBox.setSpacing(GuiDefinitions.GUI_SPACING_VALUE); tmpViewButtonsHBox.setAlignment(Pos.CENTER_RIGHT); tmpViewButtonsHBox.setMaxWidth(GuiDefinitions.GUI_GRIDPANE_FOR_NODE_ALIGNMENT_THIRD_COL_WIDTH); Button tmpOpenOverviewViewButton = GuiUtil.getButtonOfStandardSize(Message.get("MainView.showOverviewViewButton.text")); - tmpOpenOverviewViewButton.setTooltip(new Tooltip(Message.get("MainView.showOverviewViewButton.tooltip"))); + tmpOpenOverviewViewButton.setTooltip(GuiUtil.createTooltip(Message.get("MainView.showOverviewViewButton.tooltip"))); tmpViewButtonsHBox.getChildren().add(tmpOpenOverviewViewButton); tmpMoleculesTab.addNodeToGridPane(tmpViewButtonsHBox, 2, 1, 1, 1); tmpOpenOverviewViewButton.setOnAction(event -> this.openOverviewView(OverviewViewController.DataSources.MOLECULES_TAB)); @@ -1020,9 +997,9 @@ private void openMoleculesTab() { GuiUtil.copySelectedTableViewCellsToClipboard(this.moleculesDataTableView); } }); - this.moleculesDataTableView.setOnSort((EventHandler>) event -> { - GuiUtil.sortTableViewGlobally(event, tmpPagination, tmpRowsPerPage); - }); + int tmpRowsPerPage = this.settingsContainer.getRowsPerPageSetting(); + this.moleculesDataTableView.setOnSort((EventHandler>) event -> + GuiUtil.sortTableViewGlobally(event, tmpPagination, tmpRowsPerPage)); this.moleculesDataTableView.widthProperty().addListener((observable, oldValue, newValue) -> { for(Object tmpObject : this.moleculesDataTableView.getItems()) { ((MoleculeDataModel) tmpObject).setStructureImageWidth(this.moleculesDataTableView.getStructureColumn().getWidth()); @@ -1030,27 +1007,48 @@ private void openMoleculesTab() { }); } // - /** - * Cancels import task and interrupts the corresponding thread + * Creates a new JavaFx pagination control that is configured with a suitable page count for the given number + * of molecules/fragments taking into account the rows per page setting. Also sets the MORTAR custom pagination skin + * as skin of the new pagination instance and configures its growth behavior. The page factory is *NOT* set. + * + * @param aListSize number of molecules/fragments to display + * @return configured pagination control instance + */ + private Pagination createPaginationWithSuitablePageCount(int aListSize) { + int tmpRowsPerPage = this.settingsContainer.getRowsPerPageSetting(); + int tmpPageCount = aListSize / tmpRowsPerPage; + if (aListSize % tmpRowsPerPage > 0) { + tmpPageCount++; + } + if (aListSize == 0) { + tmpPageCount = 1; + } + Pagination tmpPagination = new Pagination(tmpPageCount, 0); + tmpPagination.setSkin(new CustomPaginationSkin(tmpPagination)); + VBox.setVgrow(tmpPagination, Priority.ALWAYS); + HBox.setHgrow(tmpPagination, Priority.ALWAYS); + return tmpPagination; + } + // + /** + * Cancels import task and interrupts the corresponding thread. */ private void interruptImport() { this.importTask.cancel(); this.importerThread.interrupt(); } // - /** - * Cancels export task and interrupts the corresponding thread + * Cancels export task and interrupts the corresponding thread. */ private void interruptExport() { this.exportTask.cancel(); this.exporterThread.interrupt(); } // - /** - * Gets called by the cancel fragmentation button + * Gets called by the cancel fragmentation button. */ private void interruptFragmentation() { //cancel() of the task was overridden to shut down the executor service in FragmentationService @@ -1059,35 +1057,30 @@ private void interruptFragmentation() { this.fragmentationButton.setDisable(false); } // - /** - * Starts fragmentation for only one algorithm + * Starts fragmentation for only one algorithm. */ private void startFragmentation() { this.startFragmentation(false); } // - /** - * Starts fragmentation task and opens fragment and itemization tabs + * Starts fragmentation task and opens fragment and itemization tabs. */ private void startFragmentation(boolean isPipelining) { long tmpStartTime = System.nanoTime(); - this.cancelFragmentationButton.setPrefWidth(GuiDefinitions.GUI_BUTTON_WIDTH_VALUE); - this.cancelFragmentationButton.setMinWidth(GuiDefinitions.GUI_BUTTON_WIDTH_VALUE); - this.cancelFragmentationButton.setMaxWidth(GuiDefinitions.GUI_BUTTON_WIDTH_VALUE); - LOGGER.info("Start of method startFragmentation"); - List tmpSelectedMolecules = this.moleculeDataModelList.stream().filter(mol -> mol.isSelected()).collect(Collectors.toList()); + MainViewController.LOGGER.info("Start of method startFragmentation"); + List tmpSelectedMolecules = this.moleculeDataModelList.stream().filter(MoleculeDataModel::isSelected).toList(); int tmpNumberOfCores = this.settingsContainer.getNumberOfTasksForFragmentationSetting(); try { this.fragmentationButton.setDisable(true); this.cancelFragmentationButton.setVisible(true); - this.parallelFragmentationMainTask = new Task() { + this.parallelFragmentationMainTask = new Task<>() { @Override protected Void call() throws Exception { if (isPipelining) { - MainViewController.this.fragmentationService.startPipelineFragmentation(tmpSelectedMolecules, - tmpNumberOfCores); + MainViewController.this.fragmentationService.startPipelineFragmentation(tmpSelectedMolecules, + tmpNumberOfCores); // fragmentationService.startPipelineFragmentationMolByMol(tmpSelectedMolecules, tmpNumberOfCores); } else { MainViewController.this.fragmentationService.startSingleFragmentation(tmpSelectedMolecules, @@ -1096,13 +1089,14 @@ protected Void call() throws Exception { return null; } + // @Override public boolean cancel(boolean anInterruptThread) { MainViewController.this.fragmentationService.abortExecutor(); return super.cancel(anInterruptThread); } }; - this.parallelFragmentationMainTask.setOnSucceeded(event -> { + this.parallelFragmentationMainTask.setOnSucceeded(event -> //note: setOnSucceeded() takes place in the JavaFX GUI thread again but still runLater() is necessary to wait // for the thread to be free for the update Platform.runLater(() -> { @@ -1125,8 +1119,8 @@ public boolean cancel(boolean anInterruptThread) { } catch (Exception anException) { MainViewController.LOGGER.log(Level.SEVERE, anException.toString(), anException); } - }); - }); + }) + ); this.parallelFragmentationMainTask.setOnCancelled(event -> { this.updateStatusBar(this.fragmentationThread, Message.get("Status.canceled")); this.mainView.getMainMenuBar().getExportMenu().setDisable(false); @@ -1158,57 +1152,43 @@ public boolean cancel(boolean anInterruptThread) { } } // - /** - * Adds a tab for fragments and a tab for items (results of fragmentation) + * Adds a tab for fragments and a tab for items (results of fragmentation). * - * @param aFragmentationName + * @param aFragmentationName name of the fragmentation process */ private void addFragmentationResultTabs(String aFragmentationName) { //fragments tab Tab tmpFragmentsTab = this.createFragmentsTab(aFragmentationName); //itemization tab Tab tmpItemsTab = this.createItemsTab(aFragmentationName); - // this.mainTabPane.getSelectionModel().select(tmpFragmentsTab); } // - /** - * Creates and returns a tab, which visualizes the resulting fragments of the fragmentation with given name + * Creates and returns a tab, which visualizes the resulting fragments of the fragmentation with given name. * * @param aFragmentationName String, unique name for fragmentation job * @return Tab */ private Tab createFragmentsTab(String aFragmentationName){ - FragmentsDataTableView tmpFragmentsDataTableView = new FragmentsDataTableView(); + FragmentsDataTableView tmpFragmentsDataTableView = new FragmentsDataTableView(this.configuration); GridTabForTableView tmpFragmentsTab = new GridTabForTableView(Message.get("MainTabPane.fragmentsTab.title") + " - " + aFragmentationName, TabNames.FRAGMENTS.name(), tmpFragmentsDataTableView); this.mainTabPane.getTabs().add(tmpFragmentsTab); ObservableList tmpList = FXCollections.observableArrayList(this.mapOfFragmentDataModelLists.get(aFragmentationName)); - for(MoleculeDataModel tmpMoleculeDataModel : tmpList){ + for (MoleculeDataModel tmpMoleculeDataModel : tmpList) { tmpMoleculeDataModel.setStructureImageWidth(tmpFragmentsDataTableView.getStructureColumn().getWidth()); } tmpFragmentsDataTableView.setItemsList(tmpList); - int tmpRowsPerPage = this.settingsContainer.getRowsPerPageSetting(); - int tmpPageCount = tmpList.size() / tmpRowsPerPage; - if (tmpList.size() % tmpRowsPerPage > 0) { - tmpPageCount++; - } - if(tmpList.isEmpty() || tmpList.size() == 0){ - tmpPageCount = 1; - } - Pagination tmpPagination = new Pagination(tmpPageCount, 0); - tmpPagination.setSkin(new CustomPaginationSkin(tmpPagination)); - tmpPagination.setPageFactory((pageIndex) -> tmpFragmentsDataTableView.createFragmentsTableViewPage(pageIndex, this.settingsContainer)); - VBox.setVgrow(tmpPagination, Priority.ALWAYS); - HBox.setHgrow(tmpPagination, Priority.ALWAYS); + Pagination tmpPagination = this.createPaginationWithSuitablePageCount(tmpList.size()); + tmpPagination.setPageFactory(pageIndex -> tmpFragmentsDataTableView.createFragmentsTableViewPage(pageIndex, this.settingsContainer)); tmpFragmentsTab.addPaginationToGridPane(tmpPagination); Button tmpExportCsvButton = GuiUtil.getButtonOfStandardSize(Message.get("MainTabPane.fragments.buttonCSV.txt")); - tmpExportCsvButton.setTooltip(new Tooltip(Message.get("MainTabPane.fragments.buttonCSV.tooltip"))); + tmpExportCsvButton.setTooltip(GuiUtil.createTooltip(Message.get("MainTabPane.fragments.buttonCSV.tooltip"))); Button tmpExportPdfButton = GuiUtil.getButtonOfStandardSize(Message.get("MainTabPane.fragments.buttonPDF.txt")); - tmpExportPdfButton.setTooltip(new Tooltip(Message.get("MainTabPane.fragments.buttonPDF.tooltip"))); + tmpExportPdfButton.setTooltip(GuiUtil.createTooltip(Message.get("MainTabPane.fragments.buttonPDF.tooltip"))); Button tmpCancelExportButton = GuiUtil.getButtonOfStandardSize(Message.get("MainTabPane.fragments.buttonCancelExport.txt")); - tmpCancelExportButton.setTooltip(new Tooltip(Message.get("MainTabPane.fragments.buttonCancelExport.tooltip"))); + tmpCancelExportButton.setTooltip(GuiUtil.createTooltip(Message.get("MainTabPane.fragments.buttonCancelExport.tooltip"))); tmpCancelExportButton.visibleProperty().bind(this.isExportRunningProperty); HBox tmpExportButtonsHBox = new HBox(); tmpExportButtonsHBox.setPadding(new Insets(GuiDefinitions.GUI_INSETS_VALUE, GuiDefinitions.GUI_INSETS_VALUE, GuiDefinitions.GUI_INSETS_VALUE, GuiDefinitions.GUI_INSETS_VALUE)); @@ -1225,20 +1205,19 @@ private Tab createFragmentsTab(String aFragmentationName){ tmpViewButtonsHBox.setAlignment(Pos.CENTER_RIGHT); tmpViewButtonsHBox.setMaxWidth(GuiDefinitions.GUI_GRIDPANE_FOR_NODE_ALIGNMENT_THIRD_COL_WIDTH); Button tmpOpenOverviewViewButton = GuiUtil.getButtonOfStandardSize(Message.get("MainView.showOverviewViewButton.text")); - tmpOpenOverviewViewButton.setTooltip(new Tooltip(Message.get("MainView.showOverviewViewButton.tooltip"))); + tmpOpenOverviewViewButton.setTooltip(GuiUtil.createTooltip(Message.get("MainView.showOverviewViewButton.tooltip"))); Button tmpOpenHistogramViewButton = GuiUtil.getButtonOfStandardSize(Message.get("MainView.showHistogramViewButton.text")); - tmpOpenHistogramViewButton.setTooltip(new Tooltip(Message.get("MainView.showHistogramViewButton.tooltip"))); + tmpOpenHistogramViewButton.setTooltip(GuiUtil.createTooltip(Message.get("MainView.showHistogramViewButton.tooltip"))); tmpViewButtonsHBox.getChildren().addAll(tmpOpenOverviewViewButton, tmpOpenHistogramViewButton); tmpFragmentsTab.addNodeToGridPane(tmpViewButtonsHBox, 2, 1, 1, 1); tmpOpenOverviewViewButton.setOnAction(event -> this.openOverviewView(OverviewViewController.DataSources.FRAGMENTS_TAB)); tmpOpenHistogramViewButton.setOnAction(event -> this.openHistogramView()); - if(tmpList.size() == 0){ + if (tmpList.isEmpty()) { tmpOpenOverviewViewButton.setDisable(true); tmpOpenHistogramViewButton.setDisable(true); } - tmpFragmentsDataTableView.setOnSort((EventHandler>) event -> { - GuiUtil.sortTableViewGlobally(event, tmpPagination, tmpRowsPerPage); - }); + int tmpRowsPerPage = this.settingsContainer.getRowsPerPageSetting(); + tmpFragmentsDataTableView.setOnSort((EventHandler>) event -> GuiUtil.sortTableViewGlobally(event, tmpPagination, tmpRowsPerPage)); tmpFragmentsDataTableView.widthProperty().addListener((observable, oldValue, newValue) -> { for(Object tmpObject : tmpFragmentsDataTableView.getItems()) { ((MoleculeDataModel) tmpObject).setStructureImageWidth(tmpFragmentsDataTableView.getStructureColumn().getWidth()); @@ -1257,38 +1236,27 @@ private Tab createFragmentsTab(String aFragmentationName){ } // /** - * Creates and returns a tab which visualizes the resulting fragments of each molecule that has undergone the fragmentation with the given name + * Creates and returns a tab which visualizes the resulting fragments of each molecule that has undergone the + * fragmentation with the given name. * * @param aFragmentationName String, unique name for the fragmentation job * @return Tab */ private Tab createItemsTab(String aFragmentationName){ - int tmpAmount = GuiUtil.getLargestNumberOfFragmentsForGivenMoleculeListAndFragmentationName(this.moleculeDataModelList, aFragmentationName); - ItemizationDataTableView tmpItemizationDataTableView = new ItemizationDataTableView(tmpAmount, aFragmentationName); + ItemizationDataTableView tmpItemizationDataTableView = new ItemizationDataTableView(aFragmentationName, this.configuration); tmpItemizationDataTableView.setItemsList( - this.moleculeDataModelList.stream().filter(x -> x.hasMoleculeUndergoneSpecificFragmentation(aFragmentationName)).collect(Collectors.toList())); + this.moleculeDataModelList.stream().filter(x -> x.hasMoleculeUndergoneSpecificFragmentation(aFragmentationName)).toList()); GridTabForTableView tmpItemizationTab = new GridTabForTableView(Message.get("MainTabPane.itemizationTab.title") + " - " + aFragmentationName, TabNames.ITEMIZATION.name(), tmpItemizationDataTableView); this.mainTabPane.getTabs().add(tmpItemizationTab); - int tmpRowsPerPage = this.settingsContainer.getRowsPerPageSetting(); - int tmpPageCount = this.moleculeDataModelList.size() / tmpRowsPerPage; - if (this.moleculeDataModelList.size() % tmpRowsPerPage > 0) { - tmpPageCount++; - } - if(this.moleculeDataModelList.isEmpty() || this.moleculeDataModelList.size() == 0){ - tmpPageCount = 1; - } - Pagination tmpPagination = new Pagination(tmpPageCount, 0); - tmpPagination.setSkin(new CustomPaginationSkin(tmpPagination)); - tmpPagination.setPageFactory((pageIndex) -> tmpItemizationDataTableView.createItemizationTableViewPage(pageIndex, aFragmentationName, this.settingsContainer)); - VBox.setVgrow(tmpPagination, Priority.ALWAYS); - HBox.setHgrow(tmpPagination, Priority.ALWAYS); + Pagination tmpPagination = this.createPaginationWithSuitablePageCount(this.moleculeDataModelList.size()); + tmpPagination.setPageFactory(pageIndex -> tmpItemizationDataTableView.createItemizationTableViewPage(pageIndex, aFragmentationName, this.settingsContainer)); tmpItemizationTab.addPaginationToGridPane(tmpPagination); Button tmpItemizationTabExportPDfButton = GuiUtil.getButtonOfStandardSize(Message.get("MainTabPane.itemizationTab.pdfButton.txt")); - tmpItemizationTabExportPDfButton.setTooltip(new Tooltip(Message.get("MainTabPane.itemizationTab.pdfButton.tooltip"))); + tmpItemizationTabExportPDfButton.setTooltip(GuiUtil.createTooltip(Message.get("MainTabPane.itemizationTab.pdfButton.tooltip"))); Button tmpItemizationExportCsvButton = GuiUtil.getButtonOfStandardSize(Message.get("MainTabPane.itemizationTab.csvButton.txt")); - tmpItemizationExportCsvButton.setTooltip(new Tooltip(Message.get("MainTabPane.itemizationTab.csvButton.tooltip"))); + tmpItemizationExportCsvButton.setTooltip(GuiUtil.createTooltip(Message.get("MainTabPane.itemizationTab.csvButton.tooltip"))); Button tmpCancelExportButton = GuiUtil.getButtonOfStandardSize(Message.get("MainTabPane.fragments.buttonCancelExport.txt")); - tmpCancelExportButton.setTooltip(new Tooltip(Message.get("MainTabPane.fragments.buttonCancelExport.tooltip"))); + tmpCancelExportButton.setTooltip(GuiUtil.createTooltip(Message.get("MainTabPane.fragments.buttonCancelExport.tooltip"))); tmpCancelExportButton.visibleProperty().bind(this.isExportRunningProperty); tmpItemizationExportCsvButton.setOnAction(event -> this.exportFile(Exporter.ExportTypes.ITEM_CSV_FILE)); tmpItemizationTabExportPDfButton.setOnAction(event -> this.exportFile(Exporter.ExportTypes.ITEM_PDF_FILE)); @@ -1305,13 +1273,12 @@ private Tab createItemsTab(String aFragmentationName){ tmpViewButtonsHBox.setAlignment(Pos.CENTER_RIGHT); tmpViewButtonsHBox.setMaxWidth(GuiDefinitions.GUI_GRIDPANE_FOR_NODE_ALIGNMENT_THIRD_COL_WIDTH); Button tmpOpenHistogramViewButton = GuiUtil.getButtonOfStandardSize(Message.get("MainView.showHistogramViewButton.text")); - tmpOpenHistogramViewButton.setTooltip(new Tooltip(Message.get("MainView.showHistogramViewButton.tooltip"))); + tmpOpenHistogramViewButton.setTooltip(GuiUtil.createTooltip(Message.get("MainView.showHistogramViewButton.tooltip"))); tmpViewButtonsHBox.getChildren().add(tmpOpenHistogramViewButton); tmpItemizationTab.addNodeToGridPane(tmpViewButtonsHBox, 2, 1, 1, 1); tmpOpenHistogramViewButton.setOnAction(event -> this.openHistogramView()); - tmpItemizationDataTableView.setOnSort((EventHandler>) event -> { - GuiUtil.sortTableViewGlobally(event, tmpPagination, tmpRowsPerPage); - }); + int tmpRowsPerPage = this.settingsContainer.getRowsPerPageSetting(); + tmpItemizationDataTableView.setOnSort((EventHandler>) event -> GuiUtil.sortTableViewGlobally(event, tmpPagination, tmpRowsPerPage)); tmpItemizationDataTableView.widthProperty().addListener((observable, oldValue, newValue) -> { for(Object tmpObject : tmpItemizationDataTableView.getItems()) { ((MoleculeDataModel) tmpObject).setStructureImageWidth(tmpItemizationDataTableView.getMoleculeStructureColumn().getWidth()); @@ -1325,14 +1292,14 @@ private Tab createItemsTab(String aFragmentationName){ GuiUtil.copySelectedTableViewCellsToClipboard(tmpItemizationDataTableView); } }); - if(this.mapOfFragmentDataModelLists.get(aFragmentationName).size() == 0 ){ + if (this.mapOfFragmentDataModelLists.get(aFragmentationName).isEmpty()) { tmpOpenHistogramViewButton.setDisable(true); } return tmpItemizationTab; } // /** - * Clears the gui and all collections + * Clears the gui and all collections. */ private void clearGuiAndCollections() { this.moleculeDataModelList.clear(); @@ -1341,24 +1308,28 @@ private void clearGuiAndCollections() { this.mainTabPane.getTabs().clear(); } // - /** - * Returns the items list of the table view of the selected tab + * Returns the items list of the table view of the selected tab. * * @param aTabName Enum which specifies which kind of tab - * @return List + * @return List {@literal <}MoleculeDataModel{@literal >} */ - private List getItemsListOfSelectedFragmenterByTabId(TabNames aTabName) { - return ((IDataTableView) ((GridTabForTableView) (this.mainTabPane.getTabs().stream().filter(tab -> - ((GridTabForTableView) this.mainTabPane.getSelectionModel().getSelectedItem()).getFragmentationNameOutOfTitle().equals(((GridTabForTableView) tab).getFragmentationNameOutOfTitle()) && tab.getId().equals(aTabName.name()) - ).findFirst().get())).getTableView()).getItemsList(); + private List getItemsListOfSelectedFragmentationByTabId(TabNames aTabName) { + GridTabForTableView tmpSelectedTab = (GridTabForTableView) (this.mainTabPane.getTabs().stream().filter(tab -> + ((GridTabForTableView) this.mainTabPane.getSelectionModel().getSelectedItem()).getFragmentationNameOutOfTitle() + .equals(((GridTabForTableView) tab).getFragmentationNameOutOfTitle()) && tab.getId().equals(aTabName.name()) + ).findFirst().orElse(null)); + if (tmpSelectedTab == null) { + return new ArrayList<>(); + } else { + return ((IDataTableView) tmpSelectedTab.getTableView()).getItemsList(); + } } // - /** - * Updates StatusBar + * Updates StatusBar. * - * @param aThread Thread which was started or end + * @param aThread Thread which was started or ended * @param aMessage String message to display in StatusBar */ private void updateStatusBar(Thread aThread, String aMessage) { @@ -1367,9 +1338,8 @@ private void updateStatusBar(Thread aThread, String aMessage) { this.mainView.getStatusBar().getStatusLabel().setText(aMessage); this.mainView.getStatusBar().getStatusLabel().setVisible(true); this.mainView.getStatusBar().getProgressBar().setVisible(true); - return; - } - if (this.threadList.contains(aThread)) { + //return; + } else { this.threadList.remove(aThread); if (this.threadList.isEmpty()) { this.mainView.getStatusBar().getProgressBar().setVisible(false); @@ -1379,38 +1349,32 @@ private void updateStatusBar(Thread aThread, String aMessage) { this.mainView.getStatusBar().getStatusLabel().setText( this.getStatusMessageByThreadType( Objects.requireNonNull(ThreadType.get( - this.threadList.get(this.threadList.size() - 1).getName() + this.threadList.getLast().getName() )) ) ); } } // - /** - * Returns status message as string by given ThreadType + * Returns status message as string by given ThreadType. * * @param aThreadType ThreadType * @return String status message */ private String getStatusMessageByThreadType(ThreadType aThreadType) { - switch (aThreadType) { - case FRAGMENTATION_THREAD: - return Message.get("Status.running"); - case IMPORT_THREAD: - return Message.get("Status.importing"); - case EXPORT_THREAD: - return Message.get("Status.exporting"); - default: - return "Could not find message"; - } + return switch (aThreadType) { + case FRAGMENTATION_THREAD -> Message.get("Status.running"); + case IMPORT_THREAD -> Message.get("Status.importing"); + case EXPORT_THREAD -> Message.get("Status.exporting"); + default -> "Could not find message"; + }; } // // // - /** - * Enum for different thread types, set as thread name + * Enum for different thread types, set as thread name. */ public enum ThreadType { /** @@ -1426,12 +1390,12 @@ public enum ThreadType { */ EXPORT_THREAD("Export_Thread"); - private String threadName; + private final String threadName; ThreadType(String aThreadName) { this.threadName = aThreadName; } - + // /** * Returns the name of this thread type * @@ -1440,7 +1404,7 @@ public enum ThreadType { public String getThreadName() { return this.threadName; } - + // /** * Reverse lookup * Returns ThreadType by given thread name; diff --git a/src/main/java/de/unijena/cheminf/mortar/controller/OverviewViewController.java b/src/main/java/de/unijena/cheminf/mortar/controller/OverviewViewController.java index 17651c93..aad21b1a 100644 --- a/src/main/java/de/unijena/cheminf/mortar/controller/OverviewViewController.java +++ b/src/main/java/de/unijena/cheminf/mortar/controller/OverviewViewController.java @@ -25,12 +25,7 @@ package de.unijena.cheminf.mortar.controller; -/** - * TODO: - * - add export functionality to overview view - * - extract enlarged structure view, so that it can also be used in other places - */ - +import de.unijena.cheminf.mortar.configuration.IConfiguration; import de.unijena.cheminf.mortar.gui.controls.CustomPaginationSkin; import de.unijena.cheminf.mortar.gui.util.GuiDefinitions; import de.unijena.cheminf.mortar.gui.util.GuiUtil; @@ -79,7 +74,6 @@ import org.openscience.cdk.exception.CDKException; -import java.io.InputStream; import java.util.ArrayList; import java.util.List; import java.util.Objects; @@ -116,11 +110,7 @@ public static enum DataSources { /** * Enum value for an item of an items tab as data source. */ - ITEM_WITH_FRAGMENTS_SAMPLE, - /** - * Enum value for any other data source. - */ - ANY; + ITEM_WITH_FRAGMENTS_SAMPLE; } // // @@ -130,45 +120,45 @@ public static enum DataSources { */ public static final String VIEW_TOOL_NAME_FOR_DISPLAY = Message.get("MainView.menuBar.viewsMenu.overviewViewMenuItem.text"); /** - * Width of grid lines of the structure grid pane in overview view + * Width of grid lines of the structure grid pane in overview view. */ public static final double OVERVIEW_VIEW_STRUCTURE_GRID_PANE_GRIDLINES_WIDTH = 8.0; /** - * Minimum value for the width of structure images displayed in the overview view + * Minimum value for the width of structure images displayed in the overview view. */ public static final double OVERVIEW_VIEW_STRUCTURE_IMAGE_MIN_WIDTH = 30.0; /** - * Minimum value for the height of structure images displayed in the overview view + * Minimum value for the height of structure images displayed in the overview view. */ public static final double OVERVIEW_VIEW_STRUCTURE_IMAGE_MIN_HEIGHT = 20.0; /** - * Default value for columns of structure images per overview view page + * Default value for columns of structure images per overview view page. */ public static final int OVERVIEW_VIEW_STRUCTURE_GRID_PANE_COLUMNS_PER_PAGE_DEFAULT = 5; /** - * Default value for rows of structure images per overview view page + * Default value for rows of structure images per overview view page. */ public static final int OVERVIEW_VIEW_STRUCTURE_GRID_PANE_ROWS_PER_PAGE_DEFAULT = 5; /** - * Minimum value for the width of the enlarged structure view + * Minimum value for the width of the enlarged structure view. */ public static final double ENLARGED_STRUCTURE_VIEW_MIN_WIDTH_VALUE = 250.0; /** - * Minimum value for the height of the enlarged structure view + * Minimum value for the height of the enlarged structure view. */ public static final double ENLARGED_STRUCTURE_VIEW_MIN_HEIGHT_VALUE = 200.0; /** - * Initial width of the enlarged structure view's scene + * Initial width of the enlarged structure view's scene. */ public static final double ENLARGED_STRUCTURE_VIEW_SCENE_INITIAL_WIDTH = 500.0; /** - * Initial height of the enlarged structure view's scene + * Initial height of the enlarged structure view's scene. */ public static final double ENLARGED_STRUCTURE_VIEW_SCENE_INITIAL_HEIGHT = 400.0; /** - * Ratio of width and height of the enlarged structure view's structure image to the one of its parent stack pane + * Ratio of width and height of the enlarged structure view's structure image to the one of its parent stack pane. */ - public static final double ENLARGED_STRUCTURE_VIEW_IMAGE_TO_STACKPANE_SIZE_RATIO = 0.9; + public static final double ENLARGED_STRUCTURE_VIEW_IMAGE_TO_STACK_PANE_SIZE_RATIO = 0.9; // // // @@ -179,6 +169,10 @@ public static enum DataSources { // // // + /** + * Configuration class to read resource file paths from. + */ + private final IConfiguration configuration; /** * Integer property that holds the number of rows of structure images to be displayed per page. */ @@ -190,7 +184,7 @@ public static enum DataSources { /** * All settings of this view tool, encapsulated in JavaFX properties for binding in GUI and persistence. */ - private final List settings; + private final List> settings; // // // @@ -234,8 +228,13 @@ public static enum DataSources { * Boolean value saying whether the option to show a specific structure in the main view via double-click or context menu * should be available. The value depends on the given data source. */ - //TODO: separate this into two variables? One for highlighting the first structure, one for being able to jump to structures in the main view? private boolean withShowInMainViewOption; + /** + * Boolean value saying whether the first structure in the overview should be highlighted, e.g. because it is + * a fragment and all other structures are its parents or because it is the parent structure and all other structures + * are its fragments. + */ + private boolean withFirstStructureHighlight; /** * Boolean value whether an event to return to a specific structure in the MainView occurred. */ @@ -253,7 +252,7 @@ public static enum DataSources { */ private ScheduledFuture scheduledFuture; /** - * Boolean value to distinguish between drag from mouse click events. + * Boolean value to distinguish between drag and mouse click events. */ private boolean dragFlag; // @@ -261,8 +260,11 @@ public static enum DataSources { // /** * Constructor, initialises all settings with their default values. Does *not* open the view. + * + * @param aConfiguration configuration instance to read resource file paths from */ - public OverviewViewController() { + public OverviewViewController(IConfiguration aConfiguration) { + this.configuration = aConfiguration; this.settings = new ArrayList<>(2); this.rowsPerPageSetting = new SimpleIntegerProperty(this, //the name could be displayed but is not used for that currently @@ -304,6 +306,7 @@ public void set(int newValue) throws NullPointerException, IllegalArgumentExcept * Returns the index of the structure in the MoleculeDataModelList that has been the latest target to a left- or * right-click on its image view if an event to return to a specific structure in the main view occurred; else -1 * is returned. + * * @return Integer value of the cached index of structure or -1 */ public int getCachedIndexOfStructureInMoleculeDataModelList() { @@ -333,13 +336,10 @@ public void resetCachedIndexOfStructureInMoleculeDataModelList() { *

*/ @Override - public List settingsProperties() { + public List> settingsProperties() { //note: see comments in constructor for how the setting values are transferred in both directions (GUI <-> Properties) return this.settings; } - /** - * {@inheritDoc} - */ @Override public String getViewToolNameForDisplay() { return OverviewViewController.VIEW_TOOL_NAME_FOR_DISPLAY; @@ -356,9 +356,6 @@ public void restoreDefaultSettings() { this.rowsPerPageSetting.set(OverviewViewController.OVERVIEW_VIEW_STRUCTURE_GRID_PANE_ROWS_PER_PAGE_DEFAULT); this.columnsPerPageSetting.set(OverviewViewController.OVERVIEW_VIEW_STRUCTURE_GRID_PANE_COLUMNS_PER_PAGE_DEFAULT); } - /** - * {@inheritDoc} - */ @Override public boolean canBeUsedOnTab(TabNames aTabNameEnumConstant) { return switch (aTabNameEnumConstant) { @@ -400,6 +397,7 @@ public void initializeAndShowOverviewView( ? "OverviewView.titleOfView.molecule" : "OverviewView.titleOfView.fragment") + (aMoleculeDataModelList.size() != 1 ? "s" : "")); this.withShowInMainViewOption = true; + this.withFirstStructureHighlight = false; } case PARENT_MOLECULES_SAMPLE -> { this.overviewViewTitle = Message.get("OverviewView.titleOfDataSource.parentMolecules") + @@ -408,6 +406,7 @@ public void initializeAndShowOverviewView( Message.get(((aMoleculeDataModelList.size() - 1 == 1) ? "OverviewView.titleOfView.molecule" : "OverviewView.titleOfView.molecules")); this.withShowInMainViewOption = false; + this.withFirstStructureHighlight = true; } case ITEM_WITH_FRAGMENTS_SAMPLE -> { this.overviewViewTitle = Message.get("OverviewView.titleOfDataSource.itemsTab") + @@ -416,19 +415,18 @@ public void initializeAndShowOverviewView( Message.get(((aMoleculeDataModelList.size() - 1 == 1) ? "OverviewView.titleOfView.fragment" : "OverviewView.titleOfView.fragments")); this.withShowInMainViewOption = false; - } - case ANY -> { - this.setOverviewViewTitleForDefaultOrAnyDataSource(aTabName, aMoleculeDataModelList.size()); + this.withFirstStructureHighlight = true; } default -> { - this.setOverviewViewTitleForDefaultOrAnyDataSource(aTabName, aMoleculeDataModelList.size()); + this.setOverviewViewTitleForDefaultDataSource(aTabName, aMoleculeDataModelList.size()); } } this.mainStage = aMainStage; this.dataSource = aDataSource; this.moleculeDataModelList = aMoleculeDataModelList; - if (this.overviewView == null) + if (this.overviewView == null) { this.overviewView = new OverviewView(this.columnsPerPageSetting.get(), this.rowsPerPageSetting.get()); + } this.overviewViewStage = new Stage(); Scene tmpScene = new Scene(this.overviewView, GuiDefinitions.GUI_MAIN_VIEW_WIDTH_VALUE, GuiDefinitions.GUI_MAIN_VIEW_HEIGHT_VALUE); @@ -436,14 +434,10 @@ public void initializeAndShowOverviewView( this.overviewViewStage.initModality(Modality.WINDOW_MODAL); this.overviewViewStage.initOwner(this.mainStage); this.overviewViewStage.setTitle(this.overviewViewTitle); - InputStream tmpImageInputStream = MainViewController.class.getResourceAsStream( - "/de/unijena/cheminf/mortar/images/Mortar_Logo_Icon1.png" - ); - if (tmpImageInputStream != null) { - this.overviewViewStage.getIcons().add(new Image(tmpImageInputStream)); - } else { - OverviewViewController.LOGGER.log(Level.WARNING, "MORTAR icon could not be imported."); - } + String tmpIconURL = this.getClass().getClassLoader().getResource( + this.configuration.getProperty("mortar.imagesFolder") + + this.configuration.getProperty("mortar.logo.icon.name")).toExternalForm(); + this.overviewViewStage.getIcons().add(new Image(tmpIconURL)); this.overviewViewStage.setMinHeight(GuiDefinitions.GUI_MAIN_VIEW_HEIGHT_VALUE); this.overviewViewStage.setMinWidth(GuiDefinitions.GUI_MAIN_VIEW_WIDTH_VALUE); // @@ -451,12 +445,12 @@ public void initializeAndShowOverviewView( if (this.moleculeDataModelList.size() % (this.rowsPerPageSetting.get() * this.columnsPerPageSetting.get()) > 0) { tmpPageCount++; } - if(this.moleculeDataModelList.size() == 0){ + if (this.moleculeDataModelList.isEmpty()) { tmpPageCount = 1; } Pagination tmpPagination = new Pagination(tmpPageCount, 0); tmpPagination.setSkin(new CustomPaginationSkin(tmpPagination)); - tmpPagination.setPageFactory((aPageIndex) -> this.createOverviewViewPage(aPageIndex, + tmpPagination.setPageFactory(aPageIndex -> this.createOverviewViewPage(aPageIndex, this.rowsPerPageSetting.get(), this.columnsPerPageSetting.get())); VBox.setVgrow(tmpPagination, Priority.ALWAYS); HBox.setHgrow(tmpPagination, Priority.ALWAYS); @@ -481,7 +475,7 @@ public void initializeAndShowOverviewView( * @param aTabName given title for the overview view, derived from the tab name where it was opened * @throws NullPointerException if tab name is null */ - private void setOverviewViewTitleForDefaultOrAnyDataSource(String aTabName, int aMoleculeDataModelListSize) + private void setOverviewViewTitleForDefaultDataSource(String aTabName, int aMoleculeDataModelListSize) throws NullPointerException { Objects.requireNonNull(aTabName, "aTabName (instance of String) is null"); if (aTabName.isBlank()) { @@ -493,18 +487,15 @@ private void setOverviewViewTitleForDefaultOrAnyDataSource(String aTabName, int Message.get(((aMoleculeDataModelListSize - 1 == 1) ? "OverviewView.titleOfView.molecule" : "OverviewView.titleOfView.molecules")); this.withShowInMainViewOption = false; + this.withFirstStructureHighlight = false; } /** * Adds listeners and event handlers to elements of the overview view. */ private void addListeners() { //listener for resize events - ChangeListener tmpStageSizeListener = (observable, oldValue, newValue) -> { - Platform.runLater(() -> { - this.createOverviewViewPage(this.overviewView.getPagination().getCurrentPageIndex(), - this.rowsPerPageSetting.get(), this.columnsPerPageSetting.get()); - }); - }; + ChangeListener tmpStageSizeListener = (observable, oldValue, newValue) -> Platform.runLater(() -> this.createOverviewViewPage(this.overviewView.getPagination().getCurrentPageIndex(), + this.rowsPerPageSetting.get(), this.columnsPerPageSetting.get())); this.overviewViewStage.heightProperty().addListener(tmpStageSizeListener); this.overviewViewStage.widthProperty().addListener(tmpStageSizeListener); // @@ -512,16 +503,14 @@ private void addListeners() { this.overviewViewStage.addEventFilter(WindowEvent.WINDOW_CLOSE_REQUEST, event -> this.closeOverviewViewEvent()); // //close button listener - this.overviewView.getCloseButton().setOnAction((actionEvent) -> { - this.closeOverviewViewEvent(); - }); + this.overviewView.getCloseButton().setOnAction(actionEvent -> this.closeOverviewViewEvent()); // //listener to distinguish a drag from a click event (on the image views) /* * The following block of code was inspired by a post of the user "mipa" of the stackoverflow community; * link to their answer / comment: https://stackoverflow.com/a/36245807 (2022_12_01; 12:00 GMT) */ - this.overviewView.getStructureGridPane().setOnMouseDragged((aMouseEvent) -> { + this.overviewView.getStructureGridPane().setOnMouseDragged(aMouseEvent -> { if (aMouseEvent.getButton().equals(MouseButton.PRIMARY)) { this.dragFlag = true; } @@ -532,7 +521,7 @@ private void addListeners() { * The following block of code was inspired by a post of the user "mipa" of the stackoverflow community; * link to their answer / comment: https://stackoverflow.com/a/36245807 (2022_12_01; 12:00 GMT) */ - this.overviewView.getStructureGridPane().setOnMouseClicked((aMouseEvent) -> { + this.overviewView.getStructureGridPane().setOnMouseClicked(aMouseEvent -> { if (aMouseEvent.getButton().equals(MouseButton.PRIMARY) && (aMouseEvent.getTarget().getClass().equals(ImageView.class) || (aMouseEvent.getTarget().getClass().equals(StackPane.class) @@ -557,20 +546,18 @@ private void addListeners() { TimeUnit.MILLISECONDS ); } - } else if (aMouseEvent.getClickCount() > 1) { - if (this.scheduledFuture != null && !this.scheduledFuture.isCancelled() + } else if (aMouseEvent.getClickCount() > 1 + && this.scheduledFuture != null + && !this.scheduledFuture.isCancelled() && !this.scheduledFuture.isDone()) { - //terminating the scheduled single-click action - this.scheduledFuture.cancel(false); - //check whether it is the same structure - if (this.getIndexOfStructureInMoleculeDataModelList(aMouseEvent) - == this.cachedIndexOfStructureInMoleculeDataModelList) { - //double-click action - if (this.withShowInMainViewOption) { - this.returnToStructureEventOccurred = true; - this.closeOverviewViewEvent(); - } - } + //terminating the scheduled single-click action + this.scheduledFuture.cancel(false); + //check whether it is the same structure + if (this.getIndexOfStructureInMoleculeDataModelList(aMouseEvent) + == this.cachedIndexOfStructureInMoleculeDataModelList + && (this.withShowInMainViewOption)) { + this.returnToStructureEventOccurred = true; + this.closeOverviewViewEvent(); } } } @@ -596,35 +583,27 @@ private void addListeners() { }); // //apply new grid configuration event handler - EventHandler tmpApplyNewGridConfigurationEventHandler = (actionEvent) -> { - this.applyChangeOfGridConfiguration(false); - }; + EventHandler tmpApplyNewGridConfigurationEventHandler = actionEvent -> this.applyChangeOfGridConfiguration(false); //listeners for apply new grid configuration events this.overviewView.getApplyButton().setOnAction(tmpApplyNewGridConfigurationEventHandler); this.overviewView.getColumnsPerPageTextField().setOnAction(tmpApplyNewGridConfigurationEventHandler); this.overviewView.getRowsPerPageTextField().setOnAction(tmpApplyNewGridConfigurationEventHandler); // //default button listener - this.overviewView.getDefaultButton().setOnAction((actionEvent) -> { - this.applyChangeOfGridConfiguration(true); - }); + this.overviewView.getDefaultButton().setOnAction(actionEvent -> this.applyChangeOfGridConfiguration(true)); // //focused property change listener for columns per page text field this.overviewView.getColumnsPerPageTextField().focusedProperty().addListener((observable, oldValue, newValue) -> { - if (!newValue) { - if (this.overviewView.getColumnsPerPageTextField().getText().isBlank() - || Integer.parseInt(this.overviewView.getColumnsPerPageTextField().getText()) == 0) { - this.overviewView.getColumnsPerPageTextField().setText(Integer.toString(this.columnsPerPageSetting.get())); - } + if (Boolean.FALSE.equals(newValue) && (this.overviewView.getColumnsPerPageTextField().getText().isBlank() + || Integer.parseInt(this.overviewView.getColumnsPerPageTextField().getText()) == 0)) { + this.overviewView.getColumnsPerPageTextField().setText(Integer.toString(this.columnsPerPageSetting.get())); } }); //focused property change listener for rows per page text field this.overviewView.getRowsPerPageTextField().focusedProperty().addListener((observable, oldValue, newValue) -> { - if (!newValue) { - if (this.overviewView.getRowsPerPageTextField().getText().isBlank() - || Integer.parseInt(this.overviewView.getRowsPerPageTextField().getText()) == 0) { - this.overviewView.getRowsPerPageTextField().setText(Integer.toString(this.rowsPerPageSetting.get())); - } + if (Boolean.FALSE.equals(newValue) && (this.overviewView.getRowsPerPageTextField().getText().isBlank() + || Integer.parseInt(this.overviewView.getRowsPerPageTextField().getText()) == 0)) { + this.overviewView.getRowsPerPageTextField().setText(Integer.toString(this.rowsPerPageSetting.get())); } }); // @@ -690,6 +669,7 @@ private void clearGUICachesAtClosing() { this.moleculeDataModelList = null; this.createStructureImages = false; this.withShowInMainViewOption = false; + this.withFirstStructureHighlight = false; if (!this.returnToStructureEventOccurred) { this.resetCachedIndexOfStructureInMoleculeDataModelList(); } @@ -752,7 +732,7 @@ hold the pagination node (since the structureGridPane and pagination node size a //check if the limits for the image dimensions are being exceeded if ((tmpImageHeight >= OverviewViewController.OVERVIEW_VIEW_STRUCTURE_IMAGE_MIN_HEIGHT) && (tmpImageWidth >= OverviewViewController.OVERVIEW_VIEW_STRUCTURE_IMAGE_MIN_WIDTH)) { - //optional setting for change in usage of shadow effect + //optional setting for change in usage of shadow effect - deprecated boolean tmpDrawImagesWithShadow = true; //main loop for generation of the page content generationOfStructureImagesLoop: @@ -770,7 +750,7 @@ hold the pagination node (since the structureGridPane and pagination node size a } //depiction of structure image final Node tmpFinalContentNode; - if (!(tmpIterator == 0 && !this.withShowInMainViewOption)) { + if (!(tmpIterator == 0 && this.withFirstStructureHighlight)) { tmpFinalContentNode = new ImageView( DepictionUtil.depictImageWithZoomAndFillToFitAndWhiteBackground( tmpMoleculeDataModel.getAtomContainer(), 1.0, tmpImageWidth, @@ -797,7 +777,7 @@ hold the pagination node (since the structureGridPane and pagination node size a tmpFinalContentNode = tmpStackPane; } //changing the shadow effects at mouse entering an image - tmpFinalContentNode.setOnMouseEntered((aMouseEvent) -> { + tmpFinalContentNode.setOnMouseEntered(aMouseEvent -> { if (tmpDrawImagesWithShadow) { tmpFinalContentNode.setStyle("-fx-effect: null" + ((tmpFinalContentNode.getClass() == StackPane.class) @@ -805,7 +785,7 @@ hold the pagination node (since the structureGridPane and pagination node size a : "")); } else { tmpFinalContentNode.setStyle( - "-fx-effect: dropshadow(gaussian, rgba(100, 100, 100, 0.8), " + + "-fx-effect: dropshadow(gaussian, rgba(100, 100, 100, 0.6), " + OverviewViewController.OVERVIEW_VIEW_STRUCTURE_GRID_PANE_GRIDLINES_WIDTH + ", 0, 0, 0)" + ((tmpFinalContentNode.getClass() == StackPane.class) @@ -815,7 +795,7 @@ hold the pagination node (since the structureGridPane and pagination node size a } }); //resetting the shadow effects at mouse leaving the image - tmpFinalContentNode.setOnMouseExited((aMouseEvent) -> { + tmpFinalContentNode.setOnMouseExited(aMouseEvent -> { if (tmpDrawImagesWithShadow) { tmpFinalContentNode.setStyle( "-fx-effect: dropshadow(three-pass-box, rgba(100, 100, 100, 0.6), " + @@ -847,7 +827,7 @@ hold the pagination node (since the structureGridPane and pagination node size a tmpErrorLabel.setMinHeight(tmpImageHeight); tmpErrorLabel.setMaxHeight(tmpImageHeight); tmpErrorLabel.setStyle("-fx-alignment: CENTER; -fx-background-color: WHITE"); - Tooltip tmpErrorLabelTooltip = new Tooltip(Message.get("OverviewView.ErrorLabel.tooltip")); + Tooltip tmpErrorLabelTooltip = GuiUtil.createTooltip(Message.get("OverviewView.ErrorLabel.tooltip")); tmpErrorLabel.setTooltip(tmpErrorLabelTooltip); tmpContentNode = new StackPane(tmpErrorLabel); tmpContentNode.disableProperty().set(true); @@ -1001,15 +981,20 @@ private ContextMenu generateContextMenuWithListeners(boolean aForEnlargedStructu ContextMenu tmpContextMenu = new ContextMenu(); //copyImageMenuItem MenuItem tmpCopyImageMenuItem = new MenuItem(Message.get("OverviewView.contextMenu.copyImageMenuItem")); - try { - tmpCopyImageMenuItem.setGraphic(new ImageView(new Image("de/unijena/cheminf/mortar/images/copy_icon_16x16.png"))); - } catch (NullPointerException anException) { - OverviewViewController.LOGGER.log(Level.WARNING, "Copy icon could not be imported."); - } //copySmilesMenuItem MenuItem tmpCopySmilesMenuItem = new MenuItem(Message.get("OverviewView.contextMenu.copySmilesMenuItem")); //copyNameMenuItem MenuItem tmpCopyNameMenuItem = new MenuItem(Message.get("OverviewView.contextMenu.copyNameMenuItem")); + try { + String tmpCopyIconURL = this.getClass().getClassLoader().getResource( + this.configuration.getProperty("mortar.imagesFolder") + + this.configuration.getProperty("mortar.icon.copy.name")).toExternalForm(); + tmpCopyImageMenuItem.setGraphic(new ImageView(new Image(tmpCopyIconURL))); + tmpCopySmilesMenuItem.setGraphic(new ImageView(new Image(tmpCopyIconURL))); + tmpCopyNameMenuItem.setGraphic(new ImageView(new Image(tmpCopyIconURL))); + } catch (NullPointerException anException) { + OverviewViewController.LOGGER.log(Level.WARNING, "Copy icon could not be imported."); + } //adding Listeners to MenuItems //copyImageMenuItem listener tmpCopyImageMenuItem.setOnAction((ActionEvent anActionEvent) -> { @@ -1230,14 +1215,10 @@ private void showEnlargedStructureView(MoleculeDataModel aMoleculeDataModel, Sta tmpEnlargedStructureViewStage.initModality(Modality.WINDOW_MODAL); tmpEnlargedStructureViewStage.initOwner(anOwnerStage); tmpEnlargedStructureViewStage.setTitle(Message.get("OverviewView.enlargedStructureView.title")); - InputStream tmpImageInputStream = MainViewController.class.getResourceAsStream( - "/de/unijena/cheminf/mortar/images/Mortar_Logo_Icon1.png" - ); - if (tmpImageInputStream != null) { - tmpEnlargedStructureViewStage.getIcons().add(new Image(tmpImageInputStream)); - } else { - OverviewViewController.LOGGER.log(Level.WARNING, "MORTAR icon could not be imported"); - } + String tmpIconURL = this.getClass().getClassLoader().getResource( + this.configuration.getProperty("mortar.imagesFolder") + + this.configuration.getProperty("mortar.logo.icon.name")).toExternalForm(); + tmpEnlargedStructureViewStage.getIcons().add(new Image(tmpIconURL)); tmpEnlargedStructureViewStage.setMinHeight(OverviewViewController.ENLARGED_STRUCTURE_VIEW_MIN_HEIGHT_VALUE); tmpEnlargedStructureViewStage.setMinWidth(OverviewViewController.ENLARGED_STRUCTURE_VIEW_MIN_WIDTH_VALUE); // @@ -1251,47 +1232,41 @@ private void showEnlargedStructureView(MoleculeDataModel aMoleculeDataModel, Sta ImageView tmpStructureImage = new ImageView(DepictionUtil.depictImageWithZoomAndFillToFitAndWhiteBackground( aMoleculeDataModel.getAtomContainer(),1.0, OverviewViewController.ENLARGED_STRUCTURE_VIEW_SCENE_INITIAL_WIDTH - * OverviewViewController.ENLARGED_STRUCTURE_VIEW_IMAGE_TO_STACKPANE_SIZE_RATIO, + * OverviewViewController.ENLARGED_STRUCTURE_VIEW_IMAGE_TO_STACK_PANE_SIZE_RATIO, OverviewViewController.ENLARGED_STRUCTURE_VIEW_SCENE_INITIAL_HEIGHT - * OverviewViewController.ENLARGED_STRUCTURE_VIEW_IMAGE_TO_STACKPANE_SIZE_RATIO, + * OverviewViewController.ENLARGED_STRUCTURE_VIEW_IMAGE_TO_STACK_PANE_SIZE_RATIO, true, true )); tmpEnlargedStructureViewStackPane.getChildren().add(tmpStructureImage); - tmpStructureImage.setOnContextMenuRequested((event) -> { - tmpContextMenu.show(tmpStructureImage, event.getScreenX(), event.getScreenY()); - }); + tmpStructureImage.setOnContextMenuRequested(event -> tmpContextMenu.show(tmpStructureImage, event.getScreenX(), event.getScreenY())); //listener for resize events to fit the structure depiction to the view size - ChangeListener tmpStageResizeEventListener = (observable, oldValue, newValue) -> { - Platform.runLater(() -> { - tmpEnlargedStructureViewStackPane.getChildren().clear(); - try { - ImageView tmpUpdatedStructureImage = new ImageView( - DepictionUtil.depictImageWithZoomAndFillToFitAndWhiteBackground( - aMoleculeDataModel.getAtomContainer(), 1.0, - tmpEnlargedStructureViewStackPane.getWidth() - * OverviewViewController.ENLARGED_STRUCTURE_VIEW_IMAGE_TO_STACKPANE_SIZE_RATIO, - tmpEnlargedStructureViewStackPane.getHeight() - * OverviewViewController.ENLARGED_STRUCTURE_VIEW_IMAGE_TO_STACKPANE_SIZE_RATIO, - true, true - ) - ); - tmpEnlargedStructureViewStackPane.getChildren().add(tmpUpdatedStructureImage); - tmpUpdatedStructureImage.setOnContextMenuRequested((event) -> { - tmpContextMenu.show(tmpUpdatedStructureImage, event.getScreenX(), event.getScreenY()); - }); - } catch (CDKException aCDKException) { - //logging and guiMessageAlert at issues with structure depiction - OverviewViewController.LOGGER.log(Level.SEVERE, aCDKException.toString(), aCDKException); - GuiUtil.guiMessageAlert( - Alert.AlertType.WARNING, - Message.get("OverviewView.enlargedStructureView.issueWithStructureDepiction.title"), - Message.get("OverviewView.enlargedStructureView.issueWithStructureDepiction.header"), - Message.get("OverviewView.enlargedStructureView.issueWithStructureDepiction.text") - ); - tmpEnlargedStructureViewStage.close(); - } - }); - }; + ChangeListener tmpStageResizeEventListener = (observable, oldValue, newValue) -> Platform.runLater(() -> { + tmpEnlargedStructureViewStackPane.getChildren().clear(); + try { + ImageView tmpUpdatedStructureImage = new ImageView( + DepictionUtil.depictImageWithZoomAndFillToFitAndWhiteBackground( + aMoleculeDataModel.getAtomContainer(), 1.0, + tmpEnlargedStructureViewStackPane.getWidth() + * OverviewViewController.ENLARGED_STRUCTURE_VIEW_IMAGE_TO_STACK_PANE_SIZE_RATIO, + tmpEnlargedStructureViewStackPane.getHeight() + * OverviewViewController.ENLARGED_STRUCTURE_VIEW_IMAGE_TO_STACK_PANE_SIZE_RATIO, + true, true + ) + ); + tmpEnlargedStructureViewStackPane.getChildren().add(tmpUpdatedStructureImage); + tmpUpdatedStructureImage.setOnContextMenuRequested(event -> tmpContextMenu.show(tmpUpdatedStructureImage, event.getScreenX(), event.getScreenY())); + } catch (CDKException aCDKException) { + //logging and guiMessageAlert at issues with structure depiction + OverviewViewController.LOGGER.log(Level.SEVERE, aCDKException.toString(), aCDKException); + GuiUtil.guiMessageAlert( + Alert.AlertType.WARNING, + Message.get("OverviewView.enlargedStructureView.issueWithStructureDepiction.title"), + Message.get("OverviewView.enlargedStructureView.issueWithStructureDepiction.header"), + Message.get("OverviewView.enlargedStructureView.issueWithStructureDepiction.text") + ); + tmpEnlargedStructureViewStage.close(); + } + }); tmpEnlargedStructureViewStage.heightProperty().addListener(tmpStageResizeEventListener); tmpEnlargedStructureViewStage.widthProperty().addListener(tmpStageResizeEventListener); } catch (CDKException aCDKException) { diff --git a/src/main/java/de/unijena/cheminf/mortar/controller/PipelineSettingsViewController.java b/src/main/java/de/unijena/cheminf/mortar/controller/PipelineSettingsViewController.java index 6018a6d5..03a353fd 100644 --- a/src/main/java/de/unijena/cheminf/mortar/controller/PipelineSettingsViewController.java +++ b/src/main/java/de/unijena/cheminf/mortar/controller/PipelineSettingsViewController.java @@ -25,7 +25,9 @@ package de.unijena.cheminf.mortar.controller; +import de.unijena.cheminf.mortar.configuration.IConfiguration; import de.unijena.cheminf.mortar.gui.util.GuiDefinitions; +import de.unijena.cheminf.mortar.gui.util.GuiUtil; import de.unijena.cheminf.mortar.gui.views.PipelineSettingsView; import de.unijena.cheminf.mortar.message.Message; import de.unijena.cheminf.mortar.model.fragmentation.FragmentationService; @@ -40,14 +42,12 @@ import javafx.scene.control.Button; import javafx.scene.control.ComboBox; import javafx.scene.control.Label; -import javafx.scene.control.Tooltip; import javafx.scene.image.Image; import javafx.scene.image.ImageView; import javafx.scene.layout.GridPane; import javafx.stage.Modality; import javafx.stage.Stage; -import java.io.InputStream; import java.util.Arrays; import java.util.LinkedList; import java.util.List; @@ -55,76 +55,84 @@ import java.util.logging.Logger; /** - * Controller class for the PipelineSettingsView + * Controller class for the PipelineSettingsView. * * @author Felix Baensch * @version 1.0.0.0 */ public class PipelineSettingsViewController { - // private final StringProperty pipelineName; /** - * Stage of the MainView + * Stage of the MainView. */ private final Stage mainStage; + /** + * Configuration class to read resource file paths from. + */ + private final IConfiguration configuration; // // // /** - * PipelineSettingsView + * PipelineSettingsView. */ private PipelineSettingsView pipelineSettingsView; /** - * Stage of the PipelineSettingsView + * Stage of the PipelineSettingsView. */ private Stage pipelineSettingsViewStage; /** - * Counts the amount of fragmentation algorithms in the pipeline + * Counts the amount of fragmentation algorithms in the pipeline. */ private int algorithmCounter; /** - * Service for fragmentation, controls the process of a fragmentation + * Service for fragmentation, controls the process of a fragmentation. */ - private FragmentationService fragmentationService; + private final FragmentationService fragmentationService; /** - * Array of the available fragmentation algorithms + * Array of the available fragmentation algorithms. */ - private IMoleculeFragmenter[] fragmenters; + private final IMoleculeFragmenter[] fragmenters; /** - * Lists of fragmentation algorithms for the pipeline + * Lists of fragmentation algorithms for the pipeline. */ private List fragmenterList; /** - * Boolean to mark if pipeline fragmentation was started from the dialog + * Boolean to mark if pipeline fragmentation was started from the dialog. */ private boolean isFragmentationStarted; /** - * Boolean value to enable fragment button if molecules are loaded + * Boolean value to enable fragment button if molecules are loaded. */ - private boolean isMoleculeDataLoaded; + private final boolean isMoleculeDataLoaded; /** - * Boolean value whether a fragmentation is running + * Boolean value whether a fragmentation is running. */ - private boolean isFragmentationRunning; + private final boolean isFragmentationRunning; // // // /** - * Logger + * Logger. */ private static final Logger LOGGER = Logger.getLogger(PipelineSettingsViewController.class.getName()); // // /** - * Constructor + * Constructor. * * @param aMainStage Stage of the MainView * @param aFragmentationService FragmentationService * @param isMoleculeDataLoaded boolean whether molecule data is loaded * @param isFragmentationRunning boolean whether fragmentation is running + * @param aConfiguration configuration instance to read resource file paths from */ - public PipelineSettingsViewController(Stage aMainStage, FragmentationService aFragmentationService, boolean isMoleculeDataLoaded, boolean isFragmentationRunning){ + public PipelineSettingsViewController(Stage aMainStage, + FragmentationService aFragmentationService, + boolean isMoleculeDataLoaded, + boolean isFragmentationRunning, + IConfiguration aConfiguration) { this.mainStage = aMainStage; this.algorithmCounter = 0; this.fragmentationService = aFragmentationService; @@ -135,16 +143,18 @@ public PipelineSettingsViewController(Stage aMainStage, FragmentationService aFr this.isFragmentationStarted = false; this.isMoleculeDataLoaded = isMoleculeDataLoaded; this.isFragmentationRunning = isFragmentationRunning; + this.configuration = aConfiguration; this.showPipelineSettingsView(); } // // /** - * Initialises stage and view and opens view in the initialised stage + * Initialises stage and view and opens view in the initialised stage. */ - private void showPipelineSettingsView(){ - if(this.pipelineSettingsView == null) + private void showPipelineSettingsView() { + if (this.pipelineSettingsView == null) { this.pipelineSettingsView = new PipelineSettingsView(); + } this.pipelineSettingsViewStage = new Stage(); Scene tmpScene = new Scene(this.pipelineSettingsView, GuiDefinitions.GUI_MAIN_VIEW_WIDTH_VALUE, GuiDefinitions.GUI_MAIN_VIEW_HEIGHT_VALUE); this.pipelineSettingsViewStage.setScene(tmpScene); @@ -153,13 +163,15 @@ private void showPipelineSettingsView(){ this.pipelineSettingsViewStage.setTitle(Message.get("PipelineSettingsView.title.text")); this.pipelineSettingsViewStage.setMinHeight(GuiDefinitions.GUI_MAIN_VIEW_HEIGHT_VALUE); this.pipelineSettingsViewStage.setMinWidth(GuiDefinitions.GUI_MAIN_VIEW_WIDTH_VALUE); - InputStream tmpImageInputStream = PipelineSettingsViewController.class.getResourceAsStream("/de/unijena/cheminf/mortar/images/Mortar_Logo_Icon1.png"); - this.pipelineSettingsViewStage.getIcons().add(new Image(tmpImageInputStream)); + String tmpIconURL = this.getClass().getClassLoader().getResource( + this.configuration.getProperty("mortar.imagesFolder") + + this.configuration.getProperty("mortar.logo.icon.name")).toExternalForm(); + this.pipelineSettingsViewStage.getIcons().add(new Image(tmpIconURL)); Platform.runLater(()->{ this.pipelineSettingsView.addGrid(this.pipelineSettingsViewStage); this.addListenerAndBindings(); for(IMoleculeFragmenter tmpFragmenter : this.fragmenterList){ - this.addNewChoiceRow(tmpFragmenter.getFragmentationAlgorithmName()); + this.addNewChoiceRow(tmpFragmenter.getFragmentationAlgorithmDisplayName()); } this.setPipelineName(this.fragmentationService.getPipeliningFragmentationName()); this.pipelineSettingsView.getFragmentButton().setDisable(!this.isMoleculeDataLoaded || this.isFragmentationRunning); @@ -168,9 +180,9 @@ private void showPipelineSettingsView(){ } // /** - * Add listeners and bindings + * Add listeners and bindings. */ - private void addListenerAndBindings(){ + private void addListenerAndBindings() { //text field binding this.pipelineSettingsView.getTextField().textProperty().bindBidirectional(this.pipelineName); //stage close request @@ -182,7 +194,7 @@ private void addListenerAndBindings(){ this.pipelineSettingsView.getFragmentButton().setOnAction(event -> { this.isFragmentationStarted = true; this.fragmentationService.setPipeliningFragmentationName(this.pipelineName.get()); - this.fragmentationService.setPipelineFragmenter(this.fragmenterList.toArray(new IMoleculeFragmenter[this.fragmenterList.size()])); + this.fragmentationService.setPipelineFragmenter(this.fragmenterList.toArray(new IMoleculeFragmenter[0])); this.pipelineSettingsViewStage.close(); }); //cancel button @@ -193,52 +205,55 @@ private void addListenerAndBindings(){ //apply button this.pipelineSettingsView.getApplyButton().setOnAction(event -> { this.fragmentationService.setPipeliningFragmentationName(this.pipelineName.get()); - this.fragmentationService.setPipelineFragmenter(this.fragmenterList.toArray(new IMoleculeFragmenter[this.fragmenterList.size()])); + this.fragmentationService.setPipelineFragmenter(this.fragmenterList.toArray(new IMoleculeFragmenter[0])); this.pipelineSettingsViewStage.close(); }); //default button - this.pipelineSettingsView.getDefaultButton().setOnAction(event -> { - this.reset(); - }); + this.pipelineSettingsView.getDefaultButton().setOnAction(event -> this.reset()); } // /** * Adds a new row to the pipeline settings view, which allows to add a new fragmentation algorithms. ComboBox is - * initially set to fragmentation algorithm corresponding to the given name + * initially set to fragmentation algorithm corresponding to the given name. * * @param aFragmenterName name of Fragmenter to initially set ComboBox */ - private void addNewChoiceRow(String aFragmenterName){ - ComboBox tmpComboBox = new ComboBox(); - for(IMoleculeFragmenter tmpFragmenter : this.fragmenters){ - tmpComboBox.getItems().add(tmpFragmenter.getFragmentationAlgorithmName()); + private void addNewChoiceRow(String aFragmenterName) { + ComboBox tmpComboBox = new ComboBox<>(); + for (IMoleculeFragmenter tmpFragmenter : this.fragmenters) { + tmpComboBox.getItems().add(tmpFragmenter.getFragmentationAlgorithmDisplayName()); } tmpComboBox.setPromptText(Message.get("PipelineSettingsView.comboBox.promptText")); - if(aFragmenterName != null){ + if (aFragmenterName != null) { tmpComboBox.getSelectionModel().select(aFragmenterName); } tmpComboBox.setOnAction(anActionEvent -> { Object tmpSelectedFragmenterString = tmpComboBox.getSelectionModel().getSelectedItem(); int tmpIndex = GridPane.getRowIndex(tmpComboBox) - 1; for (IMoleculeFragmenter tmpFragmenter : this.fragmenters) { - if (tmpSelectedFragmenterString.equals(tmpFragmenter.getFragmentationAlgorithmName())){ - if(this.fragmenterList.size() > tmpIndex){ // will not work cause size of list is set - why not, it is in case a fragmenter that has already been set is changed - this.fragmenterList.set(tmpIndex, Arrays.stream(this.fragmenters).filter(x -> x.getFragmentationAlgorithmName().equals(tmpSelectedFragmenterString)).findFirst().orElse(null)); - } - else{ - this.fragmenterList.add( Arrays.stream(this.fragmenters).filter(x -> x.getFragmentationAlgorithmName().equals(tmpSelectedFragmenterString)).findFirst().orElse(null)); + if (tmpSelectedFragmenterString.equals(tmpFragmenter.getFragmentationAlgorithmDisplayName())) { + // will not work cause size of list is set - why not, it is in case a fragmenter that has already been set is changed + if (this.fragmenterList.size() > tmpIndex) { + this.fragmenterList.set(tmpIndex, Arrays.stream(this.fragmenters) + .filter(x -> x.getFragmentationAlgorithmDisplayName().equals(tmpSelectedFragmenterString)).findFirst().orElse(null)); + } else { + this.fragmenterList.add(Arrays.stream(this.fragmenters) + .filter(x -> x.getFragmentationAlgorithmDisplayName().equals(tmpSelectedFragmenterString)).findFirst().orElse(null)); } break; } } - if(this.fragmenterList.contains(null)){ + if (this.fragmenterList.contains(null)) { PipelineSettingsViewController.LOGGER.log(Level.SEVERE, "Error in pipeline fragmenter list, it contains null"); throw new IllegalArgumentException("fragmenter list for pipeline must not contain null"); } }); Button tmpFragmenterSettingsButton = new Button(); - tmpFragmenterSettingsButton.setTooltip(new Tooltip(Message.get("PipelineSettingsView.settingButton.toolTip"))); - tmpFragmenterSettingsButton.setGraphic(new ImageView(new Image("de/unijena/cheminf/mortar/images/settings_gear_icon_16x16.png"))); + tmpFragmenterSettingsButton.setTooltip(GuiUtil.createTooltip(Message.get("PipelineSettingsView.settingButton.toolTip"))); + String tmpIconURL = this.getClass().getClassLoader().getResource( + this.configuration.getProperty("mortar.imagesFolder") + + this.configuration.getProperty("mortar.icon.gear.name")).toExternalForm(); + tmpFragmenterSettingsButton.setGraphic(new ImageView(new Image(tmpIconURL))); tmpFragmenterSettingsButton.setMinHeight(GuiDefinitions.GUI_BUTTON_HEIGHT_VALUE); tmpFragmenterSettingsButton.setPrefHeight(GuiDefinitions.GUI_BUTTON_HEIGHT_VALUE); tmpFragmenterSettingsButton.setMaxHeight(GuiDefinitions.GUI_BUTTON_HEIGHT_VALUE); @@ -249,33 +264,33 @@ private void addNewChoiceRow(String aFragmenterName){ IMoleculeFragmenter[] tmpArray = new IMoleculeFragmenter[1]; tmpArray[0] = this.fragmenterList.get(tmpFragmenterListIndex); FragmentationSettingsViewController tmpFragmentationSettingsViewController - = new FragmentationSettingsViewController(this.pipelineSettingsViewStage, tmpArray, this.fragmenterList.get(tmpFragmenterListIndex).getFragmentationAlgorithmName()); + = new FragmentationSettingsViewController(this.pipelineSettingsViewStage, tmpArray, this.fragmenterList.get(tmpFragmenterListIndex).getFragmentationAlgorithmDisplayName(), this.configuration); }); Label tmpLabel = new Label(String.valueOf(++this.algorithmCounter)); //remove removeButton from upper Row - if(this.algorithmCounter > 1) - { - this.pipelineSettingsView.getGridPane().getChildren().removeIf(node -> node instanceof Button && (GridPane.getRowIndex(node) == this.algorithmCounter - 1) && ((Button)node).getText().equals("-") ); + if (this.algorithmCounter > 1) { + this.pipelineSettingsView.getGridPane().getChildren().removeIf(node -> node instanceof Button button && (GridPane.getRowIndex(node) == this.algorithmCounter - 1) && (button).getText().equals("-") ); } //remove addButton this.pipelineSettingsView.getGridPane().getChildren().removeIf(node -> GridPane.getRowIndex(node) == this.algorithmCounter); //add new content to row this.pipelineSettingsView.addAlgorithmChoiceRow(tmpLabel, tmpComboBox, tmpFragmenterSettingsButton, this.algorithmCounter); //add new remove button to row - if(this.algorithmCounter > 1) + if (this.algorithmCounter > 1) { this.addRemoveRowButton(this.algorithmCounter); + } //add new add button to next row this.addAddRowButton(this.algorithmCounter+1); //+1 cause of next row } // /** - * Adds button to row of given index which adds a new row + * Adds button to row of given index which adds a new row. * * @param aRowNumber int to specify row position */ - private void addAddRowButton(int aRowNumber){ + private void addAddRowButton(int aRowNumber) { Button tmpAddButton = new Button(); - tmpAddButton.setTooltip(new Tooltip(Message.get("PipelineSettingsView.addNewRowButton.toolTip"))); + tmpAddButton.setTooltip(GuiUtil.createTooltip(Message.get("PipelineSettingsView.addNewRowButton.toolTip"))); tmpAddButton.setText("+"); tmpAddButton.setMinHeight(GuiDefinitions.GUI_BUTTON_HEIGHT_VALUE); tmpAddButton.setPrefHeight(GuiDefinitions.GUI_BUTTON_HEIGHT_VALUE); @@ -283,20 +298,18 @@ private void addAddRowButton(int aRowNumber){ tmpAddButton.setMinWidth(GuiDefinitions.GUI_BUTTON_HEIGHT_VALUE); tmpAddButton.setPrefWidth(GuiDefinitions.GUI_BUTTON_HEIGHT_VALUE); tmpAddButton.setMaxWidth(GuiDefinitions.GUI_BUTTON_HEIGHT_VALUE); - tmpAddButton.setOnAction(anActionEvent ->{ - this.addNewChoiceRow(null); - }); + tmpAddButton.setOnAction(anActionEvent -> this.addNewChoiceRow(null)); this.pipelineSettingsView.addAddRowButton(tmpAddButton, aRowNumber); } // /** - * Adds button to row of given index which removes its own row + * Adds button to row of given index which removes its own row. * - * @param aRowNumber + * @param aRowNumber specifies which row should be removed */ - private void addRemoveRowButton(int aRowNumber){ + private void addRemoveRowButton(int aRowNumber) { Button tmpRemoveButton = new Button(); - tmpRemoveButton.setTooltip(new Tooltip(Message.get("PipelineSettingsView.removeRowButton.toolTip"))); + tmpRemoveButton.setTooltip(GuiUtil.createTooltip(Message.get("PipelineSettingsView.removeRowButton.toolTip"))); tmpRemoveButton.setText("-"); tmpRemoveButton.setMinHeight(GuiDefinitions.GUI_BUTTON_HEIGHT_VALUE); tmpRemoveButton.setPrefHeight(GuiDefinitions.GUI_BUTTON_HEIGHT_VALUE); @@ -314,37 +327,37 @@ private void addRemoveRowButton(int aRowNumber){ this.addAddRowButton(tmpRowIndex); // this.algorithmCounter--; - this.fragmenterList.remove(this.fragmenterList.size()-1); - if(this.algorithmCounter > 1) + this.fragmenterList.removeLast(); + if (this.algorithmCounter > 1) { this.addRemoveRowButton(this.algorithmCounter); + } }); this.pipelineSettingsView.addRemoveRowButton(tmpRemoveButton, aRowNumber); } // /** - * Cancels changes that are made in the fragmenter list + * Cancels changes that are made in the fragmenter list. */ - private void cancelChangesInFragmenterList(){ - if(this.fragmentationService.getPipelineFragmenter() == null || this.fragmentationService.getPipelineFragmenter().length < 1){ - try{ + private void cancelChangesInFragmenterList() { + if (this.fragmentationService.getPipelineFragmenter() == null || this.fragmentationService.getPipelineFragmenter().length < 1) { + try { this.fragmenterList.add(this.fragmentationService.getSelectedFragmenter().copy()); } catch (Exception anException) { - LOGGER.log(Level.SEVERE, anException.toString(), anException); + PipelineSettingsViewController.LOGGER.log(Level.SEVERE, anException.toString(), anException); } - } - else{ - for(IMoleculeFragmenter tmpFragmenter : this.fragmentationService.getPipelineFragmenter()){ + } else { + for (IMoleculeFragmenter tmpFragmenter : this.fragmentationService.getPipelineFragmenter()) { this.fragmenterList.add(tmpFragmenter.copy()); } } } // /** - * Resets the complete pipeline and the view, selected fragmenter for single fragmentation will be set as default value + * Resets the complete pipeline and the view. Selected fragmenter for single fragmentation will be set as default value. */ - private void reset(){ + private void reset() { this.setPipelineName(""); - for(int i = 0; i < this.pipelineSettingsView.getGridPane().getChildren().size(); i++){ + for (int i = 0; i < this.pipelineSettingsView.getGridPane().getChildren().size(); i++) { //remove addButton this.pipelineSettingsView.getGridPane().getChildren().removeIf(node -> GridPane.getRowIndex(node) == 2); //remove complete row content @@ -354,9 +367,10 @@ private void reset(){ } this.fragmenterList = new LinkedList<>(); this.algorithmCounter = 1; - ComboBox tmpBox = (ComboBox) this.pipelineSettingsView.getGridPane().getChildren().stream().filter(node -> GridPane.getRowIndex(node) == 1 && (node instanceof ComboBox)).findFirst().orElse(null); - if(tmpBox != null) - tmpBox.getSelectionModel().select(this.fragmentationService.getSelectedFragmenter().getFragmentationAlgorithmName()); + ComboBox tmpBox = (ComboBox) this.pipelineSettingsView.getGridPane().getChildren().stream().filter(node -> GridPane.getRowIndex(node) == 1 && (node instanceof ComboBox)).findFirst().orElse(null); + if (tmpBox != null) { + tmpBox.getSelectionModel().select(this.fragmentationService.getSelectedFragmenter().getFragmentationAlgorithmDisplayName()); + } } // // @@ -366,30 +380,30 @@ private void reset(){ * * @return StringProperty */ - public StringProperty pipelineNameProperty(){ + public StringProperty pipelineNameProperty() { return this.pipelineName; } // /** - * Returns pipeline name + * Returns pipeline name. * * @return String pipeline name */ - public String getPipelineName(){ + public String getPipelineName() { return this.pipelineName.get(); } // /** - * Sets pipeline name + * Sets pipeline name. * * @param aName String */ - public void setPipelineName(String aName){ + public void setPipelineName(String aName) { this.pipelineName.set(aName); } // /** - * Return boolean value whether fragmentation is started inside PipelineSettingsView via FragmentButton + * Return boolean value whether fragmentation is started inside PipelineSettingsView via FragmentButton. * * @return true if fragmentation is started */ diff --git a/src/main/java/de/unijena/cheminf/mortar/controller/SettingsViewController.java b/src/main/java/de/unijena/cheminf/mortar/controller/SettingsViewController.java index 1d6eab8a..ca44dd51 100644 --- a/src/main/java/de/unijena/cheminf/mortar/controller/SettingsViewController.java +++ b/src/main/java/de/unijena/cheminf/mortar/controller/SettingsViewController.java @@ -25,6 +25,7 @@ package de.unijena.cheminf.mortar.controller; +import de.unijena.cheminf.mortar.configuration.IConfiguration; import de.unijena.cheminf.mortar.gui.util.GuiDefinitions; import de.unijena.cheminf.mortar.gui.views.SettingsView; import de.unijena.cheminf.mortar.message.Message; @@ -38,72 +39,74 @@ import javafx.stage.Modality; import javafx.stage.Stage; -import java.io.InputStream; import java.util.HashMap; import java.util.Map; /** - * SettingsViewController - * controls {@link SettingsView} for {@link SettingsContainer} + * SettingsViewController controls {@link SettingsView} for {@link SettingsContainer}. * * @author Felix Baensch * @version 1.0.0.0 */ public class SettingsViewController { - // /** - * SettingsContainer + * SettingsContainer. */ private final SettingsContainer settingsContainer; - private final SettingsContainer recentSettingsContainer; /** - * Main stage object of the application + * Main stage object of the application. */ private final Stage mainStage; /** - * Stage for the SettingsView + * Configuration class to read resource file paths from. + */ + private final IConfiguration configuration; + /** + * Stage for the SettingsView. */ private Stage settingsViewStage; /** - * SettingsView + * SettingsView. */ private SettingsView settingsView; /** - * Map to hold the initial settings properties + * Map to hold the initial settings properties. */ - private Map recentProperties; + private final Map recentProperties; /** - * Boolean value to check if the rowsPerPage property has changed + * Boolean value to check if the rowsPerPage property has changed. */ private boolean hasRowsPerPageChanged; /** - * Boolean value to check if the keepAtomContainerInDataModel property has changed + * Boolean value to check if the keepAtomContainerInDataModel property has changed. */ private boolean hasKeepAtomContainerInDataModelChanged; // // /** - * Constructor + * Constructor. * * @param aStage Parent stage * @param aSettingsContainer SettingsContainer + * @param aConfiguration configuration instance to read resource file paths from */ - public SettingsViewController(Stage aStage, SettingsContainer aSettingsContainer){ + public SettingsViewController(Stage aStage, SettingsContainer aSettingsContainer, IConfiguration aConfiguration) { this.mainStage = aStage; this.settingsContainer = aSettingsContainer; - this.recentSettingsContainer = aSettingsContainer; + this.configuration = aConfiguration; this.recentProperties = new HashMap<>(CollectionUtil.calculateInitialHashCollectionCapacity(this.settingsContainer.settingsProperties().size())); this.showSettingsView(); } // // /** - * Initialises and opens settingsView + * Initialises and opens settingsView. */ - private void showSettingsView(){ - if(this.settingsView == null) + private void showSettingsView() { + if (this.settingsView == null) { this.settingsView = new SettingsView(); + } this.settingsViewStage = new Stage(); Scene tmpScene = new Scene(this.settingsView, GuiDefinitions.GUI_MAIN_VIEW_WIDTH_VALUE, GuiDefinitions.GUI_MAIN_VIEW_HEIGHT_VALUE); this.settingsViewStage.setScene(tmpScene); @@ -112,21 +115,23 @@ private void showSettingsView(){ this.settingsViewStage.setTitle(Message.get("SettingsView.title.default.text")); this.settingsViewStage.setMinHeight(GuiDefinitions.GUI_MAIN_VIEW_HEIGHT_VALUE); this.settingsViewStage.setMinWidth(GuiDefinitions.GUI_MAIN_VIEW_WIDTH_VALUE); - InputStream tmpImageInputStream = SettingsViewController.class.getResourceAsStream("/de/unijena/cheminf/mortar/images/Mortar_Logo_Icon1.png"); - this.settingsViewStage.getIcons().add(new Image(tmpImageInputStream)); + String tmpIconURL = this.getClass().getClassLoader().getResource( + this.configuration.getProperty("mortar.imagesFolder") + + this.configuration.getProperty("mortar.logo.icon.name")).toExternalForm(); + this.settingsViewStage.getIcons().add(new Image(tmpIconURL)); Platform.runLater(()->{ this.addListeners(); - this.settingsView.addTab(this.settingsViewStage, Message.get("GlobalSettingsView.title.text"), - this.settingsContainer.settingsProperties(), this.settingsContainer.getSettingNameToTooltipTextMap(), - this.recentProperties); + this.settingsView.addTab(Message.get("GlobalSettingsView.title.text"), + this.settingsContainer.settingsProperties(), this.settingsContainer.getSettingNameToDisplayNameMap(), + this.settingsContainer.getSettingNameToTooltipTextMap(), this.recentProperties); }); this.settingsViewStage.showAndWait(); } // /** - * Adds listeners and event handlers to the buttons of the settings view + * Adds listeners and event handlers to the buttons of the settings view. */ - private void addListeners(){ + private void addListeners() { //stage close request this.settingsViewStage.setOnCloseRequest(event -> { this.setRecentProperties(); @@ -134,27 +139,25 @@ private void addListeners(){ }); //apply button this.settingsView.getApplyButton().setOnAction(event -> { - this.hasRowsPerPageChanged = (int) this.settingsContainer.rowsPerPageSettingProperty().getValue() + this.hasRowsPerPageChanged = this.settingsContainer.rowsPerPageSettingProperty().getValue() != (int) this.recentProperties.get(this.settingsContainer.rowsPerPageSettingProperty().getName()); this.hasKeepAtomContainerInDataModelChanged = this.settingsContainer.keepAtomContainerInDataModelSettingProperty().getValue() != this.recentProperties.get(this.settingsContainer.keepAtomContainerInDataModelSettingProperty().getName()); this.settingsViewStage.close(); }); //cancel button - this.settingsView.getCancelButton().setOnAction(event ->{ + this.settingsView.getCancelButton().setOnAction(event -> { this.setRecentProperties(); this.settingsViewStage.close(); }); //default button - this.settingsView.getDefaultButton().setOnAction(event -> { - this.settingsContainer.restoreDefaultSettings(); - }); + this.settingsView.getDefaultButton().setOnAction(event -> this.settingsContainer.restoreDefaultSettings()); } // /** - * Sets the properties to the values of the 'recentPropertiesMap' + * Sets the properties to the values of the 'recentPropertiesMap'. */ - private void setRecentProperties(){ + private void setRecentProperties() { Platform.runLater(()->{ for (Property tmpProperty : this.settingsContainer.settingsProperties()) { if (this.recentProperties.containsKey(tmpProperty.getName())){ @@ -167,7 +170,8 @@ private void setRecentProperties(){ // // /** - * Returns boolean value whether if rowsPerPage property has changed or not + * Returns boolean value whether if rowsPerPage property has changed or not. + * * @return hasRowsPerPageChanged */ public boolean hasRowsPerPageChanged() { @@ -176,6 +180,7 @@ public boolean hasRowsPerPageChanged() { // /** * Returns boolean value whether if keepAtomContainerInDataModel property has changed or not. + * * @return hasKeepAtomContainerInDataModelChanged */ public boolean hasKeepAtomContainerInDataModelChanged() { diff --git a/src/main/java/de/unijena/cheminf/mortar/controller/TabNames.java b/src/main/java/de/unijena/cheminf/mortar/controller/TabNames.java index 66393762..09c7fa48 100644 --- a/src/main/java/de/unijena/cheminf/mortar/controller/TabNames.java +++ b/src/main/java/de/unijena/cheminf/mortar/controller/TabNames.java @@ -26,22 +26,22 @@ package de.unijena.cheminf.mortar.controller; /** - * Enum for tab names + * Enum for tab names. * * @author Felix Baensch * @version 1.0.0.0 */ public enum TabNames { /** - * enum value for molecules tab + * enum value for molecules tab. */ MOLECULES, /** - * enum value for fragments tab + * enum value for fragments tab. */ FRAGMENTS, /** - * enum value for items tab + * enum value for items tab. */ ITEMIZATION; } diff --git a/src/main/java/de/unijena/cheminf/mortar/controller/ViewToolsManager.java b/src/main/java/de/unijena/cheminf/mortar/controller/ViewToolsManager.java index 38ebb5d8..2fb3d9b9 100644 --- a/src/main/java/de/unijena/cheminf/mortar/controller/ViewToolsManager.java +++ b/src/main/java/de/unijena/cheminf/mortar/controller/ViewToolsManager.java @@ -25,26 +25,18 @@ package de.unijena.cheminf.mortar.controller; +import de.unijena.cheminf.mortar.configuration.IConfiguration; import de.unijena.cheminf.mortar.gui.util.GuiUtil; import de.unijena.cheminf.mortar.message.Message; import de.unijena.cheminf.mortar.model.data.FragmentDataModel; import de.unijena.cheminf.mortar.model.data.MoleculeDataModel; import de.unijena.cheminf.mortar.model.util.BasicDefinitions; import de.unijena.cheminf.mortar.model.util.FileUtil; -import de.unijena.cheminf.mortar.model.util.SimpleEnumConstantNameProperty; -import de.unijena.cheminf.mortar.preference.BooleanPreference; -import de.unijena.cheminf.mortar.preference.IPreference; import de.unijena.cheminf.mortar.preference.PreferenceContainer; import de.unijena.cheminf.mortar.preference.PreferenceUtil; -import de.unijena.cheminf.mortar.preference.SingleIntegerPreference; -import de.unijena.cheminf.mortar.preference.SingleNumberPreference; import de.unijena.cheminf.mortar.preference.SingleTermPreference; import javafx.beans.property.Property; -import javafx.beans.property.SimpleBooleanProperty; -import javafx.beans.property.SimpleDoubleProperty; -import javafx.beans.property.SimpleIntegerProperty; -import javafx.beans.property.SimpleStringProperty; import javafx.scene.control.Alert; import javafx.stage.Stage; @@ -90,18 +82,25 @@ public class ViewToolsManager { * OverviewViewController instance. */ private final OverviewViewController overviewViewController; + /** + * Configuration class to read resource file paths from. + */ + private final IConfiguration configuration; // // // /** * Constructor that initialises the view tool instances and checks whether they are all valid (see {@link #checkViewTools()}). * Opens a GUI exception alert if they are not. + * + * @param aConfiguration configuration instance to read resource file paths from */ - public ViewToolsManager() { + public ViewToolsManager(IConfiguration aConfiguration) { + this.configuration = aConfiguration; this.viewToolsArray = new IViewToolController[2]; - this.histogramViewController = new HistogramViewController(); + this.histogramViewController = new HistogramViewController(this.configuration); this.viewToolsArray[0] = this.histogramViewController; - this.overviewViewController = new OverviewViewController(); + this.overviewViewController = new OverviewViewController(this.configuration); this.viewToolsArray[1] = this.overviewViewController; try { this.checkViewTools(); @@ -191,7 +190,7 @@ public void persistViewToolsSettings() { if (Objects.isNull(tmpViewTool)) { continue; } - List tmpSettings = tmpViewTool.settingsProperties(); + List> tmpSettings = tmpViewTool.settingsProperties(); if (Objects.isNull(tmpSettings)) { continue; } @@ -202,13 +201,14 @@ public void persistViewToolsSettings() { PreferenceContainer tmpPrefContainer = PreferenceUtil.translateJavaFxPropertiesToPreferences(tmpSettings, tmpFilePath); tmpPrefContainer.writeRepresentation(); } catch (NullPointerException | IllegalArgumentException | IOException | SecurityException anException) { - ViewToolsManager.LOGGER.log(Level.WARNING, "View tools settings persistence went wrong, exception: " - + anException.toString(), anException); + ViewToolsManager.LOGGER.log(Level.WARNING, + String.format("View tools settings persistence went wrong, exception: %s", anException.toString()), + anException); GuiUtil.guiExceptionAlert(Message.get("Error.ExceptionAlert.Title"), Message.get("Error.ExceptionAlert.Header"), Message.get("ViewToolsManager.Error.settingsPersistence"), anException); - continue; + //continue; } } } @@ -228,14 +228,15 @@ public void reloadViewToolsSettings() { try { tmpContainer = new PreferenceContainer(tmpViewToolsSettingsFile); } catch (IllegalArgumentException | IOException anException) { - ViewToolsManager.LOGGER.log(Level.WARNING, "Unable to reload settings of view tool " - + tmpClassName + " : " + anException.toString(), anException); + ViewToolsManager.LOGGER.log(Level.WARNING, + String.format("Unable to reload settings of view tool %s: %s", tmpClassName, anException.toString()), + anException); continue; } - this.updatePropertiesFromPreferences(tmpViewTool.settingsProperties(), tmpContainer); + PreferenceUtil.updatePropertiesFromPreferences(tmpViewTool.settingsProperties(), tmpContainer); } else { //settings will remain in default - ViewToolsManager.LOGGER.log(Level.WARNING, "No persisted settings for " + tmpClassName + " available."); + ViewToolsManager.LOGGER.log(Level.WARNING, "No persisted settings for {0} available.", tmpClassName); } } } @@ -243,90 +244,28 @@ public void reloadViewToolsSettings() { // // /** - * Sets the values of the given properties according to the preferences in the given container with the same name. - * If no matching preference for a given property is found, the value will remain in its default setting. - */ - private void updatePropertiesFromPreferences(List aPropertiesList, PreferenceContainer aPreferenceContainer) { - for (Property tmpSettingProperty : aPropertiesList) { - String tmpPropertyName = tmpSettingProperty.getName(); - if (aPreferenceContainer.containsPreferenceName(tmpPropertyName)) { - IPreference[] tmpPreferences = aPreferenceContainer.getPreferences(tmpPropertyName); - try { - if (tmpSettingProperty instanceof SimpleBooleanProperty) { - BooleanPreference tmpBooleanPreference = (BooleanPreference) tmpPreferences[0]; - tmpSettingProperty.setValue(tmpBooleanPreference.getContent()); - } else if (tmpSettingProperty instanceof SimpleIntegerProperty) { - SingleIntegerPreference tmpIntPreference = (SingleIntegerPreference) tmpPreferences[0]; - tmpSettingProperty.setValue(tmpIntPreference.getContent()); - } else if (tmpSettingProperty instanceof SimpleDoubleProperty) { - SingleNumberPreference tmpDoublePreference = (SingleNumberPreference) tmpPreferences[0]; - tmpSettingProperty.setValue(tmpDoublePreference.getContent()); - } else if (tmpSettingProperty instanceof SimpleEnumConstantNameProperty || tmpSettingProperty instanceof SimpleStringProperty) { - SingleTermPreference tmpStringPreference = (SingleTermPreference) tmpPreferences[0]; - tmpSettingProperty.setValue(tmpStringPreference.getContent()); - } else { - //setting will remain in default - ViewToolsManager.LOGGER.log(Level.WARNING, "Setting " + tmpPropertyName + " is of unknown type."); - } - } catch (ClassCastException | IllegalArgumentException anException) { - //setting will remain in default - ViewToolsManager.LOGGER.log(Level.WARNING, anException.toString(), anException); - } - } else { - //setting will remain in default - ViewToolsManager.LOGGER.log(Level.WARNING, "No persisted settings for " + tmpPropertyName + " available."); - } - } - } - /** - * Checks the available view tools and their settings for restrictions imposed by persistence. Throws an exception if + * Checks the available view tools and their settings for restrictions imposed by persistence. Throws an IOException if * anything does not meet the requirements. + * - setting names must be singletons + * - setting names and values must adhere to the preference input restrictions + * - setting values are only tested for their current state, not the entire possible input space! It is tested again at persistence + * + * @throws UnsupportedOperationException if a setting does not fulfil the requirements */ - private void checkViewTools() throws Exception { + private void checkViewTools() throws UnsupportedOperationException { HashSet tmpViewToolNames = new HashSet<>((int)(this.viewToolsArray.length * 1.5), 0.75f); for (IViewToolController tmpViewTool : this.viewToolsArray) { //view tool name should be singleton and must be persistable String tmpViewToolName = tmpViewTool.getViewToolNameForDisplay(); if (!PreferenceUtil.isValidName(tmpViewToolName) || !SingleTermPreference.isValidContent(tmpViewToolName)) { - throw new Exception("View tool name " + tmpViewToolName + " is invalid."); + throw new UnsupportedOperationException(String.format("View tool name %s is invalid.", tmpViewToolName)); } if (tmpViewToolNames.contains(tmpViewToolName)) { - throw new Exception("View tool name " + tmpViewToolName + " is used multiple times."); + throw new UnsupportedOperationException(String.format("View tool name %s is used multiple times.", tmpViewToolName)); } else { tmpViewToolNames.add(tmpViewToolName); } - //setting names must be singletons within the respective class - //setting names and values must adhere to the preference input restrictions - //setting values are only tested for their current state, not the entire possible input space! It is tested again at persistence - List tmpSettingsList = tmpViewTool.settingsProperties(); - HashSet tmpSettingNames = new HashSet<>((int) (tmpSettingsList.size() * 1.5), 0.75f); - for (Property tmpSetting : tmpSettingsList) { - if (!PreferenceUtil.isValidName(tmpSetting.getName())) { - throw new Exception("Setting " + tmpSetting.getName() + " has an invalid name."); - } - if (tmpSettingNames.contains(tmpSetting.getName())) { - throw new Exception("Setting name " + tmpSetting.getName() + " is used multiple times."); - } else { - tmpSettingNames.add(tmpSetting.getName()); - } - if (tmpSetting instanceof SimpleBooleanProperty) { - //nothing to do here, booleans cannot have invalid values - } else if (tmpSetting instanceof SimpleIntegerProperty) { - if (!SingleIntegerPreference.isValidContent(Integer.toString(((SimpleIntegerProperty) tmpSetting).get()))) { - throw new Exception("Setting value " + ((SimpleIntegerProperty) tmpSetting).get() + " of setting name " + tmpSetting.getName() + " is invalid."); - } - } else if (tmpSetting instanceof SimpleDoubleProperty) { - if (!SingleNumberPreference.isValidContent(((SimpleDoubleProperty) tmpSetting).get())) { - throw new Exception("Setting value " + ((SimpleDoubleProperty) tmpSetting).get() + " of setting name " + tmpSetting.getName() + " is invalid."); - } - } else if (tmpSetting instanceof SimpleEnumConstantNameProperty || tmpSetting instanceof SimpleStringProperty) { - if (!SingleTermPreference.isValidContent(((SimpleStringProperty) tmpSetting).get())) { - throw new Exception("Setting value " + ((SimpleStringProperty) tmpSetting).get() + " of setting name " + tmpSetting.getName() + " is invalid."); - } - } else { - throw new Exception("Setting " + tmpSetting.getName() + " is of an invalid type."); - } - } + PreferenceUtil.checkPropertiesForPreferenceRestrictions(tmpViewTool.settingsProperties()); } } // diff --git a/src/main/java/de/unijena/cheminf/mortar/gui/controls/CustomPaginationSkin.java b/src/main/java/de/unijena/cheminf/mortar/gui/controls/CustomPaginationSkin.java index 55d8499f..fd576fd8 100644 --- a/src/main/java/de/unijena/cheminf/mortar/gui/controls/CustomPaginationSkin.java +++ b/src/main/java/de/unijena/cheminf/mortar/gui/controls/CustomPaginationSkin.java @@ -43,33 +43,32 @@ import javafx.scene.layout.HBox; /** - * Customized pagination skin to add first and last page button and a text field to jump to a page specified by the user - * + * Customized pagination skin to add first and last page button and a text field to jump to a page specified by the user. * See kleopatra's comment on https://stackoverflow.com/questions/31540001/how-to-extend-javafx-pagination-navigation-to-display-additional-controls * (retrieved August 18, 2022) for more details + * * @author kleopatra (https://stackoverflow.com/users/203657/kleopatra, retireved August 18, 2022), Felix Baensch */ public class CustomPaginationSkin extends PaginationSkin { - // /** - * HBox to hold the control elements + * HBox to hold the control elements. */ private HBox controlHBox; /** - * Button to jump to next page, necessary here to add the new control elements + * Button to jump to next page, necessary here to add the new control elements. */ private Button nextButton; /** - * Button to jump to the first page + * Button to jump to the first page. */ private Button firstButton; /** - * Button to jump to the last page + * Button to jump to the last page. */ private Button lastButton; /** - * TextField to jump to a page specified by the user + * TextField to jump to a page specified by the user. */ private TextField jumpToTextField; // @@ -77,7 +76,7 @@ public class CustomPaginationSkin extends PaginationSkin { /** * Creates a new PaginationSkin instance, installing the necessary child * nodes into the Control {@link Control} children list, as - * well as the necessary input mappings for handling key, mouse, etc events. + * well as the necessary input mappings for handling key, mouse, etc. events. * * @param control The control that this skin should be installed onto. */ @@ -87,28 +86,25 @@ public CustomPaginationSkin(Pagination control) { } // /** - * Adds new control elements and their functionality + * Adds new control elements and their functionality. */ private void patchNavigation() { - Pagination tmpPagination = getSkinnable(); + Pagination tmpPagination = this.getSkinnable(); Node tmpControl = tmpPagination.lookup(".control-box"); - if (!(tmpControl instanceof HBox)) + if (!(tmpControl instanceof HBox)) { return; + } this.controlHBox = (HBox) tmpControl; // prev = (Button) controlBox.getChildren().get(0); - this.nextButton = (Button) this.controlHBox.getChildren().get(this.controlHBox.getChildren().size() - 1); + this.nextButton = (Button) this.controlHBox.getChildren().getLast(); this.firstButton = new Button(Message.get("CustomPaginationSkin.controlBox.firstButton.text")); this.firstButton.setTooltip(new Tooltip(Message.get("CustomPaginationSkin.controlBox.firstButton.tooltip"))); - this.firstButton.setOnAction(e -> { - tmpPagination.setCurrentPageIndex(0); - }); + this.firstButton.setOnAction(e -> tmpPagination.setCurrentPageIndex(0)); this.firstButton.disableProperty().bind( tmpPagination.currentPageIndexProperty().isEqualTo(0)); this.lastButton = new Button(Message.get("CustomPaginationSkin.controlBox.lastButton.text")); this.lastButton.setTooltip(new Tooltip(Message.get("CustomPaginationSkin.controlBox.lastButton.tooltip"))); - this.lastButton.setOnAction(e -> { - tmpPagination.setCurrentPageIndex(tmpPagination.pageCountProperty().get()); - }); + this.lastButton.setOnAction(e -> tmpPagination.setCurrentPageIndex(tmpPagination.pageCountProperty().get())); this.lastButton.disableProperty().bind( tmpPagination.currentPageIndexProperty().isEqualTo( tmpPagination.pageCountProperty().subtract(1))); @@ -118,19 +114,22 @@ private void patchNavigation() { this.jumpToTextField.setMinWidth(GuiDefinitions.PAGINATION_TEXT_FIELD_WIDTH); this.jumpToTextField.setPrefWidth(GuiDefinitions.PAGINATION_TEXT_FIELD_WIDTH); this.jumpToTextField.setAlignment(Pos.CENTER_RIGHT); - this.jumpToTextField.setTextFormatter( new TextFormatter<>(GuiUtil.getStringToIntegerConverter(), tmpPagination.getCurrentPageIndex()+1, GuiUtil.getPositiveIntegerWithoutZeroFilter())); + int tmpDefaultValue = tmpPagination.getCurrentPageIndex() + 1; + this.jumpToTextField.setTextFormatter(new TextFormatter<>(GuiUtil.getStringToIntegerConverter(), + tmpDefaultValue, + GuiUtil.getPositiveIntegerFilter(false))); this.jumpToTextField.setOnKeyPressed(key -> { - if(key.getCode().equals(KeyCode.ENTER)){ + if (key.getCode().equals(KeyCode.ENTER)) { int tmpPageNumber = Integer.parseInt(jumpToTextField.getText()) - 1; - if(tmpPageNumber > tmpPagination.pageCountProperty().get()){ + if (tmpPageNumber > tmpPagination.pageCountProperty().get()) { tmpPageNumber = tmpPagination.pageCountProperty().get(); } tmpPagination.setCurrentPageIndex(tmpPageNumber); } }); tmpPagination.currentPageIndexProperty().addListener((observable, oldValue, newValue) -> { - if((newValue.intValue() + 1) != Integer.parseInt(this.jumpToTextField.getText())) { - jumpToTextField.setText(Integer.toString(newValue.intValue() + 1)); + if ((newValue.intValue() + 1) != Integer.parseInt(this.jumpToTextField.getText())) { + this.jumpToTextField.setText(Integer.toString(newValue.intValue() + 1)); } }); ListChangeListener childrenListener = c -> { @@ -138,23 +137,25 @@ private void patchNavigation() { // implementation detail: when nextButton is added, the setup is complete if (c.wasAdded() && !c.wasRemoved() // real addition && c.getAddedSize() == 1 // single addition - && c.getAddedSubList().get(0) == nextButton) { - addCustomNodes(); + && c.getAddedSubList().getFirst() == this.nextButton) { + this.addCustomNodes(); } } }; this.controlHBox.getChildren().addListener(childrenListener); - addCustomNodes(); + this.addCustomNodes(); } // /** - * Adds the control elements to the control box + * Adds the control elements to the control box. */ protected void addCustomNodes() { // guarding against duplicate child exception // (some weird internals that I don't fully understand...) - if (this.firstButton.getParent() == this.controlHBox) return; - this.controlHBox.getChildren().add(0, this.firstButton); + if (this.firstButton.getParent() == this.controlHBox) { + return; + } + this.controlHBox.getChildren().addFirst(this.firstButton); this.controlHBox.getChildren().add(this.lastButton); this.controlHBox.getChildren().add(this.jumpToTextField); } diff --git a/src/main/java/de/unijena/cheminf/mortar/gui/controls/GridTabForTableView.java b/src/main/java/de/unijena/cheminf/mortar/gui/controls/GridTabForTableView.java index 95aebfb4..8c349ec6 100644 --- a/src/main/java/de/unijena/cheminf/mortar/gui/controls/GridTabForTableView.java +++ b/src/main/java/de/unijena/cheminf/mortar/gui/controls/GridTabForTableView.java @@ -38,48 +38,47 @@ import javafx.scene.layout.RowConstraints; /** - * Custom tab which contains a grid pane + * Custom tab which contains a grid pane. * * @author Felix Baensch * @version 1.0.0.0 */ public class GridTabForTableView extends Tab { - // /** - * GridPane to align nodes + * GridPane to align nodes. */ - private GridPane gridPane; + private final GridPane gridPane; /** - * Pagination for table view + * Pagination for table view. */ private Pagination pagination; /** - * + * Encapsulated table view. */ - private TableView tableView; + private final TableView tableView; // // + // /** - * Constructor - * - * Creates a 'No Title' grid tab + * Constructor. Creates a 'No Title' grid tab * * @param aTableView TableView to add */ - public GridTabForTableView(TableView aTableView){ + public GridTabForTableView(TableView aTableView) { this("No Title", "", aTableView); } // /** - * Constructor + * Constructor. + * * @param aTitle String title of Tab * @param anIdString String ID of Tab * @param aTableView TableView to add */ - public GridTabForTableView(String aTitle, String anIdString, TableView aTableView){ + public GridTabForTableView(String aTitle, String anIdString, TableView aTableView) { super(); - setText(aTitle); + this.setText(aTitle); this.setClosable(false); this.setId(anIdString); this.tableView = aTableView; @@ -89,34 +88,36 @@ public GridTabForTableView(String aTitle, String anIdString, TableView aTableVie RowConstraints tmpRowCon1 = new RowConstraints(); tmpRowCon1.setFillHeight(true); tmpRowCon1.setVgrow(Priority.ALWAYS); - gridPane.getRowConstraints().add(tmpRowCon1); + this.gridPane.getRowConstraints().add(tmpRowCon1); RowConstraints tmpRowCon2 = new RowConstraints(); tmpRowCon2.setMaxHeight(GuiDefinitions.GUI_CONTROL_CONTAINER_HEIGHT); tmpRowCon2.setMinHeight(GuiDefinitions.GUI_CONTROL_CONTAINER_HEIGHT); tmpRowCon2.setPrefHeight(GuiDefinitions.GUI_CONTROL_CONTAINER_HEIGHT); tmpRowCon2.setVgrow(Priority.ALWAYS); - gridPane.getRowConstraints().add(tmpRowCon2); + this.gridPane.getRowConstraints().add(tmpRowCon2); ColumnConstraints tmpColCon1 = new ColumnConstraints(); tmpColCon1.setHgrow(Priority.ALWAYS); tmpColCon1.setMaxWidth(GuiDefinitions.GUI_SPACING_VALUE); tmpColCon1.setMinWidth(GuiDefinitions.GUI_SPACING_VALUE); tmpColCon1.setPrefWidth(GuiDefinitions.GUI_SPACING_VALUE); - gridPane.getColumnConstraints().add(tmpColCon1); + this.gridPane.getColumnConstraints().add(tmpColCon1); ColumnConstraints tmpColCon2 = new ColumnConstraints(); tmpColCon2.setFillWidth(true); tmpColCon2.setHgrow(Priority.ALWAYS); - gridPane.getColumnConstraints().add(tmpColCon2); + this.gridPane.getColumnConstraints().add(tmpColCon2); ColumnConstraints tmpColCon3 = new ColumnConstraints(); tmpColCon3.setHgrow(Priority.ALWAYS); tmpColCon3.setMaxWidth(GuiDefinitions.GUI_GRIDPANE_FOR_NODE_ALIGNMENT_THIRD_COL_WIDTH); tmpColCon3.setMinWidth(GuiDefinitions.GUI_GRIDPANE_FOR_NODE_ALIGNMENT_THIRD_COL_WIDTH); tmpColCon3.setPrefWidth(GuiDefinitions.GUI_GRIDPANE_FOR_NODE_ALIGNMENT_THIRD_COL_WIDTH); tmpColCon3.setHalignment(HPos.RIGHT); - gridPane.getColumnConstraints().add(tmpColCon3); + this.gridPane.getColumnConstraints().add(tmpColCon3); } + // // + // /** - * Adds given Node (aNode) to specified column (aColIndex) and row (aRowIndex) to GridPane + * Adds given Node (aNode) to specified column (aColIndex) and row (aRowIndex) to GridPane. * Necessary to add nodes via MainViewController * * @param aNode Node to add @@ -125,12 +126,12 @@ public GridTabForTableView(String aTitle, String anIdString, TableView aTableVie * @param aColSpan index how many cols should this node span * @param aRowSpan index how many rows should this node span */ - public void addNodeToGridPane(javafx.scene.Node aNode, int aColIndex, int aRowIndex, int aColSpan, int aRowSpan){ + public void addNodeToGridPane(javafx.scene.Node aNode, int aColIndex, int aRowIndex, int aColSpan, int aRowSpan) { this.gridPane.add(aNode, aColIndex, aRowIndex, aColSpan, aRowSpan); } // /** - * Adds given Pagination to GridPane + * Adds given Pagination to GridPane. * Necessary to add the pagination via MainViewController * * @param aPagination Pagination to add @@ -141,48 +142,51 @@ public void addPaginationToGridPane(Pagination aPagination) { } // /** - * Sets the given string as title of this tab + * Sets the given string as title of this tab. * * @param aTitle String */ - public void setTitle(String aTitle){ + public void setTitle(String aTitle) { this.setText(aTitle); } // /** - * Returns Pagination + * Returns Pagination. + * * @return pagination */ - public Pagination getPagination(){ + public Pagination getPagination() { return this.pagination; } // /** - * Returns TableView + * Returns TableView. + * * @return tableView */ - public TableView getTableView(){ + public TableView getTableView() { return this.tableView; } // /** - * Returns the title of this tab + * Returns the title of this tab. * * @return title */ - public String getTitle(){ + public String getTitle() { return this.getText(); } // - /** - * Returns the name of the fragmentation used + * Returns the name of the fragmentation used. * * @return fragmentation name */ - public String getFragmentationNameOutOfTitle(){ - if(this.getId().equals(TabNames.MOLECULES.name())) + public String getFragmentationNameOutOfTitle() { + if (this.getId().equals(TabNames.MOLECULES.name())) { return TabNames.MOLECULES.name(); + } return this.getText().split("-", 2)[1].trim(); } + // } diff --git a/src/main/java/de/unijena/cheminf/mortar/gui/controls/MainMenuBar.java b/src/main/java/de/unijena/cheminf/mortar/gui/controls/MainMenuBar.java index 1d5f7c7d..7c53a086 100644 --- a/src/main/java/de/unijena/cheminf/mortar/gui/controls/MainMenuBar.java +++ b/src/main/java/de/unijena/cheminf/mortar/gui/controls/MainMenuBar.java @@ -35,131 +35,129 @@ /** * A MenuBar for the application's {@link MainView}. - * It contains menus for file handling (I/O), shutting down the application, settings and help menu entries + * It contains menus for file handling (I/O), shutting down the application, settings and help menu entries. * * @author Felix Baensch, Jonas Schaub, Samuel Behr * @version 1.0.0.0 */ public class MainMenuBar extends MenuBar { - - // + // /** - * FileMenu + * FileMenu. */ - private Menu fileMenu; + private final Menu fileMenu; /** - * MenuItem to import molecules + * MenuItem to import molecules. */ - private MenuItem openMenuItem; + private final MenuItem openMenuItem; /** - * MenuItem for export + * MenuItem for export. */ - private Menu exportMenu; + private final Menu exportMenu; /** - * Menu for fragments export + * Menu for fragments export. */ - private Menu fragmentsExportMenu; + private final Menu fragmentsExportMenu; /** - * MenuItem to export fragments as csv file + * MenuItem to export fragments as csv file. */ - private MenuItem fragmentsExportToCSVMenuItem; + private final MenuItem fragmentsExportToCSVMenuItem; /** - * MenuItem to export fragments as pdb file + * MenuItem to export fragments as pdb file. */ - private MenuItem fragmentsExportToPDBMenuItem; + private final MenuItem fragmentsExportToPDBMenuItem; /** - * MenuItem to export fragments as pdf file + * MenuItem to export fragments as pdf file. */ - private MenuItem fragmentsExportToPDFMenuItem; + private final MenuItem fragmentsExportToPDFMenuItem; /** - * Menu for fragments export as sdf + * Menu for fragments export as sdf. */ - private Menu fragmentsExportToSDFMenu; + private final Menu fragmentsExportToSDFMenu; /** - * MenuItem to export fragments as sd file + * MenuItem to export fragments as sd file. */ - private MenuItem fragmentsExportToSingleSDFMenuItem; + private final MenuItem fragmentsExportToSingleSDFMenuItem; /** - * MenuItem to export fragments as sd file separately + * MenuItem to export fragments as sd file separately. */ - private MenuItem fragmentsExportToSeparateSDFsMenuItem; + private final MenuItem fragmentsExportToSeparateSDFsMenuItem; /** - * Menu for items export + * Menu for items export. */ - private Menu itemsExportMenu; + private final Menu itemsExportMenu; /** - * MenuItem to export items as csv file + * MenuItem to export items as csv file. */ - private MenuItem itemsExportToCSVMenuItem; + private final MenuItem itemsExportToCSVMenuItem; /** - * MenuItem to export fragments as pdf file + * MenuItem to export fragments as pdf file. */ - private MenuItem itemsExportToPDFMenuItem; + private final MenuItem itemsExportToPDFMenuItem; /** - * MenuItem to exit app + * MenuItem to exit app. */ - private MenuItem exitMenuItem; + private final MenuItem exitMenuItem; /** - * Menu for settings + * Menu for settings. */ - private Menu settingsMenu; + private final Menu settingsMenu; /** - * Menu for pipeline + * Menu for pipeline. */ - private Menu pipelineMenu; + private final Menu pipelineMenu; /** - * Menu for help + * Menu for help. */ - private Menu helpMenu; + private final Menu helpMenu; /** - * MenuItem to open fragmentation settings + * MenuItem to open fragmentation settings. */ - private MenuItem fragmentationSettingsMenuItem; + private final MenuItem fragmentationSettingsMenuItem; /** - * MenuItem to open global settings + * MenuItem to open global settings. */ - private MenuItem globalSettingsMenuItem; + private final MenuItem globalSettingsMenuItem; /** - * MenuItem to open pipeline settings + * MenuItem to open pipeline settings. */ - private MenuItem pipelineSettingsMenuItem; + private final MenuItem pipelineSettingsMenuItem; /** - * MenuItem to open AboutView + * MenuItem to open AboutView. */ - private MenuItem aboutViewMenuItem; + private final MenuItem aboutViewMenuItem; /** - * Menu to choose fragmentation algorithm + * Menu to choose fragmentation algorithm. */ - private Menu fragmentationAlgorithmMenu; + private final Menu fragmentationAlgorithmMenu; /** - * MenuItem to cancel molecule import, only visible if import is running + * MenuItem to cancel molecule import, only visible if import is running. */ - private MenuItem cancelImportMenuItem; + private final MenuItem cancelImportMenuItem; /** - * MenuItem to cancel export, only visible if import is running + * MenuItem to cancel export, only visible if import is running. */ - private MenuItem cancelExportMenuItem; + private final MenuItem cancelExportMenuItem; /** - * Menu to open views + * Menu to open views. */ - private Menu viewsMenu; + private final Menu viewsMenu; /** - * MenuItem to open the histogram + * MenuItem to open the histogram. */ - private MenuItem histogramViewerMenuItem; + private final MenuItem histogramViewerMenuItem; /** - * MenuItem to open the OverviewView + * MenuItem to open the OverviewView. */ - private MenuItem overviewViewMenuItem; + private final MenuItem overviewViewMenuItem; // // // /** - * Constructor - * It initialises the MainMenuBar's components, adds them to the MainMenuBar. - * No listeners were added here. + * Initialises the MainMenuBar's components and adds them to the MainMenuBar. + * No listeners are added here. */ - public MainMenuBar(){ + public MainMenuBar() { super(); // //fileMenu @@ -210,9 +208,9 @@ public MainMenuBar(){ // // /** - * Adds the menu items to the menus and the menus to the menu bar + * Adds the menu items to the menus and the menus to the menu bar. */ - private void addComponentsToMenuBar(){ + private void addComponentsToMenuBar() { // this.getMenus().add(this.fileMenu); //openMenuItem @@ -264,9 +262,9 @@ private void addComponentsToMenuBar(){ // // /** - * Adds the menu items to the export menu + * Adds the menu items to the export menu. */ - private void addComponentsToExportMenu(){ + private void addComponentsToExportMenu() { // this.exportMenu.getItems().add(this.fragmentsExportMenu); this.fragmentsExportMenu.getItems().add(this.fragmentsExportToCSVMenuItem); @@ -275,7 +273,6 @@ private void addComponentsToExportMenu(){ this.fragmentsExportMenu.getItems().add(this.fragmentsExportToSDFMenu); this.fragmentsExportToSDFMenu.getItems().add(this.fragmentsExportToSingleSDFMenuItem); this.fragmentsExportToSDFMenu.getItems().add(this.fragmentsExportToSeparateSDFsMenuItem); -// this.exportMenu.show(); // // this.exportMenu.getItems().add(this.itemsExportMenu); @@ -289,7 +286,7 @@ private void addComponentsToExportMenu(){ // // /** - * Returns the menu item that is supposed to open a molecule set + * Returns the menu item that is supposed to open a molecule set. * * @return the menu item that should open a molecule set */ @@ -299,7 +296,7 @@ public MenuItem getOpenMenuItem() { // // /** - * Returns the menu that is supposed to open a list of export options + * Returns the menu that is supposed to open a list of export options. * * @return the menu that should open a list of export options */ @@ -309,7 +306,7 @@ public Menu getExportMenu() { // // /** - * Returns the menu that is supposed to open a list of fragments export options + * Returns the menu that is supposed to open a list of fragments export options. * * @return the menu that should open a list of fragments export options */ @@ -319,7 +316,7 @@ public Menu getFragmentsExportMenu() { // // /** - * Returns the menu item that is supposed to export the fragments to a CSV file + * Returns the menu item that is supposed to export the fragments to a CSV file. * * @return the menu item that should export the fragments to a CSV file */ @@ -329,7 +326,7 @@ public MenuItem getFragmentsExportToCSVMenuItem() { // // /** - * Returns the menu item that is supposed to export the fragments to a PDB file + * Returns the menu item that is supposed to export the fragments to a PDB file. * * @return the menu item that should export the fragments to a PDB file */ @@ -339,7 +336,7 @@ public MenuItem getFragmentsExportToPDBMenuItem() { // // /** - * Returns the menu item that is supposed to export the fragments to a PDF + * Returns the menu item that is supposed to export the fragments to a PDF. * * @return the menu item that should export the fragments to a PDF */ @@ -349,7 +346,7 @@ public MenuItem getFragmentsExportToPDFMenuItem() { // // /** - * Returns the menu that is supposed to open a list of SDF export options + * Returns the menu that is supposed to open a list of SDF export options. * * @return the menu that should open a list of SDF export options */ @@ -359,7 +356,7 @@ public Menu getFragmentsExportToSDFMenu() { // // /** - * Returns the menu item that is supposed to export the fragments to a single SD file + * Returns the menu item that is supposed to export the fragments to a single SD file. * * @return the menu item that should export the fragments to a single SD file */ @@ -369,7 +366,7 @@ public MenuItem getFragmentsExportToSingleSDFMenuItem() { // // /** - * Returns the menu item that is supposed to export the fragments to separate SD files + * Returns the menu item that is supposed to export the fragments to separate SD files. * * @return the menu item that should export the fragments to separate SD files */ @@ -379,7 +376,7 @@ public MenuItem getFragmentsExportToSeparateSDFsMenuItem() { // // /** - * Returns the menu that is supposed to open a list of items export options + * Returns the menu that is supposed to open a list of items export options. * * @return the menu that should open a list of items export options */ @@ -389,7 +386,7 @@ public Menu getItemsExportMenu() { // // /** - * Returns the menu item that is supposed to export the items to a CSV file + * Returns the menu item that is supposed to export the items to a CSV file. * * @return the menu item that should export the items to a CSV file */ @@ -399,7 +396,7 @@ public MenuItem getItemsExportToCSVMenuItem() { // // /** - * Returns the menu item that is supposed to export the items to a PDF + * Returns the menu item that is supposed to export the items to a PDF. * * @return the menu item that should export the items to a PDF */ @@ -409,7 +406,7 @@ public MenuItem getItemsExportToPDFMenuItem() { // // /** - * Returns the menu item that is supposed to shut down the application + * Returns the menu item that is supposed to shut down the application. * * @return the menu item that should shut down the application */ @@ -419,7 +416,7 @@ public MenuItem getExitMenuItem() { // // /** - * Returns the menu item that opens the global settings view + * Returns the menu item that opens the global settings view. * * @return the menu item that opens the global settings view */ @@ -429,7 +426,7 @@ public MenuItem getGlobalSettingsMenuItem() { // // /** - * Returns the menu item that is supposed to choose the fragmentation algorithm + * Returns the menu item that is supposed to choose the fragmentation algorithm. * * @return the menu item that should choose the fragmentation algorithm */ @@ -439,7 +436,7 @@ public Menu getFragmentationAlgorithmMenu() { // // /** - * Returns the menu item that is supposed to open the fragmentation settings window + * Returns the menu item that is supposed to open the fragmentation settings window. * * @return the menu item that should open the fragmentation settings window */ @@ -449,7 +446,7 @@ public MenuItem getFragmentationSettingsMenuItem() { // // /** - * Returns the menu item that is supposed to open the pipeline settings window + * Returns the menu item that is supposed to open the pipeline settings window. * * @return the menu item that should open the pipeline settings window */ @@ -459,7 +456,7 @@ public MenuItem getPipelineSettingsMenuItem() { // // /** - * Returns the menu item that is supposed to open the OverviewView + * Returns the menu item that is supposed to open the OverviewView. * * @return the menu item that should open the OverviewView */ @@ -469,7 +466,7 @@ public MenuItem getOverviewViewMenuItem() { // // /** - * Returns the menu item that is supposed to open the AboutView + * Returns the menu item that is supposed to open the AboutView. * * @return the menu item that should open the AboutView */ @@ -479,7 +476,7 @@ public MenuItem getAboutViewMenuItem() { // // /** - * Returns the menu item that is supposed to open histogram window + * Returns the menu item that is supposed to open histogram window. * * @return MenuItem to open histogram view */ @@ -489,7 +486,7 @@ public MenuItem getHistogramViewerMenuItem(){ // // /** - * Returns MenuItem to cancel running import, only visible if import is running + * Returns MenuItem to cancel running import, only visible if import is running. * * @return MenuItem to cancel import */ @@ -498,7 +495,7 @@ public MenuItem getCancelImportMenuItem(){ } // /** - * Returns MenuItem to cancel running export, only visible if import is running + * Returns MenuItem to cancel running export, only visible if import is running. * * @return MenuItem to cancel export */ diff --git a/src/main/java/de/unijena/cheminf/mortar/gui/controls/StatusBar.java b/src/main/java/de/unijena/cheminf/mortar/gui/controls/StatusBar.java index d33cf4b7..c36bc993 100644 --- a/src/main/java/de/unijena/cheminf/mortar/gui/controls/StatusBar.java +++ b/src/main/java/de/unijena/cheminf/mortar/gui/controls/StatusBar.java @@ -35,26 +35,25 @@ import javafx.scene.layout.FlowPane; /** - * StatusBar to show status of the application and progress of a running task + * StatusBar to show status of the application and progress of a running task. * * @author Felix Baensch, Jonas Schaub * @version 1.0.0.0 */ public class StatusBar extends FlowPane { - - // + // /** - * Label to show status message + * Label to show status message. */ - private Label statusLabel; + private final Label statusLabel; /** - * ProgressBar + * ProgressBar. */ - private ProgressBar progressBar; + private final ProgressBar progressBar; // // /** - * Constructor + * Constructor. */ public StatusBar(){ super(); @@ -73,7 +72,8 @@ public StatusBar(){ // // /** - * Returns statusLabel + * Returns statusLabel. + * * @return Label */ public Label getStatusLabel() { @@ -81,7 +81,8 @@ public Label getStatusLabel() { } // /** - * Returns the progressBar + * Returns the progressBar. + * * @return ProgressBar */ public ProgressBar getProgressBar() { diff --git a/src/main/java/de/unijena/cheminf/mortar/gui/util/ExternalTool.java b/src/main/java/de/unijena/cheminf/mortar/gui/util/ExternalTool.java index 8eb4d3ad..d9c91467 100644 --- a/src/main/java/de/unijena/cheminf/mortar/gui/util/ExternalTool.java +++ b/src/main/java/de/unijena/cheminf/mortar/gui/util/ExternalTool.java @@ -26,41 +26,40 @@ package de.unijena.cheminf.mortar.gui.util; /** - * Wrapper class to visualize information about external tools used in MORTAR in the AboutView + * Wrapper class to visualize information about external tools used in MORTAR in the AboutView. * * @author Felix Baensch * @version 1.0.0.0 */ public class ExternalTool { - // /** - * Name of the tool + * Name of the tool. */ private String name; /** - * Number string of the used version of the tool + * Number string of the used version of the tool. */ private String version; /** - * Author(s) of the tool + * Author(s) of the tool. */ private String author; /** - * License of the tool + * License of the tool. */ private String license; // // /** - * Constructor + * Constructor. * * @param aName Name of tool * @param aVersion Version used * @param anAuthor Author * @param aLicense License used */ - public ExternalTool(String aName, String aVersion, String anAuthor, String aLicense){ + public ExternalTool(String aName, String aVersion, String anAuthor, String aLicense) { this.name = aName; this.version = aVersion; this.author = anAuthor; @@ -69,7 +68,7 @@ public ExternalTool(String aName, String aVersion, String anAuthor, String aLice // // /** - * Returns name as String + * Returns name as String. * * @return {@link String} */ @@ -78,7 +77,7 @@ public String getName() { } // /** - * Returns version number as String + * Returns version number as String. * * @return {@link String} */ @@ -87,7 +86,7 @@ public String getVersion() { } // /** - * Returns author(s) name + * Returns author(s) name. * * @return {@link String} */ @@ -96,7 +95,7 @@ public String getAuthor() { } // /** - * Returns license + * Returns license. * * @return {@link String} */ @@ -105,7 +104,8 @@ public String getLicense() { } // /** - * Sets given String as name + * Sets given String as name. + * * @param name {@link String} */ public void setName(String name) { @@ -113,7 +113,7 @@ public void setName(String name) { } // /** - * Sets given String as version + * Sets given String as version. * * @param version {@link String} */ @@ -122,7 +122,7 @@ public void setVersion(String version) { } // /** - * Sets given String as author + * Sets given String as author. * * @param author {@link String} */ @@ -131,7 +131,7 @@ public void setAuthor(String author) { } // /** - * Sets given String as license + * Sets given String as license. * * @param license {@link String} */ diff --git a/src/main/java/de/unijena/cheminf/mortar/gui/util/GuiDefinitions.java b/src/main/java/de/unijena/cheminf/mortar/gui/util/GuiDefinitions.java index d212adc8..f5ceabab 100644 --- a/src/main/java/de/unijena/cheminf/mortar/gui/util/GuiDefinitions.java +++ b/src/main/java/de/unijena/cheminf/mortar/gui/util/GuiDefinitions.java @@ -30,13 +30,13 @@ import javafx.scene.input.KeyCombination; /** - * GUI definitions + * GUI definitions. * * @author Felix Baensch * @version 1.0.0.0 */ public final class GuiDefinitions { - // + // /** * Private parameter-less constructor. * Introduced because javadoc build complained about classes without declared default constructor. @@ -44,113 +44,124 @@ public final class GuiDefinitions { private GuiDefinitions() { } // - /** - * Value for the GUI insets + * Value for the GUI insets. */ public static final double GUI_INSETS_VALUE = 10.0; /** - * Value for the GUI spacing + * Value for the GUI spacing. */ public static final double GUI_SPACING_VALUE = 5.0; /** - * Value for the width of the main view + * Value for the width of the main view. */ public static final double GUI_MAIN_VIEW_WIDTH_VALUE = 1024.0; /** - * Value for the height of the main view + * Value for the height of the main view. */ public static final double GUI_MAIN_VIEW_HEIGHT_VALUE = 768.0; /** - * Value for the width of a button + * Value for the width of a button. */ public static final double GUI_BUTTON_WIDTH_VALUE = 75.0; /** - * Value for the height of a button + * Value for the height of a button. */ public static final double GUI_BUTTON_HEIGHT_VALUE = 25.0; /** - * Value for the distance between the buttons + * Value for the distance between the buttons. */ public static final double GUI_BUTTON_SPACING_VALUE = 5.0; /** - * Value for the Button insets + * Value for the Button insets. */ public static final double GUI_BUTTON_INSETS_VALUE = 13.0; /** - * Value for the height of status bar + * Value for the height of status bar. */ public static final double GUI_STATUSBAR_HEIGHT_VALUE = 25.0; /** - * Value for the height of control containers + * Value for the height of control containers. */ public static final double GUI_CONTROL_CONTAINER_HEIGHT = 50.0; /** - * Value for the preference width of a text field + * Value for the preference width of a text field. */ public static final double GUI_TEXT_FIELD_PREF_WIDTH_VALUE = 50.0; /** - * Value for the maximum width of a text field + * Value for the maximum width of a text field. */ public static final double GUI_SETTINGS_TEXT_FIELD_MAX_WIDTH_VALUE = 70.0; /** - * Selection column width + * Value for the preferred width of a combo box in the settings view. + */ + public static final double GUI_SETTING_COMBO_BOX_PREF_WIDTH_VALUE = 210.0; + /** + * Value for the maximum width of a combo box in the settings view. + */ + public static final double GUI_SETTING_COMBO_BOX_MAX_WIDTH_VALUE = 210.0; + /** + * Selection column width. */ public static final double GUI_MOLECULES_TAB_TABLEVIEW_SELECTION_COLUMN_WIDTH = 40.0; /** - * Height of the table view header + * Height of the table view header. */ public static final double GUI_TABLE_VIEW_HEADER_HEIGHT = 24.0; /** - * Min height for a structure image of a molecule shown in gui + * Min height for a structure image of a molecule shown in gui. */ public static final double GUI_STRUCTURE_IMAGE_MIN_HEIGHT = 50.0; /** - * Height of the control panel of pagination + * Height of the control panel of pagination. */ public static final double GUI_PAGINATION_CONTROL_PANEL_HEIGHT = 45.0; /** - * Width of the third column of gid panes used to align nodes + * Width of the third column of gid panes used to align nodes. */ public static final double GUI_GRIDPANE_FOR_NODE_ALIGNMENT_THIRD_COL_WIDTH = 200.0; /** - * Max width of tooltip + * Max width for tooltips. */ public static final double GUI_TOOLTIP_MAX_WIDTH = 500.0; /** - * Width of an image copied to clipboard + * Show duration for tooltips. + */ + public static final double GUI_TOOLTIP_SHOW_DURATION = 10.0; + /** + * Width of an image copied to clipboard. */ public static final double GUI_COPY_IMAGE_IMAGE_WIDTH = 1500.0; /** - * Height of an image copied to clipboard + * Height of an image copied to clipboard. */ public static final double GUI_COPY_IMAGE_IMAGE_HEIGHT = 1000.0; /** - * KeyCodeCombination for Control + C + * KeyCodeCombination for Control + C. */ public static final KeyCodeCombination KEY_CODE_COPY = new KeyCodeCombination(KeyCode.C, KeyCombination.CONTROL_DOWN); /** - * KeyCodeCombination to navigate to last page of pagination + * KeyCodeCombination to navigate to last page of pagination. */ public static final KeyCodeCombination KEY_CODE_LAST_PAGE = new KeyCodeCombination(KeyCode.RIGHT, KeyCombination.CONTROL_DOWN); /** - * KeyCodeCombination to navigate to first page of pagination + * KeyCodeCombination to navigate to first page of pagination. */ public static final KeyCodeCombination KEY_CODE_FIRST_PAGE = new KeyCodeCombination(KeyCode.LEFT, KeyCombination.CONTROL_DOWN); /** - * Value for the delay of a second click to be registered as double-click (in ms) + * Value for the delay of a second click to be registered as double-click (in ms). */ public static final int DOUBLE_CLICK_DELAY = 250; /** - * Value for the not scrollable histogram + * Value for the not scrollable histogram. */ public static final double GUI_NOT_SCROLLABLE_HEIGHT = 651.0; /** - * Value for the width of the textField + * Value for the width of the textField. */ public static final double GUI_TEXT_FIELD_WIDTH = 50.0; /** - * Width for the text field in the pagination control box + * Width for the text field in the pagination control box. */ public static final double PAGINATION_TEXT_FIELD_WIDTH = 40.0; } diff --git a/src/main/java/de/unijena/cheminf/mortar/gui/util/GuiUtil.java b/src/main/java/de/unijena/cheminf/mortar/gui/util/GuiUtil.java index 270e9807..797d0ed9 100644 --- a/src/main/java/de/unijena/cheminf/mortar/gui/util/GuiUtil.java +++ b/src/main/java/de/unijena/cheminf/mortar/gui/util/GuiUtil.java @@ -32,7 +32,6 @@ import de.unijena.cheminf.mortar.model.data.FragmentDataModel; import de.unijena.cheminf.mortar.model.data.MoleculeDataModel; import de.unijena.cheminf.mortar.model.depict.DepictionUtil; -import de.unijena.cheminf.mortar.model.settings.SettingsContainer; import de.unijena.cheminf.mortar.model.util.CollectionUtil; import javafx.scene.control.Alert; @@ -48,6 +47,7 @@ import javafx.scene.control.TableView; import javafx.scene.control.TextArea; import javafx.scene.control.TextFormatter; +import javafx.scene.control.Tooltip; import javafx.scene.control.cell.PropertyValueFactory; import javafx.scene.image.Image; import javafx.scene.image.ImageView; @@ -57,6 +57,7 @@ import javafx.scene.layout.Pane; import javafx.scene.layout.Priority; import javafx.scene.layout.Region; +import javafx.util.Duration; import javafx.util.StringConverter; import org.openscience.cdk.exception.CDKException; @@ -64,8 +65,8 @@ import java.io.PrintWriter; import java.io.StringWriter; -import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.Objects; import java.util.function.UnaryOperator; import java.util.logging.Level; @@ -73,7 +74,7 @@ import java.util.regex.Pattern; /** - * GUI utility + * GUI utility. * * @author Jonas Schaub, Felix Baensch * @version 1.0.0.0 @@ -86,7 +87,7 @@ public class GuiUtil { private static final Logger LOGGER = Logger.getLogger(GuiUtil.class.getName()); // // - // + // /** * Private parameter-less constructor. * Introduced because javadoc build complained about classes without declared default constructor. @@ -105,7 +106,7 @@ private GuiUtil() { * @param aHeaderText Header of the alert message * @param aContentText Text that the alert message contains */ - public static void guiMessageAlert(Alert.AlertType anAlertType, String aTitle, String aHeaderText, String aContentText){ + public static void guiMessageAlert(Alert.AlertType anAlertType, String aTitle, String aHeaderText, String aContentText) { Alert tmpAlert = new Alert(anAlertType); tmpAlert.setTitle(aTitle); tmpAlert.setHeaderText(aHeaderText); @@ -125,7 +126,7 @@ public static void guiMessageAlert(Alert.AlertType anAlertType, String aTitle, S * @param aHeaderText Header of the alert message * @param aHyperlink Hyperlink that the alert message contains */ - public static void guiMessageAlertWithHyperlink(Alert.AlertType anAlertType, String aTitle, String aHeaderText, Hyperlink aHyperlink){ + public static void guiMessageAlertWithHyperlink(Alert.AlertType anAlertType, String aTitle, String aHeaderText, Hyperlink aHyperlink) { Alert tmpAlert = new Alert(anAlertType); tmpAlert.setTitle(aTitle); tmpAlert.setHeaderText(aHeaderText); @@ -145,7 +146,7 @@ public static void guiMessageAlertWithHyperlink(Alert.AlertType anAlertType, Str * @param aContentText Text that the confirmation alert contains * @return ButtonType selected by user - ButtonType.OK or ButtonType.CANCEL */ - public static ButtonType guiConfirmationAlert(String aTitle, String aHeaderText, String aContentText){ + public static ButtonType guiConfirmationAlert(String aTitle, String aHeaderText, String aContentText) { Alert tmpAlert = new Alert(Alert.AlertType.CONFIRMATION); //tmpAlert.setResizable(true); tmpAlert.setTitle(aTitle); @@ -165,9 +166,9 @@ public static ButtonType guiConfirmationAlert(String aTitle, String aHeaderText, * @param aContentText Text of the alert dialog * @param anException exception to report, may be null */ - public static void guiExceptionAlert(String aTitle, String aHeaderText, String aContentText, Exception anException){ + public static void guiExceptionAlert(String aTitle, String aHeaderText, String aContentText, Exception anException) { String tmpExceptionString; - if(Objects.isNull(anException)){ + if (Objects.isNull(anException)) { tmpExceptionString = "Exception is null."; } else { StringWriter tmpStringWriter = new StringWriter(); @@ -188,8 +189,8 @@ public static void guiExceptionAlert(String aTitle, String aHeaderText, String a * @param aLabelText Text to show above expandable area * @param anExpandableString Text to show in expandable area */ - public static void guiExpandableAlert(String aTitle, String aHeaderText, String aContentText, String aLabelText, String anExpandableString){ - try{ + public static void guiExpandableAlert(String aTitle, String aHeaderText, String aContentText, String aLabelText, String anExpandableString) { + try { Alert tmpAlert = new Alert(Alert.AlertType.ERROR); tmpAlert.setTitle(aTitle); tmpAlert.setHeaderText(aHeaderText); @@ -210,24 +211,25 @@ public static void guiExpandableAlert(String aTitle, String aHeaderText, String tmpAlert.getDialogPane().setExpandableContent(tmpGridPane); //Show and wait alert tmpAlert.showAndWait(); - }catch(Exception aNewThrownException){ + } catch(Exception aNewThrownException) { guiMessageAlert(Alert.AlertType.ERROR, Message.get("Error.ExceptionAlert.Title"), Message.get("Error.ExceptionAlert.Header"), aNewThrownException.toString()); - LOGGER.log(Level.SEVERE, aNewThrownException.toString(), aNewThrownException); + GuiUtil.LOGGER.log(Level.SEVERE, aNewThrownException.toString(), aNewThrownException); } } // /** - * Sorts the items of the TableView over all pages of the pagination and adds + * Sorts the items of the TableView over all pages of the pagination and adds. * * @param anEvent SortEvent {@literal <}TableView {@literal >} * @param tmpPagination Pagination * @param tmpRowsPerPage int */ - public static void sortTableViewGlobally(SortEvent anEvent, Pagination tmpPagination, int tmpRowsPerPage){ - if(anEvent == null || anEvent.getSource().getSortOrder().size() == 0) + public static void sortTableViewGlobally(SortEvent anEvent, Pagination tmpPagination, int tmpRowsPerPage) { + if (anEvent == null || anEvent.getSource().getSortOrder().isEmpty()) { return; - String tmpSortProp = ((PropertyValueFactory)((TableColumn) anEvent.getSource().getSortOrder().get(0)).cellValueFactoryProperty().getValue()).getProperty().toString(); - TableColumn.SortType tmpSortType = ((TableColumn) anEvent.getSource().getSortOrder().get(0)).getSortType(); + } + String tmpSortProp = ((PropertyValueFactory)((TableColumn) anEvent.getSource().getSortOrder().getFirst()).cellValueFactoryProperty().getValue()).getProperty().toString(); + TableColumn.SortType tmpSortType = ((TableColumn) anEvent.getSource().getSortOrder().getFirst()).getSortType(); CollectionUtil.sortGivenFragmentListByPropertyAndSortType(((IDataTableView)anEvent.getSource()).getItemsList(), tmpSortProp, tmpSortType == TableColumn.SortType.ASCENDING); int fromIndex = tmpPagination.getCurrentPageIndex() * tmpRowsPerPage; int toIndex = Math.min(fromIndex + tmpRowsPerPage, ((IDataTableView)anEvent.getSource()).getItemsList().size()); @@ -236,12 +238,12 @@ public static void sortTableViewGlobally(SortEvent anEvent, Paginatio } // /** - * Binds height and width property of the child control to the parent pane properties + * Binds height and width property of the child control to the parent pane properties. * * @param aParentPane Pane * @param aChildControl Control */ - public static void guiBindControlSizeToParentPane(Pane aParentPane, Control aChildControl){ + public static void guiBindControlSizeToParentPane(Pane aParentPane, Control aChildControl) { aChildControl.prefHeightProperty().bind(aParentPane.heightProperty()); aChildControl.prefWidthProperty().bind(aParentPane.widthProperty()); } @@ -251,7 +253,7 @@ public static void guiBindControlSizeToParentPane(Pane aParentPane, Control aChi * * @return GUI input pattern for integer values */ - public static Pattern getIntegerPattern(){ + public static Pattern getIntegerPattern() { return Pattern.compile("-?(([1-9][0-9]*)|0)?"); } // @@ -260,7 +262,7 @@ public static Pattern getIntegerPattern(){ * * @return GUI input pattern for positive integer values */ - public static Pattern getPositiveIntegerInclZeroPattern(){ + public static Pattern getPositiveIntegerInclZeroPattern() { return Pattern.compile("[0-9]*"); } // @@ -269,19 +271,28 @@ public static Pattern getPositiveIntegerInclZeroPattern(){ * * @return GUI input pattern for double values */ - public static Pattern getDoublePattern(){ + public static Pattern getDoublePattern() { return Pattern.compile("-?(([1-9][0-9]*)|0)?(\\.[0-9]*)?"); } // + /** + * Returns an input pattern for positive double values, including 0.0 and equal notations of zero. + * + * @return GUI input pattern for positive double values + */ + public static Pattern getPositiveDoublePattern() { + return Pattern.compile("(([1-9][0-9]*)|0)?(\\.[0-9]*)?"); + } + // /** * Returns an input filter for integer values. "-" may be the first sign, the first number may not be 0. * * @return GUI input filter for integer values */ - public static UnaryOperator getIntegerFilter(){ - return c ->{ + public static UnaryOperator getIntegerFilter() { + return c -> { String tmpText = c.getControlNewText(); - if(GuiUtil.getIntegerPattern().matcher(tmpText).matches()) { + if (GuiUtil.getIntegerPattern().matcher(tmpText).matches()) { return c; } else { return null; @@ -290,16 +301,16 @@ public static UnaryOperator getIntegerFilter(){ } // /** - * * Method that creates an Integer filter to prevent the entry of unwanted - * characters such as Strings or special characters and also 0 for first entry. + * characters such as Strings or special characters and also 0 for first entry if specified. * + * @param anIsZeroIncluded true if zero should be allowed as value (text: "0") * @return GUI input filter for positive integer values */ - public static UnaryOperator getPositiveIntegerWithoutZeroFilter() { + public static UnaryOperator getPositiveIntegerFilter(boolean anIsZeroIncluded) { return c -> { String tmpText = c.getControlNewText(); - if (tmpText.equals("0")) { + if (tmpText.equals("0") && !anIsZeroIncluded) { return null; } if (GuiUtil.getPositiveIntegerInclZeroPattern().matcher(tmpText).matches()) { @@ -314,14 +325,30 @@ public static UnaryOperator getPositiveIntegerWithoutZeroF * * @return GUI input filter for double values */ - public static UnaryOperator getDoubleFilter(){ - return c ->{ - String text = c.getControlNewText(); - if(getDoublePattern().matcher(text).matches()) { - return c; - } else { - return null; - } + public static UnaryOperator getDoubleFilter() { + return c -> { + String text = c.getControlNewText(); + if (GuiUtil.getDoublePattern().matcher(text).matches()) { + return c; + } else { + return null; + } + }; + } + // + /** + * Returns an input filter for positive double values, including 0.0 and equal notations of zero. + * + * @return GUI input filter for positive double values + */ + public static UnaryOperator getPositiveDoubleFilter() { + return c -> { + String text = c.getControlNewText(); + if (GuiUtil.getPositiveDoublePattern().matcher(text).matches()) { + return c; + } else { + return null; + } }; } // @@ -331,7 +358,7 @@ public static UnaryOperator getDoubleFilter(){ * * @return String-Integer converter */ - public static StringConverter getStringToIntegerConverter(){ + public static StringConverter getStringToIntegerConverter() { return new StringConverter() { @Override public String toString(Integer anObject) { @@ -339,9 +366,9 @@ public String toString(Integer anObject) { } @Override public Integer fromString(String aString) { - if(aString.isEmpty() || "-".equals(aString) || ".".equals(aString) || "-.".equals(aString) || "0.".equals(aString)){ + if (aString.isEmpty() || "-".equals(aString) || ".".equals(aString) || "-.".equals(aString) || "0.".equals(aString)) { return 0; - } else{ + } else { return Integer.valueOf(aString); } } @@ -354,7 +381,7 @@ public Integer fromString(String aString) { * * @return String-Double converter */ - public static StringConverter getStringToDoubleConverter(){ + public static StringConverter getStringToDoubleConverter() { return new StringConverter() { @Override public String toString(Double anObject) { @@ -362,10 +389,9 @@ public String toString(Double anObject) { } @Override public Double fromString(String aString) { - if(aString.isEmpty() || "-".equals(aString) || ".".equals(aString) || "-.".equals(aString)){ + if (aString.isEmpty() || "-".equals(aString) || ".".equals(aString) || "-.".equals(aString)) { return 0.0; - } - else { + } else { return Double.valueOf(aString); } } @@ -373,52 +399,45 @@ public Double fromString(String aString) { } // /** - * Copies content of selected cell to system clipboard + * Copies content of selected cell to system clipboard. * * @param aTableView TableView to copy from */ - public static void copySelectedTableViewCellsToClipboard(TableView aTableView){ - for(TablePosition tmpPos :aTableView.getSelectionModel().getSelectedCells()){ + public static void copySelectedTableViewCellsToClipboard(TableView aTableView) { + for (TablePosition tmpPos : aTableView.getSelectionModel().getSelectedCells()) { int tmpRowIndex = tmpPos.getRow(); int tmpColIndex = tmpPos.getColumn(); int tmpFragmentColIndexItemsTab = 2; Object tmpCell; - if(aTableView.getClass() == ItemizationDataTableView.class && tmpColIndex > tmpFragmentColIndexItemsTab -1){ + if (aTableView.getClass() == ItemizationDataTableView.class && tmpColIndex > tmpFragmentColIndexItemsTab -1) { tmpCell = aTableView.getColumns().get(tmpFragmentColIndexItemsTab).getColumns().get(tmpColIndex - 2).getCellData(tmpRowIndex); - }else{ + } else { tmpCell = aTableView.getColumns().get(tmpColIndex).getCellData(tmpRowIndex); } - if(tmpCell == null){ + if (tmpCell == null) { return; - } - else{ + } else { ClipboardContent tmpClipboardContent = new ClipboardContent(); - if(tmpCell.getClass() == String.class){ + if (tmpCell.getClass() == String.class) { tmpClipboardContent.putString((String) tmpCell); - } - else if(tmpCell.getClass() == Integer.class){ + } else if(tmpCell.getClass() == Integer.class) { tmpClipboardContent.putString(((Integer)tmpCell).toString()); - } - else if(tmpCell.getClass() == Double.class){ + } else if(tmpCell.getClass() == Double.class) { tmpClipboardContent.putString(((Double)tmpCell).toString()); - } - else if(tmpCell.getClass() == ImageView.class){ + } else if(tmpCell.getClass() == ImageView.class) { Image tmpImage; IAtomContainer tmpAtomContainer; try { - if(aTableView.getClass() == FragmentsDataTableView.class){ + if (aTableView.getClass() == FragmentsDataTableView.class) { tmpAtomContainer = ((FragmentDataModel) aTableView.getItems().get(tmpRowIndex)).getFirstParentMolecule().getAtomContainer(); - } - else if(aTableView.getClass() == ItemizationDataTableView.class){ - if(tmpColIndex > 1){ + } else if(aTableView.getClass() == ItemizationDataTableView.class) { + if (tmpColIndex > 1) { String tmpFragmentationName = ((ItemizationDataTableView) aTableView).getFragmentationName(); - tmpAtomContainer = ((MoleculeDataModel) aTableView.getItems().get(tmpRowIndex)).getFragmentsOfSpecificAlgorithm(tmpFragmentationName).get(tmpColIndex-2).getAtomContainer(); //magic number - } - else { + tmpAtomContainer = ((MoleculeDataModel) aTableView.getItems().get(tmpRowIndex)).getFragmentsOfSpecificFragmentation(tmpFragmentationName).get(tmpColIndex-2).getAtomContainer(); //magic number + } else { tmpAtomContainer = ((MoleculeDataModel) aTableView.getItems().get(tmpRowIndex)).getAtomContainer(); } - } - else { + } else { tmpAtomContainer = ((MoleculeDataModel) aTableView.getItems().get(tmpRowIndex)).getAtomContainer(); } tmpImage = DepictionUtil.depictImageWithZoomAndFillToFitAndWhiteBackground(tmpAtomContainer, 1, GuiDefinitions.GUI_COPY_IMAGE_IMAGE_WIDTH, GuiDefinitions.GUI_COPY_IMAGE_IMAGE_HEIGHT,true, true); @@ -426,8 +445,7 @@ else if(aTableView.getClass() == ItemizationDataTableView.class){ } catch (CDKException e) { tmpClipboardContent.putImage(((ImageView) tmpCell).getImage()); } - } - else{ + } else { return; } Clipboard.getSystemClipboard().setContent(tmpClipboardContent); @@ -437,58 +455,58 @@ else if(aTableView.getClass() == ItemizationDataTableView.class){ // /** * Sets the height for structure images to each MoleculeDataModel object of the items list of the tableView. - * If image height is too small it will be set to GuiDefinitions.GUI_STRUCTURE_IMAGE_MIN_HEIGHT (50.0) + * If image height is too small it will be set to GuiDefinitions.GUI_STRUCTURE_IMAGE_MIN_HEIGHT (50.0). * * @param aTableView TableView * @param aHeight double - * @param aSettingsContainer SettingsContainer + * @param aRowsPerPage int */ - public static void setImageStructureHeight(TableView aTableView, double aHeight, SettingsContainer aSettingsContainer){ + public static void setImageStructureHeight(TableView aTableView, double aHeight, int aRowsPerPage) { double tmpHeight = (aHeight - GuiDefinitions.GUI_TABLE_VIEW_HEADER_HEIGHT - GuiDefinitions.GUI_PAGINATION_CONTROL_PANEL_HEIGHT) - / aSettingsContainer.getRowsPerPageSetting(); - if(aTableView.getClass().equals(ItemizationDataTableView.class)){ + / aRowsPerPage; + if (aTableView.getClass().equals(ItemizationDataTableView.class)) { tmpHeight = (aHeight - 2*GuiDefinitions.GUI_TABLE_VIEW_HEADER_HEIGHT - GuiDefinitions.GUI_PAGINATION_CONTROL_PANEL_HEIGHT) - / aSettingsContainer.getRowsPerPageSetting(); + / aRowsPerPage; } - if(tmpHeight < GuiDefinitions.GUI_STRUCTURE_IMAGE_MIN_HEIGHT){ + if (tmpHeight < GuiDefinitions.GUI_STRUCTURE_IMAGE_MIN_HEIGHT) { tmpHeight = GuiDefinitions.GUI_STRUCTURE_IMAGE_MIN_HEIGHT; } - if(aTableView.getClass().equals(ItemizationDataTableView.class)){ - for(MoleculeDataModel tmpMoleculeDataModel : ((IDataTableView)aTableView).getItemsList()){ + if (aTableView.getClass().equals(ItemizationDataTableView.class)) { + for (MoleculeDataModel tmpMoleculeDataModel : ((IDataTableView)aTableView).getItemsList()) { tmpMoleculeDataModel.setStructureImageHeight(tmpHeight); String tmpFragmentationName = ((ItemizationDataTableView) aTableView).getFragmentationName(); - if(!tmpMoleculeDataModel.hasMoleculeUndergoneSpecificFragmentation(tmpFragmentationName)){ + if (!tmpMoleculeDataModel.hasMoleculeUndergoneSpecificFragmentation(tmpFragmentationName)) { continue; } - for(FragmentDataModel tmpFragmentDataModel : tmpMoleculeDataModel.getFragmentsOfSpecificAlgorithm(tmpFragmentationName)){ + for (FragmentDataModel tmpFragmentDataModel : tmpMoleculeDataModel.getFragmentsOfSpecificFragmentation(tmpFragmentationName)) { tmpFragmentDataModel.setStructureImageHeight(tmpHeight); } } - } - else{ - for(MoleculeDataModel tmpMoleculeDataModel : ((IDataTableView)aTableView).getItemsList()){ + } else { + for (MoleculeDataModel tmpMoleculeDataModel : ((IDataTableView)aTableView).getItemsList()) { tmpMoleculeDataModel.setStructureImageHeight(tmpHeight); } } } // /** - * Returns the largest number of fragments of one molecule found in the given list for the given fragmentation name + * Returns the largest number of fragments of one molecule found in the given list for the given fragmentation name. * * @param aListOfMolecules List of MoleculeDataModels * @param aFragmentationName String for the fragmentation name * @return largest number of fragments of one molecule */ - public static int getLargestNumberOfFragmentsForGivenMoleculeListAndFragmentationName(List aListOfMolecules, String aFragmentationName){ - int tmpAmount = 0; //tmpAmount is the number of fragments appearing in the molecule with the highest number of fragments - for (int i = 0; i < aListOfMolecules.size(); i++) { - if(!aListOfMolecules.get(i).hasMoleculeUndergoneSpecificFragmentation(aFragmentationName)){ + public static int getLargestNumberOfFragmentsForGivenMoleculeListAndFragmentationName(List aListOfMolecules, String aFragmentationName) { + //tmpAmount is the number of fragments appearing in the molecule with the highest number of fragments + int tmpAmount = 0; + for (MoleculeDataModel aListOfMolecule : aListOfMolecules) { + if (!aListOfMolecule.hasMoleculeUndergoneSpecificFragmentation(aFragmentationName)) { continue; } - HashMap tmpCurrentFragmentsMap = aListOfMolecules.get(i).getFragmentFrequencyOfSpecificAlgorithm(aFragmentationName); - if (tmpCurrentFragmentsMap == null) { //redundant, see if clause above + Map tmpCurrentFragmentsMap = aListOfMolecule.getFragmentFrequencyOfSpecificFragmentation(aFragmentationName); + if (tmpCurrentFragmentsMap == null) { //redundant, see if-clause above continue; } int tmpNrOfFragmentsOfCurrentMolecule = tmpCurrentFragmentsMap.size(); @@ -511,5 +529,20 @@ public static Button getButtonOfStandardSize(String aText) { tmpButton.setPrefHeight(GuiDefinitions.GUI_BUTTON_HEIGHT_VALUE); return tmpButton; } + // + /** + * Returns a new tooltip with the given text, configured with a fixed maximum width, text wrap activated, and + * a set show duration. + * + * @param aText text for the tooltip + * @return new tooltip instance + */ + public static Tooltip createTooltip(String aText) { + Tooltip tmpTooltip = new Tooltip(aText); + tmpTooltip.setMaxWidth(GuiDefinitions.GUI_TOOLTIP_MAX_WIDTH); + tmpTooltip.setWrapText(true); + tmpTooltip.setShowDuration(Duration.seconds(GuiDefinitions.GUI_TOOLTIP_SHOW_DURATION)); + return tmpTooltip; + } // } diff --git a/src/main/java/de/unijena/cheminf/mortar/gui/views/AboutView.java b/src/main/java/de/unijena/cheminf/mortar/gui/views/AboutView.java index aeb64ddc..90711e65 100644 --- a/src/main/java/de/unijena/cheminf/mortar/gui/views/AboutView.java +++ b/src/main/java/de/unijena/cheminf/mortar/gui/views/AboutView.java @@ -25,8 +25,10 @@ package de.unijena.cheminf.mortar.gui.views; +import de.unijena.cheminf.mortar.configuration.IConfiguration; import de.unijena.cheminf.mortar.gui.util.ExternalTool; import de.unijena.cheminf.mortar.gui.util.GuiDefinitions; +import de.unijena.cheminf.mortar.gui.util.GuiUtil; import de.unijena.cheminf.mortar.message.Message; import de.unijena.cheminf.mortar.model.util.BasicDefinitions; @@ -43,7 +45,6 @@ import javafx.scene.control.TableColumn; import javafx.scene.control.TableView; import javafx.scene.control.TitledPane; -import javafx.scene.control.Tooltip; import javafx.scene.control.cell.PropertyValueFactory; import javafx.scene.image.Image; import javafx.scene.image.ImageView; @@ -56,8 +57,6 @@ import javafx.scene.layout.VBox; import javafx.scene.text.Text; -import java.io.InputStream; - /** * "About" window view. * @@ -65,49 +64,55 @@ * @version 1.0.0.0 */ public class AboutView extends AnchorPane { - // /** - * TableView to show ExternalTool properties + * TableView to show ExternalTool properties. */ - private TableView tableView; + private final TableView tableView; /** - * Button to open log file directory + * Button to open log file directory. */ - private Button logFileButton; + private final Button logFileButton; /** - * Button to open GitHub repository + * Button to open GitHub repository. */ - private Button gitHubButton; + private final Button gitHubButton; /** - * Button to close this view + * Button to close this view. */ - private Button closeButton; + private final Button closeButton; /** - * Button to open tutorial pdf + * Button to open tutorial pdf. */ - private Button tutorialButton; + private final Button tutorialButton; /** - * ImageView for application logo + * ImageView for application logo. */ - private ImageView logoImageView; + private final ImageView logoImageView; /** - * GridPane to align information and logo + * GridPane to align information and logo. */ - private GridPane gridPane; + private final GridPane gridPane; + /** + * Configuration class to read resource file paths from. + */ + private final IConfiguration configuration; // // /** - * Constructor + * Constructor. + * + * @param aConfiguration configuration instance to read resource file paths from */ - public AboutView(){ + public AboutView(IConfiguration aConfiguration) { super(); + this.configuration = aConfiguration; //borderPane BorderPane borderPane = new BorderPane(); - AboutView.setTopAnchor(borderPane, 0.0); - AboutView.setRightAnchor(borderPane, 0.0); - AboutView.setLeftAnchor(borderPane, 0.0); - AboutView.setBottomAnchor(borderPane, 0.0); + AnchorPane.setTopAnchor(borderPane, 0.0); + AnchorPane.setRightAnchor(borderPane, 0.0); + AnchorPane.setLeftAnchor(borderPane, 0.0); + AnchorPane.setBottomAnchor(borderPane, 0.0); //borderPane bottom -> buttons HBox hBoxButtonsHBox = new HBox(); hBoxButtonsHBox.setStyle("-fx-background-color: LightGrey"); @@ -115,12 +120,12 @@ public AboutView(){ hBoxButtonsHBox.prefWidthProperty().bind(this.widthProperty()); hBoxButtonsHBox.maxWidthProperty().bind(this.widthProperty()); //-left side - this.logFileButton = new Button(Message.get("AboutView.logFileButton.text")); - this.logFileButton.setTooltip(new Tooltip(Message.get("AboutView.logFileButton.tooltip"))); - this.gitHubButton = new Button(Message.get("AboutView.gitHubButton.text")); - this.gitHubButton.setTooltip(new Tooltip(Message.get("AboutView.gitHubButton.tooltip"))); - this.tutorialButton = new Button(Message.get("AboutView.tutorialButton.text")); - this.tutorialButton.setTooltip(new Tooltip(Message.get("AboutView.tutorialButton.tooltip"))); + this.logFileButton = GuiUtil.getButtonOfStandardSize(Message.get("AboutView.logFileButton.text")); + this.logFileButton.setTooltip(GuiUtil.createTooltip(Message.get("AboutView.logFileButton.tooltip"))); + this.gitHubButton = GuiUtil.getButtonOfStandardSize(Message.get("AboutView.gitHubButton.text")); + this.gitHubButton.setTooltip(GuiUtil.createTooltip(Message.get("AboutView.gitHubButton.tooltip"))); + this.tutorialButton = GuiUtil.getButtonOfStandardSize(Message.get("AboutView.tutorialButton.text")); + this.tutorialButton.setTooltip(GuiUtil.createTooltip(Message.get("AboutView.tutorialButton.tooltip"))); HBox hBoxLeftSideButtons = new HBox(); hBoxLeftSideButtons.getChildren().addAll(this.logFileButton, this.gitHubButton, this.tutorialButton); hBoxLeftSideButtons.setAlignment(Pos.CENTER_LEFT); @@ -129,7 +134,7 @@ public AboutView(){ HBox.setHgrow(hBoxLeftSideButtons, Priority.ALWAYS); hBoxButtonsHBox.getChildren().add(hBoxLeftSideButtons); //-right side - this.closeButton = new Button(Message.get("AboutView.closeButton.text")); + this.closeButton = GuiUtil.getButtonOfStandardSize(Message.get("AboutView.closeButton.text")); HBox hBoxRightSideButtons = new HBox(); hBoxRightSideButtons.getChildren().addAll(this.closeButton); hBoxRightSideButtons.setAlignment(Pos.CENTER_RIGHT); @@ -151,7 +156,7 @@ public AboutView(){ tmpTextCol.prefWidthProperty().bind( this.gridPane.widthProperty().multiply(0.4975) ); - this.gridPane.getColumnConstraints().add(0, tmpTextCol); + this.gridPane.getColumnConstraints().addFirst(tmpTextCol); ColumnConstraints tmpLogoCol = new ColumnConstraints(); tmpLogoCol.prefWidthProperty().bind( @@ -163,7 +168,7 @@ public AboutView(){ this.gridPane.setVgap(GuiDefinitions.GUI_SPACING_VALUE); this.gridPane.setHgap(GuiDefinitions.GUI_SPACING_VALUE); this.gridPane.setAlignment(Pos.TOP_LEFT); - tmpSplitPane.getItems().add(0, this.gridPane); + tmpSplitPane.getItems().addFirst(this.gridPane); //text //-title Text tmpAppTitle = new Text(Message.get("AboutView.appTitle.text")); @@ -197,9 +202,11 @@ public AboutView(){ tmpTitledPaneAcknowledgement.setExpanded(false); this.gridPane.add(tmpTitledPaneAcknowledgement,0,5); //-image - InputStream tmpImageInputStream = AboutView.class.getResourceAsStream("/de/unijena/cheminf/mortar/images/Mortar_Logo1.png"); - Double tmpImageSize = 495.3125; // magic number, do not touch - Image tmpLogo = new Image(tmpImageInputStream,tmpImageSize,tmpImageSize/1.414, true,true ); + String tmpLogoURL = this.getClass().getClassLoader().getResource( + this.configuration.getProperty("mortar.imagesFolder") + + this.configuration.getProperty("mortar.logo.name")).toExternalForm(); + double tmpImageSize = 495.3125; // magic number, do not touch + Image tmpLogo = new Image(tmpLogoURL, tmpImageSize, tmpImageSize/1.414, true,true ); this.logoImageView = new ImageView(tmpLogo); this.gridPane.add(this.logoImageView, 1,0, 1, 6); GridPane.setHalignment(this.logoImageView, HPos.CENTER); @@ -258,17 +265,13 @@ public AboutView(){ tmpTitledPaneAcknowledgement.setMinWidth(newValue.doubleValue() * 0.5); tmpTitledPaneAcknowledgement.setMaxWidth(newValue.doubleValue() * 0.5); })); - tmpTitledPaneLicense.expandedProperty().addListener((obs, oldValue, newValue) ->{ - tmpTitledPaneAcknowledgement.setExpanded(!newValue); - }); - tmpTitledPaneAcknowledgement.expandedProperty().addListener((obs, oldValue, newValue) ->{ - tmpTitledPaneLicense.setExpanded(!newValue); - }); + tmpTitledPaneLicense.expandedProperty().addListener((obs, oldValue, newValue) -> tmpTitledPaneAcknowledgement.setExpanded(!newValue)); + tmpTitledPaneAcknowledgement.expandedProperty().addListener((obs, oldValue, newValue) -> tmpTitledPaneLicense.setExpanded(!newValue)); } // // /** - * Returns button to open log files directory + * Returns button to open log files directory. * * @return button */ @@ -277,7 +280,7 @@ public Button getLogFileButton(){ } // /** - * Returns button to open GitHub repository + * Returns button to open GitHub repository. * * @return button */ @@ -286,7 +289,7 @@ public Button getGitHubButton(){ } // /** - * Returns button to open tutorial + * Returns button to open tutorial. * * @return Button to open the MORTAR tutorial */ @@ -295,7 +298,7 @@ public Button getTutorialButton() { } // /** - * Returns button to close this view + * Returns button to close this view. * * @return button */ @@ -304,7 +307,7 @@ public Button getCloseButton(){ } // /** - * Returns the TableView which shows ExternalTool properties + * Returns the TableView which shows ExternalTool properties. * @return TableView {@literal <}ExternalTool {@literal >} */ public TableView getTableView(){ @@ -312,7 +315,7 @@ public TableView getTableView(){ } // /** - * Returns grid pane to hold application information and logo + * Returns grid pane to hold application information and logo. * * @return GridPane */ @@ -321,7 +324,7 @@ public GridPane getGridPane() { } // /** - * Returns the ImageView for the logo image + * Returns the ImageView for the logo image. * * @return ImageView */ @@ -330,11 +333,11 @@ public ImageView getLogoImageView() { } // /** - * Sets given image to image vie + * Sets given image to image view. * * @param anImage Image to set as logo */ - public void setLogoImageView(Image anImage){ + public void setLogoImageViewImage(Image anImage){ this.logoImageView.setImage(anImage); } // diff --git a/src/main/java/de/unijena/cheminf/mortar/gui/views/FragmentsDataTableView.java b/src/main/java/de/unijena/cheminf/mortar/gui/views/FragmentsDataTableView.java index 7dad6ecd..07f8969c 100644 --- a/src/main/java/de/unijena/cheminf/mortar/gui/views/FragmentsDataTableView.java +++ b/src/main/java/de/unijena/cheminf/mortar/gui/views/FragmentsDataTableView.java @@ -25,6 +25,7 @@ package de.unijena.cheminf.mortar.gui.views; +import de.unijena.cheminf.mortar.configuration.IConfiguration; import de.unijena.cheminf.mortar.gui.util.GuiUtil; import de.unijena.cheminf.mortar.message.Message; import de.unijena.cheminf.mortar.model.data.DataModelPropertiesForTableView; @@ -35,18 +36,17 @@ import javafx.collections.FXCollections; import javafx.scene.Node; import javafx.scene.control.ContextMenu; -import javafx.scene.control.Control; import javafx.scene.control.Label; import javafx.scene.control.MenuItem; import javafx.scene.control.SeparatorMenuItem; import javafx.scene.control.TableCell; import javafx.scene.control.TableColumn; import javafx.scene.control.TableView; -import javafx.scene.control.Tooltip; import javafx.scene.control.cell.PropertyValueFactory; import javafx.scene.image.Image; import javafx.scene.image.ImageView; import javafx.scene.layout.BorderPane; +import javafx.scene.layout.Region; import javafx.scene.text.Text; import javafx.scene.text.TextAlignment; @@ -60,66 +60,76 @@ * @version 1.0.0.0 */ public class FragmentsDataTableView extends TableView implements IDataTableView{ - - // + // /** - * TableColumn for 2D structure state of the fragment + * TableColumn for 2D structure state of the fragment. */ - private TableColumn structureColumn; + private final TableColumn structureColumn; /** - * TableColumn for SMILES of the fragment + * TableColumn for SMILES of the fragment. */ - private TableColumn smilesColumn; + private final TableColumn smilesColumn; /** - * TableColumn for 2D structure state of one (random/first occurred) parent molecule + * TableColumn for 2D structure state of one (random/first occurred) parent molecule. */ - private TableColumn parentMolColumn; + private final TableColumn parentMolColumn; /** - * TableColumn for name of one (random/first occurred) parent molecule + * TableColumn for name of one (random/first occurred) parent molecule. */ - private TableColumn parentMolNameColumn; + private final TableColumn parentMolNameColumn; /** - * TableColumn for frequency of the fragment + * TableColumn for frequency of the fragment. */ - private TableColumn frequencyColumn; + private final TableColumn frequencyColumn; /** - * TableColumn for percentage frequency of the fragment + * TableColumn for percentage frequency of the fragment. */ - private TableColumn percentageColumn; + private final TableColumn percentageColumn; /** - * TableColumn for the frequency in how many molecules this fragment occurs in + * TableColumn for the frequency in how many molecules this fragment occurs in. */ - private TableColumn moleculeFrequencyColumn; + private final TableColumn moleculeFrequencyColumn; /** - * TableColumn for the percentage frequency in how many molecules this fragment occurs in + * TableColumn for the percentage frequency in how many molecules this fragment occurs in. */ - private TableColumn moleculePercentageColumn; + private final TableColumn moleculePercentageColumn; /** - * List which contains all items to be shown in this tableview not only the displayed ones for this page (Pagination) + * MenuItem of ContextMenu to copy selected cell to clipboard. */ - private List itemsList; + private final MenuItem copyMenuItem; /** - * ContextMenu ot the TableView + * MenuItem of ContextMenu to open an overview view with the parent molecules of the row of the selected cell. */ - private ContextMenu contextMenu; + private final MenuItem overviewViewMenuItem; + /** + * Configuration class to read resource file paths from. + */ + private final IConfiguration configuration; + // + // + // /** - * MenuItem of ContextMenu to copy selected cell to clipboard + * ContextMenu ot the TableView. */ - private MenuItem copyMenuItem; + private final ContextMenu contextMenu; /** - * MenuItem of ContextMenu to open an overview view with the parent molecules of the row of the selected cell + * List which contains all items to be shown in this tableview not only the displayed ones for this page (Pagination). */ - private MenuItem overviewViewMenuItem; + private List itemsList; // // /** - * Constructor + * Constructor. + * + * @param aConfiguration configuration instance to read resource file paths from */ - public FragmentsDataTableView(){ + public FragmentsDataTableView(IConfiguration aConfiguration){ super(); + this.configuration = aConfiguration; this.setEditable(false); this.getSelectionModel().setCellSelectionEnabled(true); -// this.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE); + //activate for future bulk export? + //this.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE); DecimalFormat tmpPercentageForm = new DecimalFormat("#.##%"); //-structureColumn this.structureColumn = new TableColumn<>(Message.get("MainTabPane.fragmentsTab.tableView.structureColumn.header")); @@ -148,7 +158,7 @@ public FragmentsDataTableView(){ Text tmpText = new Text(); tmpText.setTextAlignment(TextAlignment.CENTER); tmpCell.setGraphic(tmpText); - tmpCell.setPrefHeight(Control.USE_COMPUTED_SIZE); + tmpCell.setPrefHeight(Region.USE_COMPUTED_SIZE); tmpText.wrappingWidthProperty().bind(this.smilesColumn.widthProperty()); tmpText.textProperty().bind(tmpCell.itemProperty()); return tmpCell; @@ -158,7 +168,7 @@ public FragmentsDataTableView(){ //-parentMolColumn this.parentMolColumn = new TableColumn<>(); Label tmpParentMolLabel = new Label(Message.get("MainTabPane.fragmentsTab.tableView.parentMolColumn.header")); - tmpParentMolLabel.setTooltip(new Tooltip(Message.get("MainTabPane.fragmentsTab.tableView.parentMolColumn.tooltip"))); + tmpParentMolLabel.setTooltip(GuiUtil.createTooltip(Message.get("MainTabPane.fragmentsTab.tableView.parentMolColumn.tooltip"))); this.parentMolColumn.setGraphic(tmpParentMolLabel); this.parentMolColumn.setMinWidth(150); //magic number this.parentMolColumn.prefWidthProperty().bind( @@ -173,7 +183,7 @@ public FragmentsDataTableView(){ //-parentMolNameColumn this.parentMolNameColumn = new TableColumn<>(); Label tmpParentNameLabel = new Label(Message.get("MainTabPane.fragmentsTab.tableView.parentMolNameColumn.header")); - tmpParentNameLabel.setTooltip(new Tooltip(Message.get("MainTabPane.fragmentsTab.tableView.parentMolNameColumn.tooltip"))); + tmpParentNameLabel.setTooltip(GuiUtil.createTooltip(Message.get("MainTabPane.fragmentsTab.tableView.parentMolNameColumn.tooltip"))); this.parentMolNameColumn.setGraphic(tmpParentNameLabel); this.parentMolNameColumn.prefWidthProperty().bind( this.widthProperty().multiply(0.075) //magic number @@ -206,14 +216,14 @@ public FragmentsDataTableView(){ this.percentageColumn.setEditable(false); this.percentageColumn.setSortable(true); this.percentageColumn.setCellValueFactory(new PropertyValueFactory(DataModelPropertiesForTableView.ABSOLUTE_PERCENTAGE.getText())); - this.percentageColumn.setCellFactory(tc -> new TableCell<>(){ + this.percentageColumn.setCellFactory(tc -> new TableCell<>() { @Override protected void updateItem(Double value, boolean empty){ super.updateItem(value, empty); - if(empty){ - setText(null); - } else{ - setText(tmpPercentageForm.format(value)); + if (empty) { + this.setText(null); + } else { + this.setText(tmpPercentageForm.format(value)); } } }); @@ -246,9 +256,9 @@ protected void updateItem(Double value, boolean empty){ protected void updateItem(Double value, boolean empty) { super.updateItem(value, empty); if (empty) { - setText(null); + this.setText(null); } else { - setText(tmpPercentageForm.format(value)); + this.setText(tmpPercentageForm.format(value)); } } }); @@ -259,7 +269,10 @@ protected void updateItem(Double value, boolean empty) { this.setContextMenu(this.contextMenu); //-copyMenuItem this.copyMenuItem = new MenuItem(Message.get("TableView.contextMenu.copyMenuItem")); - this.copyMenuItem.setGraphic(new ImageView(new Image("de/unijena/cheminf/mortar/images/copy_icon_16x16.png"))); + String tmpCopyIconURL = this.getClass().getClassLoader().getResource( + this.configuration.getProperty("mortar.imagesFolder") + + this.configuration.getProperty("mortar.icon.copy.name")).toExternalForm(); + this.copyMenuItem.setGraphic(new ImageView(new Image(tmpCopyIconURL))); this.contextMenu.getItems().add(this.copyMenuItem); //-separatorMenuItem this.contextMenu.getItems().add(new SeparatorMenuItem()); @@ -270,7 +283,7 @@ protected void updateItem(Double value, boolean empty) { // // /** - * Creates and returns a fragments tableview page inside a BorderPane + * Creates and returns a fragments tableview page inside a BorderPane. * * @param aPageIndex int page index * @param aSettingsContainer SettingsContainer @@ -287,14 +300,14 @@ public Node createFragmentsTableViewPage(int aPageIndex, SettingsContainer aSett // /** * Adds a change listener to the height property of table view which sets the height for structure images to - * each MoleculeDataModel object of the items list and refreshes the table view - * If image height is too small it will be set to GuiDefinitions.GUI_STRUCTURE_IMAGE_MIN_HEIGHT (50.0) + * each MoleculeDataModel object of the items list and refreshes the table view. + * If image height is too small it will be set to GuiDefinitions.GUI_STRUCTURE_IMAGE_MIN_HEIGHT (50.0). * * @param aSettingsContainer SettingsContainer */ - public void addTableViewHeightListener(SettingsContainer aSettingsContainer){ + public void addTableViewHeightListener(SettingsContainer aSettingsContainer) { this.heightProperty().addListener((observable, oldValue, newValue) -> { - GuiUtil.setImageStructureHeight(this, newValue.doubleValue(), aSettingsContainer); + GuiUtil.setImageStructureHeight(this, newValue.doubleValue(), aSettingsContainer.getRowsPerPageSetting()); this.refresh(); }); } @@ -302,72 +315,88 @@ public void addTableViewHeightListener(SettingsContainer aSettingsContainer){ // // /** - * Returns the column that shows the 2d structure + * Returns the column that shows the 2d structure. * * @return TableColumn for 2d structure */ - public TableColumn getStructureColumn() { return this.structureColumn; } + public TableColumn getStructureColumn() { + return this.structureColumn; + } // /** - * Returns the column that hold the SMILES + * Returns the column that hold the SMILES. * * @return TableColumn for SMILES */ - public TableColumn getSmilesColumn() { return this.smilesColumn; } + public TableColumn getSmilesColumn() { + return this.smilesColumn; + } // /** - * Returns the column that shows the 2d structure for a parent molecule + * Returns the column that shows the 2d structure for a parent molecule. * * @return TableColumn for 2d structure */ - public TableColumn getParentMolColumn(){ return this.parentMolColumn; } + public TableColumn getParentMolColumn(){ + return this.parentMolColumn; + } // /** - * Returns the column that holds the name for a parent molecule + * Returns the column that holds the name for a parent molecule. * * @return TableColumn */ - public TableColumn getParentMolNameColumn() { return this.parentMolNameColumn; } + public TableColumn getParentMolNameColumn() { + return this.parentMolNameColumn; + } // /** - * Returns the column that holds the frequency how this fragment occurs + * Returns the column that holds the frequency how often this fragment occurs. * * @return TableColumn */ - public TableColumn getFrequencyColumn() { return this.frequencyColumn; } + public TableColumn getFrequencyColumn() { + return this.frequencyColumn; + } // /** - * Returns the column that holds the percentage frequency how this fragment occurs + * Returns the column that holds the percentage frequency how often this fragment occurs. * * @return TableColumn */ - public TableColumn getPercentageColumn() { return this.percentageColumn; } + public TableColumn getPercentageColumn() { + return this.percentageColumn; + } // /** - * Returns the column that holds the frequency in how many molecules this fragment occurs + * Returns the column that holds the frequency in how many molecules this fragment occurs. * * @return TableColumn */ - public TableColumn getMoleculeFrequencyColumn() { return this.moleculeFrequencyColumn; } + public TableColumn getMoleculeFrequencyColumn() { + return this.moleculeFrequencyColumn; + } // /** - * Returns the column that holds the percentage frequency in how many molecules this fragment occurs + * Returns the column that holds the percentage frequency in how many molecules this fragment occurs. * * @return TableColumn */ - public TableColumn getMoleculePercentageColumn() { return this.moleculePercentageColumn; } + public TableColumn getMoleculePercentageColumn() { + return this.moleculePercentageColumn; + } // /** - * Returns the MenuItem to copy + * Returns the MenuItem to copy. * * @return MenuItem */ - public MenuItem getCopyMenuItem(){ + public MenuItem getCopyMenuItem() { return this.copyMenuItem; } // /** - * Returns the MenuItem to open the parent molecules overview view + * Returns the MenuItem to open the parent molecules overview view. * * @return MenuItem */ @@ -376,14 +405,16 @@ public MenuItem getOverviewViewMenuItem() { } // /** - * Returns the items as a list of {@link MoleculeDataModel} objects + * Returns the items as a list of {@link MoleculeDataModel} objects. * * @return List */ - public List getItemsList() { return this.itemsList; } + public List getItemsList() { + return this.itemsList; + } // /** - * Sets the given list of {@link MoleculeDataModel} objects as items + * Sets the given list of {@link MoleculeDataModel} objects as items. * * @param aListOfFragments list of fragments to set as items */ diff --git a/src/main/java/de/unijena/cheminf/mortar/gui/views/HistogramView.java b/src/main/java/de/unijena/cheminf/mortar/gui/views/HistogramView.java index 51b1dd51..7d54892a 100644 --- a/src/main/java/de/unijena/cheminf/mortar/gui/views/HistogramView.java +++ b/src/main/java/de/unijena/cheminf/mortar/gui/views/HistogramView.java @@ -27,6 +27,7 @@ import de.unijena.cheminf.mortar.controller.HistogramViewController; import de.unijena.cheminf.mortar.gui.util.GuiDefinitions; +import de.unijena.cheminf.mortar.gui.util.GuiUtil; import de.unijena.cheminf.mortar.message.Message; import javafx.geometry.Insets; @@ -37,7 +38,6 @@ import javafx.scene.control.Label; import javafx.scene.control.ScrollPane; import javafx.scene.control.TextField; -import javafx.scene.control.Tooltip; import javafx.scene.effect.DropShadow; import javafx.scene.image.ImageView; import javafx.scene.layout.AnchorPane; @@ -58,73 +58,73 @@ public class HistogramView extends AnchorPane { // /** - * Button to close view + * Button to close view. */ - private Button closeButton; + private final Button closeButton; /** - * Button to refresh the histogram + * Button to refresh the histogram. */ - private Button applyButton; + private final Button applyButton; /** - * Text field for creating a new histogram with the given number of fragments + * Text field for creating a new histogram with the given number of fragments. */ - private TextField displayedFragmentsNumberTextField; + private final TextField displayedFragmentsNumberTextField; /** - * Label for the displayed fragments number text field + * Label for the displayed fragments number text field. */ - private Label displayedFragmentsNumberLabel; + private final Label displayedFragmentsNumberLabel; /** - * ImageView to display the structures when the cursor hovers over a bar + * ImageView to display the structures when the cursor hovers over a bar. */ - private ImageView structureDisplayImageView; + private final ImageView structureDisplayImageView; /** * Text field for defining the maximum SMILES length to display fully on the y-axis. */ - private TextField maximumSMILESLengthTextField; + private final TextField maximumSMILESLengthTextField; /** * Label for the maximum SMILES length text field. */ - private Label maximumSMILESLengthLabel; + private final Label maximumSMILESLengthLabel; /** * Checkbox to choose to show or hide the bar labels that display the exact frequency. */ - private CheckBox displayBarLabelsCheckBox; + private final CheckBox displayBarLabelsCheckBox; /** - * CheckBox to display or hide histogram gridlines + * CheckBox to display or hide histogram gridlines. */ - private CheckBox displayGridLinesCheckBox; + private final CheckBox displayGridLinesCheckBox; /** - * ScrollPane to make histogram scrollable + * ScrollPane to make histogram scrollable. */ - private ScrollPane histogramScrollPane; + private final ScrollPane histogramScrollPane; /** - * ComboBox to make the gap between the bars adjustable + * ComboBox to make the gap between the bars adjustable. */ - private ComboBox barWidthsComboBox; + private final ComboBox barWidthsComboBox; /** * Label for the bar widths combo box. */ - private Label barWidthsLabel; + private final Label barWidthsLabel; /** - * CheckBox to scale the X-axis logarithmically + * CheckBox to scale the X-axis logarithmically. */ - private CheckBox logarithmicScale; //TODO: currently unused + private final CheckBox logarithmicScale; //currently unused /** - * CheckBox to show or hide the bar shadows + * CheckBox to show or hide the bar shadows. */ - private CheckBox barStylingCheckBox; + private final CheckBox barStylingCheckBox; /** - * CheckBox to show or hide the SMILES labels on the y-axis + * CheckBox to show or hide the SMILES labels on the y-axis. */ - private CheckBox displaySMILESonYaxisCheckBox; + private final CheckBox displaySMILESonYaxisCheckBox; /** - * ComboBox to choose which frequency is used + * ComboBox to choose which frequency is used. */ - private ComboBox frequencyComboBox; + private final ComboBox frequencyComboBox; /** * Label for the frequency combo box. */ - private Label frequencyLabel; + private final Label frequencyLabel; // // /** @@ -142,10 +142,10 @@ public HistogramView(int aMaxFragmentNumber) { this.histogramScrollPane.setVbarPolicy(ScrollPane.ScrollBarPolicy.ALWAYS); //borderPane BorderPane tmpBorderPane = new BorderPane(); - HistogramView.setTopAnchor(tmpBorderPane, 0.0); - HistogramView.setRightAnchor(tmpBorderPane, 0.0); - HistogramView.setLeftAnchor(tmpBorderPane, 0.0); - HistogramView.setBottomAnchor(tmpBorderPane, 0.0); + AnchorPane.setTopAnchor(tmpBorderPane, 0.0); + AnchorPane.setRightAnchor(tmpBorderPane, 0.0); + AnchorPane.setLeftAnchor(tmpBorderPane, 0.0); + AnchorPane.setBottomAnchor(tmpBorderPane, 0.0); //mainGrid (4x4 grid) GridPane tmpMainGrid = new GridPane(); RowConstraints tmpRow1 = new RowConstraints(); @@ -180,32 +180,30 @@ public HistogramView(int aMaxFragmentNumber) { // left side controls this.displayedFragmentsNumberTextField = new TextField(); this.displayedFragmentsNumberTextField.setPrefWidth(GuiDefinitions.GUI_TEXT_FIELD_WIDTH); - this.displayedFragmentsNumberTextField.setTooltip(new Tooltip(Message.get("HistogramView.textField.toolTip") + " " + aMaxFragmentNumber)); - this.applyButton = new Button(Message.get("HistogramView.refreshButton.text")); - this.applyButton.setTooltip(new Tooltip(Message.get("HistogramView.refreshButton.toolTip"))); - this.applyButton.setPrefWidth(GuiDefinitions.GUI_BUTTON_WIDTH_VALUE); - this.applyButton.setPrefHeight(GuiDefinitions.GUI_BUTTON_HEIGHT_VALUE); + this.displayedFragmentsNumberTextField.setTooltip(GuiUtil.createTooltip(Message.get("HistogramView.textField.toolTip") + " " + aMaxFragmentNumber)); + this.applyButton = GuiUtil.getButtonOfStandardSize(Message.get("HistogramView.refreshButton.text")); + this.applyButton.setTooltip(GuiUtil.createTooltip(Message.get("HistogramView.refreshButton.toolTip"))); this.maximumSMILESLengthTextField = new TextField(); this.maximumSMILESLengthTextField.setPrefWidth(GuiDefinitions.GUI_TEXT_FIELD_WIDTH); - this.maximumSMILESLengthTextField.setTooltip(new Tooltip(Message.get("HistogramView.smilesField.toolTip"))); + this.maximumSMILESLengthTextField.setTooltip(GuiUtil.createTooltip(Message.get("HistogramView.smilesField.toolTip"))); this.maximumSMILESLengthLabel = new Label(Message.get("HistogramView.smilesLabel.text")); - this.maximumSMILESLengthLabel.setTooltip(new Tooltip(Message.get("HistogramView.smilesField.toolTip"))); + this.maximumSMILESLengthLabel.setTooltip(GuiUtil.createTooltip(Message.get("HistogramView.smilesField.toolTip"))); this.displayedFragmentsNumberLabel = new Label(Message.get("HistogramView.displayedFragmentsTextFieldLabel.text")); - this.displayedFragmentsNumberLabel.setTooltip(new Tooltip(Message.get("HistogramView.textField.toolTip") + " " + aMaxFragmentNumber)); + this.displayedFragmentsNumberLabel.setTooltip(GuiUtil.createTooltip(Message.get("HistogramView.textField.toolTip") + " " + aMaxFragmentNumber)); this.barWidthsComboBox = new ComboBox<>(); for (HistogramViewController.BarWidthOption tmpBarWidthOptionConstant : HistogramViewController.BarWidthOption.values()) { this.barWidthsComboBox.getItems().add(tmpBarWidthOptionConstant.getDisplayName()); } - this.barWidthsComboBox.setTooltip(new Tooltip(Message.get("HistogramView.comboBox.toolTip"))); + this.barWidthsComboBox.setTooltip(GuiUtil.createTooltip(Message.get("HistogramView.comboBox.toolTip"))); this.barWidthsLabel = new Label(Message.get("HistogramView.gapSettingLabel.text")); - this.barWidthsLabel.setTooltip(new Tooltip(Message.get("HistogramView.comboBox.toolTip"))); + this.barWidthsLabel.setTooltip(GuiUtil.createTooltip(Message.get("HistogramView.comboBox.toolTip"))); this.frequencyLabel = new Label(Message.get("HistogramView.chooseDataComboBox.text")); - this.frequencyLabel.setTooltip(new Tooltip(Message.get("HistogramView.chooseDataComboBox.toolTip"))); + this.frequencyLabel.setTooltip(GuiUtil.createTooltip(Message.get("HistogramView.chooseDataComboBox.toolTip"))); this.frequencyComboBox = new ComboBox<>(); for (HistogramViewController.FrequencyOption tmpFrequencyOptionConstant : HistogramViewController.FrequencyOption.values()) { this.frequencyComboBox.getItems().add(tmpFrequencyOptionConstant.getDisplayName()); } - this.frequencyComboBox.setTooltip(new Tooltip(Message.get("HistogramView.chooseDataComboBox.toolTip"))); + this.frequencyComboBox.setTooltip(GuiUtil.createTooltip(Message.get("HistogramView.chooseDataComboBox.toolTip"))); tmpLeftSideGrid.setVgap(GuiDefinitions.GUI_INSETS_VALUE); tmpLeftSideGrid.setHgap(GuiDefinitions.GUI_INSETS_VALUE); tmpLeftSideGrid.setPadding(new Insets(GuiDefinitions.GUI_INSETS_VALUE)); @@ -229,20 +227,18 @@ public HistogramView(int aMaxFragmentNumber) { this.structureDisplayImageView.setEffect(new DropShadow(10,2,3, Color.BLACK)); this.structureDisplayImageView.setStyle("fx-padding: 50px; fx-margin: 50px"); // right side controls - this.closeButton = new Button(Message.get("HistogramView.cancelButton.text")); - this.closeButton.setTooltip(new Tooltip(Message.get("HistogramView.cancelButton.toolTip"))); - this.closeButton.setPrefHeight(GuiDefinitions.GUI_BUTTON_HEIGHT_VALUE); - this.closeButton.setPrefWidth(GuiDefinitions.GUI_BUTTON_WIDTH_VALUE); + this.closeButton = GuiUtil.getButtonOfStandardSize(Message.get("HistogramView.cancelButton.text")); + this.closeButton.setTooltip(GuiUtil.createTooltip(Message.get("HistogramView.cancelButton.toolTip"))); this.displayBarLabelsCheckBox = new CheckBox(Message.get("HistogramView.checkBox.text")); - this.displayBarLabelsCheckBox.setTooltip(new Tooltip(Message.get("HistogramView.checkBox.toolTip"))); + this.displayBarLabelsCheckBox.setTooltip(GuiUtil.createTooltip(Message.get("HistogramView.checkBox.toolTip"))); this.displayGridLinesCheckBox = new CheckBox(Message.get("HistogramView.checkBoxGridlines.text")); - this.displayGridLinesCheckBox.setTooltip(new Tooltip(Message.get("HistogramView.checkBoxGridlines.toolTip"))); + this.displayGridLinesCheckBox.setTooltip(GuiUtil.createTooltip(Message.get("HistogramView.checkBoxGridlines.toolTip"))); this.logarithmicScale = new CheckBox(Message.get("HistogramView.checkBoxLogarithmicScale.text")); - this.logarithmicScale.setTooltip(new Tooltip(Message.get("HistogramView.checkBoxLogarithmicScale.toolTip"))); + this.logarithmicScale.setTooltip(GuiUtil.createTooltip(Message.get("HistogramView.checkBoxLogarithmicScale.toolTip"))); this.barStylingCheckBox = new CheckBox(Message.get("HistogramView.stylingCheckBox.text")); - this.barStylingCheckBox.setTooltip(new Tooltip(Message.get("HistogramView.stylingCheckBox.tooltip"))); + this.barStylingCheckBox.setTooltip(GuiUtil.createTooltip(Message.get("HistogramView.stylingCheckBox.tooltip"))); this.displaySMILESonYaxisCheckBox = new CheckBox(Message.get("HistogramView.checkBoxSmilesTickLabel.text")); - this.displaySMILESonYaxisCheckBox.setTooltip(new Tooltip(Message.get("HistogramView.checkBoxSmilesTickLabel.toolTip"))); + this.displaySMILESonYaxisCheckBox.setTooltip(GuiUtil.createTooltip(Message.get("HistogramView.checkBoxSmilesTickLabel.toolTip"))); HBox tmpHBoxRightSideControls = new HBox(); tmpRightSideGrid.setHgap(GuiDefinitions.GUI_INSETS_VALUE); tmpRightSideGrid.setVgap(GuiDefinitions.GUI_INSETS_VALUE * 2); @@ -267,7 +263,7 @@ public HistogramView(int aMaxFragmentNumber) { // // /** - * Returns button for closing the view + * Returns button for closing the view. * * @return button for closing histogram view */ @@ -290,7 +286,9 @@ public Button getApplyButton() { * * @return String number of fragments to be displayed in the histogram */ - public String getDisplayedFragmentsNumberTextFieldContent() {return this.displayedFragmentsNumberTextField.getText();} + public String getDisplayedFragmentsNumberTextFieldContent() { + return this.displayedFragmentsNumberTextField.getText(); + } // /** * Returns the text field where the maximum SMILES string length to display is entered. @@ -316,7 +314,9 @@ public TextField getDisplayedFragmentsNumberTextField() { * * @return String maximum SMILES length */ - public String getMaximumSMILESLengthTextFieldContent() {return this.maximumSMILESLengthTextField.getText();} + public String getMaximumSMILESLengthTextFieldContent() { + return this.maximumSMILESLengthTextField.getText(); + } // /** * Returns an ImageView to enable the display of the structures when the cursor hovers over a bar. @@ -345,15 +345,15 @@ public CheckBox getDisplayGridLinesCheckBox() { return this.displayGridLinesCheckBox; } // - //TODO currently unused + //currently unused /** - * Returns a CheckBox to make the number axis logarithmically + * Returns a CheckBox to make the number axis logarithmically. * * @return CheckBox for logarithmic number axis */ - /*public CheckBox getLogarithmicScale(){ + public CheckBox getLogarithmicScale(){ return this.logarithmicScale; - }*/ + } // /** * Returns the display bar shadows check box. @@ -378,7 +378,7 @@ public ScrollPane getHistogramScrollPane() { * * @return ComboBox for setting bar widths */ - public ComboBox getBarWidthsComboBox() { + public ComboBox getBarWidthsComboBox() { return this.barWidthsComboBox; } // @@ -396,7 +396,7 @@ public CheckBox getDisplaySmilesOnYAxisCheckBox() { * * @return ComboBox for choosing frequency type to display */ - public ComboBox getFrequencyComboBox() { + public ComboBox getFrequencyComboBox() { return this.frequencyComboBox; } // diff --git a/src/main/java/de/unijena/cheminf/mortar/gui/views/IDataTableView.java b/src/main/java/de/unijena/cheminf/mortar/gui/views/IDataTableView.java index b60c8c6e..3a9e06cc 100644 --- a/src/main/java/de/unijena/cheminf/mortar/gui/views/IDataTableView.java +++ b/src/main/java/de/unijena/cheminf/mortar/gui/views/IDataTableView.java @@ -31,22 +31,21 @@ import java.util.List; /** - * Interface for implementing TableViews for molecule data. It is necessary for the sorting method + * Interface for implementing TableViews for molecule data. It is necessary for the sorting method. * * @author Felix Baensch * @version 1.0.0.0 */ public interface IDataTableView { - /** - * Returns a list of MoleculeDataModel + * Returns a list of MoleculeDataModel. * * @return List of MoleculeDataModel */ public List getItemsList(); // /** - * Sets given list + * Sets given list. * * @param aListOfFragments List */ @@ -54,7 +53,7 @@ public interface IDataTableView { // /** * Adds a change listener to the height property of table view which sets the height for structure images to - * each MoleculeDataModel/FragmentDataModel object of the items list and refreshes the table view + * each MoleculeDataModel/FragmentDataModel object of the items list and refreshes the table view. * * @param aSettingsContainer SettingsContainer */ diff --git a/src/main/java/de/unijena/cheminf/mortar/gui/views/ItemizationDataTableView.java b/src/main/java/de/unijena/cheminf/mortar/gui/views/ItemizationDataTableView.java index fc6221ba..1351502d 100644 --- a/src/main/java/de/unijena/cheminf/mortar/gui/views/ItemizationDataTableView.java +++ b/src/main/java/de/unijena/cheminf/mortar/gui/views/ItemizationDataTableView.java @@ -25,6 +25,7 @@ package de.unijena.cheminf.mortar.gui.views; +import de.unijena.cheminf.mortar.configuration.IConfiguration; import de.unijena.cheminf.mortar.gui.util.GuiUtil; import de.unijena.cheminf.mortar.message.Message; import de.unijena.cheminf.mortar.model.data.DataModelPropertiesForTableView; @@ -55,52 +56,56 @@ * @version 1.0.0.0 * */ -public class ItemizationDataTableView extends TableView implements IDataTableView{ - +public class ItemizationDataTableView extends TableView implements IDataTableView { // /** - * TableColumn for name of the molecule + * TableColumn for name of the molecule. */ - private TableColumn nameColumn; + private final TableColumn nameColumn; /** - * TableColumn for 2D structure of the molecule + * TableColumn for 2D structure of the molecule. */ - private TableColumn moleculeStructureColumn; + private final TableColumn moleculeStructureColumn; /** * TableColumn for 2D structure of the fragments of the molecule with labelling of how often * the respective fragment occurs in the molecule. BorderPane is used to align text and image. */ - private TableColumn fragmentStructureColumn; + private final TableColumn fragmentStructureColumn; /** - * Name of the fragmentation algorithm used + * Name of the fragmentation algorithm used. */ - private String fragmentationName; + private final String fragmentationName; /** - * List which contains all items to be shown in this tableview not only the displayed ones for this page (Pagination) + * List which contains all items to be shown in this tableview not only the displayed ones for this page (Pagination). */ private List itemsList; /** - * ContextMenu ot the TableView + * ContextMenu ot the TableView. + */ + private final ContextMenu contextMenu; + /** + * MenuItem of ContextMenu to copy selected cell to clipboard. */ - private ContextMenu contextMenu; + private final MenuItem copyMenuItem; /** - * MenuItem of ContextMenu to copy selected cell to clipboard + * MenuItem of ContextMenu to open an overview view with the item and its fragments. */ - private MenuItem copyMenuItem; + private final MenuItem overviewViewMenuItem; /** - * MenuItem of ContextMenu to open an overview view with the item and its fragments + * Configuration class to read resource file paths from. */ - private MenuItem overviewViewMenuItem; + private final IConfiguration configuration; // // /** - * Constructor + * Constructor. * - * @param anItemAmount max amount of fragments to be displayed in fragment * @param aFragmentationName String of fragmentation name used as title + * @param aConfiguration configuration instance to read resource file paths from */ - public ItemizationDataTableView(int anItemAmount, String aFragmentationName){ + public ItemizationDataTableView(String aFragmentationName, IConfiguration aConfiguration) { super(); + this.configuration = aConfiguration; this.setEditable(false); this.fragmentationName = aFragmentationName; this.getSelectionModel().setCellSelectionEnabled(true); @@ -111,7 +116,7 @@ public ItemizationDataTableView(int anItemAmount, String aFragmentationName){ this.nameColumn.setEditable(false); this.nameColumn.setSortable(true); this.nameColumn.setCellValueFactory(new PropertyValueFactory<>(DataModelPropertiesForTableView.NAME.getText())); - this.nameColumn.setCellFactory(TextFieldTableCell.forTableColumn()); + this.nameColumn.setCellFactory(TextFieldTableCell.forTableColumn()); this.nameColumn.setStyle("-fx-alignment: CENTER"); this.nameColumn.prefWidthProperty().bind( this.widthProperty().multiply(0.15) //magic number @@ -140,7 +145,10 @@ public ItemizationDataTableView(int anItemAmount, String aFragmentationName){ this.setContextMenu(this.contextMenu); //-copyMenuItem this.copyMenuItem = new MenuItem(Message.get("TableView.contextMenu.copyMenuItem")); - this.copyMenuItem.setGraphic(new ImageView(new Image("de/unijena/cheminf/mortar/images/copy_icon_16x16.png"))); + String tmpCopyIconURL = this.getClass().getClassLoader().getResource( + this.configuration.getProperty("mortar.imagesFolder") + + this.configuration.getProperty("mortar.icon.copy.name")).toExternalForm(); + this.copyMenuItem.setGraphic(new ImageView(new Image(tmpCopyIconURL))); this.contextMenu.getItems().add(this.copyMenuItem); //-separatorMenuItem this.contextMenu.getItems().add(new SeparatorMenuItem()); @@ -151,21 +159,21 @@ public ItemizationDataTableView(int anItemAmount, String aFragmentationName){ // // /** - * Creates and returns an itemization tableview page + * Creates and returns an itemization tableview page. * * @param aPageIndex integer value for the page index * @param aFragmentationName String for unique name of fragmentation job * @param aSettingsContainer SettingsContainer * @return Node BorderPane which holds TableView as page for Pagination */ - public Node createItemizationTableViewPage(int aPageIndex, String aFragmentationName, SettingsContainer aSettingsContainer){ + public Node createItemizationTableViewPage(int aPageIndex, String aFragmentationName, SettingsContainer aSettingsContainer) { int tmpRowsPerPage = aSettingsContainer.getRowsPerPageSetting(); int fromIndex = aPageIndex * tmpRowsPerPage; int toIndex = Math.min(fromIndex + tmpRowsPerPage, this.itemsList.size()); int tmpItemAmount = GuiUtil.getLargestNumberOfFragmentsForGivenMoleculeListAndFragmentationName(this.itemsList.subList(fromIndex, toIndex), aFragmentationName); this.resetFragmentStructureColumns(tmpItemAmount); List tmpList = this.itemsList.subList(fromIndex, toIndex); - for(MoleculeDataModel tmpMoleculeDataModel : tmpList){ + for (MoleculeDataModel tmpMoleculeDataModel : tmpList) { tmpMoleculeDataModel.setStructureImageWidth(this.moleculeStructureColumn.getWidth()); } this.setItems(FXCollections.observableArrayList(this.itemsList.subList(fromIndex, toIndex))); @@ -175,14 +183,14 @@ public Node createItemizationTableViewPage(int aPageIndex, String aFragmentation // /** * Adds a change listener to the height property of table view which sets the height for structure images to - * each MoleculeDataModel object of the items list and refreshes the table view - * If image height is too small it will be set to GuiDefinitions.GUI_STRUCTURE_IMAGE_MIN_HEIGHT (50.0) + * each MoleculeDataModel object of the items list and refreshes the table view. + * If image height is too small it will be set to GuiDefinitions.GUI_STRUCTURE_IMAGE_MIN_HEIGHT (50.0). * * @param aSettingsContainer SettingsContainer */ - public void addTableViewHeightListener(SettingsContainer aSettingsContainer){ + public void addTableViewHeightListener(SettingsContainer aSettingsContainer) { this.heightProperty().addListener((observable, oldValue, newValue) -> { - GuiUtil.setImageStructureHeight(this, newValue.doubleValue(),aSettingsContainer); + GuiUtil.setImageStructureHeight(this, newValue.doubleValue(), aSettingsContainer.getRowsPerPageSetting()); this.refresh(); }); } @@ -196,7 +204,7 @@ public void addTableViewHeightListener(SettingsContainer aSettingsContainer){ */ private void resetFragmentStructureColumns(int anItemAmount) { this.fragmentStructureColumn.getColumns().clear(); - for(int i = 0; i < anItemAmount; i++){ + for (int i = 0; i < anItemAmount; i++) { int tmpIndex = i; TableColumn tmpColumn = new TableColumn<>("Fragment " + (i + 1)); //+1 to avoid 0 in GUI tmpColumn.setResizable(true); @@ -204,16 +212,17 @@ private void resetFragmentStructureColumns(int anItemAmount) { tmpColumn.setSortable(false); tmpColumn.setStyle( "-fx-alignment: CENTER;"); tmpColumn.setCellValueFactory(cellData -> Bindings.createObjectBinding(() -> { - if(!cellData.getValue().hasMoleculeUndergoneSpecificFragmentation(this.fragmentationName)){ + if (!cellData.getValue().hasMoleculeUndergoneSpecificFragmentation(this.fragmentationName)) { return null; } - if(tmpIndex >= cellData.getValue().getFragmentsOfSpecificAlgorithm(this.fragmentationName).size()) + if (tmpIndex >= cellData.getValue().getFragmentsOfSpecificFragmentation(this.fragmentationName).size()) { return null; - FragmentDataModel tmpFragment = cellData.getValue().getFragmentsOfSpecificAlgorithm(this.fragmentationName).get(tmpIndex); - if(!cellData.getValue().hasMoleculeUndergoneSpecificFragmentation(this.fragmentationName)){ + } + FragmentDataModel tmpFragment = cellData.getValue().getFragmentsOfSpecificFragmentation(this.fragmentationName).get(tmpIndex); + if (!cellData.getValue().hasMoleculeUndergoneSpecificFragmentation(this.fragmentationName)) { return null; } - String tmpFrequency = cellData.getValue().getFragmentFrequencyOfSpecificAlgorithm(this.fragmentationName).get(tmpFragment.getUniqueSmiles()).toString(); + String tmpFrequency = cellData.getValue().getFragmentFrequencyOfSpecificFragmentation(this.fragmentationName).get(tmpFragment.getUniqueSmiles()).toString(); return tmpFragment.getStructureWithText(tmpFrequency); })); tmpColumn.setMinWidth(300); @@ -224,7 +233,7 @@ private void resetFragmentStructureColumns(int anItemAmount) { // // /** - * Returns TableColumn for the 2D structure of the molecule + * Returns TableColumn for the 2D structure of the molecule. * * @return TableColumn for 2D structure */ @@ -233,32 +242,34 @@ public TableColumn getMoleculeStructureColumn() { } // /** - * Returns name of fragmentation + * Returns name of fragmentation. * * @return String */ - public String getFragmentationName(){ + public String getFragmentationName() { return this.fragmentationName; } // /** - * Returns list of items shown in TableView + * Returns list of items shown in TableView. * * @return List {@literal <}MoleculeDataModel {@literal >} */ - public List getItemsList() { return this.itemsList; } + public List getItemsList() { + return this.itemsList; + } // /** - * Returns MenuItem to copy selected cell + * Returns MenuItem to copy selected cell. * * @return MenuItem */ - public MenuItem getCopyMenuItem(){ + public MenuItem getCopyMenuItem() { return this.copyMenuItem; } // /** - * Returns MenuItem to open an overview view of an item and its fragments + * Returns MenuItem to open an overview view of an item and its fragments. * * @return MenuItem */ @@ -267,7 +278,7 @@ public MenuItem getOverviewViewMenuItem() { } // /** - * Sets items to list + * Sets items to list. * * @param aListOfFragments List {@literal <}MoleculeDataModel {@literal >} */ diff --git a/src/main/java/de/unijena/cheminf/mortar/gui/views/MainView.java b/src/main/java/de/unijena/cheminf/mortar/gui/views/MainView.java index 47783ba0..db32a742 100644 --- a/src/main/java/de/unijena/cheminf/mortar/gui/views/MainView.java +++ b/src/main/java/de/unijena/cheminf/mortar/gui/views/MainView.java @@ -25,6 +25,7 @@ package de.unijena.cheminf.mortar.gui.views; +import de.unijena.cheminf.mortar.configuration.IConfiguration; import de.unijena.cheminf.mortar.gui.controls.MainMenuBar; import de.unijena.cheminf.mortar.gui.controls.StatusBar; @@ -36,41 +37,49 @@ import javafx.scene.layout.VBox; /** - * MainView Class of MORTAR. + * MainView class of MORTAR. * * @author Felix Baensch * @version 1.0.0.0 */ public class MainView extends AnchorPane { - // - private BorderPane mainBorderPane; - private Pane mainCenterPane; - private MainMenuBar mainMenuBar; - private StatusBar statusBar; - + private final BorderPane mainBorderPane; + private final Pane mainCenterPane; + private final MainMenuBar mainMenuBar; + private final StatusBar statusBar; + /** + * Configuration class to read resource file paths from. + */ + private final IConfiguration configuration; // - /** - * Constructor - * * Initialises the variables and fields and adds the components to the frame. * No event listeners are added to any component. + * + * @param aConfiguration configuration class reading from properties file */ - public MainView(){ + public MainView(IConfiguration aConfiguration) { super(); + this.configuration = aConfiguration; //BorderPane this.mainBorderPane = new BorderPane(); - MainView.setTopAnchor(this.mainBorderPane, 0.0); - MainView.setBottomAnchor(this.mainBorderPane, 0.0); - MainView.setLeftAnchor(this.mainBorderPane, 0.0); - MainView.setRightAnchor(this.mainBorderPane, 0.0); + AnchorPane.setTopAnchor(this.mainBorderPane, 0.0); + AnchorPane.setBottomAnchor(this.mainBorderPane, 0.0); + AnchorPane.setLeftAnchor(this.mainBorderPane, 0.0); + AnchorPane.setRightAnchor(this.mainBorderPane, 0.0); HBox.setHgrow(this.mainBorderPane, Priority.ALWAYS); VBox.setVgrow(this.mainBorderPane, Priority.ALWAYS); //mainCenterPane this.mainCenterPane = new Pane(); this.mainCenterPane.setStyle("-fx-background-color: LIGHTGREY"); - this.mainCenterPane.setStyle("-fx-background-image: url('/de/unijena/cheminf/mortar/images/Mortar_Logo1_alpha50.png'); -fx-background-repeat: no-repeat; -fx-background-size: 521 362; -fx-background-position: center center;"); + String tmpLogoURL = this.getClass().getClassLoader().getResource( + this.configuration.getProperty("mortar.imagesFolder") + + this.configuration.getProperty("mortar.logo.withHalfAlpha.name")).toExternalForm(); + this.mainCenterPane.setStyle("-fx-background-image: url('" + tmpLogoURL + "'); " + + "-fx-background-repeat: no-repeat; " + + "-fx-background-size: 521 362; " + + "-fx-background-position: center center;"); this.mainBorderPane.setCenter(this.mainCenterPane); //menuBar this.mainMenuBar = new MainMenuBar(); @@ -79,13 +88,12 @@ public MainView(){ this.statusBar = new StatusBar(); this.mainBorderPane.setBottom(this.statusBar); this.getChildren().add(this.mainBorderPane); - } - // // /** - * Returns the main menubar that contains menus for file handling (I/O), shutting down the application, settings and help menu entries + * Returns the main menubar that contains menus for file handling (I/O), shutting down the application, settings and help menu entries. + * * @return main menubar */ public MainMenuBar getMainMenuBar() { @@ -95,7 +103,8 @@ public MainMenuBar getMainMenuBar() { // // /** - * Returns the main center pane that contains + * Returns the main center pane that contains the GUI elements. + * * @return main center pane that is supposed to contain GUI elements of interest */ public Pane getMainCenterPane() { @@ -105,7 +114,8 @@ public Pane getMainCenterPane() { // // /** - * Returns the status bar + * Returns the status bar. + * * @return statusBar */ public StatusBar getStatusBar() { diff --git a/src/main/java/de/unijena/cheminf/mortar/gui/views/MoleculesDataTableView.java b/src/main/java/de/unijena/cheminf/mortar/gui/views/MoleculesDataTableView.java index d26db8b6..1661a909 100644 --- a/src/main/java/de/unijena/cheminf/mortar/gui/views/MoleculesDataTableView.java +++ b/src/main/java/de/unijena/cheminf/mortar/gui/views/MoleculesDataTableView.java @@ -25,6 +25,7 @@ package de.unijena.cheminf.mortar.gui.views; +import de.unijena.cheminf.mortar.configuration.IConfiguration; import de.unijena.cheminf.mortar.gui.util.GuiDefinitions; import de.unijena.cheminf.mortar.gui.util.GuiUtil; import de.unijena.cheminf.mortar.message.Message; @@ -57,44 +58,54 @@ * @version 1.0.1.0 */ public class MoleculesDataTableView extends TableView implements IDataTableView { - // /** - * TableColumn for selection state of the molecule + * TableColumn for selection state of the molecule. */ - private TableColumn selectionColumn; + private final TableColumn selectionColumn; /** - * TableColumn for name of the molecule + * TableColumn for name of the molecule. */ - private TableColumn nameColumn; + private final TableColumn nameColumn; /** - * TableColumn for 2D structure of the molecule + * TableColumn for 2D structure of the molecule. */ - private TableColumn structureColumn; + private final TableColumn structureColumn; /** - * CheckBox in table header to select or deselect all items + * CheckBox in table header to select or deselect all items. */ - private CheckBox selectAllCheckBox; + private final CheckBox selectAllCheckBox; /** - * Observable list which contains all items to be shown in this tableView not only the displayed ones for this page (Pagination) + * Observable list which contains all items to be shown in this tableView not only the displayed ones for this page (Pagination). */ private ObservableList itemsObservableList; /** - * ContextMenu ot the TableView + * ContextMenu ot the TableView. + */ + private final ContextMenu contextMenu; + /** + * MenuItem of ContextMenu to copy selected cell to clipboard. */ - private ContextMenu contextMenu; + private final MenuItem copyMenuItem; /** - * MenuItem of ContextMenu to copy selected cell to clipboard + * Boolean value to suppress going through all table rows and check selection status when the checx for selecting all + * rows was used. */ - private MenuItem copyMenuItem; private boolean selectionAllCheckBoxAction; + /** + * Configuration class to read resource file paths from. + */ + private final IConfiguration configuration; // // /** - * Constructor + * Constructor. + * + * @param aConfiguration configuration instance to read resource file paths from */ - public MoleculesDataTableView(){ + public MoleculesDataTableView(IConfiguration aConfiguration) { super(); + this.configuration = aConfiguration; this.setEditable(true); this.getSelectionModel().setCellSelectionEnabled(true); //-selectionColumn @@ -122,7 +133,7 @@ public MoleculesDataTableView(){ this.nameColumn.setEditable(false); this.nameColumn.setSortable(true); this.nameColumn.setCellValueFactory(new PropertyValueFactory<>(DataModelPropertiesForTableView.NAME.getText())); - this.nameColumn.setCellFactory(TextFieldTableCell.forTableColumn()); + this.nameColumn.setCellFactory(TextFieldTableCell.forTableColumn()); this.nameColumn.setStyle("-fx-alignment: CENTER"); //-structureColumn this.structureColumn = new TableColumn<>(Message.get("MainTabPane.moleculesTab.tableView.structureColumn.header")); @@ -142,56 +153,57 @@ public MoleculesDataTableView(){ this.setContextMenu(this.contextMenu); //-copyMenuItem this.copyMenuItem = new MenuItem(Message.get("TableView.contextMenu.copyMenuItem")); - this.copyMenuItem.setGraphic(new ImageView(new Image("de/unijena/cheminf/mortar/images/copy_icon_16x16.png"))); + String tmpCopyIconURL = this.getClass().getClassLoader().getResource( + this.configuration.getProperty("mortar.imagesFolder") + + this.configuration.getProperty("mortar.icon.copy.name")).toExternalForm(); + this.copyMenuItem.setGraphic(new ImageView(new Image(tmpCopyIconURL))); this.contextMenu.getItems().add(this.copyMenuItem); } // // /** * Creates a page for the pagination for the dataTableView based on page index and settings, which shows the imported - * molecules + * molecules. * * @param aPageIndex index * @param aSettingsContainer SettingsContainer * @return Node page of pagination */ - public Node createMoleculeTableViewPage(int aPageIndex, SettingsContainer aSettingsContainer){ + public Node createMoleculeTableViewPage(int aPageIndex, SettingsContainer aSettingsContainer) { int tmpRowsPerPage = aSettingsContainer.getRowsPerPageSetting(); int tmpFromIndex = aPageIndex * tmpRowsPerPage; int tmpToIndex = Math.min(tmpFromIndex + tmpRowsPerPage, this.itemsObservableList.size()); this.getSelectAllCheckBox().setOnAction(event -> { this.selectionAllCheckBoxAction = true; - for (int i = 0; i < this.itemsObservableList.size(); i++) { - if(this.getSelectAllCheckBox().isSelected()){ - this.itemsObservableList.get(i).setSelection(true); - } - else if(!this.getSelectAllCheckBox().isSelected()){ - this.itemsObservableList.get(i).setSelection(false); + for (MoleculeDataModel moleculeDataModel : this.itemsObservableList) { + if (this.getSelectAllCheckBox().isSelected()) { + moleculeDataModel.setSelection(true); + } else if (!this.getSelectAllCheckBox().isSelected()) { + moleculeDataModel.setSelection(false); } } this.selectionAllCheckBoxAction = false; }); - this.itemsObservableList.addListener((ListChangeListener) change ->{ - if(this.selectionAllCheckBoxAction){ + this.itemsObservableList.addListener((ListChangeListener) change -> { + if (this.selectionAllCheckBoxAction) { // No further action needed with column checkbox data when the select all checkbox is operated on return; } - while(change.next()){ - if(change.wasUpdated()){ + while (change.next()) { + if (change.wasUpdated()) { int checked = 0; - for(MoleculeDataModel tmpMoleculeDataModel : this.itemsObservableList){ - if(tmpMoleculeDataModel.isSelected()) + for (MoleculeDataModel tmpMoleculeDataModel : this.itemsObservableList) { + if (tmpMoleculeDataModel.isSelected()) { checked++; + } } - if(checked == this.itemsObservableList.size()){ + if (checked == this.itemsObservableList.size()) { this.getSelectAllCheckBox().setSelected(true); this.getSelectAllCheckBox().setIndeterminate(false); - } - else if(checked == 0){ + } else if(checked == 0) { this.getSelectAllCheckBox().setSelected(false); this.getSelectAllCheckBox().setIndeterminate(false); - } - else if(checked > 0){ + } else if(checked > 0) { this.getSelectAllCheckBox().setSelected(false); this.getSelectAllCheckBox().setIndeterminate(true); } @@ -199,7 +211,7 @@ else if(checked > 0){ } }); List tmpItems = this.itemsObservableList.subList(tmpFromIndex, tmpToIndex); - for(MoleculeDataModel tmpMoleculeDataModel : tmpItems){ + for (MoleculeDataModel tmpMoleculeDataModel : tmpItems) { tmpMoleculeDataModel.setStructureImageWidth(this.structureColumn.getWidth()); } this.setItems(FXCollections.observableArrayList(this.itemsObservableList.subList(tmpFromIndex, tmpToIndex))); @@ -209,14 +221,14 @@ else if(checked > 0){ // /** * Adds a change listener to the height property of table view which sets the height for structure images to - * each MoleculeDataModel object of the items list and refreshes the table view - * If image height is too small it will be set to GuiDefinitions.GUI_STRUCTURE_IMAGE_MIN_HEIGHT (50.0) + * each MoleculeDataModel object of the items list and refreshes the table view. + * If image height is too small it will be set to GuiDefinitions.GUI_STRUCTURE_IMAGE_MIN_HEIGHT (50.0). * * @param aSettingsContainer SettingsContainer */ - public void addTableViewHeightListener(SettingsContainer aSettingsContainer){ + public void addTableViewHeightListener(SettingsContainer aSettingsContainer) { this.heightProperty().addListener((observable, oldValue, newValue) -> { - GuiUtil.setImageStructureHeight(this, newValue.doubleValue(), aSettingsContainer); + GuiUtil.setImageStructureHeight(this, newValue.doubleValue(), aSettingsContainer.getRowsPerPageSetting()); this.refresh(); }); } @@ -224,25 +236,25 @@ public void addTableViewHeightListener(SettingsContainer aSettingsContainer){ // // /** - * Returns the column which holds the checkbox to select the corresponding item + * Returns the column which holds the checkbox to select the corresponding item. * * @return TableColumn */ - public TableColumn getSelectionColumn(){ + public TableColumn getSelectionColumn() { return this.selectionColumn; } // /** - * Returns the column that holds the name of the molecule + * Returns the column that holds the name of the molecule. * * @return TableColumn */ - public TableColumn getNameColumn(){ + public TableColumn getNameColumn() { return this.nameColumn; } // /** - * Returns the column which shows the 2d structure of the molecule + * Returns the column which shows the 2d structure of the molecule. * * @return TableColumn */ @@ -251,33 +263,34 @@ public TableColumn getStructureColumn() { } // /** - * Returns menu item to copy + * Returns menu item to copy. * * @return MenuItem */ - public MenuItem getCopyMenuItem(){ + public MenuItem getCopyMenuItem() { return this.copyMenuItem; } // /** - * Returns checkbox to de/select all molecules + * Returns checkbox to de/select all molecules. * * @return CheckBox */ - public CheckBox getSelectAllCheckBox() { return this.selectAllCheckBox; } + public CheckBox getSelectAllCheckBox() { + return this.selectAllCheckBox; + } // /** - * Returns the items of this tableview as a list of {@link MoleculeDataModel} objects + * Returns the items of this tableview as a list of {@link MoleculeDataModel} objects. * * @return List items */ - public List getItemsList() - { + public List getItemsList() { return this.itemsObservableList; } // /** - * Sets the given list of {@link MoleculeDataModel} objects as items of this table view + * Sets the given list of {@link MoleculeDataModel} objects as items of this table view. * * @param aListOfMolecules List */ diff --git a/src/main/java/de/unijena/cheminf/mortar/gui/views/OverviewView.java b/src/main/java/de/unijena/cheminf/mortar/gui/views/OverviewView.java index 367ca4cd..7c7caa2d 100644 --- a/src/main/java/de/unijena/cheminf/mortar/gui/views/OverviewView.java +++ b/src/main/java/de/unijena/cheminf/mortar/gui/views/OverviewView.java @@ -60,15 +60,16 @@ public class OverviewView extends AnchorPane { // /** - * Width of columns and rows per page label of the overview view + * Width of columns and rows per page label of the overview view. */ public static final double OVERVIEW_VIEW_GRID_CONFIGURATION_LABEL_PREF_WIDTH = 10.0; // + // // /** * Grid pane used to style the view. */ - private GridPane mainGridPane; + private final GridPane mainGridPane; /** * Grid pane that holds the displayed structure images and can be reconfigured by the user. */ @@ -77,31 +78,31 @@ public class OverviewView extends AnchorPane { * Horizontal box that holds the nodes placed in the bottom-left corner of the view being the text fields and * apply button for the reconfiguration of the structure grid pane. */ - private HBox bottomLeftHBox; + private final HBox bottomLeftHBox; /** * Horizontal box that holds the nodes placed in the bottom-right corner of the view. */ - private HBox bottomRightHBox; + private final HBox bottomRightHBox; /** * Text field for columns per page input. */ - private TextField columnsPerPageTextField; + private final TextField columnsPerPageTextField; /** * Text field for rows per page input. */ - private TextField rowsPerPageTextField; + private final TextField rowsPerPageTextField; /** * Button to apply changes to the structure grid pane configuration. */ - private Button applyButton; + private final Button applyButton; /** * Button to apply the default configuration to the structure grid pane. */ - private Button defaultButton; + private final Button defaultButton; /** * Button to close the view. */ - private Button closeButton; + private final Button closeButton; /** * Pagination that holds the structure grid pane and enables the user to switch pages. */ @@ -109,13 +110,12 @@ public class OverviewView extends AnchorPane { /** * Vertical box to be shown when the dimensions of the structure images fell below a limit. */ - private VBox imageDimensionsBelowLimitVBox; + private final VBox imageDimensionsBelowLimitVBox; // // // /** * Constructor. - * * Initializes the main components of the overview view and does the basic styling. The grid pane to display the * structure images that is to be hold by the pagination node of the overview view gets generated, configured and * styled. The horizontal boxes holding the text fields and buttons of the lower side of the view are not yet being @@ -133,10 +133,12 @@ public OverviewView(int aColumnsPerPage, int aRowsPerPage) throws IllegalArgumen super(); // // - if (aColumnsPerPage <= 0) + if (aColumnsPerPage <= 0) { throw new IllegalArgumentException("aColumnsPerPage (Integer value) was <= to 0."); - if (aRowsPerPage <= 0) + } + if (aRowsPerPage <= 0) { throw new IllegalArgumentException("aRowsPerPage (Integer value) was <= to 0."); + } // // //mainGridPane to style the view and set up its components @@ -216,7 +218,7 @@ public OverviewView(int aColumnsPerPage, int aRowsPerPage) throws IllegalArgumen tmpColumnsPerPageLabel.setMaxWidth(OverviewView.OVERVIEW_VIEW_GRID_CONFIGURATION_LABEL_PREF_WIDTH); tmpColumnsPerPageLabel.setPrefHeight(GuiDefinitions.GUI_BUTTON_HEIGHT_VALUE); tmpColumnsPerPageLabel.setAlignment(Pos.CENTER_LEFT); - Tooltip tmpColumnsPerPageTooltip = new Tooltip(Message.get("OverviewView.columnsPerPageLabel.tooltip")); + Tooltip tmpColumnsPerPageTooltip = GuiUtil.createTooltip(Message.get("OverviewView.columnsPerPageLabel.tooltip")); tmpColumnsPerPageLabel.setTooltip(tmpColumnsPerPageTooltip); this.columnsPerPageTextField = new TextField(); this.columnsPerPageTextField.setMinWidth(GuiDefinitions.PAGINATION_TEXT_FIELD_WIDTH); @@ -225,7 +227,7 @@ public OverviewView(int aColumnsPerPage, int aRowsPerPage) throws IllegalArgumen this.columnsPerPageTextField.setPrefHeight(GuiDefinitions.GUI_BUTTON_HEIGHT_VALUE); this.columnsPerPageTextField.setAlignment(Pos.CENTER_RIGHT); this.columnsPerPageTextField.setTextFormatter(new TextFormatter<>(GuiUtil.getStringToIntegerConverter(), - aColumnsPerPage, GuiUtil.getPositiveIntegerWithoutZeroFilter())); + aColumnsPerPage, GuiUtil.getPositiveIntegerFilter(false))); this.columnsPerPageTextField.setTooltip(tmpColumnsPerPageTooltip); Label tmpRowsPerPageLabel = new Label(Message.get("OverviewView.rowsPerPageLabel.text")); tmpRowsPerPageLabel.setMinWidth(OverviewView.OVERVIEW_VIEW_GRID_CONFIGURATION_LABEL_PREF_WIDTH); @@ -233,7 +235,7 @@ public OverviewView(int aColumnsPerPage, int aRowsPerPage) throws IllegalArgumen tmpRowsPerPageLabel.setMaxWidth(OverviewView.OVERVIEW_VIEW_GRID_CONFIGURATION_LABEL_PREF_WIDTH); tmpRowsPerPageLabel.setPrefHeight(GuiDefinitions.GUI_BUTTON_HEIGHT_VALUE); tmpRowsPerPageLabel.setAlignment(Pos.CENTER_LEFT); - Tooltip tmpRowsPerPageTooltip = new Tooltip(Message.get("OverviewView.rowsPerPageLabel.tooltip")); + Tooltip tmpRowsPerPageTooltip = GuiUtil.createTooltip(Message.get("OverviewView.rowsPerPageLabel.tooltip")); tmpRowsPerPageLabel.setTooltip(tmpRowsPerPageTooltip); this.rowsPerPageTextField = new TextField(); this.rowsPerPageTextField.setMinWidth(GuiDefinitions.PAGINATION_TEXT_FIELD_WIDTH); @@ -242,22 +244,14 @@ public OverviewView(int aColumnsPerPage, int aRowsPerPage) throws IllegalArgumen this.rowsPerPageTextField.setPrefHeight(GuiDefinitions.GUI_BUTTON_HEIGHT_VALUE); this.rowsPerPageTextField.setAlignment(Pos.CENTER_RIGHT); this.rowsPerPageTextField.setTextFormatter(new TextFormatter<>(GuiUtil.getStringToIntegerConverter(), - aRowsPerPage, GuiUtil.getPositiveIntegerWithoutZeroFilter())); + aRowsPerPage, GuiUtil.getPositiveIntegerFilter(false))); this.rowsPerPageTextField.setTooltip(tmpRowsPerPageTooltip); // - this.applyButton = new Button(Message.get("OverviewView.applyButton.text")); - this.applyButton.setPrefWidth(GuiDefinitions.GUI_BUTTON_WIDTH_VALUE); - this.applyButton.setMinWidth(GuiDefinitions.GUI_BUTTON_WIDTH_VALUE); - this.applyButton.setMaxWidth(GuiDefinitions.GUI_BUTTON_WIDTH_VALUE); - this.applyButton.setPrefHeight(GuiDefinitions.GUI_BUTTON_HEIGHT_VALUE); - this.applyButton.setTooltip(new Tooltip(Message.get("OverviewView.applyButton.tooltip"))); + this.applyButton = GuiUtil.getButtonOfStandardSize(Message.get("OverviewView.applyButton.text")); + this.applyButton.setTooltip(GuiUtil.createTooltip(Message.get("OverviewView.applyButton.tooltip"))); // - this.defaultButton = new Button(Message.get("OverviewView.defaultButton.text")); - this.defaultButton.setPrefWidth(GuiDefinitions.GUI_BUTTON_WIDTH_VALUE); - this.defaultButton.setMinWidth(GuiDefinitions.GUI_BUTTON_WIDTH_VALUE); - this.defaultButton.setMaxWidth(GuiDefinitions.GUI_BUTTON_WIDTH_VALUE); - this.defaultButton.setPrefHeight(GuiDefinitions.GUI_BUTTON_HEIGHT_VALUE); - this.defaultButton.setTooltip(new Tooltip(Message.get("OverviewView.defaultButton.tooltip"))); + this.defaultButton = GuiUtil.getButtonOfStandardSize(Message.get("OverviewView.defaultButton.text")); + this.defaultButton.setTooltip(GuiUtil.createTooltip(Message.get("OverviewView.defaultButton.tooltip"))); // this.bottomLeftHBox.getChildren().addAll( tmpColumnsPerPageLabel, this.columnsPerPageTextField, @@ -278,12 +272,8 @@ public OverviewView(int aColumnsPerPage, int aRowsPerPage) throws IllegalArgumen this.bottomRightHBox.setMaxWidth(GuiDefinitions.GUI_GRIDPANE_FOR_NODE_ALIGNMENT_THIRD_COL_WIDTH); this.bottomRightHBox.setAlignment(Pos.CENTER_RIGHT); // - this.closeButton = new Button(Message.get("OverviewView.closeButton.text")); - this.closeButton.setPrefWidth(GuiDefinitions.GUI_BUTTON_WIDTH_VALUE); - this.closeButton.setMinWidth(GuiDefinitions.GUI_BUTTON_WIDTH_VALUE); - this.closeButton.setMaxWidth(GuiDefinitions.GUI_BUTTON_WIDTH_VALUE); - this.closeButton.setPrefHeight(GuiDefinitions.GUI_BUTTON_HEIGHT_VALUE); - this.closeButton.setTooltip(new Tooltip(Message.get("OverviewView.closeButton.tooltip"))); + this.closeButton = GuiUtil.getButtonOfStandardSize(Message.get("OverviewView.closeButton.text")); + this.closeButton.setTooltip(GuiUtil.createTooltip(Message.get("OverviewView.closeButton.tooltip"))); // this.bottomRightHBox.getChildren().add(this.closeButton); // @@ -389,7 +379,7 @@ public void addBottomRightHBoxToMainGridPane() { * @param aColSpan Integer value for number of columns for the node to span * @param aRowSpan Integer value for number of rows for the node to span */ - private void addNodeToMainGridPane(Node aNode, int aColIndex, int aRowIndex, int aColSpan, int aRowSpan){ + private void addNodeToMainGridPane(Node aNode, int aColIndex, int aRowIndex, int aColSpan, int aRowSpan) { //method is private and only called with magic numbers this.mainGridPane.add(aNode, aColIndex, aRowIndex, aColSpan, aRowSpan); } diff --git a/src/main/java/de/unijena/cheminf/mortar/gui/views/PipelineSettingsView.java b/src/main/java/de/unijena/cheminf/mortar/gui/views/PipelineSettingsView.java index 927ebdd9..91279d26 100644 --- a/src/main/java/de/unijena/cheminf/mortar/gui/views/PipelineSettingsView.java +++ b/src/main/java/de/unijena/cheminf/mortar/gui/views/PipelineSettingsView.java @@ -26,6 +26,7 @@ package de.unijena.cheminf.mortar.gui.views; import de.unijena.cheminf.mortar.gui.util.GuiDefinitions; +import de.unijena.cheminf.mortar.gui.util.GuiUtil; import de.unijena.cheminf.mortar.message.Message; import javafx.geometry.HPos; @@ -37,7 +38,6 @@ import javafx.scene.control.Label; import javafx.scene.control.ScrollPane; import javafx.scene.control.TextField; -import javafx.scene.control.Tooltip; import javafx.scene.layout.AnchorPane; import javafx.scene.layout.BorderPane; import javafx.scene.layout.ColumnConstraints; @@ -55,56 +55,55 @@ * @version 1.0.0.0 */ public class PipelineSettingsView extends AnchorPane { - // /** - * BorderPane to hold and adjust child panes + * BorderPane to hold and adjust child panes. */ - private BorderPane borderPane; + private final BorderPane borderPane; /** - * GridPane to structure "algorithm choice rows" + * GridPane to structure "algorithm choice rows". */ private GridPane gridPane; /** - * Button to cancel changes and close view + * Button to cancel changes and close view. */ - private Button cancelButton; + private final Button cancelButton; /** - * Button to apply changes and close view + * Button to apply changes and close view. */ - private Button applyButton; + private final Button applyButton; /** - * Button to start pipeline fragmentation and close view + * Button to start pipeline fragmentation and close view. */ - private Button fragmentButton; + private final Button fragmentButton; /** - * Button to set pipeline and view o default state + * Button to set pipeline and view o default state. */ - private Button defaultButton; + private final Button defaultButton; /** - * TextField for the pipeline name + * TextField for the pipeline name. */ private TextField textField; // // /** - * Constructor + * Constructor. */ public PipelineSettingsView(){ super(); //borderPane this.borderPane = new BorderPane(); - PipelineSettingsView.setTopAnchor(this.borderPane, 0.0); - PipelineSettingsView.setRightAnchor(this.borderPane, 0.0); - PipelineSettingsView.setLeftAnchor(this.borderPane, 0.0); - PipelineSettingsView.setBottomAnchor(this.borderPane, 0.0); + AnchorPane.setTopAnchor(this.borderPane, 0.0); + AnchorPane.setRightAnchor(this.borderPane, 0.0); + AnchorPane.setLeftAnchor(this.borderPane, 0.0); + AnchorPane.setBottomAnchor(this.borderPane, 0.0); //buttons HBox tmpHBoxButtonsHBox = new HBox(); tmpHBoxButtonsHBox.setStyle("-fx-background-color: LightGrey"); this.borderPane.setBottom(tmpHBoxButtonsHBox); //-left side - this.defaultButton = new Button(Message.get("PipelineSettingsView.defaultButton.text")); - this.defaultButton.setTooltip(new Tooltip(Message.get("PipelineSettingsView.defaultButton.tooltip"))); + this.defaultButton = GuiUtil.getButtonOfStandardSize(Message.get("PipelineSettingsView.defaultButton.text")); + this.defaultButton.setTooltip(GuiUtil.createTooltip(Message.get("PipelineSettingsView.defaultButton.tooltip"))); HBox tmpHBoxLeftSideButtons = new HBox(); tmpHBoxLeftSideButtons.getChildren().add(this.defaultButton); tmpHBoxLeftSideButtons.setAlignment(Pos.CENTER_LEFT); @@ -114,12 +113,12 @@ public PipelineSettingsView(){ tmpHBoxButtonsHBox.getChildren().add(tmpHBoxLeftSideButtons); //Do not delete //-right side HBox tmpHBoxRightSideButtons = new HBox(); - this.cancelButton = new Button(Message.get("PipelineSettingsView.cancelButton.text")); - this.cancelButton.setTooltip(new Tooltip(Message.get("PipelineSettingsView.cancelButton.toolTip"))); - this.fragmentButton = new Button(Message.get("PipelineSettingsView.fragmentButton.text")); - this.fragmentButton.setTooltip(new Tooltip(Message.get("PipelineSettingsView.fragmentButton.toolTip"))); - this.applyButton = new Button(Message.get("PipelineSettingsView.applyButton.text")); - this.applyButton.setTooltip(new Tooltip(Message.get("PipelineSettingsView.applyButton.toolTip"))); + this.cancelButton = GuiUtil.getButtonOfStandardSize(Message.get("PipelineSettingsView.cancelButton.text")); + this.cancelButton.setTooltip(GuiUtil.createTooltip(Message.get("PipelineSettingsView.cancelButton.toolTip"))); + this.fragmentButton = GuiUtil.getButtonOfStandardSize(Message.get("PipelineSettingsView.fragmentButton.text")); + this.fragmentButton.setTooltip(GuiUtil.createTooltip(Message.get("PipelineSettingsView.fragmentButton.toolTip"))); + this.applyButton = GuiUtil.getButtonOfStandardSize(Message.get("PipelineSettingsView.applyButton.text")); + this.applyButton.setTooltip(GuiUtil.createTooltip(Message.get("PipelineSettingsView.applyButton.toolTip"))); tmpHBoxRightSideButtons.getChildren().addAll(this.fragmentButton, this.applyButton, this.cancelButton); tmpHBoxRightSideButtons.setAlignment(Pos.CENTER_RIGHT); tmpHBoxRightSideButtons.setSpacing(GuiDefinitions.GUI_SPACING_VALUE); @@ -132,11 +131,11 @@ public PipelineSettingsView(){ // // /** - * Adds the GridPane for the "algorithm choice rows" inside of a ScrollPane, Stage is necessary to bind width + * Adds the GridPane for the "algorithm choice rows" inside of a ScrollPane, Stage is necessary to bind width. * * @param aStage Stage to bind width */ - public void addGrid(Stage aStage){ + public void addGrid(Stage aStage) { ScrollPane tmpScrollPane = new ScrollPane(); HBox.setHgrow(tmpScrollPane,Priority.ALWAYS); VBox.setVgrow(tmpScrollPane,Priority.ALWAYS); @@ -146,48 +145,50 @@ public void addGrid(Stage aStage){ } // /** - * Adds a new "algorithm choice row" + * Adds a new "algorithm choice row". * * @param aNumberingLabel Label to show row number * @param aComboBox CombBox to select fragmentation algorithm * @param aSettingsButton Button to open SettingsView for the corresponding algorithm * @param aRowNumber int row number, shown in Label */ - public void addAlgorithmChoiceRow(Label aNumberingLabel, ComboBox aComboBox, Button aSettingsButton, int aRowNumber){ + public void addAlgorithmChoiceRow(Label aNumberingLabel, ComboBox aComboBox, Button aSettingsButton, int aRowNumber) { this.gridPane.add(aNumberingLabel, 0, aRowNumber); this.gridPane.add(aComboBox, 1, aRowNumber); this.gridPane.add(aSettingsButton, 2, aRowNumber); } // /** - * Adds a new Button which removes last "algorithm choice row" of GridPane + * Adds a new Button which removes last "algorithm choice row" of GridPane. * * @param aRemoveButton Button to remove a row * @param aRowNumber int specifies which row should be removed */ - public void addRemoveRowButton(Button aRemoveButton, int aRowNumber){ + public void addRemoveRowButton(Button aRemoveButton, int aRowNumber) { this.gridPane.add(aRemoveButton, 3, aRowNumber); } // /** - * Adds given button to given row number + * Adds given button to given row number. + * * @param anAddButton Button ad a row * @param aRowNumber int specifies to which row the button should be added */ - public void addAddRowButton(Button anAddButton, int aRowNumber){ + public void addAddRowButton(Button anAddButton, int aRowNumber) { this.gridPane.add(anAddButton, 2, aRowNumber); } // // // /** - * Creates the GridPane to align "algorithm rows", Stage is necessary to bind width + * Creates the GridPane to align "algorithm rows", Stage is necessary to bind width. * * @param aStage Stage to bind width */ - private void createGridPane(Stage aStage){ - if(this.gridPane != null) + private void createGridPane(Stage aStage) { + if (this.gridPane != null) { this.gridPane = null; + } this.gridPane = new GridPane(); this.gridPane.setPadding(new Insets(GuiDefinitions.GUI_INSETS_VALUE)); this.gridPane.setVgap(GuiDefinitions.GUI_INSETS_VALUE); @@ -252,58 +253,56 @@ private void createGridPane(Stage aStage){ // // /** - * Returns GridPane which holds the "algorithm choice rows" + * Returns GridPane which holds the "algorithm choice rows". * * @return GridPane */ - public GridPane getGridPane(){ + public GridPane getGridPane() { return this.gridPane; } // /** - * Returns cancelButton to cancel changes and close view + * Returns cancelButton to cancel changes and close view. * * @return Button */ - public Button getCancelButton(){ + public Button getCancelButton() { return this.cancelButton; } // /** - * Returns fragmentButton to start pipeline fragmentation and close view + * Returns fragmentButton to start pipeline fragmentation and close view. * * @return Button */ - public Button getFragmentButton(){ + public Button getFragmentButton() { return this.fragmentButton; } // /** - * Returns defaultButton to set settings and view to default + * Returns defaultButton to set settings and view to default. * * @return Button */ - public Button getDefaultButton(){ + public Button getDefaultButton() { return this.defaultButton; } // /** - * Returns applyButton to apply changes and close view + * Returns applyButton to apply changes and close view. * * @return Button */ - public Button getApplyButton() - { + public Button getApplyButton() { return this.applyButton; } // /** - * Returns textField which shows pipeline name + * Returns textField which shows pipeline name. * * @return TextField */ - public TextField getTextField() - { + public TextField getTextField() { return this.textField; } // diff --git a/src/main/java/de/unijena/cheminf/mortar/gui/views/SettingsView.java b/src/main/java/de/unijena/cheminf/mortar/gui/views/SettingsView.java index 5f1026b6..5c2cec02 100644 --- a/src/main/java/de/unijena/cheminf/mortar/gui/views/SettingsView.java +++ b/src/main/java/de/unijena/cheminf/mortar/gui/views/SettingsView.java @@ -28,13 +28,17 @@ import de.unijena.cheminf.mortar.gui.util.GuiDefinitions; import de.unijena.cheminf.mortar.gui.util.GuiUtil; import de.unijena.cheminf.mortar.message.Message; +import de.unijena.cheminf.mortar.model.util.IDisplayEnum; import de.unijena.cheminf.mortar.model.util.SimpleEnumConstantNameProperty; +import de.unijena.cheminf.mortar.model.util.SimpleIDisplayEnumConstantProperty; import javafx.beans.property.Property; import javafx.beans.property.SimpleBooleanProperty; import javafx.beans.property.SimpleDoubleProperty; import javafx.beans.property.SimpleIntegerProperty; import javafx.beans.property.SimpleStringProperty; +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; import javafx.geometry.HPos; import javafx.geometry.Insets; import javafx.geometry.Pos; @@ -43,6 +47,7 @@ import javafx.scene.control.Button; import javafx.scene.control.ComboBox; import javafx.scene.control.Label; +import javafx.scene.control.ListCell; import javafx.scene.control.ScrollPane; import javafx.scene.control.SelectionModel; import javafx.scene.control.Tab; @@ -58,8 +63,8 @@ import javafx.scene.layout.Priority; import javafx.scene.layout.RowConstraints; import javafx.scene.layout.StackPane; -import javafx.stage.Stage; +import java.util.Collections; import java.util.List; import java.util.Map; @@ -70,30 +75,29 @@ * @version 1.0.0.0 */ public class SettingsView extends AnchorPane { - // - private TabPane tabPane; - private BorderPane borderPane; - private Button cancelButton; - private Button applyButton; - private Button defaultButton; - private HBox hBoxRightSideButtons; - private HBox hBoxLeftSideButtons; - private HBox hBoxButtonsHBox; - private SelectionModel selectionModel; + private final TabPane tabPane; + private final BorderPane borderPane; + private final Button cancelButton; + private final Button applyButton; + private final Button defaultButton; + private final HBox hBoxRightSideButtons; + private final HBox hBoxLeftSideButtons; + private final HBox hBoxButtonsHBox; + private final SelectionModel selectionModel; // // /** - * Constructor + * Constructor. */ public SettingsView(){ super(); //borderPane this.borderPane = new BorderPane(); - SettingsView.setTopAnchor(this.borderPane, 0.0); - SettingsView.setRightAnchor(this.borderPane, 0.0); - SettingsView.setLeftAnchor(this.borderPane, 0.0); - SettingsView.setBottomAnchor(this.borderPane, 0.0); + AnchorPane.setTopAnchor(this.borderPane, 0.0); + AnchorPane.setRightAnchor(this.borderPane, 0.0); + AnchorPane.setLeftAnchor(this.borderPane, 0.0); + AnchorPane.setBottomAnchor(this.borderPane, 0.0); //tabPane this.tabPane = new TabPane(); this.selectionModel = this.tabPane.getSelectionModel(); @@ -104,8 +108,8 @@ public SettingsView(){ this.hBoxButtonsHBox.setStyle("-fx-background-color: LightGrey"); this.borderPane.setBottom(hBoxButtonsHBox); //-left side - this.defaultButton = new Button(Message.get("SettingsView.defaultButton.text")); - this.defaultButton.setTooltip(new Tooltip(Message.get("SettingsView.defaultButton.toolTip"))); + this.defaultButton = GuiUtil.getButtonOfStandardSize(Message.get("SettingsView.defaultButton.text")); + this.defaultButton.setTooltip(GuiUtil.createTooltip(Message.get("SettingsView.defaultButton.toolTip"))); this.hBoxLeftSideButtons = new HBox(); this.hBoxLeftSideButtons.getChildren().add(this.defaultButton); this.hBoxLeftSideButtons.setAlignment(Pos.CENTER_LEFT); @@ -115,10 +119,10 @@ public SettingsView(){ this.hBoxButtonsHBox.getChildren().add(this.hBoxLeftSideButtons); //-right side this.hBoxRightSideButtons = new HBox(); - this.cancelButton = new Button(Message.get("SettingsView.cancelButton.text")); - this.cancelButton.setTooltip(new Tooltip(Message.get("SettingsView.cancelButton.toolTip"))); - this.applyButton = new Button(Message.get("SettingsView.applyButton.text")); - this.applyButton.setTooltip(new Tooltip(Message.get("SettingsView.applyButton.toolTip"))); + this.cancelButton = GuiUtil.getButtonOfStandardSize(Message.get("SettingsView.cancelButton.text")); + this.cancelButton.setTooltip(GuiUtil.createTooltip(Message.get("SettingsView.cancelButton.toolTip"))); + this.applyButton = GuiUtil.getButtonOfStandardSize(Message.get("SettingsView.applyButton.text")); + this.applyButton.setTooltip(GuiUtil.createTooltip(Message.get("SettingsView.applyButton.toolTip"))); this.hBoxRightSideButtons.getChildren().addAll(this.applyButton, this.cancelButton); this.hBoxRightSideButtons.setAlignment(Pos.CENTER_RIGHT); this.hBoxRightSideButtons.setSpacing(GuiDefinitions.GUI_SPACING_VALUE); @@ -131,15 +135,16 @@ public SettingsView(){ // // /** - * Adds a tab which contains the properties of the given properties list - * @param aStage Stage to bind width and height + * Adds a tab which contains the properties of the given properties list. + * * @param aLabel Label for the tab title and the tab Id * @param aPropertiesList List of properties to show in created tab + * @param aDisplayNamesMap Map containing setting names as keys and language-specific names for the settings to display in the GUI * @param aTooltipTextsMap Map containing setting names as keys and tooltip text as values * @param aRecentPropertiesMap Map to hold recent properties to restore them if necessary * @return Tab */ - public Tab addTab(Stage aStage, String aLabel, List aPropertiesList, Map aTooltipTextsMap, Map aRecentPropertiesMap){ + public Tab addTab(String aLabel, List> aPropertiesList, Map aDisplayNamesMap, Map aTooltipTextsMap, Map aRecentPropertiesMap) { Tab tmpTab = new Tab(); tmpTab.setClosable(false); tmpTab.setId(aLabel); @@ -166,7 +171,7 @@ public Tab addTab(Stage aStage, String aLabel, List aPropertiesList, M tmpScrollPane.widthProperty().multiply(0.5) ); tmpGridPane.getColumnConstraints().add(tmpColCon2); - this.addPropertyItems(tmpGridPane, aPropertiesList, aTooltipTextsMap, aRecentPropertiesMap); + this.addPropertyItems(tmpGridPane, aPropertiesList, aDisplayNamesMap, aTooltipTextsMap, aRecentPropertiesMap); tmpScrollPane.setContent(tmpGridPane); tmpTab.setContent(tmpScrollPane); this.tabPane.getTabs().add(tmpTab); @@ -174,85 +179,137 @@ public Tab addTab(Stage aStage, String aLabel, List aPropertiesList, M } // /** - * Adds a row for each {@link Property} of given List which contains of properties name and a control to change properties value + * Adds a row for each {@link Property} of given List which contains the property name and a control to change the property value. + * * @param aGridPane GridPane to add row * @param aPropertiesList List of properties to show in created tab + * @param aDisplayNamesMap Map containing setting names as keys and language-specific names for the settings to display in the GUI * @param aTooltipTextsMap Map containing setting names as keys and tooltip text as values * @param aRecentPropertiesMap Map to hold recent properties to restore them if necessary */ - private void addPropertyItems(GridPane aGridPane, List aPropertiesList, Map aTooltipTextsMap, Map aRecentPropertiesMap){ + private void addPropertyItems(GridPane aGridPane, List> aPropertiesList, Map aDisplayNamesMap, Map aTooltipTextsMap, Map aRecentPropertiesMap) { int tmpRowIndex = 0; - for(Property tmpProperty : aPropertiesList){ + for (Property tmpProperty : aPropertiesList) { RowConstraints tmpRow = new RowConstraints(); tmpRow.setVgrow(Priority.ALWAYS); - tmpRow.setPrefHeight(50); - tmpRow.setMaxHeight(50); - tmpRow.setMinHeight(50); + tmpRow.setPrefHeight(50); //magic number + tmpRow.setMaxHeight(50); //magic number + tmpRow.setMinHeight(50); //magic number aGridPane.getRowConstraints().add(tmpRow); String tmpPropName = tmpProperty.getName(); - Label tmpNameLabel = new Label(tmpPropName); - Tooltip tmpTooltip = new Tooltip(aTooltipTextsMap.get(tmpProperty.getName())); - tmpTooltip.setMaxWidth(GuiDefinitions.GUI_TOOLTIP_MAX_WIDTH); - tmpTooltip.setWrapText(true); + Label tmpNameLabel = new Label(aDisplayNamesMap.get(tmpPropName)); + Tooltip tmpTooltip = GuiUtil.createTooltip(aTooltipTextsMap.get(tmpPropName)); tmpNameLabel.setTooltip(tmpTooltip); aGridPane.add(tmpNameLabel, 0, tmpRowIndex); GridPane.setMargin(tmpNameLabel, new Insets(GuiDefinitions.GUI_INSETS_VALUE)); Object tmpRecentValue = tmpProperty.getValue(); aRecentPropertiesMap.put(tmpPropName, tmpRecentValue); - if(tmpProperty instanceof SimpleBooleanProperty){ - ComboBox tmpBooleanComboBox = new ComboBox<>(); - tmpBooleanComboBox.getItems().addAll(Boolean.FALSE, Boolean.TRUE); - tmpBooleanComboBox.valueProperty().bindBidirectional(tmpProperty); - tmpBooleanComboBox.setTooltip(tmpTooltip); - //add to gridpane - aGridPane.add(tmpBooleanComboBox, 1, tmpRowIndex++); - GridPane.setMargin(tmpBooleanComboBox, new Insets(GuiDefinitions.GUI_INSETS_VALUE)); - } - else if(tmpProperty instanceof SimpleIntegerProperty){ - TextField tmpIntegerTextField = new TextField(); - tmpIntegerTextField.setPrefWidth(GuiDefinitions.GUI_TEXT_FIELD_PREF_WIDTH_VALUE); - tmpIntegerTextField.setMaxWidth(GuiDefinitions.GUI_SETTINGS_TEXT_FIELD_MAX_WIDTH_VALUE); - tmpIntegerTextField.setAlignment(Pos.CENTER_RIGHT); - TextFormatter tmpFormatter = new TextFormatter<>(GuiUtil.getStringToIntegerConverter(), 0, GuiUtil.getIntegerFilter()); - tmpIntegerTextField.setTextFormatter(tmpFormatter); - tmpFormatter.valueProperty().bindBidirectional(tmpProperty); - tmpIntegerTextField.setTooltip(tmpTooltip); - //add to gridpane - aGridPane.add(tmpIntegerTextField, 1, tmpRowIndex++); - GridPane.setMargin(tmpIntegerTextField, new Insets(GuiDefinitions.GUI_INSETS_VALUE)); - } - else if(tmpProperty instanceof SimpleDoubleProperty){ - TextField tmpDoubleTextField = new TextField(); - tmpDoubleTextField.setPrefWidth(GuiDefinitions.GUI_TEXT_FIELD_PREF_WIDTH_VALUE); - tmpDoubleTextField.setMaxWidth(GuiDefinitions.GUI_SETTINGS_TEXT_FIELD_MAX_WIDTH_VALUE); - tmpDoubleTextField.setAlignment(Pos.CENTER_RIGHT); - TextFormatter tmpFormatter = new TextFormatter<>(GuiUtil.getStringToDoubleConverter(), 0.0, GuiUtil.getDoubleFilter()); - tmpDoubleTextField.setTextFormatter(tmpFormatter); - tmpFormatter.valueProperty().bindBidirectional(tmpProperty); - tmpDoubleTextField.setTooltip(tmpTooltip); - //add to gridpane - aGridPane.add(tmpDoubleTextField, 1, tmpRowIndex++); - GridPane.setMargin(tmpDoubleTextField, new Insets(GuiDefinitions.GUI_INSETS_VALUE)); - } - else if(tmpProperty instanceof SimpleEnumConstantNameProperty){ - ComboBox tmpEnumComboBox = new ComboBox(); - tmpEnumComboBox.getItems().addAll(((SimpleEnumConstantNameProperty) tmpProperty).getAssociatedEnumConstantNames()); - tmpEnumComboBox.valueProperty().bindBidirectional(tmpProperty); - tmpEnumComboBox.setTooltip(tmpTooltip); - //add to gridpane - aGridPane.add(tmpEnumComboBox, 1, tmpRowIndex++); - GridPane.setMargin(tmpEnumComboBox, new Insets(GuiDefinitions.GUI_INSETS_VALUE)); - } - else if(tmpProperty instanceof SimpleStringProperty){ - TextField tmpStringTextField = new TextField(); - tmpStringTextField.setPrefWidth(GuiDefinitions.GUI_TEXT_FIELD_PREF_WIDTH_VALUE); - tmpStringTextField.setMaxWidth(GuiDefinitions.GUI_SETTINGS_TEXT_FIELD_MAX_WIDTH_VALUE); - tmpStringTextField.setAlignment(Pos.CENTER_RIGHT); - tmpStringTextField.textProperty().bindBidirectional(tmpProperty); - tmpStringTextField.setTooltip(tmpTooltip); - //add to gridpane - aGridPane.add(tmpStringTextField, 1, tmpRowIndex++); - GridPane.setMargin(tmpStringTextField, new Insets(GuiDefinitions.GUI_INSETS_VALUE)); + switch (tmpProperty) { + case SimpleBooleanProperty tmpSimpleBooleanProperty -> { + ComboBox tmpBooleanComboBox = new ComboBox<>(); + tmpBooleanComboBox.setPrefWidth(GuiDefinitions.GUI_TEXT_FIELD_PREF_WIDTH_VALUE); + tmpBooleanComboBox.setMaxWidth(GuiDefinitions.GUI_SETTINGS_TEXT_FIELD_MAX_WIDTH_VALUE); + tmpBooleanComboBox.getItems().addAll(Boolean.FALSE, Boolean.TRUE); + tmpBooleanComboBox.valueProperty().bindBidirectional(tmpSimpleBooleanProperty); + tmpBooleanComboBox.setTooltip(tmpTooltip); + //add to gridpane + aGridPane.add(tmpBooleanComboBox, 1, tmpRowIndex++); + GridPane.setMargin(tmpBooleanComboBox, new Insets(GuiDefinitions.GUI_INSETS_VALUE)); + } + case SimpleIntegerProperty simpleIntegerProperty -> { + TextField tmpIntegerTextField = new TextField(); + tmpIntegerTextField.setPrefWidth(GuiDefinitions.GUI_TEXT_FIELD_PREF_WIDTH_VALUE); + tmpIntegerTextField.setMaxWidth(GuiDefinitions.GUI_SETTINGS_TEXT_FIELD_MAX_WIDTH_VALUE); + tmpIntegerTextField.setAlignment(Pos.CENTER_RIGHT); + int tmpDefaultValue = 0; + //note: setting the filter to only accept positive integers including zero is an assumption that is true + // for all settings so far but might have to be changed in the future + TextFormatter tmpFormatter = new TextFormatter<>(GuiUtil.getStringToIntegerConverter(), + tmpDefaultValue, + GuiUtil.getPositiveIntegerFilter(true)); + tmpIntegerTextField.setTextFormatter(tmpFormatter); + tmpFormatter.valueProperty().bindBidirectional(tmpProperty); + tmpIntegerTextField.setTooltip(tmpTooltip); + //add to gridpane + aGridPane.add(tmpIntegerTextField, 1, tmpRowIndex++); + GridPane.setMargin(tmpIntegerTextField, new Insets(GuiDefinitions.GUI_INSETS_VALUE)); + } + case SimpleDoubleProperty simpleDoubleProperty -> { + TextField tmpDoubleTextField = new TextField(); + tmpDoubleTextField.setPrefWidth(GuiDefinitions.GUI_TEXT_FIELD_PREF_WIDTH_VALUE); + tmpDoubleTextField.setMaxWidth(GuiDefinitions.GUI_SETTINGS_TEXT_FIELD_MAX_WIDTH_VALUE); + tmpDoubleTextField.setAlignment(Pos.CENTER_RIGHT); + double tmpDefaultValue = 0.0; + //note: setting the filter to only accept positive double values including zero is an assumption that is true + // for all settings so far but might have to be changed in the future + TextFormatter tmpFormatter = new TextFormatter<>(GuiUtil.getStringToDoubleConverter(), + tmpDefaultValue, + GuiUtil.getPositiveDoubleFilter()); + tmpDoubleTextField.setTextFormatter(tmpFormatter); + tmpFormatter.valueProperty().bindBidirectional(tmpProperty); + tmpDoubleTextField.setTooltip(tmpTooltip); + //add to gridpane + aGridPane.add(tmpDoubleTextField, 1, tmpRowIndex++); + GridPane.setMargin(tmpDoubleTextField, new Insets(GuiDefinitions.GUI_INSETS_VALUE)); + } + case SimpleIDisplayEnumConstantProperty tmpSimpleIDisplayEnumConstantProperty -> { + ComboBox tmpEnumComboBox = new ComboBox<>(); + tmpEnumComboBox.setPrefWidth(GuiDefinitions.GUI_SETTING_COMBO_BOX_PREF_WIDTH_VALUE); + tmpEnumComboBox.setMaxWidth(GuiDefinitions.GUI_SETTING_COMBO_BOX_MAX_WIDTH_VALUE); + final ObservableList tmpItems = FXCollections.observableArrayList(); + Collections.addAll(tmpItems, (IDisplayEnum[]) tmpSimpleIDisplayEnumConstantProperty.getAssociatedEnumConstants()); + tmpEnumComboBox.setItems(tmpItems); + tmpEnumComboBox.setCellFactory(param -> new ListCell<>() { + @Override + protected void updateItem(IDisplayEnum iDisplayEnum, boolean empty) { + super.updateItem(iDisplayEnum, empty); + if (!empty) { + this.setText(iDisplayEnum.getDisplayName()); + this.setTooltip(GuiUtil.createTooltip(iDisplayEnum.getTooltipText())); + } + } + }); + //note this is called to set the initial value, so yes, we need to overwrite both methods with the same functionality here + tmpEnumComboBox.setButtonCell(new ListCell<>() { + @Override + protected void updateItem(IDisplayEnum item, boolean empty) { + super.updateItem(item, empty); + if (!empty) { + this.setText(item.getDisplayName()); + this.setTooltip(GuiUtil.createTooltip(item.getTooltipText())); + } + } + }); + tmpEnumComboBox.valueProperty().bindBidirectional(tmpSimpleIDisplayEnumConstantProperty); + tmpEnumComboBox.setTooltip(tmpTooltip); + //add to gridpane + aGridPane.add(tmpEnumComboBox, 1, tmpRowIndex++); + GridPane.setMargin(tmpEnumComboBox, new Insets(GuiDefinitions.GUI_INSETS_VALUE)); + } + case SimpleEnumConstantNameProperty tmpSimpleEnumConstantNameProperty -> { + ComboBox tmpEnumComboBox = new ComboBox<>(); + tmpEnumComboBox.setPrefWidth(GuiDefinitions.GUI_SETTING_COMBO_BOX_PREF_WIDTH_VALUE); + tmpEnumComboBox.setMaxWidth(GuiDefinitions.GUI_SETTING_COMBO_BOX_MAX_WIDTH_VALUE); + tmpEnumComboBox.getItems().addAll(((SimpleEnumConstantNameProperty) tmpProperty).getAssociatedEnumConstantNames()); + tmpEnumComboBox.valueProperty().bindBidirectional(tmpSimpleEnumConstantNameProperty); + tmpEnumComboBox.setTooltip(tmpTooltip); + //add to gridpane + aGridPane.add(tmpEnumComboBox, 1, tmpRowIndex++); + GridPane.setMargin(tmpEnumComboBox, new Insets(GuiDefinitions.GUI_INSETS_VALUE)); + } + case SimpleStringProperty simpleStringProperty -> { + TextField tmpStringTextField = new TextField(); + tmpStringTextField.setPrefWidth(GuiDefinitions.GUI_TEXT_FIELD_PREF_WIDTH_VALUE); + tmpStringTextField.setMaxWidth(GuiDefinitions.GUI_SETTINGS_TEXT_FIELD_MAX_WIDTH_VALUE); + tmpStringTextField.setAlignment(Pos.CENTER_RIGHT); + tmpStringTextField.textProperty().bindBidirectional(tmpProperty); + tmpStringTextField.setTooltip(tmpTooltip); + //add to gridpane + aGridPane.add(tmpStringTextField, 1, tmpRowIndex++); + GridPane.setMargin(tmpStringTextField, new Insets(GuiDefinitions.GUI_INSETS_VALUE)); + } + default -> + throw new UnsupportedOperationException("Unknown property type " + tmpProperty.getName()); } } } @@ -260,48 +317,48 @@ else if(tmpProperty instanceof SimpleStringProperty){ // // /** - * Returns the tab pane, holding tabs for the settings of the different fragmenters + * Returns the tab pane, holding tabs for the settings of the different fragmenters. * * @return TabPane */ - public TabPane getTabPane(){ + public TabPane getTabPane() { return this.tabPane; } // /** * Returns selection model, holding the active tab. - * Used to set tab of the selected fragmenter as active tab + * Used to set tab of the selected fragmenter as active tab. * * @return SelectionModel {@literal <} Tab {@literal >} */ - public SelectionModel getSelectionModel(){ + public SelectionModel getSelectionModel() { return this.selectionModel; } // /** - * Returns cancel button, which closes the view without saving changes + * Returns cancel button, which closes the view without saving changes. * * @return CancelButton */ - public Button getCancelButton(){ + public Button getCancelButton() { return this.cancelButton; } // /** - * Returns apply button, which applies changes and closes the view + * Returns apply button, which applies changes and closes the view. * * @return ApplyButton */ - public Button getApplyButton(){ + public Button getApplyButton() { return this.applyButton; } // /** - * Returns default button, which sets all options of active tab to default values + * Returns default button, which sets all options of active tab to default values. * * @return DefaultButton */ - public Button getDefaultButton(){ + public Button getDefaultButton() { return this.defaultButton; } // diff --git a/src/main/java/de/unijena/cheminf/mortar/main/Main.java b/src/main/java/de/unijena/cheminf/mortar/main/Main.java index dd55f68c..3502b269 100644 --- a/src/main/java/de/unijena/cheminf/mortar/main/Main.java +++ b/src/main/java/de/unijena/cheminf/mortar/main/Main.java @@ -38,7 +38,7 @@ * @version 1.0.0.0 */ public class Main { - // + // /** * Private parameter-less constructor. * Introduced because javadoc build complained about classes without declared default constructor. @@ -48,7 +48,7 @@ private Main() { // /** - * Main method to start the application + * Main method to start the application. * * @param args the command line arguments */ diff --git a/src/main/java/de/unijena/cheminf/mortar/main/MainApp.java b/src/main/java/de/unijena/cheminf/mortar/main/MainApp.java index 25f0daec..7fc14046 100644 --- a/src/main/java/de/unijena/cheminf/mortar/main/MainApp.java +++ b/src/main/java/de/unijena/cheminf/mortar/main/MainApp.java @@ -25,6 +25,7 @@ package de.unijena.cheminf.mortar.main; +import de.unijena.cheminf.mortar.configuration.Configuration; import de.unijena.cheminf.mortar.controller.MainViewController; import de.unijena.cheminf.mortar.gui.util.GuiUtil; import de.unijena.cheminf.mortar.gui.views.MainView; @@ -39,6 +40,7 @@ import javafx.scene.control.ButtonType; import javafx.stage.Stage; +import java.io.IOException; import java.util.List; import java.util.Locale; import java.util.logging.Level; @@ -56,6 +58,10 @@ public class MainApp extends Application { * Name (starting with "-") of the command line parameter that can be used to skip the Java version check. */ public static final String SKIP_JAVA_VERSION_CHECK_CMD_ARG_NAME = "-skipJavaVersionCheck"; + /** + * Logger of this class. + */ + private static final Logger LOGGER = Logger.getLogger(MainApp.class.getName()); // /** * Parameter-less constructor that calls super(). @@ -66,26 +72,27 @@ public MainApp() { } // /** - * Calls start(Stage) of Application class. + * Calls start(Stage) of Application class, overridden below. * * @param args the command line arguments */ - public static void main(String[] args){ + public static void main(String[] args) { Application.launch(args); } // @Override public void start(Stage aPrimaryStage) { - try{ + try { // - Locale.setDefault(new Locale("en", "GB")); - Logger.getLogger(Main.class.getName()).info(Locale.getDefault().toString()); + Locale.setDefault(Locale.of("en", "GB")); + MainApp.LOGGER.info(() -> Locale.getDefault().toString()); // + // // String tmpJavaVersion = System.getProperty("java.version"); List tmpCMDArgs = this.getParameters().getRaw(); boolean tmpSkipJavaVersionCheck = false; - if (tmpCMDArgs.size() > 0) { + if (!tmpCMDArgs.isEmpty()) { for (String tmpArg : tmpCMDArgs) { if (tmpArg.equals(MainApp.SKIP_JAVA_VERSION_CHECK_CMD_ARG_NAME)) { tmpSkipJavaVersionCheck = true; @@ -93,28 +100,46 @@ public void start(Stage aPrimaryStage) { } } } - if (!tmpSkipJavaVersionCheck) { - if (MiscUtil.compareVersions(tmpJavaVersion, BasicDefinitions.MINIMUM_JAVA_VERSION) < 0) { - Logger.getLogger(Main.class.getName()).log(Level.WARNING, "Java version lower than minimum: " + tmpJavaVersion); - String tmpContentText = String.format(Message.get("Error.InvalidJavaVersion.Content"), BasicDefinitions.MINIMUM_JAVA_VERSION, tmpJavaVersion); - if (GuiUtil.guiConfirmationAlert(Message.get("Error.InvalidJavaVersion.Title"), Message.get("Error.InvalidJavaVersion.Header"), tmpContentText) == ButtonType.CANCEL) { - System.exit(0); - } //else: The user ignores the fact that their Java version is insufficient - } + if (!tmpSkipJavaVersionCheck && (MiscUtil.compareVersions(tmpJavaVersion, BasicDefinitions.MINIMUM_JAVA_VERSION) < 0)) { + MainApp.LOGGER.log(Level.WARNING, "Java version lower than minimum: {0}", tmpJavaVersion); + String tmpContentText = String.format( + Message.get("Error.InvalidJavaVersion.Content"), + BasicDefinitions.MINIMUM_JAVA_VERSION, + tmpJavaVersion); + if (GuiUtil.guiConfirmationAlert( + Message.get("Error.InvalidJavaVersion.Title"), + Message.get("Error.InvalidJavaVersion.Header"), + tmpContentText) == ButtonType.CANCEL) { + System.exit(0); + } //else: The user ignores the fact that their Java version is insufficient } // + // + // + try { + Configuration.getInstance(); + } catch (IOException anIOException) { + MainApp.LOGGER.log(Level.SEVERE, "Configuration properties file could not be imported."); + GuiUtil.guiExceptionAlert(Message.get("Error.Notification.Title"), + Message.get("Error.NoConfigFile.Header"), + String.format(Message.get("Error.NoConfigFile.Content"), Configuration.PROPERTIES_FILE_PATH), + anIOException); + System.exit(-1); + } + // + // // boolean tmpLCKFilePresent = LogUtil.checkForLCKFileInLogDir(); - if (tmpLCKFilePresent) { - if (GuiUtil.guiConfirmationAlert( + if (tmpLCKFilePresent && (GuiUtil.guiConfirmationAlert( Message.get("Error.SecondInstance.Title"), Message.get("Error.SecondInstance.Header"), - Message.get("Error.SecondInstance.Content")) == ButtonType.CANCEL) { + Message.get("Error.SecondInstance.Content")) == ButtonType.CANCEL)) { System.exit(0); - } //else: user wants to continue despite the possible second instance; - // this means that all existing .lck files will be removed below with LogUtil.manageLogFilesFolderIfExists() + //else: user wants to continue despite the possible second instance; + // this means that all existing .lck files will be removed below with LogUtil.manageLogFilesFolderIfExists() } //else: single MORTAR instance running // + // // LogUtil.manageLogFilesFolderIfExists(); boolean tmpWasLoggingInitializationSuccessful = LogUtil.initializeLoggingEnvironment(); @@ -124,21 +149,25 @@ public void start(Stage aPrimaryStage) { Message.get("Error.LoggingInitialization.Content")); } //Start new logging session - Logger.getLogger(Main.class.getName()).info(String.format(BasicDefinitions.MORTAR_SESSION_START_FORMAT, BasicDefinitions.MORTAR_VERSION)); - Logger.getLogger(Main.class.getName()).info(String.format("Started with Java version %s.", tmpJavaVersion)); - // - // + MainApp.LOGGER.info(() -> String.format(BasicDefinitions.MORTAR_SESSION_START_FORMAT, BasicDefinitions.MORTAR_VERSION)); + MainApp.LOGGER.info(() -> String.format("Started with Java version %s.", tmpJavaVersion)); + // + // + // String tmpAppDir = FileUtil.getAppDirPath(); // - MainView tmpMainView = new MainView(); - MainViewController tmpMainViewController = new MainViewController(aPrimaryStage, tmpMainView, tmpAppDir); + // + // + MainView tmpMainView = new MainView(Configuration.getInstance()); + new MainViewController(aPrimaryStage, tmpMainView, tmpAppDir, Configuration.getInstance()); + // } catch (Exception | OutOfMemoryError anException){ - Logger.getLogger(Main.class.getName()).log(Level.SEVERE, anException.toString(), anException); + MainApp.LOGGER.log(Level.SEVERE, anException.toString(), anException); if (anException instanceof OutOfMemoryError) { GuiUtil.guiExceptionAlert(Message.get("Error.ExceptionAlert.Title"), Message.get("Error.ExceptionAlert.Header"), anException.getMessage(), - new Exception(((OutOfMemoryError) anException).toString())); + new Exception(anException.toString())); } else { GuiUtil.guiExceptionAlert(Message.get("Error.ExceptionAlert.Title"), Message.get("Error.ExceptionAlert.Header"), diff --git a/src/main/java/de/unijena/cheminf/mortar/message/Message.java b/src/main/java/de/unijena/cheminf/mortar/message/Message.java index 7c92a538..444ef085 100644 --- a/src/main/java/de/unijena/cheminf/mortar/message/Message.java +++ b/src/main/java/de/unijena/cheminf/mortar/message/Message.java @@ -32,13 +32,12 @@ import java.util.logging.Logger; /** - * Message + * Message. * * @author Felix Baensch, Jonas Schaub * @version 1.0.0.0 */ public class Message { - // /** * Logger of this class. @@ -48,22 +47,22 @@ public class Message { // // /** - * Resource bundle name + * Resource bundle name. */ private static final String BUNDLE_NAME = "de.unijena.cheminf.mortar.message.Message"; /** - * Locale default + * Locale default. */ private static final Locale LOCALE_DEFAULT = Locale.getDefault(); /** - * Resource bundle + * Resource bundle. */ private static final ResourceBundle RESOURCE_BUNDLE = ResourceBundle.getBundle(BUNDLE_NAME, LOCALE_DEFAULT); // // // /** - * Private constructor + * Private constructor. */ private Message(){ } @@ -71,16 +70,16 @@ private Message(){ // // /** - * Return resource string for key + * Return resource string for key. If the key is not found in the resource bundle, a string stating that is returned. * * @param aKey Key * @return Resource string for key */ - public static String get(String aKey){ - try{ + public static String get(String aKey) { + try { return RESOURCE_BUNDLE.getString(aKey).trim(); - } catch (MissingResourceException | NullPointerException anException){ - Message.LOGGER.log(Level.SEVERE, anException.toString(), anException); + } catch (MissingResourceException | NullPointerException anException) { + Message.LOGGER.log(Level.WARNING, anException.toString(), anException); return "Key '" + aKey + "' not found."; } } diff --git a/src/main/java/de/unijena/cheminf/mortar/model/data/DataModelPropertiesForTableView.java b/src/main/java/de/unijena/cheminf/mortar/model/data/DataModelPropertiesForTableView.java index e08bf1d2..8c5e77fc 100644 --- a/src/main/java/de/unijena/cheminf/mortar/model/data/DataModelPropertiesForTableView.java +++ b/src/main/java/de/unijena/cheminf/mortar/model/data/DataModelPropertiesForTableView.java @@ -26,56 +26,59 @@ package de.unijena.cheminf.mortar.model.data; /** - * Enum for the data model properties that are displayed in the TableViews + * Enum for the data model properties that are displayed in the TableViews. Note that the textual representations are + * used to find the respective property of the data classes that should be used to fill a specific column in the table + * views, e.g. "name" textual representation of NAME is used in the MoleculesDatatableView as property for the cell value + * factory of the names column, so that MoleculeDataModel.getName() is invoked to fill the cells of this column. * * @author Felix Baensch * @version 1.0.0.0 */ public enum DataModelPropertiesForTableView { /** - * Enum for name property + * Enum for name property. */ NAME ("name"), /** - * Enum for uniqueSmiles property + * Enum for uniqueSmiles property. */ UNIQUE_SMILES ("uniqueSmiles"), /** - * Enum for parentMoleculeName property + * Enum for parentMoleculeName property. */ PARENT_MOLECULE_NAME ("parentMoleculeName"), /** - * Enum for absoluteFrequency property + * Enum for absoluteFrequency property. */ ABSOLUTE_FREQUENCY ("absoluteFrequency"), /** - * Enum for absolutePercentage property + * Enum for absolutePercentage property. */ ABSOLUTE_PERCENTAGE ("absolutePercentage"), /** - * Enum for moleculeFrequency property + * Enum for moleculeFrequency property. */ MOLECULE_FREQUENCY ("moleculeFrequency"), /** - * Enum for moleculePercentage property + * Enum for moleculePercentage property. */ MOLECULE_PERCENTAGE ("moleculePercentage"), /** - * Enum for structure property + * Enum for structure property. */ - STRUCTURE("structure"), + STRUCTURE ("structure"), /** - * Enum for parent molecule structure property + * Enum for parent molecule structure property. */ - PARENT_MOLECULE_STRUCTURE("parentMoleculeStructure"); + PARENT_MOLECULE_STRUCTURE ("parentMoleculeStructure"); // /** - * String representation + * String representation. */ private final String text; // /** - * Constructor + * Constructor. * * @param aText String representation of enum property */ @@ -84,7 +87,7 @@ public enum DataModelPropertiesForTableView { } // /** - * Gets String representation + * Gets String representation. * * @return text String representation */ @@ -100,7 +103,7 @@ public String getText() { */ public static DataModelPropertiesForTableView fromString(String aText) { for (DataModelPropertiesForTableView property : DataModelPropertiesForTableView.values()) { - if(property.text.equalsIgnoreCase(aText)) { + if (property.text.equalsIgnoreCase(aText)) { return property; } } diff --git a/src/main/java/de/unijena/cheminf/mortar/model/data/FragmentDataModel.java b/src/main/java/de/unijena/cheminf/mortar/model/data/FragmentDataModel.java index 7baa1af1..0a188b71 100644 --- a/src/main/java/de/unijena/cheminf/mortar/model/data/FragmentDataModel.java +++ b/src/main/java/de/unijena/cheminf/mortar/model/data/FragmentDataModel.java @@ -68,20 +68,24 @@ public class FragmentDataModel extends MoleculeDataModel { private double moleculePercentage; // /** - * List of parent molecules, which contains this fragment + * List of parent molecules, which contain this fragment. */ - private Set parentMolecules; + private final Set parentMolecules; // /** - * First or random parent molecule which contains this fragment + * First or random parent molecule which contains this fragment. */ private MoleculeDataModel parentMolecule; // + // + // /** * Logger of this class. */ private static final Logger LOGGER = Logger.getLogger(FragmentDataModel.class.getName()); + // // + // /** * Constructor, sets absolute frequency to 0. Molecular information is taken from the given unique SMILES code. The * data is not kept as atom container. @@ -97,7 +101,8 @@ public FragmentDataModel(String aUniqueSmiles, String aName, Map this.absolutePercentage = 0.; this.moleculeFrequency = 0; this.moleculePercentage = 0.; - this.parentMolecules = ConcurrentHashMap.newKeySet(); // sounds weird but to set the number of the total molecule set kills the performance + // sounds weird but to set the number of the total molecule set as expected size kills the performance + this.parentMolecules = ConcurrentHashMap.newKeySet(); } // /** @@ -112,26 +117,34 @@ public FragmentDataModel(IAtomContainer anAtomContainer) throws NullPointerExcep this.absolutePercentage = 0.; this.moleculeFrequency = 0; this.moleculePercentage = 0.; - this.parentMolecules = ConcurrentHashMap.newKeySet(); // sounds weird but to set the number of the total molecule set kills the performance + // sounds weird but to set the number of the total molecule set as expected size kills the performance + this.parentMolecules = ConcurrentHashMap.newKeySet(); } + // // + // /** * Increases the absolute frequency by one. */ - public void incrementAbsoluteFrequency(){ + public void incrementAbsoluteFrequency() { this.absoluteFrequency += 1; } // /** * Increases the molecule frequency by one. */ - public void incrementMoleculeFrequency(){ + public void incrementMoleculeFrequency() { this.moleculeFrequency += 1; } + // // - // + // /** - * Returns absolute frequency of this fragment + * Returns absolute frequency of this fragment. + *
NOTE: Do not delete or rename this method, it is used by reflection (in FragmentsDataTableView, the + * CellValueFactory of the frequency column is set to a PropertyValueFactory that uses "absoluteFrequency" as + * property string to invoke this method; see also DataModelPropertiesForTableView enum). + * * @return int absoluteFrequency */ public int getAbsoluteFrequency() { @@ -139,7 +152,11 @@ public int getAbsoluteFrequency() { } // /** - * Returns absolute frequency of this fragment as a percentage + * Returns absolute frequency of this fragment as a percentage. + *
NOTE: Do not delete or rename this method, it is used by reflection (in FragmentsDataTableView, the + * CellValueFactory of the percentage column is set to a PropertyValueFactory that uses "absolutePercentage" as + * property string to invoke this method; see also DataModelPropertiesForTableView enum). + * * @return double absolutePercentage */ public double getAbsolutePercentage() { @@ -147,7 +164,11 @@ public double getAbsolutePercentage() { } // /** - * Returns molecule frequency of this fragment + * Returns molecule frequency of this fragment. + *
NOTE: Do not delete or rename this method, it is used by reflection (in FragmentsDataTableView, the + * CellValueFactory of the molecule frequency column is set to a PropertyValueFactory that uses "moleculeFrequency" as + * property string to invoke this method; see also DataModelPropertiesForTableView enum). + * * @return int moleculeFrequency */ public int getMoleculeFrequency() { @@ -155,7 +176,11 @@ public int getMoleculeFrequency() { } // /** - * Returns molecule frequency of this fragment as a percentage + * Returns molecule frequency of this fragment as a percentage. + *
NOTE: Do not delete or rename this method, it is used by reflection (in FragmentsDataTableView, the + * CellValueFactory of the molecule percentage column is set to a PropertyValueFactory that uses "moleculePercentage" as + * property string to invoke this method; see also DataModelPropertiesForTableView enum). + * * @return double moleculePercentage */ public double getMoleculePercentage() { @@ -163,121 +188,134 @@ public double getMoleculePercentage() { } // /** - * Returns list of parent molecules which contains this fragment - * @return list of parent molecules + * Returns list of parent molecules which contains this fragment. + * + * @return set of parent molecules */ - public Set getParentMolecules(){ + public Set getParentMolecules() { return this.parentMolecules; } // /** - * Returns the MoleculeDataModel that occurs at first pos in the list of parent molecules + * Returns the MoleculeDataModel that occurs at first pos in the list of parent molecules. Returns null if no parent + * molecules are set. * * @return MoleculeDataModel first molecule of list of parent molecules */ public MoleculeDataModel getFirstParentMolecule() { - if(this.parentMolecules.size() < 1){ + if (this.parentMolecules.isEmpty()) { return null; } - if(this.parentMolecule == null){ - this.parentMolecule = this.parentMolecules.stream().findFirst().get(); + if (this.parentMolecule == null) { + this.parentMolecule = this.parentMolecules.stream().findFirst().orElse(null); } return this.parentMolecule; } // /** - * Creates and returns an ImageView of first parent molecule as 2D structure of this fragment + * Creates and returns an ImageView of first parent molecule as 2D structure of this fragment. + *
NOTE: Do not delete or rename this method, it is used by reflection (in FragmentsDataTableView, the + * CellValueFactory of the parents column is set to a PropertyValueFactory that uses "parentMoleculeStructure" as + * property string to invoke this method; see also DataModelPropertiesForTableView enum). * - * NOTE: Do not delete or comment this method, it is used by reflection - * @return ImageView + * @return ImageView of the first parent molecule or error image if none is set */ - public ImageView getParentMoleculeStructure() { - if(this.parentMolecules.size() < 1){ - return null; + public ImageView getParentMoleculeStructure() throws NullPointerException { + if (this.parentMolecules.isEmpty()) { + return new ImageView(DepictionUtil.depictErrorImage("No parent molecules", 250, 250)); } - if(this.parentMolecule == null){ - this.parentMolecule = this.parentMolecules.stream().findFirst().get(); + if (this.parentMolecule == null) { + this.parentMolecule = this.parentMolecules.stream().findFirst().orElse(null); } try { + // throws NullPointerException if parent molecule is null IAtomContainer tmpAtomContainer = this.parentMolecule.getAtomContainer(); return new ImageView(DepictionUtil.depictImageWithHeight(tmpAtomContainer, super.getStructureImageHeight())); - } catch (CDKException aCDKException) { - FragmentDataModel.LOGGER.getLogger(MoleculeDataModel.class.getName()).log(Level.SEVERE, aCDKException.toString() + "_" + this.parentMolecule.getName(), aCDKException); - return new ImageView(DepictionUtil.depictErrorImage(aCDKException.getMessage(), 250, 250)); + } catch (CDKException | NullPointerException anException) { + FragmentDataModel.LOGGER.log( + Level.SEVERE, + String.format("Molecule name: %s; exception: %s", this.parentMolecule.getName(), anException.toString()), + anException); + return new ImageView(DepictionUtil.depictErrorImage(anException.getMessage(), 250, 250)); } } // /** - * Returns the name of first parent molecule of this fragment + * Returns the name of first parent molecule of this fragment. + *
NOTE: Do not delete or rename this method, it is used by reflection (in FragmentsDataTableView, the + * CellValueFactory of the parent mol name column is set to a PropertyValueFactory that uses "parentMoleculeName" as + * property string to invoke this method; see also DataModelPropertiesForTableView enum). * - * @return String + * @return parent molecule name or an empty string if no parent molecule is set */ - public String getParentMoleculeName(){ - if(this.parentMolecules.size() < 1){ - return null; + public String getParentMoleculeName() { + if (this.parentMolecules.isEmpty()) { + return ""; } - if(this.parentMolecule == null){ - this.parentMolecule = this.parentMolecules.stream().findFirst().get(); + if (this.parentMolecule == null) { + this.parentMolecule = this.parentMolecules.stream().findFirst().orElse(null); } - return this.parentMolecule.getName(); + return this.parentMolecule == null ? "" : this.parentMolecule.getName(); } + //
// + // /** - * Sets the absolute frequency + * Sets the absolute frequency. * * @param aValue absolute frequency */ public void setAbsoluteFrequency(int aValue) { - if(aValue < 0 ){ + if (aValue < 0 ) { throw new IllegalArgumentException("aValue must be positive or zero."); } this.absoluteFrequency = aValue; } // /** - * Sets the molecule frequency + * Sets the molecule frequency. * * @param aValue molecule frequency */ public void setMoleculeFrequency(int aValue) { - if(aValue < 0 ){ + if (aValue < 0 ) { throw new IllegalArgumentException("aValue must be positive or zero."); } this.moleculeFrequency = aValue; } // /** - * Sets the absolute frequency percentage + * Sets the absolute frequency percentage. * * @param aValue absolute frequency percentage */ - public void setAbsolutePercentage(double aValue){ - if(aValue < 0.0 ){ + public void setAbsolutePercentage(double aValue) { + if (aValue < 0.0 ) { throw new IllegalArgumentException("aValue must be positive or zero."); } - if(!Double.isFinite(aValue)){ + if (!Double.isFinite(aValue)) { throw new IllegalArgumentException("aValue must be finite."); } this.absolutePercentage = aValue; } // /** - * Sets the molecule frequency percentage + * Sets the molecule frequency percentage. * * @param aValue molecule frequency percentage */ - public void setMoleculePercentage(double aValue){ - if(aValue < 0.0 ){ + public void setMoleculePercentage(double aValue) { + if (aValue < 0.0 ) { throw new IllegalArgumentException("aValue must be positive or zero."); } - if(!Double.isFinite(aValue)){ + if (!Double.isFinite(aValue)) { throw new IllegalArgumentException("aValue must be finite."); } this.moleculePercentage = aValue; } // /** - * Sets the parent molecule of this fragment + * Sets the parent molecule of this fragment. * * @param aParentMolecule parent molecule */ diff --git a/src/main/java/de/unijena/cheminf/mortar/model/data/MoleculeDataModel.java b/src/main/java/de/unijena/cheminf/mortar/model/data/MoleculeDataModel.java index 6a001262..32e07cd1 100644 --- a/src/main/java/de/unijena/cheminf/mortar/model/data/MoleculeDataModel.java +++ b/src/main/java/de/unijena/cheminf/mortar/model/data/MoleculeDataModel.java @@ -50,7 +50,7 @@ * @version 1.0.1.0 */ public class MoleculeDataModel { - // + // /** * Name of molecule. */ @@ -59,9 +59,9 @@ public class MoleculeDataModel { /** * Unique SMILES code of molecule. */ - private String uniqueSmiles; + private final String uniqueSmiles; // - private BooleanProperty selection; + private final BooleanProperty selection; // /** * Boolean, whether to keep the atom container of the molecule. @@ -76,32 +76,34 @@ public class MoleculeDataModel { /** * Fragments map containing names of fragmentations done to the molecule as keys and lists of * {@link de.unijena.cheminf.mortar.model.data.FragmentDataModel} objects that resulted from these fragmentations - * as values. + * as values. {@literal Map>} */ - private HashMap> fragments; // HashMap> + private final Map> fragments; // /** * Fragment frequencies map of a specific fragmentation with the given name. Keys of the map are unique SMILES * representations of the fragments and values are the frequencies of the respective fragments in the molecule. + * {@literal Map>} */ - private HashMap> fragmentFrequencies; // HashMap> + private final Map> fragmentFrequencies; // /** * Property map of this molecule. */ - private Map properties; + private final Map properties; // /** - * Height value for image of structure + * Height value for image of structure. */ private double structureImageHeight; // /** - * Width value for image of structure + * Width value for image of structure. */ private double structureImageWidth; // // + // /** * Constructor for MoleculeDataModel. Molecular information is taken from the given unique SMILES code. The data * is not kept as atom container. @@ -135,15 +137,22 @@ public MoleculeDataModel(IAtomContainer anAtomContainer) throws NullPointerExcep this.keepAtomContainer = true; this.atomContainer = anAtomContainer; } + // // - // + // /** - * Returns name (String) of the molecule, if it is null, "NoName" will be returned + * Returns name (String) of the molecule. If it is null, "NoName" will be returned. + *
NOTE: Do not delete or rename this method, it is used by reflection (in MoleculesDataTableView, the + * CellValueFactory of the name column is set to a PropertyValueFactory that uses "name" as + * property string to invoke this method; same usage in ItemizationDataTableView; see also + * DataModelPropertiesForTableView enum). + * * @return String name of molecule */ - public String getName(){ - if(this.name == null || this.name.isEmpty()) + public String getName() { + if (this.name == null || this.name.isEmpty()) { this.name = "NoName"; + } return this.name; } // @@ -157,7 +166,7 @@ public String getName(){ * @throws CDKException if SMILES parsing fails */ public IAtomContainer getAtomContainer() throws CDKException { - if(this.atomContainer != null){ + if (this.atomContainer != null) { return this.atomContainer; } IAtomContainer tmpAtomContainer; @@ -168,34 +177,36 @@ public IAtomContainer getAtomContainer() throws CDKException { tmpAtomContainer = ChemUtil.parseSmilesToAtomContainer(this.uniqueSmiles, false, false); } tmpAtomContainer.addProperties(this.properties); - if(this.keepAtomContainer){ + if (this.keepAtomContainer) { this.atomContainer = tmpAtomContainer; } return tmpAtomContainer; } // /** - * Returns unique SMILES + * Returns unique SMILES. * * @return String uniqueSmiles */ - public String getUniqueSmiles(){ + public String getUniqueSmiles() { return this.uniqueSmiles; } // /** - * Returns boolean telling whether molecule is selected or not + * Returns boolean telling whether molecule is selected or not. + * * @return true if molecule is selected */ - public boolean isSelected(){ + public boolean isSelected() { return this.selection.get(); } // /** - * Returns BooleanProperty whether molecule is selected or not + * Returns BooleanProperty whether molecule is selected or not. + * * @return BooleanProperty */ - public BooleanProperty selectionProperty(){ + public BooleanProperty selectionProperty() { return this.selection; } // @@ -204,18 +215,19 @@ public BooleanProperty selectionProperty(){ * the molecule and as values lists of {@link de.unijena.cheminf.mortar.model.data.FragmentDataModel} objects that * resulted from these fragmentations. * - * @return HashMap {@literal <}fragmentationName, List {@literal <}FragmentDataModel {@literal >>} + * @return Map {@literal <}fragmentationName, List {@literal <}FragmentDataModel {@literal >>} */ - public HashMap> getAllFragments(){ + public Map> getAllFragments() { return this.fragments; } // /** * Returns a list of unique fragments that resulted from the fragmentation of the molecule with the given name. - * @param aKey String specifies fragmentation or fragmentation algorithm + * + * @param aKey String specifies fragmentation * @return List {@literal <}FragmentDataModel {@literal >} */ - public List getFragmentsOfSpecificAlgorithm(String aKey){ + public List getFragmentsOfSpecificFragmentation(String aKey) { Objects.requireNonNull(aKey, "Key must not be null"); return this.fragments.get(aKey); } @@ -225,9 +237,9 @@ public List getFragmentsOfSpecificAlgorithm(String aKey){ * done to the molecule and as values maps that in turn contain as keys unique SMILES representations of fragments * that resulted from the respective fragmentation and as objects the frequencies of the specific fragment in the molecule. * - * @return HashMap {@literal <}fragmentationName, HashMap {@literal <}uniqueSmiles, frequency {@literal >>} + * @return Map {@literal <}fragmentationName, Map {@literal <}uniqueSmiles, frequency {@literal >>} */ - public HashMap> getFragmentFrequencies(){ + public Map> getFragmentFrequencies() { return this.fragmentFrequencies; } // @@ -235,10 +247,10 @@ public HashMap> getFragmentFrequencies(){ * Returns the fragment frequencies map of a specific fragmentation with the given name. Keys of the map are unique * SMILES representations of the fragments and values are the frequencies of the respective fragments in the molecule. * - * @param aKey String specifies fragmentation or fragmentation algorithm - * @return HashMap {@literal <}uniqueSmiles, frequency {@literal >} + * @param aKey String specifies fragmentation + * @return Map {@literal <}uniqueSmiles, frequency {@literal >} */ - public HashMap getFragmentFrequencyOfSpecificAlgorithm(String aKey){ + public Map getFragmentFrequencyOfSpecificFragmentation(String aKey) { Objects.requireNonNull(aKey, "Key must not be null"); return this.fragmentFrequencies.get(aKey); } @@ -249,13 +261,18 @@ public HashMap getFragmentFrequencyOfSpecificAlgorithm(String a * @param aKey fragmentation name * @return true if the molecule has undergone the fragmentation with the specified name */ - public boolean hasMoleculeUndergoneSpecificFragmentation(String aKey){ + public boolean hasMoleculeUndergoneSpecificFragmentation(String aKey) { Objects.requireNonNull(aKey, "aKey must not be null"); return this.fragments.containsKey(aKey); } // /** - * Creates and returns an ImageView of this molecule as 2D structure + * Creates and returns an ImageView of this molecule as 2D structure. + *
NOTE: Do not delete or rename this method, it is used by reflection (in MoleculesDataTableView, the + * CellValueFactory of the structure column is set to a PropertyValueFactory that uses "name" as + * property string to invoke this method; same usage in ItemizationDataTableView molecule structure column; see also + * DataModelPropertiesForTableView enum). + * * @return ImageView */ public ImageView getStructure() { @@ -269,7 +286,8 @@ public ImageView getStructure() { } // /** - * Creates and returns an ImageView of this molecule as 2D structure with the given text below the structure + * Creates and returns an ImageView of this molecule as 2D structure with the given text below the structure. + * Mainly used for fragments in items tab. * * @param aText to show below structure * @return ImageView with text @@ -285,43 +303,49 @@ public ImageView getStructureWithText(String aText){ } // /** - * Returns property map of this molecule + * Returns property map of this molecule. + * * @return property map */ - public Map getProperties() { + public Map getProperties() { return this.properties; } // /** - * Returns whether the atom container of the molecule should be kept or not + * Returns whether the atom container of the molecule should be kept or not. + * * @return boolean keepAtomContainer */ - public boolean isKeepAtomContainer(){ + public boolean isKeepAtomContainer() { return this.keepAtomContainer; } // /** - * Sets given String as name of this molecule + * Sets given String as name of this molecule. + * * @param aName String */ - public void setName(String aName){ + public void setName(String aName) { this.name = aName; } // /** - * Sets selection state of this molecule depending on the specified boolean + * Sets selection state of this molecule depending on the specified boolean. + * * @param aValue boolean */ - public void setSelection(boolean aValue){ + public void setSelection(boolean aValue) { this.selection.set(aValue); } // /** - * Sets whether the molecule's atom container should be kept. If not, the atom container is set to null + * Sets whether the molecule's atom container should be kept. If not, the atom container is set to null. + * * @param aValue boolean */ - public void setKeepAtomContainer(boolean aValue){ - if(!(this.keepAtomContainer = aValue)){ + public void setKeepAtomContainer(boolean aValue) { + this.keepAtomContainer = aValue; + if (!this.keepAtomContainer) { this.atomContainer = null; } } @@ -333,7 +357,7 @@ public void setKeepAtomContainer(boolean aValue){ * @return height of image */ public double getStructureImageHeight() { - if(this.structureImageHeight == 0.0) { + if (this.structureImageHeight == 0.0) { return BasicDefinitions.DEFAULT_IMAGE_HEIGHT_DEFAULT; } else { return this.structureImageHeight; @@ -347,7 +371,7 @@ public double getStructureImageHeight() { * @return width of image */ public double getStructureImageWidth() { - if(this.structureImageWidth == 0.0) { + if (this.structureImageWidth == 0.0) { return BasicDefinitions.DEFAULT_IMAGE_WIDTH_DEFAULT; } else { return this.structureImageWidth; @@ -355,7 +379,7 @@ public double getStructureImageWidth() { } // /** - * Sets the height of the image of the molecular structure + * Sets the height of the image of the molecular structure. * * @param aStructureImageHeight double */ @@ -364,7 +388,7 @@ public void setStructureImageHeight(double aStructureImageHeight) { } // /** - * Sets the width of the image of the molecular structure + * Sets the width of the image of the molecular structure. * * @param aStructureImageWidth double */ diff --git a/src/main/java/de/unijena/cheminf/mortar/model/depict/DepictionUtil.java b/src/main/java/de/unijena/cheminf/mortar/model/depict/DepictionUtil.java index 8d2a2d63..61d6f1c0 100644 --- a/src/main/java/de/unijena/cheminf/mortar/model/depict/DepictionUtil.java +++ b/src/main/java/de/unijena/cheminf/mortar/model/depict/DepictionUtil.java @@ -39,12 +39,14 @@ import java.awt.FontMetrics; import java.awt.Graphics2D; import java.awt.RenderingHints; +import java.awt.Transparency; import java.awt.image.BufferedImage; +import java.util.Objects; import java.util.logging.Level; import java.util.logging.Logger; /** - * Util class for depiction + * Util class for depiction. * * @author Felix Baensch, Jonas Schaub * @version 1.0.0.0 @@ -57,7 +59,7 @@ public class DepictionUtil { private static final Logger LOGGER = Logger.getLogger(DepictionUtil.class.getName()); //
// - // + // /** * Private parameter-less constructor. * Introduced because javadoc build complained about classes without declared default constructor. @@ -68,7 +70,7 @@ private DepictionUtil() { // // /** - * Creates and returns an Image of the given AtomContainer + * Creates and returns an Image of the given AtomContainer. Background will be transparent. * * @param anAtomContainer IAtomContainer * @param aWidth double for image width @@ -76,43 +78,45 @@ private DepictionUtil() { * @return Image of 2D structure of IAtomContainer */ public static Image depictImage(IAtomContainer anAtomContainer, double aWidth, double aHeight) { - return depictImageWithZoom(anAtomContainer, 1.0, aWidth, aHeight); + return DepictionUtil.depictImageWithZoom(anAtomContainer, 1.0, aWidth, aHeight); } // /** - * Creates and returns an Image of the AtomContainer with given height and default width (250.0). + * Creates and returns an Image of the AtomContainer with given height and default width (250.0). Background will be transparent. * * @param anAtomContainer IAtomContainer * @param aHeight double * @return Image of 2D structure of IAtomContainer */ public static Image depictImageWithHeight(IAtomContainer anAtomContainer, double aHeight) { - return depictImageWithZoom(anAtomContainer, 1.0, BasicDefinitions.DEFAULT_IMAGE_WIDTH_DEFAULT, aHeight); + return DepictionUtil.depictImageWithZoom(anAtomContainer, 1.0, BasicDefinitions.DEFAULT_IMAGE_WIDTH_DEFAULT, aHeight); } // /** - * Creates and returns an Image of the AtomContainer with given width and default height (250.0). + * Creates and returns an Image of the AtomContainer with given width and default height (250.0). Background will be transparent. * * @param anAtomContainer IAtomContainer * @param aWidth double * @return Image of 2D structure of IAtomContainer */ public static Image depictImageWithWidth(IAtomContainer anAtomContainer, double aWidth) { - return depictImageWithZoom(anAtomContainer, 1.0, aWidth, BasicDefinitions.DEFAULT_IMAGE_HEIGHT_DEFAULT); + return DepictionUtil.depictImageWithZoom(anAtomContainer, 1.0, aWidth, BasicDefinitions.DEFAULT_IMAGE_HEIGHT_DEFAULT); } // /** - * Creates and returns an Image of the AtomContainer with any zoom factor and default width (250.0) and height (250.0) + * Creates and returns an Image of the AtomContainer with any zoom factor and default width (250.0) and height (250.0). + * Background will be transparent. * * @param anAtomContainer IAtomContainer * @param aZoom double * @return Image of 2D structure of IAtomContainer */ - public static Image depictImageWithZoom(IAtomContainer anAtomContainer, double aZoom){ - return depictImageWithZoom(anAtomContainer, aZoom, BasicDefinitions.DEFAULT_IMAGE_WIDTH_DEFAULT, BasicDefinitions.DEFAULT_IMAGE_HEIGHT_DEFAULT); + public static Image depictImageWithZoom(IAtomContainer anAtomContainer, double aZoom) { + return DepictionUtil.depictImageWithZoom(anAtomContainer, aZoom, BasicDefinitions.DEFAULT_IMAGE_WIDTH_DEFAULT, BasicDefinitions.DEFAULT_IMAGE_HEIGHT_DEFAULT); } /** * Creates and returns an Image of the AtomContainer with any zoom factor and given width and height. + * Background will be transparent. * * @param anAtomContainer IAtomContainer * @param aZoom double @@ -126,6 +130,7 @@ public static Image depictImageWithZoom(IAtomContainer anAtomContainer, double a // /** * Creates and returns an Image of the AtomContainer with any zoom factor and given width and height and fill to fit. + * Background will be transparent. * * @param anAtomContainer IAtomContainer * @param aZoom double @@ -153,7 +158,8 @@ public static Image depictImageWithZoomAndFillToFit(IAtomContainer anAtomContain * @param aWidth double * @param aHeight double * @param fillToFit boolean Resize depictions to fill all available space (only if a size is specified) - * @param isBackgroundWhite boolean if image has white background + * @param isBackgroundWhite true if the image should have an opaque white background; false if the background + * should be transparent * @return Image of 2D structure of IAtomContainer */ public static Image depictImageWithZoomAndFillToFitAndWhiteBackground(IAtomContainer anAtomContainer, double aZoom, double aWidth, double aHeight, boolean fillToFit, boolean isBackgroundWhite) { @@ -175,12 +181,7 @@ public static Image depictImageWithZoomAndFillToFitAndWhiteBackground(IAtomConta * @return Image of given String */ public static Image depictErrorImage(String aMessage, int aWidth, int aHeight) { - String tmpMessage; - if (aMessage == null) { - tmpMessage = "Error"; - } else { - tmpMessage = aMessage; - } + String tmpMessage = Objects.requireNonNullElse(aMessage, "Error"); BufferedImage tmpBufferedImage = new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB); Graphics2D tmpGraphic = tmpBufferedImage.createGraphics(); Font tmpFont = new Font("Arial", Font.PLAIN, 20); @@ -206,7 +207,7 @@ public static Image depictErrorImage(String aMessage, int aWidth, int aHeight) { } // /** - * Depicts and returns an image with the given text below + * Depicts and returns an image with the given text below. Background will be transparent. * * @param anAtomContainer IAtomContainer * @param aZoom double @@ -215,10 +216,11 @@ public static Image depictErrorImage(String aMessage, int aWidth, int aHeight) { * @param aString String * @return Image of 2D structure of IAtomContainer with given String below */ - public static Image depictImageWithText(IAtomContainer anAtomContainer, double aZoom, double aWidth, double aHeight, String aString){ - try{ - BufferedImage tmpMolBufferedImage = DepictionUtil.depictBufferedImageWithZoom(anAtomContainer, aZoom, aWidth, aHeight - 25, false, false); //magic number to compensate for the height of the text - BufferedImage tmpBufferedImage = new BufferedImage(tmpMolBufferedImage.getWidth(), tmpMolBufferedImage.getHeight() + BasicDefinitions.DEFAULT_IMAGE_TEXT_DISTANCE, BufferedImage.TRANSLUCENT); + public static Image depictImageWithText(IAtomContainer anAtomContainer, double aZoom, double aWidth, double aHeight, String aString) { + try { + // height - 25 magic number to compensate for the height of the text + BufferedImage tmpMolBufferedImage = DepictionUtil.depictBufferedImageWithZoom(anAtomContainer, aZoom, aWidth, aHeight - 25, false, false); + BufferedImage tmpBufferedImage = new BufferedImage(tmpMolBufferedImage.getWidth(), tmpMolBufferedImage.getHeight() + BasicDefinitions.DEFAULT_IMAGE_TEXT_DISTANCE, Transparency.TRANSLUCENT); Graphics2D tmpGraphics2d = tmpBufferedImage.createGraphics(); tmpGraphics2d.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY); tmpGraphics2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); @@ -237,7 +239,7 @@ public static Image depictImageWithText(IAtomContainer anAtomContainer, double a tmpGraphics2d.drawString(aString, (tmpBufferedImage.getWidth() / 2) - tmpTextWidth / 2, tmpBufferedImage.getHeight()); tmpGraphics2d.dispose(); return SwingFXUtils.toFXImage(tmpBufferedImage, null); - } catch (CDKException anException){ + } catch (CDKException anException) { DepictionUtil.LOGGER.log(Level.SEVERE, anException.toString(), anException); return DepictionUtil.depictErrorImage(anException.getMessage(), 250,250); } @@ -246,34 +248,28 @@ public static Image depictImageWithText(IAtomContainer anAtomContainer, double a // // /** - * Creates and returns a BufferedImage of given IAtomContainer + * Creates and returns a BufferedImage of given IAtomContainer. * * @param anAtomContainer IAtomContainer * @param aZoom double * @param aWidth double * @param aHeight double * @param fillToFit boolean - * @param isBackgroundWhite boolean + * @param isBackgroundWhite true if the image should have an opaque white background; false if the background + * should be transparent * @return BufferedImage of given IAtomContainer - * @throws CDKException + * @throws CDKException if a depiction cannot be generated */ private static BufferedImage depictBufferedImageWithZoom(IAtomContainer anAtomContainer, double aZoom, double aWidth, double aHeight, boolean fillToFit, boolean isBackgroundWhite) throws CDKException { - DepictionGenerator tmpGenerator = new DepictionGenerator(); - if(fillToFit){ - if(isBackgroundWhite){ - tmpGenerator = tmpGenerator.withAtomColors().withAromaticDisplay().withSize(aWidth,aHeight).withZoom(aZoom).withFillToFit(); - } - else { - tmpGenerator = tmpGenerator.withAtomColors().withAromaticDisplay().withSize(aWidth,aHeight).withZoom(aZoom).withFillToFit().withBackgroundColor(new Color(1f,0f,0f,.0f )); - } - } - else { - if(isBackgroundWhite){ - tmpGenerator = tmpGenerator.withAtomColors().withAromaticDisplay().withSize(aWidth,aHeight).withZoom(aZoom); - } - else { - tmpGenerator = tmpGenerator.withAtomColors().withAromaticDisplay().withSize(aWidth,aHeight).withZoom(aZoom).withBackgroundColor(new Color(1f,0f,0f,.0f )); - } + DepictionGenerator tmpGenerator = new DepictionGenerator() + .withAtomColors() + .withAromaticDisplay() + .withSize(aWidth, aHeight) + .withZoom(aZoom) + //color is white by sRGB values and fourth param alpha decides over opaque (1.0f) and transparent (0.0f) + .withBackgroundColor(new Color(1.0f, 1.0f, 1.0f, isBackgroundWhite? 1.0f : 0.0f)); + if (fillToFit) { + tmpGenerator = tmpGenerator.withFillToFit(); } return tmpGenerator.depict(anAtomContainer).toImg(); } diff --git a/src/main/java/de/unijena/cheminf/mortar/model/fragmentation/FragmentationService.java b/src/main/java/de/unijena/cheminf/mortar/model/fragmentation/FragmentationService.java index 0e17522c..010e6dfd 100644 --- a/src/main/java/de/unijena/cheminf/mortar/model/fragmentation/FragmentationService.java +++ b/src/main/java/de/unijena/cheminf/mortar/model/fragmentation/FragmentationService.java @@ -38,19 +38,12 @@ import de.unijena.cheminf.mortar.model.util.ChemUtil; import de.unijena.cheminf.mortar.model.util.CollectionUtil; import de.unijena.cheminf.mortar.model.util.FileUtil; -import de.unijena.cheminf.mortar.model.util.SimpleEnumConstantNameProperty; -import de.unijena.cheminf.mortar.preference.BooleanPreference; -import de.unijena.cheminf.mortar.preference.IPreference; import de.unijena.cheminf.mortar.preference.PreferenceContainer; import de.unijena.cheminf.mortar.preference.PreferenceUtil; import de.unijena.cheminf.mortar.preference.SingleIntegerPreference; -import de.unijena.cheminf.mortar.preference.SingleNumberPreference; import de.unijena.cheminf.mortar.preference.SingleTermPreference; import javafx.beans.property.Property; -import javafx.beans.property.SimpleBooleanProperty; -import javafx.beans.property.SimpleDoubleProperty; -import javafx.beans.property.SimpleIntegerProperty; import javafx.beans.property.SimpleStringProperty; import javafx.scene.control.Alert; @@ -66,9 +59,11 @@ import java.util.Hashtable; import java.util.LinkedList; import java.util.List; +import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.concurrent.CancellationException; +import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; @@ -85,12 +80,12 @@ public class FragmentationService { // /** - * Default selected fragmenter + * Default selected fragmenter. */ public static final String DEFAULT_SELECTED_FRAGMENTER_ALGORITHM_NAME = ErtlFunctionalGroupsFinderFragmenter.ALGORITHM_NAME; /** - * Default pipeline name + * Default pipeline name. */ public static final String DEFAULT_PIPELINE_NAME = Message.get("FragmentationService.defaultPipelineName"); @@ -105,7 +100,7 @@ public class FragmentationService { public static final String FRAGMENTATION_SERVICE_SETTINGS_SUBFOLDER_NAME = "Fragmentation_Service_Settings"; /** - * File name to persist the fragmentation service settings + * File name to persist the fragmentation service settings. */ public static final String FRAGMENTATION_SERVICE_SETTINGS_FILE_NAME = "FragmentationServiceSettings"; @@ -137,64 +132,70 @@ public class FragmentationService { // // /** - * Array for the different fragmentation algorithms available - */ - private IMoleculeFragmenter[] fragmenters; - /** - * Selected fragmentation algorithm + * Selected fragmentation algorithm. */ private IMoleculeFragmenter selectedFragmenter; /** - * Array for the fragmentation algorithms to use during pipeline fragmentation + * Array for the fragmentation algorithms to use during pipeline fragmentation. */ private IMoleculeFragmenter[] pipelineFragmenter; /** - * Ertl algorithm fragmenter + * List of names of fragmentation algorithms that have already been run. */ - private IMoleculeFragmenter ertlFGF; + private List existingFragmentations; /** - * Sugar Removal Utility fragmenter + * Hashtable for fragments. */ - private IMoleculeFragmenter sugarRUF; + private Hashtable fragments; /** - * Scaffold Generator + * String for the name of the current fragmentation algorithm. */ - private IMoleculeFragmenter ScaffoldGF; + private String currentFragmentationName; /** - * List of names of fragmentation algorithms that have already been run + * String for the name of the current pipeline fragmentation. */ - private List existingFragmentations; + private String pipeliningFragmentationName; /** - * Hashtable for fragments + * ExecutorService for the fragmentation tasks. */ - private Hashtable fragments; + private ExecutorService executorService; + // + // + // /** - * String for the name of the current fragmentation algorithm + * Logger. */ - private String currentFragmentationName; + private static final Logger LOGGER = Logger.getLogger(FragmentationService.class.getName()); + // /** - * String for the name of the current pipeline fragmentation + * Array for the different fragmentation algorithms available. */ - private String pipeliningFragmentationName; + private final IMoleculeFragmenter[] fragmenters; + // /** - * ExecutorService for the fragmentation tasks + * Ertl algorithm fragmenter. */ - private ExecutorService executorService; + private final IMoleculeFragmenter ertlFGF; + // /** - * SettingsContainer to hold settings + * Sugar Removal Utility fragmenter. */ - private SettingsContainer settingsContainer; + private final IMoleculeFragmenter sugarRUF; + // /** - * Property of name of selected fragmenter - */ - private SimpleStringProperty selectedFragmenterNameProperty; - // + * Scaffold Generator. + */ + private final IMoleculeFragmenter scaffoldGF; // - // /** - * Logger + * SettingsContainer to hold settings. */ - private static final Logger LOGGER = Logger.getLogger(FragmentationService.class.getName()); + private final SettingsContainer settingsContainer; + // + /** + * Property of display name of selected fragmenter. + */ + private final SimpleStringProperty selectedFragmenterDisplayNameProperty; // // // @@ -203,19 +204,19 @@ public class FragmentationService { * * @param aSettingsContainer SettingsContainer which holds the settings */ - public FragmentationService(SettingsContainer aSettingsContainer){ + public FragmentationService(SettingsContainer aSettingsContainer) { //Note: Every fragmenter class should only be added once to the array or there will be problems with setting persistence! this.fragmenters = new IMoleculeFragmenter[3]; this.ertlFGF = new ErtlFunctionalGroupsFinderFragmenter(); this.fragmenters[0] = this.ertlFGF; this.sugarRUF = new SugarRemovalUtilityFragmenter(); this.fragmenters[1] = this.sugarRUF; - this.ScaffoldGF = new ScaffoldGeneratorFragmenter(); - this.fragmenters[2] = this.ScaffoldGF; + this.scaffoldGF = new ScaffoldGeneratorFragmenter(); + this.fragmenters[2] = this.scaffoldGF; // Objects.requireNonNull(aSettingsContainer, "aSettingsContainer must not be null"); this.settingsContainer = aSettingsContainer; - this.selectedFragmenterNameProperty = new SimpleStringProperty(); + this.selectedFragmenterDisplayNameProperty = new SimpleStringProperty(); try { this.checkFragmenters(); } catch (Exception anException) { @@ -233,15 +234,17 @@ public FragmentationService(SettingsContainer aSettingsContainer){ if (Objects.isNull(this.selectedFragmenter)) { this.selectedFragmenter = this.ertlFGF; } - this.setSelectedFragmenterNameProperty(this.selectedFragmenter.getFragmentationAlgorithmName()); + this.setSelectedFragmenterDisplayName(this.selectedFragmenter.getFragmentationAlgorithmDisplayName()); try { - this.pipelineFragmenter = new IMoleculeFragmenter[] {this.createNewFragmenterObjectByAlgorithmName(this.selectedFragmenter.getFragmentationAlgorithmName())}; + this.pipelineFragmenter = new IMoleculeFragmenter[] { + this.createNewFragmenterObjectByAlgorithmName(this.selectedFragmenter.getFragmentationAlgorithmName()) + }; } catch (Exception anException) { //settings of this fragmenter instance are still in default at this point this.pipelineFragmenter = new IMoleculeFragmenter[] {this.selectedFragmenter.copy()}; } this.pipeliningFragmentationName = FragmentationService.DEFAULT_PIPELINE_NAME; - this.existingFragmentations = new LinkedList(); + this.existingFragmentations = new LinkedList<>(); //fragments hash table, current fragmentation name, and executor service are only instantiated when needed } // @@ -255,23 +258,23 @@ public FragmentationService(SettingsContainer aSettingsContainer){ * @param aNumberOfTasks how many parallel tasks should be used * @throws Exception if anything goes wrong */ - public void startSingleFragmentation(List aListOfMolecules, int aNumberOfTasks) throws Exception{ + public void startSingleFragmentation(List aListOfMolecules, int aNumberOfTasks) throws Exception { // Objects.requireNonNull(aListOfMolecules, "aListOfMolecules must not be null"); - if(aNumberOfTasks == 0){ + if (aNumberOfTasks == 0) { aNumberOfTasks = 1; } // - String tmpFragmentationName = this.createAndCheckFragmentationName(this.selectedFragmenter.getFragmentationAlgorithmName()); + String tmpFragmentationName = this.createAndCheckFragmentationName(this.selectedFragmenter.getFragmentationAlgorithmDisplayName()); this.existingFragmentations.add(tmpFragmentationName); this.currentFragmentationName = tmpFragmentationName; this.fragments = this.startFragmentation(aListOfMolecules, aNumberOfTasks, this.selectedFragmenter, tmpFragmentationName); - LOGGER.info("Number of different fragments extracted: " + this.fragments.size()); + FragmentationService.LOGGER.log(Level.INFO, "Number of different fragments extracted: {0}", this.fragments == null ? 0 : this.fragments.size()); } // /** * Starts fragmentation pipeline for given List of molecules. - * Fragmentation will be done on fragments of previous step + * Fragmentation will be done on fragments of previous step. * * @param aListOfMolecules List {@literal <}MoleculeDataModel {@literal >} * @param aNumberOfTasks int value to define onto how many parallel task the molecules should be distributed for fragmentation @@ -281,56 +284,56 @@ public void startPipelineFragmentation(List aListOfMolecules, // Objects.requireNonNull(aListOfMolecules, "aListOfMolecules must not be null"); Objects.requireNonNull(this.pipelineFragmenter, "pipelineFragmenter must not be null"); - if(aNumberOfTasks == 0){ + if (aNumberOfTasks == 0) { aNumberOfTasks = 1; } // this.fragments = new Hashtable<>(aListOfMolecules.size() * this.pipelineFragmenter.length); Hashtable tmpFragmentHashtable; - if(this.pipeliningFragmentationName == null || this.pipeliningFragmentationName.isEmpty()){ - this.pipeliningFragmentationName = "Pipeline"; + if (this.pipeliningFragmentationName == null || this.pipeliningFragmentationName.isEmpty()) { + this.pipeliningFragmentationName = FragmentationService.DEFAULT_PIPELINE_NAME; } String tmpPipelineFragmentationName = this.createAndCheckFragmentationName(this.pipeliningFragmentationName); this.existingFragmentations.add(tmpPipelineFragmentationName); this.currentFragmentationName = tmpPipelineFragmentationName; List tmpMolsToFragment = new ArrayList<>(aListOfMolecules); - for(int i = 0; i < this.pipelineFragmenter.length; i++){ + for (int i = 0; i < this.pipelineFragmenter.length; i++) { this.fragments.clear(); tmpFragmentHashtable = this.startFragmentation(tmpMolsToFragment, aNumberOfTasks, this.pipelineFragmenter[i], tmpPipelineFragmentationName); tmpMolsToFragment.clear(); //iterate through all initial molecules - for(MoleculeDataModel tmpMolecule : aListOfMolecules) { + for (MoleculeDataModel tmpMolecule : aListOfMolecules) { List tmpNewFragmentsOfMol = new LinkedList<>(); - HashMap tmpNewFragmentFrequenciesOfMol = new HashMap<>(tmpMolecule.getFragmentsOfSpecificAlgorithm(tmpPipelineFragmentationName).size() * 2); + HashMap tmpNewFragmentFrequenciesOfMol = new HashMap<>(tmpMolecule.getFragmentsOfSpecificFragmentation(tmpPipelineFragmentationName).size() * 2); //check if molecule has undergone fragmentation (redundant) - if(!tmpMolecule.hasMoleculeUndergoneSpecificFragmentation(tmpPipelineFragmentationName)) { + if (!tmpMolecule.hasMoleculeUndergoneSpecificFragmentation(tmpPipelineFragmentationName)) { continue; } // get fragments of molecules in a new list - List tmpFragmentsOfMolList = new ArrayList<>(tmpMolecule.getFragmentsOfSpecificAlgorithm(tmpPipelineFragmentationName)); + List tmpFragmentsOfMolList = new ArrayList<>(tmpMolecule.getFragmentsOfSpecificFragmentation(tmpPipelineFragmentationName)); //clear fragments in molecule, child fragments will be added later - tmpMolecule.getFragmentsOfSpecificAlgorithm(tmpPipelineFragmentationName).clear(); + tmpMolecule.getFragmentsOfSpecificFragmentation(tmpPipelineFragmentationName).clear(); //iterate through fragments on mol list - for(FragmentDataModel tmpParentFragment : tmpFragmentsOfMolList) { + for (FragmentDataModel tmpParentFragment : tmpFragmentsOfMolList) { //check if parent fragment has undergone, but not after first fragmentation - if(i > 0 && !tmpParentFragment.hasMoleculeUndergoneSpecificFragmentation(tmpPipelineFragmentationName)) { + if (i > 0 && !tmpParentFragment.hasMoleculeUndergoneSpecificFragmentation(tmpPipelineFragmentationName)) { continue; } //get child fragments of parent fragment - List tmpChildFragmentsList = tmpParentFragment.getFragmentsOfSpecificAlgorithm(tmpPipelineFragmentationName); + List tmpChildFragmentsList = tmpParentFragment.getFragmentsOfSpecificFragmentation(tmpPipelineFragmentationName); //if parent fragment has no children, parent fragment is child fragment - if(tmpChildFragmentsList == null || tmpChildFragmentsList.size() < 1) { + if (tmpChildFragmentsList == null || tmpChildFragmentsList.isEmpty()) { //if settingsContainer.isKeepLastFragmentSetting == true or parent fragment is part of the results of fragmentation the parent fragment will be set as new fragment if no new fragment is found - if(this.settingsContainer.isKeepLastFragmentSetting() || (tmpFragmentHashtable != null && tmpFragmentHashtable.containsKey(tmpParentFragment.getUniqueSmiles()))) { + if (this.settingsContainer.isKeepLastFragmentSetting() || (tmpFragmentHashtable != null && tmpFragmentHashtable.containsKey(tmpParentFragment.getUniqueSmiles()))) { if (tmpNewFragmentsOfMol.stream().noneMatch(x -> x.getUniqueSmiles().equals(tmpParentFragment.getUniqueSmiles())) && tmpFragmentHashtable != null) { tmpNewFragmentsOfMol.add(tmpFragmentHashtable.get(tmpParentFragment.getUniqueSmiles())); - tmpNewFragmentFrequenciesOfMol.put(tmpParentFragment.getUniqueSmiles(), tmpMolecule.getFragmentFrequencyOfSpecificAlgorithm(tmpPipelineFragmentationName).get(tmpParentFragment.getUniqueSmiles())); + tmpNewFragmentFrequenciesOfMol.put(tmpParentFragment.getUniqueSmiles(), tmpMolecule.getFragmentFrequencyOfSpecificFragmentation(tmpPipelineFragmentationName).get(tmpParentFragment.getUniqueSmiles())); } //if HashTable for resulting fragments contains fragment, update frequencies = add molecules fragment frequency of fragment to absolute frequency of fragment and increment molecule frequency if (this.fragments.containsKey(tmpParentFragment.getUniqueSmiles())) { tmpParentFragment.getParentMolecules().add(tmpMolecule); this.fragments.get(tmpParentFragment.getUniqueSmiles()).setAbsoluteFrequency( - this.fragments.get(tmpParentFragment.getUniqueSmiles()).getAbsoluteFrequency() + tmpMolecule.getFragmentFrequencyOfSpecificAlgorithm(tmpPipelineFragmentationName).get(tmpParentFragment.getUniqueSmiles()) + this.fragments.get(tmpParentFragment.getUniqueSmiles()).getAbsoluteFrequency() + tmpMolecule.getFragmentFrequencyOfSpecificFragmentation(tmpPipelineFragmentationName).get(tmpParentFragment.getUniqueSmiles()) ); this.fragments.get(tmpParentFragment.getUniqueSmiles()).incrementMoleculeFrequency(); } @@ -339,25 +342,25 @@ public void startPipelineFragmentation(List aListOfMolecules, tmpParentFragment.getParentMolecules().clear(); tmpParentFragment.getParentMolecules().add(tmpMolecule); this.fragments.put(tmpParentFragment.getUniqueSmiles(), tmpParentFragment); - tmpParentFragment.setAbsoluteFrequency(tmpMolecule.getFragmentFrequencyOfSpecificAlgorithm(tmpPipelineFragmentationName).get(tmpParentFragment.getUniqueSmiles())); + tmpParentFragment.setAbsoluteFrequency(tmpMolecule.getFragmentFrequencyOfSpecificFragmentation(tmpPipelineFragmentationName).get(tmpParentFragment.getUniqueSmiles())); tmpParentFragment.setMoleculeFrequency(1); } } } //else (parent fragment has children) iterate through children fragment list else { - for(FragmentDataModel tmpChild : tmpChildFragmentsList){ + for (FragmentDataModel tmpChild : tmpChildFragmentsList) { FragmentDataModel tmpChildFragment; - if(tmpFragmentHashtable != null && tmpFragmentHashtable.containsKey(tmpChild.getUniqueSmiles())) { + if (tmpFragmentHashtable != null && tmpFragmentHashtable.containsKey(tmpChild.getUniqueSmiles())) { tmpChildFragment = tmpFragmentHashtable.get(tmpChild.getUniqueSmiles()); } else { tmpChildFragment = tmpChild; } - if(tmpNewFragmentsOfMol.stream().anyMatch(x -> x.getUniqueSmiles().equals(tmpChildFragment.getUniqueSmiles()))) { + if (tmpNewFragmentsOfMol.stream().anyMatch(x -> x.getUniqueSmiles().equals(tmpChildFragment.getUniqueSmiles()))) { tmpChildFragment.getParentMolecules().add(tmpMolecule); tmpNewFragmentFrequenciesOfMol.replace( tmpChildFragment.getUniqueSmiles(), - tmpNewFragmentFrequenciesOfMol.get(tmpChildFragment.getUniqueSmiles()) + tmpParentFragment.getFragmentFrequencyOfSpecificAlgorithm(tmpPipelineFragmentationName).get(tmpChildFragment.getUniqueSmiles()) + tmpNewFragmentFrequenciesOfMol.get(tmpChildFragment.getUniqueSmiles()) + tmpParentFragment.getFragmentFrequencyOfSpecificFragmentation(tmpPipelineFragmentationName).get(tmpChildFragment.getUniqueSmiles()) ); } else { tmpChildFragment.getParentMolecules().clear(); @@ -365,17 +368,16 @@ public void startPipelineFragmentation(List aListOfMolecules, tmpNewFragmentsOfMol.add(tmpChildFragment); tmpNewFragmentFrequenciesOfMol.put( tmpChildFragment.getUniqueSmiles(), - tmpMolecule.getFragmentFrequencyOfSpecificAlgorithm(tmpPipelineFragmentationName).get(tmpParentFragment.getUniqueSmiles()) * - tmpParentFragment.getFragmentFrequencyOfSpecificAlgorithm(tmpPipelineFragmentationName).get(tmpChildFragment.getUniqueSmiles()) + tmpMolecule.getFragmentFrequencyOfSpecificFragmentation(tmpPipelineFragmentationName).get(tmpParentFragment.getUniqueSmiles()) * + tmpParentFragment.getFragmentFrequencyOfSpecificFragmentation(tmpPipelineFragmentationName).get(tmpChildFragment.getUniqueSmiles()) ); - } //if HashTable for resulting fragments contains fragment, update frequencies = add molecules fragment frequency of fragment to absolute frequency of fragment and increment molecule frequency - if(this.fragments.containsKey(tmpChildFragment.getUniqueSmiles())) { + if (this.fragments.containsKey(tmpChildFragment.getUniqueSmiles())) { this.fragments.get(tmpChildFragment.getUniqueSmiles()).setAbsoluteFrequency( this.fragments.get(tmpChildFragment.getUniqueSmiles()).getAbsoluteFrequency() + - (tmpMolecule.getFragmentFrequencyOfSpecificAlgorithm(tmpPipelineFragmentationName).get(tmpParentFragment.getUniqueSmiles()) * - tmpParentFragment.getFragmentFrequencyOfSpecificAlgorithm(tmpPipelineFragmentationName).get(tmpChildFragment.getUniqueSmiles()) + (tmpMolecule.getFragmentFrequencyOfSpecificFragmentation(tmpPipelineFragmentationName).get(tmpParentFragment.getUniqueSmiles()) * + tmpParentFragment.getFragmentFrequencyOfSpecificFragmentation(tmpPipelineFragmentationName).get(tmpChildFragment.getUniqueSmiles()) ) ); this.fragments.get(tmpChildFragment.getUniqueSmiles()).incrementMoleculeFrequency(); @@ -384,8 +386,8 @@ public void startPipelineFragmentation(List aListOfMolecules, else { this.fragments.put(tmpChildFragment.getUniqueSmiles(), tmpChildFragment); tmpChildFragment.setAbsoluteFrequency( - tmpMolecule.getFragmentFrequencyOfSpecificAlgorithm(tmpPipelineFragmentationName).get(tmpParentFragment.getUniqueSmiles()) * - tmpParentFragment.getFragmentFrequencyOfSpecificAlgorithm(tmpPipelineFragmentationName).get(tmpChildFragment.getUniqueSmiles()) + tmpMolecule.getFragmentFrequencyOfSpecificFragmentation(tmpPipelineFragmentationName).get(tmpParentFragment.getUniqueSmiles()) * + tmpParentFragment.getFragmentFrequencyOfSpecificFragmentation(tmpPipelineFragmentationName).get(tmpChildFragment.getUniqueSmiles()) ); tmpChildFragment.setMoleculeFrequency(1); } @@ -399,26 +401,31 @@ public void startPipelineFragmentation(List aListOfMolecules, } int tmpFragmentAmount = 0; Set tmpKeySet = this.fragments.keySet(); - for(String tmpKey : tmpKeySet){ + for (String tmpKey : tmpKeySet) { tmpFragmentAmount += this.fragments.get(tmpKey).getAbsoluteFrequency(); } - for(String tmpKey : tmpKeySet){ - this.fragments.get(tmpKey).setAbsolutePercentage(1.0 * this.fragments.get(tmpKey).getAbsoluteFrequency() / tmpFragmentAmount); - this.fragments.get(tmpKey).setMoleculePercentage(1.0 * this.fragments.get(tmpKey).getMoleculeFrequency() / aListOfMolecules.size()); + if (tmpFragmentAmount != 0) { + for (String tmpKey : tmpKeySet) { + this.fragments.get(tmpKey).setAbsolutePercentage(1.0 * this.fragments.get(tmpKey).getAbsoluteFrequency() / tmpFragmentAmount); + this.fragments.get(tmpKey).setMoleculePercentage(1.0 * this.fragments.get(tmpKey).getMoleculeFrequency() / aListOfMolecules.size()); + } + } else { + FragmentationService.LOGGER.log(Level.WARNING, "Sum of absolute frequencies of fragments was 0! Percentages could not be calculated."); } - LOGGER.info("Number of different fragments extracted: " + this.fragments.size()); + FragmentationService.LOGGER.log(Level.INFO, "Number of different fragments extracted: {0}", this.fragments.size()); } // /** * Under construction * Start fragmentation pipeline * Fragmentation will be done molecule by molecule - * TODO: After adapting the data models, this method must be modified so that the resulting fragments are kept separate for each molecule. Note the setting keepLastFragment * * @param aListOfMolecules List {@literal <}MoleculeDataModel {@literal >} * @param aNumberOfTasks int * @throws Exception if anything unexpected happen + * @deprecated After adapting the data models, this method must be modified so that the resulting fragments are kept separate for each molecule. Note the setting keepLastFragment */ + @Deprecated public void startPipelineFragmentationMolByMol(List aListOfMolecules, int aNumberOfTasks) throws Exception{ // Objects.requireNonNull(aListOfMolecules, "aListOfMolecules must not be null"); @@ -428,7 +435,6 @@ public void startPipelineFragmentationMolByMol(List aListOfMo } // this.fragments = new Hashtable<>(aListOfMolecules.size() * this.pipelineFragmenter.length); - Hashtable tmpFragmentHashtable = null; if(this.pipeliningFragmentationName == null || this.pipeliningFragmentationName.isEmpty()){ this.pipeliningFragmentationName = "Pipeline"; } @@ -439,25 +445,26 @@ public void startPipelineFragmentationMolByMol(List aListOfMo this.fragments = this.startFragmentation(aListOfMolecules, aNumberOfTasks, this.pipelineFragmenter[0], tmpPipelineFragmentationName); for(int i = 1; i < this.pipelineFragmenter.length; i++){ - String tmpFragmentationName = this.createAndCheckFragmentationName(tmpPipelineFragmentationName + "_" + this.pipelineFragmenter[i].getFragmentationAlgorithmName()); + String tmpFragmentationName = this.createAndCheckFragmentationName(tmpPipelineFragmentationName + "_" + this.pipelineFragmenter[i].getFragmentationAlgorithmDisplayName()); for(MoleculeDataModel tmpParentMol : aListOfMolecules){ if(!tmpParentMol.hasMoleculeUndergoneSpecificFragmentation(tmpPipelineFragmentationName)){ continue; } - List tmpChildMols = (List)(List) tmpParentMol.getFragmentsOfSpecificAlgorithm(tmpPipelineFragmentationName); - tmpFragmentHashtable = this.startFragmentation(tmpChildMols, aNumberOfTasks, this.pipelineFragmenter[i], tmpFragmentationName); + List tmpChildMols = (List)(List) tmpParentMol.getFragmentsOfSpecificFragmentation(tmpPipelineFragmentationName); + Hashtable tmpFragmentHashtable = this.startFragmentation( + tmpChildMols, aNumberOfTasks, this.pipelineFragmenter[i], tmpFragmentationName); Set tmpKeySet = tmpFragmentHashtable.keySet(); LinkedList tmpFrags = new LinkedList<>(); for(String tmpKey : tmpKeySet){ tmpFrags.add(tmpFragmentHashtable.get(tmpKey)); } - HashMap tmpNewFrequencies = new HashMap<>(tmpParentMol.getFragmentsOfSpecificAlgorithm(tmpPipelineFragmentationName).size()*2); + HashMap tmpNewFrequencies = new HashMap<>(tmpParentMol.getFragmentsOfSpecificFragmentation(tmpPipelineFragmentationName).size()*2); for(MoleculeDataModel tmpChildMol : tmpChildMols){ if(!tmpChildMol.hasMoleculeUndergoneSpecificFragmentation(tmpFragmentationName)){ continue; } - for(FragmentDataModel tmpFrag : tmpChildMol.getFragmentsOfSpecificAlgorithm(tmpFragmentationName)){ + for(FragmentDataModel tmpFrag : tmpChildMol.getFragmentsOfSpecificFragmentation(tmpFragmentationName)){ String tmpKey; try{ tmpKey = ChemUtil.createUniqueSmiles(tmpFrag.getAtomContainer()); @@ -469,18 +476,18 @@ public void startPipelineFragmentationMolByMol(List aListOfMo tmpNewFrequencies.replace( tmpKey, tmpNewFrequencies.get(tmpKey) + - tmpChildMol.getFragmentFrequencyOfSpecificAlgorithm(tmpFragmentationName).get(tmpKey) * - tmpParentMol.getFragmentFrequencyOfSpecificAlgorithm(tmpPipelineFragmentationName).get(ChemUtil.createUniqueSmiles(tmpChildMol.getAtomContainer())) + tmpChildMol.getFragmentFrequencyOfSpecificFragmentation(tmpFragmentationName).get(tmpKey) * + tmpParentMol.getFragmentFrequencyOfSpecificFragmentation(tmpPipelineFragmentationName).get(ChemUtil.createUniqueSmiles(tmpChildMol.getAtomContainer())) ); - }else{ + } else { tmpNewFrequencies.put( tmpKey, - tmpChildMol.getFragmentFrequencyOfSpecificAlgorithm(tmpFragmentationName).get(tmpKey) * - tmpParentMol.getFragmentFrequencyOfSpecificAlgorithm(tmpPipelineFragmentationName).get(ChemUtil.createUniqueSmiles(tmpChildMol.getAtomContainer())) + tmpChildMol.getFragmentFrequencyOfSpecificFragmentation(tmpFragmentationName).get(tmpKey) * + tmpParentMol.getFragmentFrequencyOfSpecificFragmentation(tmpPipelineFragmentationName).get(ChemUtil.createUniqueSmiles(tmpChildMol.getAtomContainer())) ); } - } catch(CDKException anException){ - FragmentationService.LOGGER.getLogger(MoleculeDataModel.class.getName()).log(Level.SEVERE, anException.toString() + "_" + tmpFrag.getName(), anException); + } catch(CDKException anException) { + Logger.getLogger(MoleculeDataModel.class.getName()).log(Level.SEVERE, String.format("%s fragment name: %s", anException.toString(), tmpFrag.getName()), anException); } } } @@ -490,28 +497,26 @@ public void startPipelineFragmentationMolByMol(List aListOfMo } Hashtable tmpFragmentsHash = new Hashtable<>(this.fragments.size() * this.pipelineFragmenter.length); for(MoleculeDataModel tmpMol : aListOfMolecules){ - if(!tmpMol.hasMoleculeUndergoneSpecificFragmentation(tmpPipelineFragmentationName)){ + if (!tmpMol.hasMoleculeUndergoneSpecificFragmentation(tmpPipelineFragmentationName)) { continue; } - for(FragmentDataModel tmpFrag : tmpMol.getFragmentsOfSpecificAlgorithm(tmpPipelineFragmentationName)) - { + for(FragmentDataModel tmpFrag : tmpMol.getFragmentsOfSpecificFragmentation(tmpPipelineFragmentationName)) { String tmpKey; - try{ + try { tmpKey = ChemUtil.createUniqueSmiles(tmpFrag.getAtomContainer()); } catch (CDKException anException){ - FragmentationService.LOGGER.getLogger(MoleculeDataModel.class.getName()).log(Level.SEVERE, anException.toString() + "_" + tmpFrag.getName(), anException); + Logger.getLogger(MoleculeDataModel.class.getName()).log(Level.SEVERE, String.format("%s fragment name: %s", anException.toString(), tmpFrag.getName()), anException); continue; } - if(!tmpMol.hasMoleculeUndergoneSpecificFragmentation(tmpPipelineFragmentationName)){ + if (!tmpMol.hasMoleculeUndergoneSpecificFragmentation(tmpPipelineFragmentationName)) { continue; } - if(!tmpFragmentsHash.containsKey(tmpKey)){ + if (!tmpFragmentsHash.containsKey(tmpKey)) { tmpFragmentsHash.put(tmpKey, tmpFrag); - tmpFrag.setAbsoluteFrequency(tmpMol.getFragmentFrequencyOfSpecificAlgorithm(tmpPipelineFragmentationName).get(tmpKey)); + tmpFrag.setAbsoluteFrequency(tmpMol.getFragmentFrequencyOfSpecificFragmentation(tmpPipelineFragmentationName).get(tmpKey)); tmpFrag.setMoleculeFrequency(1); - } - else{ - tmpFragmentsHash.get(tmpKey).setAbsoluteFrequency(tmpFragmentsHash.get(tmpKey).getAbsoluteFrequency() + tmpMol.getFragmentFrequencyOfSpecificAlgorithm(tmpPipelineFragmentationName).get(tmpKey)); + } else { + tmpFragmentsHash.get(tmpKey).setAbsoluteFrequency(tmpFragmentsHash.get(tmpKey).getAbsoluteFrequency() + tmpMol.getFragmentFrequencyOfSpecificFragmentation(tmpPipelineFragmentationName).get(tmpKey)); tmpFragmentsHash.get(tmpKey).incrementMoleculeFrequency(); } } @@ -522,9 +527,13 @@ public void startPipelineFragmentationMolByMol(List aListOfMo for(String tmpKey : tmpKeySet){ tmpFragmentAmount += this.fragments.get(tmpKey).getAbsoluteFrequency(); } - for(String tmpKey : tmpKeySet){ - this.fragments.get(tmpKey).setAbsolutePercentage(1.0 * this.fragments.get(tmpKey).getAbsoluteFrequency() / tmpFragmentAmount); - this.fragments.get(tmpKey).setMoleculePercentage(1.0 * this.fragments.get(tmpKey).getMoleculeFrequency() / aListOfMolecules.size()); + if (tmpFragmentAmount != 0) { + for (String tmpKey : tmpKeySet) { + this.fragments.get(tmpKey).setAbsolutePercentage(1.0 * this.fragments.get(tmpKey).getAbsoluteFrequency() / tmpFragmentAmount); + this.fragments.get(tmpKey).setMoleculePercentage(1.0 * this.fragments.get(tmpKey).getMoleculeFrequency() / aListOfMolecules.size()); + } + } else { + FragmentationService.LOGGER.log(Level.WARNING, "Sum of absolute frequencies of fragments was 0! Percentages could not be calculated."); } } // @@ -550,14 +559,15 @@ public IMoleculeFragmenter createNewFragmenterObjectByAlgorithmName(String anAlg IllegalAccessException { String tmpClassName = ""; for (IMoleculeFragmenter tmpFragmenter : this.fragmenters) { - if (anAlgorithmName.equals(tmpFragmenter.getFragmentationAlgorithmName())) - tmpClassName = tmpFragmenter.getClass().getName(); + if (anAlgorithmName.equals(tmpFragmenter.getFragmentationAlgorithmName())) { + tmpClassName = tmpFragmenter.getClass().getName(); + } } - if(tmpClassName.isBlank() || tmpClassName.isEmpty()){ - throw new IllegalArgumentException("Given algorithm name " + anAlgorithmName + " is invalid."); + if (tmpClassName.isBlank() || tmpClassName.isEmpty()) { + throw new IllegalArgumentException(String.format("Given algorithm name %s is invalid.", anAlgorithmName)); } - Class tmpClazz = Class.forName(tmpClassName); - Constructor tmpCtor = tmpClazz.getConstructor(); + Class tmpClazz = Class.forName(tmpClassName); + Constructor tmpCtor = tmpClazz.getConstructor(); return (IMoleculeFragmenter) tmpCtor.newInstance(); } // @@ -571,12 +581,13 @@ public void persistFragmenterSettings() { String tmpDirectoryPath = FileUtil.getSettingsDirPath() + FragmentationService.FRAGMENTER_SETTINGS_SUBFOLDER_NAME + File.separator; File tmpDirectory = new File(tmpDirectoryPath); + boolean tmpMKDirsSuccessful = true; if (!tmpDirectory.exists()) { - tmpDirectory.mkdirs(); + tmpMKDirsSuccessful = tmpDirectory.mkdirs(); } else { FileUtil.deleteAllFilesInDirectory(tmpDirectoryPath); } - if (!tmpDirectory.canWrite()) { + if (!tmpDirectory.canWrite() || !tmpMKDirsSuccessful) { GuiUtil.guiMessageAlert(Alert.AlertType.ERROR, Message.get("Error.ExceptionAlert.Title"), Message.get("Error.ExceptionAlert.Header"), Message.get("FragmentationService.Error.settingsPersistence")); @@ -586,7 +597,7 @@ public void persistFragmenterSettings() { if (Objects.isNull(tmpFragmenter)) { continue; } - List tmpSettings = tmpFragmenter.settingsProperties(); + List> tmpSettings = tmpFragmenter.settingsProperties(); if (Objects.isNull(tmpSettings)) { continue; } @@ -597,12 +608,12 @@ public void persistFragmenterSettings() { PreferenceContainer tmpPrefContainer = PreferenceUtil.translateJavaFxPropertiesToPreferences(tmpSettings, tmpFilePath); tmpPrefContainer.writeRepresentation(); } catch (NullPointerException | IllegalArgumentException | IOException | SecurityException anException) { - FragmentationService.LOGGER.log(Level.WARNING, "Fragmenter settings persistence went wrong, exception: " + anException.toString(), anException); + FragmentationService.LOGGER.log(Level.WARNING, String.format("Fragmenter settings persistence went wrong, exception: %s", anException.toString()), anException); GuiUtil.guiExceptionAlert(Message.get("Error.ExceptionAlert.Title"), Message.get("Error.ExceptionAlert.Header"), Message.get("FragmentationService.Error.settingsPersistence"), anException); - continue; + //continue; } } } @@ -616,12 +627,13 @@ public void persistSelectedFragmenterAndPipeline() { String tmpFragmentationServiceSettingsPath = FileUtil.getSettingsDirPath() + FragmentationService.FRAGMENTATION_SERVICE_SETTINGS_SUBFOLDER_NAME + File.separator; File tmpFragmentationServiceSettingsDir = new File(tmpFragmentationServiceSettingsPath); + boolean tmpMKDirsSuccessful = true; if (!tmpFragmentationServiceSettingsDir.exists()) { - tmpFragmentationServiceSettingsDir.mkdirs(); + tmpMKDirsSuccessful = tmpFragmentationServiceSettingsDir.mkdirs(); } else { FileUtil.deleteAllFilesInDirectory(tmpFragmentationServiceSettingsPath); } - if (!tmpFragmentationServiceSettingsDir.canWrite()) { + if (!tmpFragmentationServiceSettingsDir.canWrite() || !tmpMKDirsSuccessful) { GuiUtil.guiMessageAlert(Alert.AlertType.ERROR, Message.get("Error.ExceptionAlert.Title"), Message.get("Error.ExceptionAlert.Header"), @@ -652,8 +664,7 @@ public void persistSelectedFragmenterAndPipeline() { this.pipeliningFragmentationName = FragmentationService.DEFAULT_PIPELINE_NAME; } if (!SingleTermPreference.isValidContent(this.pipeliningFragmentationName)) { - FragmentationService.LOGGER.log(Level.WARNING, "Given pipeline name " + this.pipeliningFragmentationName - + " is invalid, will be reset to default."); + FragmentationService.LOGGER.log(Level.WARNING, "Given pipeline name {0} is invalid, will be reset to default.", this.pipeliningFragmentationName); this.pipeliningFragmentationName = FragmentationService.DEFAULT_PIPELINE_NAME; } SingleTermPreference tmpPipelineNamePreference = new SingleTermPreference(FragmentationService.PIPELINE_SETTING_NAME, @@ -664,7 +675,7 @@ public void persistSelectedFragmenterAndPipeline() { try { tmpFragmentationServiceSettingsContainer.writeRepresentation(); } catch (IOException | SecurityException anException) { - FragmentationService.LOGGER.log(Level.WARNING, "Fragmentation service settings persistence went wrong, exception: " + anException.toString(), anException); + FragmentationService.LOGGER.log(Level.WARNING, String.format("Fragmentation service settings persistence went wrong, exception: %s", anException.toString()), anException); GuiUtil.guiExceptionAlert(Message.get("Error.ExceptionAlert.Title"), Message.get("Error.ExceptionAlert.Header"), Message.get("FragmentationService.Error.settingsPersistence"), @@ -672,7 +683,7 @@ public void persistSelectedFragmenterAndPipeline() { } for (int i = 0; i < this.pipelineFragmenter.length; i++) { IMoleculeFragmenter tmpFragmenter = this.pipelineFragmenter[i]; - List tmpSettings = tmpFragmenter.settingsProperties(); + List> tmpSettings = tmpFragmenter.settingsProperties(); String tmpFilePath = tmpFragmentationServiceSettingsPath + FragmentationService.PIPELINE_FRAGMENTER_FILE_NAME_PREFIX + i + BasicDefinitions.PREFERENCE_CONTAINER_FILE_EXTENSION; @@ -682,18 +693,18 @@ public void persistSelectedFragmenterAndPipeline() { try { tmpPrefContainer.writeRepresentation(); } catch (IOException | SecurityException anException) { - FragmentationService.LOGGER.log(Level.WARNING, "Pipeline fragmenter settings persistence went wrong, exception: " + anException.toString(), anException); + FragmentationService.LOGGER.log(Level.WARNING, String.format("Pipeline fragmenter settings persistence went wrong, exception: %s", anException.toString()), anException); GuiUtil.guiExceptionAlert(Message.get("Error.ExceptionAlert.Title"), Message.get("Error.ExceptionAlert.Header"), Message.get("FragmentationService.Error.settingsPersistence"), anException); - continue; + //continue; } } } /** - * Reloads settings of the available fragmenters. If something goes wrong, it is logged. + * Reloads settings of the available fragmenters. If something goes wrong, it is logged and the setting remains in default. */ public void reloadFragmenterSettings() { String tmpDirectoryPath = FileUtil.getSettingsDirPath() @@ -708,13 +719,13 @@ public void reloadFragmenterSettings() { try { tmpContainer = new PreferenceContainer(tmpFragmenterSettingsFile); } catch (IllegalArgumentException | IOException anException) { - FragmentationService.LOGGER.log(Level.WARNING, "Unable to reload settings of fragmenter " + tmpClassName + " : " + anException.toString(), anException); + FragmentationService.LOGGER.log(Level.WARNING, String.format("Unable to reload settings of fragmenter %s : %s", tmpClassName, anException.toString()), anException); continue; } - this.updatePropertiesFromPreferences(tmpFragmenter.settingsProperties(), tmpContainer); + PreferenceUtil.updatePropertiesFromPreferences(tmpFragmenter.settingsProperties(), tmpContainer); } else { //settings will remain in default - FragmentationService.LOGGER.log(Level.WARNING, "No persisted settings for " + tmpClassName + " available."); + FragmentationService.LOGGER.log(Level.WARNING, "No persisted settings for {0} available.", tmpClassName); } } } @@ -732,7 +743,7 @@ public void reloadActiveFragmenterAndPipeline() { File tmpServiceSettingsFile = new File(tmpServiceSettingsFilePath); if (tmpServiceSettingsFile.exists() && tmpServiceSettingsFile.isFile() && tmpServiceSettingsFile.canRead()) { PreferenceContainer tmpFragmentationServiceSettingsContainer; - int tmpPipelineSize = 0; + int tmpPipelineSize; try { tmpFragmentationServiceSettingsContainer = new PreferenceContainer(tmpServiceSettingsFile); tmpPipelineSize = ((SingleIntegerPreference)tmpFragmentationServiceSettingsContainer.getPreferences(FragmentationService.PIPELINE_SIZE_SETTING_NAME)[0]).getContent(); @@ -742,19 +753,19 @@ public void reloadActiveFragmenterAndPipeline() { for (IMoleculeFragmenter tmpFragmenter : this.fragmenters) { if (tmpFragmenter.getFragmentationAlgorithmName().equals(tmpSelectedFragmenterAlgorithmName)) { this.selectedFragmenter = tmpFragmenter; - this.selectedFragmenterNameProperty.set(this.selectedFragmenter.getFragmentationAlgorithmName()); + this.selectedFragmenterDisplayNameProperty.set(this.selectedFragmenter.getFragmentationAlgorithmDisplayName()); break; } } } catch (IllegalArgumentException | IOException anException) { - FragmentationService.LOGGER.log(Level.WARNING, "FragmentationService settings reload failed: " + anException.toString(), anException); + FragmentationService.LOGGER.log(Level.WARNING, String.format("FragmentationService settings reload failed: %s", anException.toString()), anException); GuiUtil.guiExceptionAlert(Message.get("Error.ExceptionAlert.Title"), Message.get("Error.ExceptionAlert.Header"), Message.get("FragmentationService.Error.settingsReload"), anException); return; } - List tmpFragmenterList = new ArrayList(tmpPipelineSize); + List tmpFragmenterList = new ArrayList<>(tmpPipelineSize); for (int i = 0; i < tmpPipelineSize; i++) { String tmpPath = tmpFragmentationServiceSettingsPath + FragmentationService.PIPELINE_FRAGMENTER_FILE_NAME_PREFIX + i + BasicDefinitions.PREFERENCE_CONTAINER_FILE_EXTENSION; File tmpFragmenterFile = new File(tmpPath); @@ -763,20 +774,19 @@ public void reloadActiveFragmenterAndPipeline() { PreferenceContainer tmpFragmenterSettingsContainer = new PreferenceContainer(tmpFragmenterFile); String tmpFragmenterClassName = tmpFragmenterSettingsContainer.getPreferences(FragmentationService.PIPELINE_FRAGMENTER_ALGORITHM_NAME_SETTING_NAME)[0].getContentRepresentative(); IMoleculeFragmenter tmpFragmenter = this.createNewFragmenterObjectByAlgorithmName(tmpFragmenterClassName); - this.updatePropertiesFromPreferences(tmpFragmenter.settingsProperties(), tmpFragmenterSettingsContainer); + PreferenceUtil.updatePropertiesFromPreferences(tmpFragmenter.settingsProperties(), tmpFragmenterSettingsContainer); tmpFragmenterList.add(tmpFragmenter); } catch (Exception anException) { - FragmentationService.LOGGER.log(Level.WARNING, "FragmentationService settings reload failed: " + anException.toString(), anException); + FragmentationService.LOGGER.log(Level.WARNING, String.format("FragmentationService settings reload failed: %s", anException.toString()), anException); GuiUtil.guiExceptionAlert(Message.get("Error.ExceptionAlert.Title"), Message.get("Error.ExceptionAlert.Header"), Message.get("FragmentationService.Error.settingsReload"), anException); - continue; + //continue; } } else { - FragmentationService.LOGGER.log(Level.WARNING, "Unable to reload pipeline fragmenter " + i - + " : No respective file available. Will be skipped."); - continue; + FragmentationService.LOGGER.log(Level.WARNING, "Unable to reload pipeline fragmenter {0} : No respective file available. Will be skipped.", i); + //continue; } } IMoleculeFragmenter[] tmpFragmenterArray = tmpFragmenterList.toArray(new IMoleculeFragmenter[tmpFragmenterList.size()]); @@ -787,25 +797,27 @@ public void reloadActiveFragmenterAndPipeline() { } /** - * Shuts down executor service - * Used as recommended by oracle (https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/concurrent/ExecutorService.html) + * Shuts down executor service. + * Used as recommended by oracle. */ - public void abortExecutor(){ + public void abortExecutor() { this.executorService.shutdown(); try { if (!this.executorService.awaitTermination(600, TimeUnit.MILLISECONDS)) { this.executorService.shutdownNow(); } } catch (InterruptedException e) { + FragmentationService.LOGGER.log(Level.WARNING, "Interrupted!", e); this.executorService.shutdownNow(); + Thread.currentThread().interrupt(); } } /** - * Clears all cached variables like existingFragmentations and fragments + * Clears all cached variables like existingFragmentations and fragments. */ - public void clearCache(){ - this.existingFragmentations = new LinkedList();; + public void clearCache() { + this.existingFragmentations = new LinkedList<>(); this.fragments = null; this.currentFragmentationName = null; } @@ -813,112 +825,116 @@ public void clearCache(){ // // /** - * Returns array of {@link IMoleculeFragmenter} + * Returns array of {@link IMoleculeFragmenter}. + * * @return fragmenters */ - public IMoleculeFragmenter[] getFragmenters(){ + public IMoleculeFragmenter[] getFragmenters() { return this.fragmenters; } /** - * Returns array of {@link IMoleculeFragmenter} for pipelining + * Returns array of {@link IMoleculeFragmenter} for pipelining. + * * @return pipelineFragmenters */ - public IMoleculeFragmenter[] getPipelineFragmenter(){ + public IMoleculeFragmenter[] getPipelineFragmenter() { return this.pipelineFragmenter; } /** - * Returns selected {@link IMoleculeFragmenter} + * Returns selected {@link IMoleculeFragmenter}. * * @return selectedFragmenter */ - public IMoleculeFragmenter getSelectedFragmenter(){ + public IMoleculeFragmenter getSelectedFragmenter() { return this.selectedFragmenter; } /** - * Returns Hashtable of {@link FragmentDataModel} + * Returns Map of unique SMILES codes of fragments linked to the respective {@link FragmentDataModel} instances. + * Returned map is a synchronized HashTable. * * @return fragments (results of fragmentation) */ - public Hashtable getFragments(){ + public Map getFragments() { return this.fragments; } /** - * Returns name of the running fragmentation + * Returns name of the running fragmentation. * * @return currentFragmentation */ - public String getCurrentFragmentationName(){ + public String getCurrentFragmentationName() { return this.currentFragmentationName; } /** - * Returns name of the current pipeline configuration + * Returns name of the current pipeline configuration. * * @return pipeline name */ - public String getPipeliningFragmentationName(){ + public String getPipeliningFragmentationName() { return this.pipeliningFragmentationName; } /** - * Sets the selectedFragmenter. + * Sets the selectedFragmenter using the display name. * - * @param anAlgorithmName must be retrieved using the respective method of the fragmenter object + * @param anAlgorithmDisplayName must be retrieved using the respective method of the fragmenter object */ - public void setSelectedFragmenter(String anAlgorithmName){ + public void setSelectedFragmenter(String anAlgorithmDisplayName) { for (IMoleculeFragmenter tmpFragmenter : this.fragmenters) { - if (anAlgorithmName.equals(tmpFragmenter.getFragmentationAlgorithmName())) + if (anAlgorithmDisplayName.equals(tmpFragmenter.getFragmentationAlgorithmDisplayName())) this.selectedFragmenter = tmpFragmenter; } } /** - * Sets the fragmenters to use for pipeline fragmentation + * Sets the fragmenters to use for pipeline fragmentation. * * @param anArrayOfFragmenter IMolecueFragmenter[] */ - public void setPipelineFragmenter(IMoleculeFragmenter[] anArrayOfFragmenter){ + public void setPipelineFragmenter(IMoleculeFragmenter[] anArrayOfFragmenter) { this.pipelineFragmenter = anArrayOfFragmenter; } /** - * Sets the pipeline name + * Sets the pipeline name. * * @param aName pipeline name */ - public void setPipeliningFragmentationName(String aName){ + public void setPipeliningFragmentationName(String aName) { this.pipeliningFragmentationName = aName; } /** - * Gets the name of the selected fragmenter + * Gets the display name of the selected fragmenter. * * @return String */ - public String getSelectedFragmenterNameProperty() { - return this.selectedFragmenterNameProperty.get(); + public String getSelectedFragmenterDisplayName() { + return this.selectedFragmenterDisplayNameProperty.get(); } /** - * Returns the property of the name of the selected fragmenter + * Returns the property of the display name of the selected fragmenter. * * @return SimpleStringProperty */ - public SimpleStringProperty selectedFragmenterNamePropertyProperty() { - return this.selectedFragmenterNameProperty; + public SimpleStringProperty selectedFragmenterDisplayNameProperty() { + return this.selectedFragmenterDisplayNameProperty; } /** - * Sets the name of the selected fragmenter + * Sets the display name of the selected fragmenter. * - * @param aFragmenterName String for the name of the fragmenter + * @param aFragmenterDisplayName String for the display name of the fragmenter */ - public void setSelectedFragmenterNameProperty(String aFragmenterName) { - this.selectedFragmenterNameProperty.set(aFragmenterName); + public void setSelectedFragmenterDisplayName(String aFragmenterDisplayName) { + this.selectedFragmenterDisplayNameProperty.set(aFragmenterDisplayName); } // // // /** - * Checks whether the name exists, if so, a consecutive number is appended, if not, the name is returned unchanged + * Checks whether the name exists, if so, a consecutive number is appended, if not, the name is returned unchanged. + * * @param anAlgorithmName String * @return String algorithm name */ - private String createAndCheckFragmentationName(String anAlgorithmName){ + private String createAndCheckFragmentationName(String anAlgorithmName) { String tmpFragmentationName = anAlgorithmName; if(this.existingFragmentations.contains(tmpFragmentationName)){ int tmpIndex = 0; @@ -932,24 +948,25 @@ private String createAndCheckFragmentationName(String anAlgorithmName){ // /** * Manages the fragmentation, creates {@link FragmentationTask} equal to the amount of {@param aNumberOfTasks}, - * assigns the molecules of {@param aListOfMolecules} to them and starts the fragmentation + * assigns the molecules of {@param aListOfMolecules} to them and starts the fragmentation. * - * @param aListOfMolecules - * @param aNumberOfTasks - * @throws Exception + * @param aListOfMolecules molecules to fragment and to assign the fragments to + * @param aNumberOfTasks number of parallel tasks to use for the process + * @param aFragmenter fragmenter instance to use to fragment, will be copied for the parallel tasks + * @param aFragmentationName name under which to store the fragmentation results on the molecules + * @throws Exception if anything goes wrong */ private Hashtable startFragmentation(List aListOfMolecules, int aNumberOfTasks, IMoleculeFragmenter aFragmenter, String aFragmentationName) throws Exception { - if(aListOfMolecules.size() == 0 || aNumberOfTasks == 0){ + if (aListOfMolecules.isEmpty() || aNumberOfTasks == 0) { return new Hashtable<>(0); } int tmpNumberOfTasks = aNumberOfTasks; - String tmpFragmentationName = aFragmentationName; Hashtable tmpFragmentHashtable = new Hashtable<>(aListOfMolecules.size() * 2); - if(aListOfMolecules.size() < tmpNumberOfTasks){ + if (aListOfMolecules.size() < tmpNumberOfTasks) { tmpNumberOfTasks = aListOfMolecules.size(); } int tmpMoleculesPerTask = aListOfMolecules.size() / tmpNumberOfTasks; @@ -970,29 +987,29 @@ protected void afterExecute(Runnable r, Throwable t) { } }; */ List tmpFragmentationTaskList = new LinkedList<>(); - for(int i = 1; i <= tmpNumberOfTasks; i++){ + for (int i = 1; i <= tmpNumberOfTasks; i++) { List tmpMoleculesForTask = aListOfMolecules.subList(tmpFromIndex, tmpToIndex); IMoleculeFragmenter tmpFragmenterForTask = aFragmenter.copy(); - tmpFragmentationTaskList.add(new FragmentationTask(tmpMoleculesForTask, tmpFragmenterForTask, tmpFragmentHashtable, tmpFragmentationName)); + tmpFragmentationTaskList.add(new FragmentationTask(tmpMoleculesForTask, tmpFragmenterForTask, tmpFragmentHashtable, aFragmentationName)); tmpFromIndex = tmpToIndex; tmpToIndex = tmpFromIndex + tmpMoleculesPerTask; if(tmpMoleculeModulo > 0){ tmpToIndex++; tmpMoleculeModulo--; } - if(i == tmpNumberOfTasks - 1 ){ + if (i == tmpNumberOfTasks - 1 ) { tmpToIndex = aListOfMolecules.size(); } } List> tmpFuturesList; long tmpMemoryConsumption = (Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory()) / (1024*1024); - FragmentationService.LOGGER.info("Fragmentation \"" + tmpFragmentationName - + "\" starting. Current memory consumption: " + tmpMemoryConsumption + " MB"); + FragmentationService.LOGGER.log(Level.INFO, "Fragmentation \"{0}\" ({1}) starting. Current memory consumption: {2} MB", + new Object[]{aFragmentationName, aFragmenter.getFragmentationAlgorithmDisplayName(), tmpMemoryConsumption}); long tmpStartTime = System.currentTimeMillis(); int tmpExceptionsCounter = 0; tmpFuturesList = this.executorService.invokeAll(tmpFragmentationTaskList); if (this.executorService.isShutdown() || this.executorService.isTerminated()) { - this.LOGGER.log(Level.INFO, "Fragmentation cancelled"); + FragmentationService.LOGGER.log(Level.INFO, "Fragmentation cancelled"); return null; } for (Future tmpFuture : tmpFuturesList) { @@ -1005,8 +1022,9 @@ protected void afterExecute(Runnable r, Throwable t) { //this can occur when the task has been interrupted or cancelled, nothing to do here //errors in execution will be thrown by get() and are checked in the calling method/thread } - } catch (CancellationException | InterruptedException aCancellationOrInterruptionException) { - FragmentationService.LOGGER.log(Level.INFO, aCancellationOrInterruptionException.toString(), aCancellationOrInterruptionException); + } catch (CancellationException | InterruptedException | ExecutionException aCancellationOrInterruptionException) { + FragmentationService.LOGGER.log(Level.WARNING, aCancellationOrInterruptionException.toString(), aCancellationOrInterruptionException); + Thread.currentThread().interrupt(); //continue; } } @@ -1015,111 +1033,68 @@ protected void afterExecute(Runnable r, Throwable t) { for(String tmpKey : tmpKeySet){ tmpFragmentAmount += tmpFragmentHashtable.get(tmpKey).getAbsoluteFrequency(); } - for(String tmpKey : tmpKeySet){ - tmpFragmentHashtable.get(tmpKey).setAbsolutePercentage(1.0 * tmpFragmentHashtable.get(tmpKey).getAbsoluteFrequency() / tmpFragmentAmount); - tmpFragmentHashtable.get(tmpKey).setMoleculePercentage(1.0 * tmpFragmentHashtable.get(tmpKey).getMoleculeFrequency() / aListOfMolecules.size()); + if (tmpFragmentAmount != 0) { + for(String tmpKey : tmpKeySet){ + tmpFragmentHashtable.get(tmpKey).setAbsolutePercentage(1.0 * tmpFragmentHashtable.get(tmpKey).getAbsoluteFrequency() / tmpFragmentAmount); + tmpFragmentHashtable.get(tmpKey).setMoleculePercentage(1.0 * tmpFragmentHashtable.get(tmpKey).getMoleculeFrequency() / aListOfMolecules.size()); + } + } else { + FragmentationService.LOGGER.log(Level.WARNING, "Sum of absolute frequencies of fragments was 0! Percentages could not be calculated."); } - if(tmpExceptionsCounter > 0){ - FragmentationService.LOGGER.log(Level.SEVERE, "Fragmentation \"" + tmpFragmentationName + "\" caused " + tmpExceptionsCounter + " exceptions"); + if (tmpExceptionsCounter > 0) { + FragmentationService.LOGGER.log(Level.WARNING, "Fragmentation \"{0}\" ({1}) caused {2} exceptions", + new Object[]{aFragmentationName, aFragmenter.getFragmentationAlgorithmDisplayName(), tmpExceptionsCounter}); } this.executorService.shutdown(); tmpMemoryConsumption = (Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory()) / (1024*1024); long tmpEndTime = System.currentTimeMillis(); - FragmentationService.LOGGER.info("Fragmentation \"" + tmpFragmentationName + "\" of " + aListOfMolecules.size() - + " molecules complete. It took " + (tmpEndTime - tmpStartTime) + " ms. Current memory consumption: " - + tmpMemoryConsumption + " MB"); + long tmpDuration = tmpEndTime - tmpStartTime; + FragmentationService.LOGGER.log(Level.INFO, + "Fragmentation \"{0}\" ({1}) of {2} molecules complete. It took {3} ms. Current memory consumption: {4} MB", + new Object[]{aFragmentationName, aFragmenter.getFragmentationAlgorithmDisplayName(), aListOfMolecules.size(), tmpDuration, tmpMemoryConsumption}); return tmpFragmentHashtable; } - - /** - * Sets the values of the given properties according to the preferences in the given container with the same name. - * If no matching preference for a given property is found, the value will remain in its default setting. - */ - private void updatePropertiesFromPreferences(List aPropertiesList, PreferenceContainer aPreferenceContainer) { - for (Property tmpSettingProperty : aPropertiesList) { - String tmpPropertyName = tmpSettingProperty.getName(); - if (aPreferenceContainer.containsPreferenceName(tmpPropertyName)) { - IPreference[] tmpPreferences = aPreferenceContainer.getPreferences(tmpPropertyName); - try { - if (tmpSettingProperty instanceof SimpleBooleanProperty) { - BooleanPreference tmpBooleanPreference = (BooleanPreference) tmpPreferences[0]; - tmpSettingProperty.setValue(tmpBooleanPreference.getContent()); - } else if (tmpSettingProperty instanceof SimpleIntegerProperty) { - SingleIntegerPreference tmpIntPreference = (SingleIntegerPreference) tmpPreferences[0]; - tmpSettingProperty.setValue(tmpIntPreference.getContent()); - } else if (tmpSettingProperty instanceof SimpleDoubleProperty) { - SingleNumberPreference tmpDoublePreference = (SingleNumberPreference) tmpPreferences[0]; - tmpSettingProperty.setValue(tmpDoublePreference.getContent()); - } else if (tmpSettingProperty instanceof SimpleEnumConstantNameProperty || tmpSettingProperty instanceof SimpleStringProperty) { - SingleTermPreference tmpStringPreference = (SingleTermPreference) tmpPreferences[0]; - tmpSettingProperty.setValue(tmpStringPreference.getContent()); - } else { - //setting will remain in default - FragmentationService.LOGGER.log(Level.WARNING, "Setting " + tmpPropertyName + " is of unknown type."); - } - } catch (ClassCastException | IllegalArgumentException anException) { - //setting will remain in default - FragmentationService.LOGGER.log(Level.WARNING, anException.toString(), anException); - } - } else { - //setting will remain in default - FragmentationService.LOGGER.log(Level.WARNING, "No persisted settings for " + tmpPropertyName + " available."); - } - } - } - + // /** * Checks the available fragmenters and their settings for restrictions imposed by persistence. Throws an exception if * anything does not meet the requirements. + * - setting names must be singletons + * - setting names and values must adhere to the preference input restrictions + * - setting values are only tested for their current state, not the entire possible input space! It is tested again at persistence + * + * @throws UnsupportedOperationException if a setting does not fulfil the requirements */ - private void checkFragmenters() throws Exception { + private void checkFragmenters() throws UnsupportedOperationException { int tmpAlgorithmNamesSetInitCapacity = CollectionUtil.calculateInitialHashCollectionCapacity(this.fragmenters.length, BasicDefinitions.DEFAULT_HASH_COLLECTION_LOAD_FACTOR); HashSet tmpAlgorithmNamesSet = new HashSet<>(tmpAlgorithmNamesSetInitCapacity, BasicDefinitions.DEFAULT_HASH_COLLECTION_LOAD_FACTOR); + HashSet tmpAlgorithmDisplayNamesSet = new HashSet<>(tmpAlgorithmNamesSetInitCapacity, BasicDefinitions.DEFAULT_HASH_COLLECTION_LOAD_FACTOR); for (IMoleculeFragmenter tmpFragmenter : this.fragmenters) { //algorithm name should be singleton and must be persistable String tmpAlgName = tmpFragmenter.getFragmentationAlgorithmName(); if (!PreferenceUtil.isValidName(tmpAlgName) || !SingleTermPreference.isValidContent(tmpAlgName)) { - throw new Exception("Algorithm name " + tmpAlgName + " is invalid."); + throw new UnsupportedOperationException("Algorithm name " + tmpAlgName + " is invalid."); } if (tmpAlgorithmNamesSet.contains(tmpAlgName)) { - throw new Exception("Algorithm name " + tmpAlgName + " is used multiple times."); + throw new UnsupportedOperationException("Algorithm name " + tmpAlgName + " is used multiple times."); } else { tmpAlgorithmNamesSet.add(tmpAlgName); } + //algorithm display name should be singleton and must be persistable + String tmpAlgDisplayName = tmpFragmenter.getFragmentationAlgorithmDisplayName(); + if (!PreferenceUtil.isValidName(tmpAlgDisplayName) || !SingleTermPreference.isValidContent(tmpAlgDisplayName)) { + throw new UnsupportedOperationException("Algorithm name " + tmpAlgDisplayName + " is invalid."); + } + if (tmpAlgorithmDisplayNamesSet.contains(tmpAlgDisplayName)) { + throw new UnsupportedOperationException("Algorithm name " + tmpAlgDisplayName + " is used multiple times."); + } else { + tmpAlgorithmDisplayNamesSet.add(tmpAlgDisplayName); + } //setting names must be singletons within the respective class //setting names and values must adhere to the preference input restrictions //setting values are only tested for their current state, not the entire possible input space! It is tested again at persistence - List tmpSettingsList = tmpFragmenter.settingsProperties(); - int tmpSettingNamesSetInitCapacity = CollectionUtil.calculateInitialHashCollectionCapacity(tmpSettingsList.size(), BasicDefinitions.DEFAULT_HASH_COLLECTION_LOAD_FACTOR); - HashSet tmpSettingNames = new HashSet<>(tmpSettingNamesSetInitCapacity, BasicDefinitions.DEFAULT_HASH_COLLECTION_LOAD_FACTOR); - for (Property tmpSetting : tmpSettingsList) { - if (!PreferenceUtil.isValidName(tmpSetting.getName())) { - throw new Exception("Setting " + tmpSetting.getName() + " has an invalid name."); - } - if (tmpSettingNames.contains(tmpSetting.getName())) { - throw new Exception("Setting name " + tmpSetting.getName() + " is used multiple times."); - } else { - tmpSettingNames.add(tmpSetting.getName()); - } - if (tmpSetting instanceof SimpleBooleanProperty) { - //nothing to do here, booleans cannot have invalid values - } else if (tmpSetting instanceof SimpleIntegerProperty) { - if (!SingleIntegerPreference.isValidContent(Integer.toString(((SimpleIntegerProperty) tmpSetting).get()))) { - throw new Exception("Setting value " + ((SimpleIntegerProperty) tmpSetting).get() + " of setting name " + tmpSetting.getName() + " is invalid."); - } - } else if (tmpSetting instanceof SimpleDoubleProperty) { - if (!SingleNumberPreference.isValidContent(((SimpleDoubleProperty) tmpSetting).get())) { - throw new Exception("Setting value " + ((SimpleDoubleProperty) tmpSetting).get() + " of setting name " + tmpSetting.getName() + " is invalid."); - } - } else if (tmpSetting instanceof SimpleEnumConstantNameProperty || tmpSetting instanceof SimpleStringProperty) { - if (!SingleTermPreference.isValidContent(((SimpleStringProperty) tmpSetting).get())) { - throw new Exception("Setting value " + ((SimpleStringProperty) tmpSetting).get() + " of setting name " + tmpSetting.getName() + " is invalid."); - } - } else { - throw new Exception("Setting " + tmpSetting.getName() + " is of an invalid type."); - } - } + List> tmpSettingsList = tmpFragmenter.settingsProperties(); + PreferenceUtil.checkPropertiesForPreferenceRestrictions(tmpSettingsList); } } // diff --git a/src/main/java/de/unijena/cheminf/mortar/model/fragmentation/FragmentationTask.java b/src/main/java/de/unijena/cheminf/mortar/model/fragmentation/FragmentationTask.java index c7514420..ed6813b9 100644 --- a/src/main/java/de/unijena/cheminf/mortar/model/fragmentation/FragmentationTask.java +++ b/src/main/java/de/unijena/cheminf/mortar/model/fragmentation/FragmentationTask.java @@ -36,22 +36,21 @@ import java.util.ArrayList; import java.util.HashMap; -import java.util.Hashtable; import java.util.List; +import java.util.Map; import java.util.concurrent.Callable; import java.util.concurrent.locks.ReentrantLock; import java.util.logging.Level; import java.util.logging.Logger; /** - * Callable class to fragment a list of molecules + * Callable class to fragment a list of molecules. * * @author Felix Baensch, Jonas Schaub * @version 1.0.0.0 */ public class FragmentationTask implements Callable { - - // + // /** * Lock to be used when updating the shared fragmentsHashTable. Needs to be static to be shared between all task * objects. @@ -65,23 +64,23 @@ public class FragmentationTask implements Callable { // //(0)); tmpMolecule.getFragmentFrequencies().put(this.fragmentationName, new HashMap<>(0)); continue; } - if(this.fragmenter.shouldBePreprocessed(tmpAtomContainer)){ + if (this.fragmenter.shouldBePreprocessed(tmpAtomContainer)) { tmpAtomContainer = this.fragmenter.applyPreprocessing(tmpAtomContainer); } - List tmpFragmentsList = null; + List tmpFragmentsList; try { tmpFragmentsList = this.fragmenter.fragmentMolecule(tmpAtomContainer); } catch (NullPointerException | IllegalArgumentException | CloneNotSupportedException anException) { @@ -142,35 +144,35 @@ public Integer call() throws Exception{ } List tmpFragmentsOfMolList = new ArrayList<>(tmpFragmentsList.size()); HashMap tmpFragmentFrequenciesOfMoleculeMap = new HashMap<>(CollectionUtil.calculateInitialHashCollectionCapacity(tmpFragmentsList.size())); - for(IAtomContainer tmpFragment : tmpFragmentsList){ + for (IAtomContainer tmpFragment : tmpFragmentsList) { String tmpSmiles = ChemUtil.createUniqueSmiles(tmpFragment); if (tmpSmiles == null) { this.exceptionsCounter++; continue; } FragmentDataModel tmpNewFragmentDataModel = new FragmentDataModel(tmpSmiles, tmpFragment.getTitle(), tmpFragment.getProperties()); - FragmentDataModel tmpFragmentDataModel = this.fragmentsHashTable.putIfAbsent(tmpSmiles, tmpNewFragmentDataModel); // putIfAbsent returns null if key is not present in the map, else previous value associated with this key - if(tmpFragmentDataModel == null){ + // putIfAbsent returns null if key is not present in the map, else previous value associated with this key + FragmentDataModel tmpFragmentDataModel = this.fragmentsHashTable.putIfAbsent(tmpSmiles, tmpNewFragmentDataModel); + if (tmpFragmentDataModel == null) { tmpFragmentDataModel = tmpNewFragmentDataModel; } - LOCK.lock(); + FragmentationTask.LOCK.lock(); tmpFragmentDataModel.incrementAbsoluteFrequency(); - LOCK.unlock(); + FragmentationTask.LOCK.unlock(); tmpFragmentDataModel.getParentMolecules().add(tmpMolecule); - if(tmpFragmentsOfMolList.contains(tmpFragmentDataModel)){ + if (tmpFragmentsOfMolList.contains(tmpFragmentDataModel)) { tmpFragmentFrequenciesOfMoleculeMap.replace(tmpSmiles, tmpFragmentFrequenciesOfMoleculeMap.get(tmpSmiles) + 1); - } - else{ - LOCK.lock(); + } else { + FragmentationTask.LOCK.lock(); tmpFragmentDataModel.incrementMoleculeFrequency(); - LOCK.unlock(); + FragmentationTask.LOCK.unlock(); tmpFragmentsOfMolList.add(tmpFragmentDataModel); tmpFragmentFrequenciesOfMoleculeMap.put(tmpSmiles, 1); } } tmpMolecule.getFragmentFrequencies().put(this.fragmentationName, tmpFragmentFrequenciesOfMoleculeMap); tmpMolecule.getAllFragments().put(this.fragmentationName, tmpFragmentsOfMolList); - } catch(Exception anException){ + } catch(Exception anException) { this.exceptionsCounter++; FragmentationTask.LOGGER.log(Level.SEVERE, anException.toString(), anException); if (tmpMolecule.getAllFragments() != null && !tmpMolecule.getAllFragments().containsKey(this.fragmentationName)) { @@ -180,8 +182,8 @@ public Integer call() throws Exception{ tmpMolecule.getFragmentFrequencies().put(this.fragmentationName, new HashMap<>(0)); } } - if(Thread.currentThread().isInterrupted()){ - LOGGER.log(Level.INFO, "Thread interrupted"); + if (Thread.currentThread().isInterrupted()) { + FragmentationTask.LOGGER.log(Level.INFO, "Thread interrupted"); return null; } } diff --git a/src/main/java/de/unijena/cheminf/mortar/model/fragmentation/FragmentationThread.java b/src/main/java/de/unijena/cheminf/mortar/model/fragmentation/FragmentationThread.java index 60c0f846..1bb23419 100644 --- a/src/main/java/de/unijena/cheminf/mortar/model/fragmentation/FragmentationThread.java +++ b/src/main/java/de/unijena/cheminf/mortar/model/fragmentation/FragmentationThread.java @@ -49,6 +49,7 @@ * @author Felix Baensch, Jonas Schaub * @version 1.0.0.0 */ +@Deprecated public class FragmentationThread implements Callable> { /** @@ -132,9 +133,13 @@ public Hashtable call() throws Exception { for(String tmpKey : tmpKeySet){ tmpFragmentAmount += tmpFragmentHashtable.get(tmpKey).getAbsoluteFrequency(); } - for(String tmpKey : tmpKeySet){ - tmpFragmentHashtable.get(tmpKey).setAbsolutePercentage(1.0 * tmpFragmentHashtable.get(tmpKey).getAbsoluteFrequency() / tmpFragmentAmount); - tmpFragmentHashtable.get(tmpKey).setMoleculePercentage(1.0 * tmpFragmentHashtable.get(tmpKey).getMoleculeFrequency() / this.molecules.size()); + if (tmpFragmentAmount != 0) { + for (String tmpKey : tmpKeySet) { + tmpFragmentHashtable.get(tmpKey).setAbsolutePercentage(1.0 * tmpFragmentHashtable.get(tmpKey).getAbsoluteFrequency() / tmpFragmentAmount); + tmpFragmentHashtable.get(tmpKey).setMoleculePercentage(1.0 * tmpFragmentHashtable.get(tmpKey).getMoleculeFrequency() / this.molecules.size()); + } + } else { + FragmentationThread.LOGGER.log(Level.WARNING, "Sum of absolute frequencies of fragments was 0! Percentages could not be calculated."); } if(tmpExceptionsCounter > 0){ FragmentationThread.LOGGER.log(Level.SEVERE, "Fragmentation \"" + this.fragmentationName + "\" caused " + tmpExceptionsCounter + " exceptions"); diff --git a/src/main/java/de/unijena/cheminf/mortar/model/fragmentation/algorithm/ErtlFunctionalGroupsFinderFragmenter.java b/src/main/java/de/unijena/cheminf/mortar/model/fragmentation/algorithm/ErtlFunctionalGroupsFinderFragmenter.java index 14680041..4cb22839 100644 --- a/src/main/java/de/unijena/cheminf/mortar/model/fragmentation/algorithm/ErtlFunctionalGroupsFinderFragmenter.java +++ b/src/main/java/de/unijena/cheminf/mortar/model/fragmentation/algorithm/ErtlFunctionalGroupsFinderFragmenter.java @@ -31,7 +31,8 @@ import de.unijena.cheminf.mortar.model.util.BasicDefinitions; import de.unijena.cheminf.mortar.model.util.ChemUtil; import de.unijena.cheminf.mortar.model.util.CollectionUtil; -import de.unijena.cheminf.mortar.model.util.SimpleEnumConstantNameProperty; +import de.unijena.cheminf.mortar.model.util.IDisplayEnum; +import de.unijena.cheminf.mortar.model.util.SimpleIDisplayEnumConstantProperty; import javafx.beans.property.Property; import javafx.beans.property.SimpleBooleanProperty; @@ -69,37 +70,50 @@ public class ErtlFunctionalGroupsFinderFragmenter implements IMoleculeFragmenter /** * Enum for options concerning the environment of returned functional group fragments. */ - public static enum FGEnvOption { + public static enum FGEnvOption implements IDisplayEnum { /** * Generalize environments of functional groups. */ - GENERALIZATION(ErtlFunctionalGroupsFinder.Mode.DEFAULT), - + GENERALIZATION(ErtlFunctionalGroupsFinder.Mode.DEFAULT, + Message.get("ErtlFunctionalGroupsFinderFragmenter.FGEnvOption.Generalization.displayName"), + Message.get("ErtlFunctionalGroupsFinderFragmenter.FGEnvOption.Generalization.tooltip")), /** * Do not generalize but give the full environment of functional groups. */ - FULL_ENVIRONMENT(ErtlFunctionalGroupsFinder.Mode.NO_GENERALIZATION), - + FULL_ENVIRONMENT(ErtlFunctionalGroupsFinder.Mode.NO_GENERALIZATION, + Message.get("ErtlFunctionalGroupsFinderFragmenter.FGEnvOption.NoGeneralization.displayName"), + Message.get("ErtlFunctionalGroupsFinderFragmenter.FGEnvOption.NoGeneralization.tooltip")), /** * Return only the marked atoms of a functional group, no environment. */ - NO_ENVIRONMENT(ErtlFunctionalGroupsFinder.Mode.ONLY_MARKED_ATOMS); - + NO_ENVIRONMENT(ErtlFunctionalGroupsFinder.Mode.ONLY_MARKED_ATOMS, + Message.get("ErtlFunctionalGroupsFinderFragmenter.FGEnvOption.OnlyMarkedAtoms.displayName"), + Message.get("ErtlFunctionalGroupsFinderFragmenter.FGEnvOption.OnlyMarkedAtoms.tooltip")); /** * The ErtlFunctionalGroupsFinder mode to use in the respective cases. */ private final ErtlFunctionalGroupsFinder.Mode mode; - + /** + * Language-specific name for display in GUI. + */ + private final String displayName; + /** + * Language-specific tooltip text for display in GUI. + */ + private final String tooltip; /** * Constructor. * * @param aMode the EFGF mode to use with the respective option; the internal EFGF instance will be set with this * mode when the option is chosen + * @param aDisplayName display name + * @param aTooltip tooltip text */ - private FGEnvOption(ErtlFunctionalGroupsFinder.Mode aMode) { + private FGEnvOption(ErtlFunctionalGroupsFinder.Mode aMode, String aDisplayName, String aTooltip) { this.mode = aMode; + this.displayName = aDisplayName; + this.tooltip = aTooltip; } - /** * Returns the EFGF mode to use with the respective option. * @@ -108,35 +122,16 @@ private FGEnvOption(ErtlFunctionalGroupsFinder.Mode aMode) { public ErtlFunctionalGroupsFinder.Mode getAssociatedEFGFMode() { return this.mode; } - } - // - // - // - /** - * Enum for available electron donation models that combined with a cycle finder algorithm is used to define an - * aromaticity model to use. Utility for defining the options in a GUI. The electron - * donation model specified in the constant name is used and a cycle finder algorithm set via the respective option. - */ - public static enum ElectronDonationModelOption { - /** - * Daylight electron donation model. - */ - DAYLIGHT, - - /** - * CDK electron donation model. - */ - CDK, - - /** - * CDK electron donation model that additionally allows exocyclic bonds to contribute electrons to the aromatic system. - */ - CDK_ALLOWING_EXOCYCLIC, - - /** - * Pi bonds electron donation model. - */ - PI_BONDS; + // + @Override + public String getDisplayName() { + return this.displayName; + } + // + @Override + public String getTooltipText() { + return this.tooltip; + } } // // @@ -145,70 +140,52 @@ public static enum ElectronDonationModelOption { * Enum for defining which fragments should be returned by the fragmentation methods, only the functional groups, * only the alkane fragments, or both. */ - public static enum EFGFFragmenterReturnedFragmentsOption { + public static enum EFGFFragmenterReturnedFragmentsOption implements IDisplayEnum { /** * Option to return only the identified functional groups of a molecule after fragmentation. */ - ONLY_FUNCTIONAL_GROUPS, + ONLY_FUNCTIONAL_GROUPS(Message.get("ErtlFunctionalGroupsFinderFragmenter.EFGFFragmenterReturnedFragmentsOption.OnlyFunctionalGroups.displayName"), + Message.get("ErtlFunctionalGroupsFinderFragmenter.EFGFFragmenterReturnedFragmentsOption.OnlyFunctionalGroups.tooltip")), /** * Option to return only the non-functional-group alkane fragments of a molecule after fragmentation. */ - ONLY_ALKANE_FRAGMENTS, + ONLY_ALKANE_FRAGMENTS(Message.get("ErtlFunctionalGroupsFinderFragmenter.EFGFFragmenterReturnedFragmentsOption.OnlyAlkanes.displayName"), + Message.get("ErtlFunctionalGroupsFinderFragmenter.EFGFFragmenterReturnedFragmentsOption.OnlyAlkanes.tooltip")), /** * Option to return both, functional groups and alkane fragments, after fragmentation. */ - ALL_FRAGMENTS; - } - // - // - // - /** - * Enum for defining which cycle finder algorithm should be used to define an aromaticity model. The electron - * donation model is set via the respective option. See CDK class "Cycles" for more detailed descriptions of the - * available cycle finders. - */ - public static enum CycleFinderOption { - /** - * Algorithm that tries to find all possible rings in a given structure. Might cause IntractableException. - */ - ALL, - - /** - * Algorithm that looks for cycles usually checked by the CDK when detecting aromaticity. - */ - CDK_AROMATIC_SET, - - /** - * Gives the shortest cycles through each edge. - */ - EDGE_SHORT, - - /** - * Unique set of essential cycles of a molecule. - */ - ESSENTIAL, - - /** - * Minimum Cycle Basis (MCB, aka. SSSR - smallest set of smallest rings). - */ - MCB, - + ALL_FRAGMENTS(Message.get("ErtlFunctionalGroupsFinderFragmenter.EFGFFragmenterReturnedFragmentsOption.All.displayName"), + Message.get("ErtlFunctionalGroupsFinderFragmenter.EFGFFragmenterReturnedFragmentsOption.All.tooltip")); /** - * Union of all possible MCB cycle sets of a molecule. + * Language-specific name for display in GUI. */ - RELEVANT, - + private final String displayName; /** - * Shortest cycle through each triple of vertices. + * Language-specific tooltip text for display in GUI. */ - TRIPLET_SHORT, - + private final String tooltip; /** - * Shortest cycles through each vertex. + * Constructor. + * + * @param aDisplayName display name + * @param aTooltip tooltip text */ - VERTEX_SHORT; + private EFGFFragmenterReturnedFragmentsOption(String aDisplayName, String aTooltip) { + this.displayName = aDisplayName; + this.tooltip = aTooltip; + } + // + @Override + public String getDisplayName() { + return this.displayName; + } + // + @Override + public String getTooltipText() { + return this.tooltip; + } } // // @@ -226,22 +203,26 @@ public static enum CycleFinderOption { /** * Default electron donation model for aromaticity detection. */ - public static final ElectronDonationModelOption Electron_Donation_MODEL_OPTION_DEFAULT = ElectronDonationModelOption.DAYLIGHT; + public static final IMoleculeFragmenter.ElectronDonationModelOption Electron_Donation_MODEL_OPTION_DEFAULT = + IMoleculeFragmenter.ElectronDonationModelOption.DAYLIGHT; /** * Default functional group environment option. */ - public static final FGEnvOption ENVIRONMENT_MODE_OPTION_DEFAULT = FGEnvOption.GENERALIZATION; + public static final ErtlFunctionalGroupsFinderFragmenter.FGEnvOption ENVIRONMENT_MODE_OPTION_DEFAULT = + ErtlFunctionalGroupsFinderFragmenter.FGEnvOption.GENERALIZATION; /** * Default returned fragments option. */ - public static final EFGFFragmenterReturnedFragmentsOption RETURNED_FRAGMENTS_OPTION_DEFAULT = EFGFFragmenterReturnedFragmentsOption.ALL_FRAGMENTS; + public static final ErtlFunctionalGroupsFinderFragmenter.EFGFFragmenterReturnedFragmentsOption RETURNED_FRAGMENTS_OPTION_DEFAULT + = ErtlFunctionalGroupsFinderFragmenter.EFGFFragmenterReturnedFragmentsOption.ALL_FRAGMENTS; /** * Default option for the cycle finder algorithm employed for aromaticity detection. */ - public static final CycleFinderOption CYCLE_FINDER_OPTION_DEFAULT = CycleFinderOption.CDK_AROMATIC_SET; + public static final IMoleculeFragmenter.CycleFinderOption CYCLE_FINDER_OPTION_DEFAULT = + IMoleculeFragmenter.CycleFinderOption.CDK_AROMATIC_SET; /** * Default option for whether to filter single-atom molecules from inputs. @@ -249,7 +230,8 @@ public static enum CycleFinderOption { public static final boolean FILTER_SINGLE_ATOMS_OPTION_DEFAULT = true; /** - * Default option for whether input restrictions (no metal, metalloids, pseudo atoms, charges or unconnected structures) should be applied. + * Default option for whether input restrictions (no metal, metalloids, pseudo atoms, charges or unconnected + * structures) should be applied. */ public static final boolean APPLY_INPUT_RESTRICTIONS_OPTION_DEFAULT = false; @@ -259,23 +241,19 @@ public static enum CycleFinderOption { public static final CycleFinder AUXILIARY_CYCLE_FINDER = Cycles.cdkAromaticSet(); /** - * Functional group fragments will be assigned this value for the property with key IMoleculeFragmenter.FRAGMENT_CATEGORY_PROPERTY_KEY. + * Functional group fragments will be assigned this value for the property with key + * IMoleculeFragmenter.FRAGMENT_CATEGORY_PROPERTY_KEY. */ public static final String FRAGMENT_CATEGORY_FUNCTIONAL_GROUP_VALUE = "EFGFFragmenter.FunctionalGroup"; /** - * Alkane fragments will be assigned this value for the property with key IMoleculeFragmenter.FRAGMENT_CATEGORY_PROPERTY_KEY. + * Alkane fragments will be assigned this value for the property with key + * IMoleculeFragmenter.FRAGMENT_CATEGORY_PROPERTY_KEY. */ public static final String FRAGMENT_CATEGORY_ALKANE_VALUE = "EFGFFragmenter.Alkane"; // // // - /** - * Instance of ErtlfFunctionalGroupsFinder class used to do the extraction of functional groups. If the FG - * environment setting changes, this object needs to be reset in most cases with the respectively needed mode. - */ - private ErtlFunctionalGroupsFinder ertlFGFInstance; - /** * The aromaticity model used for preprocessing prior to FG extraction. Constructed from the set electron donation * model and cycle finder algorithm. @@ -297,18 +275,15 @@ public static enum CycleFinderOption { //note: since Java 21, the javadoc build complains about "double comments" when there is a comment // for the get method of the property and the private property itself as well - private final SimpleEnumConstantNameProperty environmentModeSetting; + private final SimpleIDisplayEnumConstantProperty environmentModeSetting; - private final SimpleEnumConstantNameProperty electronDonationModelSetting; + private final SimpleIDisplayEnumConstantProperty electronDonationModelSetting; - /** - * A property that has a constant name from the IMoleculeFragmenter.FragmentSaturationOption enum as value. - */ - private final SimpleEnumConstantNameProperty fragmentSaturationSetting; + private final SimpleIDisplayEnumConstantProperty fragmentSaturationSetting; - private final SimpleEnumConstantNameProperty returnedFragmentsSetting; + private final SimpleIDisplayEnumConstantProperty returnedFragmentsSetting; - private final SimpleEnumConstantNameProperty cycleFinderSetting; + private final SimpleIDisplayEnumConstantProperty cycleFinderSetting; private final SimpleBooleanProperty filterSingleAtomsSetting; @@ -317,17 +292,27 @@ public static enum CycleFinderOption { /** * All settings of this fragmenter, encapsulated in JavaFX properties for binding in GUI. */ - private final List settings; + private final List> settings; /** * Map to store pairs of {@literal }. */ private final HashMap settingNameTooltipTextMap; + /** + * Map to store pairs of {@literal }. + */ + private final HashMap settingNameDisplayNameMap; + + /** + * Instance of ErtlfFunctionalGroupsFinder class used to do the extraction of functional groups. + */ + private final ErtlFunctionalGroupsFinder ertlFGFInstance; + /** * Logger of this class. */ - private final Logger logger = Logger.getLogger(ErtlFunctionalGroupsFinderFragmenter.class.getName()); + private static final Logger LOGGER = Logger.getLogger(ErtlFunctionalGroupsFinderFragmenter.class.getName()); // // // @@ -339,17 +324,22 @@ public ErtlFunctionalGroupsFinderFragmenter() { int tmpInitialCapacityForSettingNameTooltipTextMap = CollectionUtil.calculateInitialHashCollectionCapacity( tmpNumberOfSettingsForTooltipMapSize, BasicDefinitions.DEFAULT_HASH_COLLECTION_LOAD_FACTOR); - this.settingNameTooltipTextMap = new HashMap(tmpInitialCapacityForSettingNameTooltipTextMap, BasicDefinitions.DEFAULT_HASH_COLLECTION_LOAD_FACTOR); - this.fragmentSaturationSetting = new SimpleEnumConstantNameProperty(this, "Fragment saturation setting", - IMoleculeFragmenter.FRAGMENT_SATURATION_OPTION_DEFAULT.name(), IMoleculeFragmenter.FragmentSaturationOption.class) { + this.settingNameTooltipTextMap = new HashMap<>(tmpInitialCapacityForSettingNameTooltipTextMap, BasicDefinitions.DEFAULT_HASH_COLLECTION_LOAD_FACTOR); + this.settingNameDisplayNameMap = new HashMap<>(tmpInitialCapacityForSettingNameTooltipTextMap, BasicDefinitions.DEFAULT_HASH_COLLECTION_LOAD_FACTOR); + //these names are for internal use, the language-specific display names for the GUI are stored in the map + this.fragmentSaturationSetting = new SimpleIDisplayEnumConstantProperty(this, "Fragment saturation setting", + IMoleculeFragmenter.FRAGMENT_SATURATION_OPTION_DEFAULT, IMoleculeFragmenter.FragmentSaturationOption.class) { @Override - public void set(String newValue) throws NullPointerException, IllegalArgumentException { + public void set(IDisplayEnum newValue) throws NullPointerException, IllegalArgumentException { try { //call to super.set() for parameter checks super.set(newValue); } catch (NullPointerException | IllegalArgumentException anException) { - ErtlFunctionalGroupsFinderFragmenter.this.logger.log(Level.WARNING, anException.toString(), anException); - GuiUtil.guiExceptionAlert("Illegal Argument", "Illegal Argument was set", anException.toString(), anException); + ErtlFunctionalGroupsFinderFragmenter.LOGGER.log(Level.WARNING, anException.toString(), anException); + GuiUtil.guiExceptionAlert(Message.get("Fragmenter.IllegalSettingValue.Title"), + Message.get("Fragmenter.IllegalSettingValue.Header"), + anException.toString(), + anException); //re-throws the exception to properly reset the binding throw anException; } @@ -357,37 +347,49 @@ public void set(String newValue) throws NullPointerException, IllegalArgumentExc }; this.settingNameTooltipTextMap.put(this.fragmentSaturationSetting.getName(), Message.get("ErtlFunctionalGroupsFinderFragmenter.fragmentSaturationSetting.tooltip")); - this.environmentModeSetting = new SimpleEnumConstantNameProperty(this, "Environment mode setting", - ErtlFunctionalGroupsFinderFragmenter.ENVIRONMENT_MODE_OPTION_DEFAULT.name(), ErtlFunctionalGroupsFinderFragmenter.FGEnvOption.class) { + this.settingNameDisplayNameMap.put(this.fragmentSaturationSetting.getName(), + Message.get("ErtlFunctionalGroupsFinderFragmenter.fragmentSaturationSetting.displayName")); + this.ertlFGFInstance = new ErtlFunctionalGroupsFinder( + ErtlFunctionalGroupsFinderFragmenter.ENVIRONMENT_MODE_OPTION_DEFAULT.getAssociatedEFGFMode()); + this.environmentModeSetting = new SimpleIDisplayEnumConstantProperty(this, "Environment mode setting", + ErtlFunctionalGroupsFinderFragmenter.ENVIRONMENT_MODE_OPTION_DEFAULT, ErtlFunctionalGroupsFinderFragmenter.FGEnvOption.class) { @Override - public void set(String newValue) throws NullPointerException, IllegalArgumentException { + public void set(IDisplayEnum newValue) throws NullPointerException, IllegalArgumentException { try { //call to super.set() for parameter checks super.set(newValue); } catch (NullPointerException | IllegalArgumentException anException) { - ErtlFunctionalGroupsFinderFragmenter.this.logger.log(Level.WARNING, anException.toString(), anException); - GuiUtil.guiExceptionAlert("Illegal Argument", "Illegal Argument was set", anException.toString(), anException); + ErtlFunctionalGroupsFinderFragmenter.LOGGER.log(Level.WARNING, anException.toString(), anException); + GuiUtil.guiExceptionAlert(Message.get("Fragmenter.IllegalSettingValue.Title"), + Message.get("Fragmenter.IllegalSettingValue.Header"), + anException.toString(), + anException); //re-throws the exception to properly reset the binding throw anException; } //throws no exception if super.set() throws no exception - ErtlFunctionalGroupsFinderFragmenter.this.setErtlFGFInstance(FGEnvOption.valueOf(newValue)); + ErtlFunctionalGroupsFinderFragmenter.this.setErtlFGFInstance((ErtlFunctionalGroupsFinderFragmenter.FGEnvOption) this.get()); } }; this.settingNameTooltipTextMap.put(this.environmentModeSetting.getName(), Message.get("ErtlFunctionalGroupsFinderFragmenter.environmentModeSetting.tooltip")); + this.settingNameDisplayNameMap.put(this.environmentModeSetting.getName(), + Message.get("ErtlFunctionalGroupsFinderFragmenter.environmentModeSetting.displayName")); //initialisation of EFGF instance - this.setErtlFGFInstance(FGEnvOption.valueOf(this.environmentModeSetting.get())); - this.returnedFragmentsSetting = new SimpleEnumConstantNameProperty(this, "Returned fragments setting", - ErtlFunctionalGroupsFinderFragmenter.RETURNED_FRAGMENTS_OPTION_DEFAULT.name(), EFGFFragmenterReturnedFragmentsOption.class) { + this.setErtlFGFInstance((ErtlFunctionalGroupsFinderFragmenter.FGEnvOption) this.environmentModeSetting.get()); + this.returnedFragmentsSetting = new SimpleIDisplayEnumConstantProperty(this, "Returned fragments setting", + ErtlFunctionalGroupsFinderFragmenter.RETURNED_FRAGMENTS_OPTION_DEFAULT, ErtlFunctionalGroupsFinderFragmenter.EFGFFragmenterReturnedFragmentsOption.class) { @Override - public void set(String newValue) throws NullPointerException, IllegalArgumentException { + public void set(IDisplayEnum newValue) throws NullPointerException, IllegalArgumentException { try { //call to super.set() for parameter checks super.set(newValue); } catch (NullPointerException | IllegalArgumentException anException) { - ErtlFunctionalGroupsFinderFragmenter.this.logger.log(Level.WARNING, anException.toString(), anException); - GuiUtil.guiExceptionAlert("Illegal Argument", "Illegal Argument was set", anException.toString(), anException); + ErtlFunctionalGroupsFinderFragmenter.LOGGER.log(Level.WARNING, anException.toString(), anException); + GuiUtil.guiExceptionAlert(Message.get("Fragmenter.IllegalSettingValue.Title"), + Message.get("Fragmenter.IllegalSettingValue.Header"), + anException.toString(), + anException); //re-throws the exception to properly reset the binding throw anException; } @@ -395,23 +397,28 @@ public void set(String newValue) throws NullPointerException, IllegalArgumentExc }; this.settingNameTooltipTextMap.put(this.returnedFragmentsSetting.getName(), Message.get("ErtlFunctionalGroupsFinderFragmenter.returnedFragmentsSetting.tooltip")); + this.settingNameDisplayNameMap.put(this.returnedFragmentsSetting.getName(), + Message.get("ErtlFunctionalGroupsFinderFragmenter.returnedFragmentsSetting.displayName")); //note: cycle finder and electron donation model have to be set prior to setting the aromaticity model! - this.cycleFinderSetting = new SimpleEnumConstantNameProperty(this, "Cycle finder algorithm setting", - ErtlFunctionalGroupsFinderFragmenter.CYCLE_FINDER_OPTION_DEFAULT.name(), - ErtlFunctionalGroupsFinderFragmenter.CycleFinderOption.class) { + this.cycleFinderSetting = new SimpleIDisplayEnumConstantProperty(this, "Cycle finder algorithm setting", + ErtlFunctionalGroupsFinderFragmenter.CYCLE_FINDER_OPTION_DEFAULT, + IMoleculeFragmenter.CycleFinderOption.class) { @Override - public void set(String newValue) throws NullPointerException, IllegalArgumentException { + public void set(IDisplayEnum newValue) throws NullPointerException, IllegalArgumentException { try { //call to super.set() for parameter checks super.set(newValue); } catch (NullPointerException | IllegalArgumentException anException) { - ErtlFunctionalGroupsFinderFragmenter.this.logger.log(Level.WARNING, anException.toString(), anException); - GuiUtil.guiExceptionAlert("Illegal Argument", "Illegal Argument was set", anException.toString(), anException); + ErtlFunctionalGroupsFinderFragmenter.LOGGER.log(Level.WARNING, anException.toString(), anException); + GuiUtil.guiExceptionAlert(Message.get("Fragmenter.IllegalSettingValue.Title"), + Message.get("Fragmenter.IllegalSettingValue.Header"), + anException.toString(), + anException); //re-throws the exception to properly reset the binding throw anException; } //throws no exception if super.set() throws no exception - ErtlFunctionalGroupsFinderFragmenter.this.setCycleFinderInstance(CycleFinderOption.valueOf(newValue)); + ErtlFunctionalGroupsFinderFragmenter.this.setCycleFinderInstance((IMoleculeFragmenter.CycleFinderOption) this.get()); ErtlFunctionalGroupsFinderFragmenter.this.setAromaticityInstance( ErtlFunctionalGroupsFinderFragmenter.this.electronDonationInstance, ErtlFunctionalGroupsFinderFragmenter.this.cycleFinderInstance); @@ -419,23 +426,28 @@ public void set(String newValue) throws NullPointerException, IllegalArgumentExc }; this.settingNameTooltipTextMap.put(this.cycleFinderSetting.getName(), Message.get("ErtlFunctionalGroupsFinderFragmenter.cycleFinderSetting.tooltip")); - this.setCycleFinderInstance(CycleFinderOption.valueOf(this.cycleFinderSetting.get())); - this.electronDonationModelSetting = new SimpleEnumConstantNameProperty(this, "Electron donation model setting", - ErtlFunctionalGroupsFinderFragmenter.Electron_Donation_MODEL_OPTION_DEFAULT.name(), - ElectronDonationModelOption.class) { + this.settingNameDisplayNameMap.put(this.cycleFinderSetting.getName(), + Message.get("ErtlFunctionalGroupsFinderFragmenter.cycleFinderSetting.displayName")); + this.setCycleFinderInstance((IMoleculeFragmenter.CycleFinderOption) this.cycleFinderSetting.get()); + this.electronDonationModelSetting = new SimpleIDisplayEnumConstantProperty(this, "Electron donation model setting", + ErtlFunctionalGroupsFinderFragmenter.Electron_Donation_MODEL_OPTION_DEFAULT, + IMoleculeFragmenter.ElectronDonationModelOption.class) { @Override - public void set(String newValue) throws NullPointerException, IllegalArgumentException { + public void set(IDisplayEnum newValue) throws NullPointerException, IllegalArgumentException { try { //call to super.set() for parameter checks super.set(newValue); } catch (NullPointerException | IllegalArgumentException anException) { - ErtlFunctionalGroupsFinderFragmenter.this.logger.log(Level.WARNING, anException.toString(), anException); - GuiUtil.guiExceptionAlert("Illegal Argument", "Illegal Argument was set", anException.toString(), anException); + ErtlFunctionalGroupsFinderFragmenter.LOGGER.log(Level.WARNING, anException.toString(), anException); + GuiUtil.guiExceptionAlert(Message.get("Fragmenter.IllegalSettingValue.Title"), + Message.get("Fragmenter.IllegalSettingValue.Header"), + anException.toString(), + anException); //re-throws the exception to properly reset the binding throw anException; } //throws no exception if super.set() throws no exception - ErtlFunctionalGroupsFinderFragmenter.this.setElectronDonationInstance(ElectronDonationModelOption.valueOf(newValue)); + ErtlFunctionalGroupsFinderFragmenter.this.setElectronDonationInstance((IMoleculeFragmenter.ElectronDonationModelOption) this.get()); ErtlFunctionalGroupsFinderFragmenter.this.setAromaticityInstance( ErtlFunctionalGroupsFinderFragmenter.this.electronDonationInstance, ErtlFunctionalGroupsFinderFragmenter.this.cycleFinderInstance); @@ -443,7 +455,9 @@ public void set(String newValue) throws NullPointerException, IllegalArgumentExc }; this.settingNameTooltipTextMap.put(this.electronDonationModelSetting.getName(), Message.get("ErtlFunctionalGroupsFinderFragmenter.electronDonationModelSetting.tooltip")); - this.setElectronDonationInstance(ElectronDonationModelOption.valueOf(this.electronDonationModelSetting.get())); + this.settingNameDisplayNameMap.put(this.electronDonationModelSetting.getName(), + Message.get("ErtlFunctionalGroupsFinderFragmenter.electronDonationModelSetting.displayName")); + this.setElectronDonationInstance((IMoleculeFragmenter.ElectronDonationModelOption) this.electronDonationModelSetting.get()); this.setAromaticityInstance( ErtlFunctionalGroupsFinderFragmenter.this.electronDonationInstance, ErtlFunctionalGroupsFinderFragmenter.this.cycleFinderInstance @@ -452,11 +466,15 @@ public void set(String newValue) throws NullPointerException, IllegalArgumentExc ErtlFunctionalGroupsFinderFragmenter.FILTER_SINGLE_ATOMS_OPTION_DEFAULT); this.settingNameTooltipTextMap.put(this.filterSingleAtomsSetting.getName(), Message.get("ErtlFunctionalGroupsFinderFragmenter.filterSingleAtomsSetting.tooltip")); + this.settingNameDisplayNameMap.put(this.filterSingleAtomsSetting.getName(), + Message.get("ErtlFunctionalGroupsFinderFragmenter.filterSingleAtomsSetting.displayName")); this.applyInputRestrictionsSetting = new SimpleBooleanProperty(this, "Apply input restrictions setting", ErtlFunctionalGroupsFinderFragmenter.APPLY_INPUT_RESTRICTIONS_OPTION_DEFAULT); this.settingNameTooltipTextMap.put(this.applyInputRestrictionsSetting.getName(), Message.get("ErtlFunctionalGroupsFinderFragmenter.applyInputRestrictionsSetting.tooltip")); - this.settings = new ArrayList(7); + this.settingNameDisplayNameMap.put(this.applyInputRestrictionsSetting.getName(), + Message.get("ErtlFunctionalGroupsFinderFragmenter.applyInputRestrictionsSetting.displayName")); + this.settings = new ArrayList<>(7); this.settings.add(this.fragmentSaturationSetting); this.settings.add(this.electronDonationModelSetting); this.settings.add(this.cycleFinderSetting); @@ -469,12 +487,12 @@ public void set(String newValue) throws NullPointerException, IllegalArgumentExc // // /** - * Returns the string representation of the currently set option for the environment mode setting. + * Returns the currently set option for the environment mode setting. * - * @return enum constant name of the set option + * @return enum constant of the set option */ - public String getEnvironmentModeSetting() { - return this.environmentModeSetting.get(); + public ErtlFunctionalGroupsFinderFragmenter.FGEnvOption getEnvironmentModeSetting() { + return (ErtlFunctionalGroupsFinderFragmenter.FGEnvOption) this.environmentModeSetting.get(); } /** @@ -482,27 +500,18 @@ public String getEnvironmentModeSetting() { * * @return property object of the environment mode setting */ - public SimpleEnumConstantNameProperty environmentModeSettingProperty() { + public SimpleIDisplayEnumConstantProperty environmentModeSettingProperty() { return this.environmentModeSetting; } /** - * Returns the enum constant currently set as option for the environment mode setting. - * - * @return enum constant for environment mode setting - */ - public FGEnvOption getEnvironmentModeSettingConstant() { - return FGEnvOption.valueOf(this.environmentModeSetting.get()); - } - - /** - * Returns the string representation of the currently set option for the electron donation model setting used for + * Returns the currently set option for the electron donation model setting used for * aromaticity detection together with the set cycle finder algorithm. * - * @return enum constant name of the set option + * @return enum constant of the set option */ - public String getElectronDonationModelSetting() { - return this.electronDonationModelSetting.get(); + public IMoleculeFragmenter.ElectronDonationModelOption getElectronDonationModelSetting() { + return (IMoleculeFragmenter.ElectronDonationModelOption) this.electronDonationModelSetting.get(); } /** @@ -510,26 +519,17 @@ public String getElectronDonationModelSetting() { * * @return property object of the electron donation model setting */ - public SimpleEnumConstantNameProperty electronDonationModelSettingProperty() { + public SimpleIDisplayEnumConstantProperty electronDonationModelSettingProperty() { return this.electronDonationModelSetting; } /** - * Returns the enum constant currently set as option for the electron donation model setting. - * - * @return enum constant for electron donation model setting - */ - public ElectronDonationModelOption getElectronDonationModelSettingConstant() { - return ElectronDonationModelOption.valueOf(this.electronDonationModelSetting.get()); - } - - /** - * Returns the string representation of the currently set option for the returned fragments setting + * Returns the currently set option for the returned fragments setting * - * @return enum constant name of the set option + * @return enum constant of the set option */ - public String getReturnedFragmentsSetting() { - return this.returnedFragmentsSetting.get(); + public ErtlFunctionalGroupsFinderFragmenter.EFGFFragmenterReturnedFragmentsOption getReturnedFragmentsSetting() { + return (ErtlFunctionalGroupsFinderFragmenter.EFGFFragmenterReturnedFragmentsOption) this.returnedFragmentsSetting.get(); } /** @@ -537,27 +537,18 @@ public String getReturnedFragmentsSetting() { * * @return property object of the returned fragments setting */ - public SimpleEnumConstantNameProperty returnedFragmentsSettingProperty() { + public SimpleIDisplayEnumConstantProperty returnedFragmentsSettingProperty() { return this.returnedFragmentsSetting; } /** - * Returns the enum constant currently set as option for the returned fragments setting. - * - * @return enum constant for returned fragments setting - */ - public EFGFFragmenterReturnedFragmentsOption getReturnedFragmentsSettingConstant() { - return EFGFFragmenterReturnedFragmentsOption.valueOf(this.returnedFragmentsSetting.get()); - } - - /** - * Returns the string representation of the currently set option for the cycle finder setting used for aromaticity + * Returns the currently set option for the cycle finder setting used for aromaticity * detection together with the electron donation model setting. * - * @return enum constant name of the set option + * @return enum constant of the set option */ - public String getCycleFinderSetting() { - return this.cycleFinderSetting.get(); + public IMoleculeFragmenter.CycleFinderOption getCycleFinderSetting() { + return (IMoleculeFragmenter.CycleFinderOption) this.cycleFinderSetting.get(); } /** @@ -565,19 +556,10 @@ public String getCycleFinderSetting() { * * @return property object of the cycle finder setting */ - public SimpleEnumConstantNameProperty cycleFinderSettingProperty() { + public SimpleIDisplayEnumConstantProperty cycleFinderSettingProperty() { return this.cycleFinderSetting; } - /** - * Returns the enum constant currently set as option for the cycle finder setting. - * - * @return enum constant for cycle finder setting - */ - public CycleFinderOption getCycleFinderSettingConstant() { - return CycleFinderOption.valueOf(this.cycleFinderSetting.get()); - } - /** * Returns the boolean value of the filter single atoms setting. * @@ -616,21 +598,6 @@ public SimpleBooleanProperty applyInputRestrictionsSettingProperty() { // // // - /** - * Sets the environment mode setting defining whether the returned functional group fragments should have full environments, - * generalized environments or no environments. - * - * @param anOptionName name of a constant from the FGEnvOption enum - * @throws NullPointerException if the given string is null - * @throws IllegalArgumentException if the given string is not an enum constant name - */ - public void setEnvironmentModeSetting(String anOptionName) throws NullPointerException, IllegalArgumentException { - Objects.requireNonNull(anOptionName, "Given option name is null."); - //throws IllegalArgumentException if the given name does not match a constant name in the enum - FGEnvOption tmpConstant = FGEnvOption.valueOf(anOptionName); - this.setEnvironmentModeSetting(tmpConstant); - } - /** * Sets the environment mode setting defining whether the returned functional group fragments should have full environments, * generalized environments or no environments. @@ -638,53 +605,23 @@ public void setEnvironmentModeSetting(String anOptionName) throws NullPointerExc * @param anOption a constant from the FGEnvOption enum * @throws NullPointerException if the given parameter is null */ - public void setEnvironmentModeSetting(FGEnvOption anOption) throws NullPointerException { + public void setEnvironmentModeSetting(ErtlFunctionalGroupsFinderFragmenter.FGEnvOption anOption) throws NullPointerException { Objects.requireNonNull(anOption, "Given option is null."); //synchronisation with EFGF instance done in overridden set() function of the property - this.environmentModeSetting.set(anOption.name()); + this.environmentModeSetting.set(anOption); } /** * Sets the electron donation model setting. The set electron donation model is used for aromaticity detection in * preprocessing together with the set cycle finder algorithm. * - * @param anOptionName name of a constant from the ElectronDonationModelOption enum - * @throws NullPointerException if the given string is null - * @throws IllegalArgumentException if the given string is not an enum constant name - */ - public void setElectronDonationModelSetting(String anOptionName) throws NullPointerException, IllegalArgumentException { - Objects.requireNonNull(anOptionName, "Given option name is null."); - //throws IllegalArgumentException if the given name does not match a constant name in the enum - ElectronDonationModelOption tmpConstant = ElectronDonationModelOption.valueOf(anOptionName); - this.setElectronDonationModelSetting(tmpConstant); - } - - /** - * Sets the electron donation model setting. The set electron donation model is used for aromaticity detection in - * preprocessing together with the set cycle finder algorithm. - * - * @param anOption a constant from the ElectronDonationModelOption enum + * @param anOption a constant from the IMoleculeFragmenter.ElectronDonationModelOption enum * @throws NullPointerException is the given parameter is null */ - public void setElectronDonationModelSetting(ElectronDonationModelOption anOption) throws NullPointerException { + public void setElectronDonationModelSetting(IMoleculeFragmenter.ElectronDonationModelOption anOption) throws NullPointerException { Objects.requireNonNull(anOption, "Given option is null."); //synchronisation with aromaticity model instance done in overridden set() function of the property - this.electronDonationModelSetting.set(anOption.name()); - } - - /** - * Sets the returned fragments setting, defining whether only functional groups, only alkane fragments, or both should - * be returned. - * - * @param anOptionName name of a constant from the EFGFFragmenterReturnedFragmentsOption enum - * @throws NullPointerException if the given string is null - * @throws IllegalArgumentException if the given string is not an enum constant name - */ - public void setReturnedFragmentsSetting(String anOptionName) throws NullPointerException, IllegalArgumentException { - Objects.requireNonNull(anOptionName, "Given option name is null."); - //throws IllegalArgumentException if the given name does not match a constant name in the enum - EFGFFragmenterReturnedFragmentsOption tmpConstant = EFGFFragmenterReturnedFragmentsOption.valueOf(anOptionName); - this.setReturnedFragmentsSetting(tmpConstant); + this.electronDonationModelSetting.set(anOption); } /** @@ -694,36 +631,21 @@ public void setReturnedFragmentsSetting(String anOptionName) throws NullPointerE * @param anOption a constant from the EFGFFragmenterReturnedFragmentsOption enum * @throws NullPointerException if the given parameter is null */ - public void setReturnedFragmentsSetting(EFGFFragmenterReturnedFragmentsOption anOption) throws NullPointerException { + public void setReturnedFragmentsSetting(ErtlFunctionalGroupsFinderFragmenter.EFGFFragmenterReturnedFragmentsOption anOption) throws NullPointerException { Objects.requireNonNull(anOption, "Given option is null."); - this.returnedFragmentsSetting.set(anOption.name()); - } - - /** - * Sets the cycle finder setting. The chosen cycle finder algorithm is used for aromaticity detection in - * preprocessing together with the set electron donation model. - * - * @param anOptionName name of a constant from the CycleFinderOption enum - * @throws NullPointerException if the given string is null - * @throws IllegalArgumentException if the given string is not an enum constant name - */ - public void setCycleFinderSetting(String anOptionName) throws NullPointerException, IllegalArgumentException { - Objects.requireNonNull(anOptionName, "Given option name is null."); - //throws IllegalArgumentException if the given name does not match a constant name in the enum - CycleFinderOption tmpConstant = CycleFinderOption.valueOf(anOptionName); - this.setCycleFinderSetting(tmpConstant); + this.returnedFragmentsSetting.set(anOption); } /** * Sets the cycle finder setting. The chosen cycle finder algorithm is used for aromaticity detection in * preprocessing together with the set electron donation model. * - * @param anOption a constant from the CycleFinderOption enum + * @param anOption a constant from the IMoleculeFragmenter.CycleFinderOption enum * @throws NullPointerException if the given parameter is null */ - public void setCycleFinderSetting(CycleFinderOption anOption) throws NullPointerException { + public void setCycleFinderSetting(IMoleculeFragmenter.CycleFinderOption anOption) throws NullPointerException { Objects.requireNonNull(anOption, "Given option is null."); - this.cycleFinderSetting.set(anOption.name()); + this.cycleFinderSetting.set(anOption); } /** @@ -752,7 +674,7 @@ public void setApplyInputRestrictionsSetting(boolean aBoolean) { //without the empty line, the code folding does not work properly here... @Override - public List settingsProperties() { + public List> settingsProperties() { return this.settings; } @@ -762,47 +684,44 @@ public Map getSettingNameToTooltipTextMap() { } @Override - public String getFragmentationAlgorithmName() { - return ErtlFunctionalGroupsFinderFragmenter.ALGORITHM_NAME; + public Map getSettingNameToDisplayNameMap() { + return this.settingNameDisplayNameMap; } @Override - public String getFragmentSaturationSetting() { - return this.fragmentSaturationSetting.get(); + public String getFragmentationAlgorithmName() { + return ErtlFunctionalGroupsFinderFragmenter.ALGORITHM_NAME; } @Override - public SimpleEnumConstantNameProperty fragmentSaturationSettingProperty() { - return this.fragmentSaturationSetting; + public String getFragmentationAlgorithmDisplayName() { + return Message.get("ErtlFunctionalGroupsFinderFragmenter.displayName"); } @Override - public FragmentSaturationOption getFragmentSaturationSettingConstant() { - return FragmentSaturationOption.valueOf(this.fragmentSaturationSetting.get()); + public IMoleculeFragmenter.FragmentSaturationOption getFragmentSaturationSetting() { + return (IMoleculeFragmenter.FragmentSaturationOption) this.fragmentSaturationSetting.get(); } @Override - public void setFragmentSaturationSetting(String anOptionName) throws NullPointerException, IllegalArgumentException { - Objects.requireNonNull(anOptionName, "Given saturation option name is null."); - //throws IllegalArgumentException if the given name does not match a constant name in the enum - FragmentSaturationOption tmpConstant = FragmentSaturationOption.valueOf(anOptionName); - this.fragmentSaturationSetting.set(tmpConstant.name()); + public SimpleIDisplayEnumConstantProperty fragmentSaturationSettingProperty() { + return this.fragmentSaturationSetting; } @Override public void setFragmentSaturationSetting(FragmentSaturationOption anOption) throws NullPointerException { Objects.requireNonNull(anOption, "Given saturation option is null."); - this.fragmentSaturationSetting.set(anOption.name()); + this.fragmentSaturationSetting.set(anOption); } @Override public IMoleculeFragmenter copy() { ErtlFunctionalGroupsFinderFragmenter tmpCopy = new ErtlFunctionalGroupsFinderFragmenter(); - tmpCopy.setEnvironmentModeSetting(this.environmentModeSetting.get()); - tmpCopy.setCycleFinderSetting(this.cycleFinderSetting.get()); - tmpCopy.setElectronDonationModelSetting(this.electronDonationModelSetting.get()); - tmpCopy.setFragmentSaturationSetting(this.fragmentSaturationSetting.get()); - tmpCopy.setReturnedFragmentsSetting(this.returnedFragmentsSetting.get()); + tmpCopy.setEnvironmentModeSetting((ErtlFunctionalGroupsFinderFragmenter.FGEnvOption) this.environmentModeSetting.get()); + tmpCopy.setCycleFinderSetting((IMoleculeFragmenter.CycleFinderOption) this.cycleFinderSetting.get()); + tmpCopy.setElectronDonationModelSetting((IMoleculeFragmenter.ElectronDonationModelOption) this.electronDonationModelSetting.get()); + tmpCopy.setFragmentSaturationSetting((IMoleculeFragmenter.FragmentSaturationOption) this.fragmentSaturationSetting.get()); + tmpCopy.setReturnedFragmentsSetting((ErtlFunctionalGroupsFinderFragmenter.EFGFFragmenterReturnedFragmentsOption) this.returnedFragmentsSetting.get()); tmpCopy.setFilterSingleAtomsSetting(this.filterSingleAtomsSetting.get()); tmpCopy.setApplyInputRestrictionsSetting(this.applyInputRestrictionsSetting.get()); return tmpCopy; @@ -810,16 +729,16 @@ public IMoleculeFragmenter copy() { @Override public void restoreDefaultSettings() { - this.environmentModeSetting.set(ErtlFunctionalGroupsFinderFragmenter.ENVIRONMENT_MODE_OPTION_DEFAULT.name()); + this.environmentModeSetting.set(ErtlFunctionalGroupsFinderFragmenter.ENVIRONMENT_MODE_OPTION_DEFAULT); //this.EFGFInstance is set in the method - this.setErtlFGFInstance(FGEnvOption.valueOf(this.environmentModeSetting.get())); - this.cycleFinderSetting.set(ErtlFunctionalGroupsFinderFragmenter.CYCLE_FINDER_OPTION_DEFAULT.name()); - this.setCycleFinderSetting(CycleFinderOption.valueOf(this.cycleFinderSetting.get())); - this.electronDonationModelSetting.set(ErtlFunctionalGroupsFinderFragmenter.Electron_Donation_MODEL_OPTION_DEFAULT.name()); + this.setErtlFGFInstance(ErtlFunctionalGroupsFinderFragmenter.ENVIRONMENT_MODE_OPTION_DEFAULT); + this.cycleFinderSetting.set(ErtlFunctionalGroupsFinderFragmenter.CYCLE_FINDER_OPTION_DEFAULT); + this.setCycleFinderSetting(ErtlFunctionalGroupsFinderFragmenter.CYCLE_FINDER_OPTION_DEFAULT); + this.electronDonationModelSetting.set(ErtlFunctionalGroupsFinderFragmenter.Electron_Donation_MODEL_OPTION_DEFAULT); //this.aromaticityModel is set in the method this.setAromaticityInstance(this.electronDonationInstance, this.cycleFinderInstance); - this.fragmentSaturationSetting.set(IMoleculeFragmenter.FRAGMENT_SATURATION_OPTION_DEFAULT.name()); - this.returnedFragmentsSetting.set(ErtlFunctionalGroupsFinderFragmenter.RETURNED_FRAGMENTS_OPTION_DEFAULT.name()); + this.fragmentSaturationSetting.set(IMoleculeFragmenter.FRAGMENT_SATURATION_OPTION_DEFAULT); + this.returnedFragmentsSetting.set(ErtlFunctionalGroupsFinderFragmenter.RETURNED_FRAGMENTS_OPTION_DEFAULT); this.filterSingleAtomsSetting.set(ErtlFunctionalGroupsFinderFragmenter.FILTER_SINGLE_ATOMS_OPTION_DEFAULT); this.applyInputRestrictionsSetting.set(ErtlFunctionalGroupsFinderFragmenter.APPLY_INPUT_RESTRICTIONS_OPTION_DEFAULT); } @@ -844,7 +763,7 @@ public List fragmentMolecule(IAtomContainer aMolecule) tmpAtom.setProperty(ErtlFunctionalGroupsFinderFragmenter.INTERNAL_INDEX_PROPERTY_KEY, i); tmpIdToAtomMap.put(i, tmpAtom); } - List tmpFunctionalGroupFragments = null; + List tmpFunctionalGroupFragments; List tmpNonFGFragments = null; try { //generate FG fragments using EFGF @@ -854,25 +773,25 @@ public List fragmentMolecule(IAtomContainer aMolecule) //post-processing FG fragments tmpFunctionalGroup.setProperty(IMoleculeFragmenter.FRAGMENT_CATEGORY_PROPERTY_KEY, ErtlFunctionalGroupsFinderFragmenter.FRAGMENT_CATEGORY_FUNCTIONAL_GROUP_VALUE); - if (this.fragmentSaturationSetting.get().equals(FragmentSaturationOption.HYDROGEN_SATURATION.name())) { + if (this.fragmentSaturationSetting.get().equals(FragmentSaturationOption.HYDROGEN_SATURATION)) { ChemUtil.saturateWithHydrogen(tmpFunctionalGroup); } ChemUtil.checkAndCorrectElectronConfiguration(tmpFunctionalGroup); //FG fragments are removed from molecule to generate alkane fragments - if (this.returnedFragmentsSetting.get().equals(EFGFFragmenterReturnedFragmentsOption.ALL_FRAGMENTS.name()) - || this.returnedFragmentsSetting.get().equals(EFGFFragmenterReturnedFragmentsOption.ONLY_ALKANE_FRAGMENTS.name())) { + if (this.returnedFragmentsSetting.get().equals(ErtlFunctionalGroupsFinderFragmenter.EFGFFragmenterReturnedFragmentsOption.ALL_FRAGMENTS) + || this.returnedFragmentsSetting.get().equals(ErtlFunctionalGroupsFinderFragmenter.EFGFFragmenterReturnedFragmentsOption.ONLY_ALKANE_FRAGMENTS)) { for (IAtom tmpAtom : tmpFunctionalGroup.atoms()) { //FG fragments contain new atoms added by EFGF, they must not be removed from the original molecule if (!Objects.isNull(tmpAtom.getProperty(ErtlFunctionalGroupsFinderFragmenter.INTERNAL_INDEX_PROPERTY_KEY))) { - int tmpIndex = tmpAtom.getProperty("EFGFFragmenter.INDEX"); + int tmpIndex = tmpAtom.getProperty(ErtlFunctionalGroupsFinderFragmenter.INTERNAL_INDEX_PROPERTY_KEY); tmpMoleculeClone.removeAtom(tmpIdToAtomMap.get(tmpIndex)); } } } } //Partition unconnected alkane fragments in distinct atom containers - if (this.returnedFragmentsSetting.get().equals(EFGFFragmenterReturnedFragmentsOption.ALL_FRAGMENTS.name()) - || this.returnedFragmentsSetting.get().equals(EFGFFragmenterReturnedFragmentsOption.ONLY_ALKANE_FRAGMENTS.name())) { + if (this.returnedFragmentsSetting.get().equals(ErtlFunctionalGroupsFinderFragmenter.EFGFFragmenterReturnedFragmentsOption.ALL_FRAGMENTS) + || this.returnedFragmentsSetting.get().equals(ErtlFunctionalGroupsFinderFragmenter.EFGFFragmenterReturnedFragmentsOption.ONLY_ALKANE_FRAGMENTS)) { if (!tmpMoleculeClone.isEmpty()) { IAtomContainerSet tmpPartitionedMoietiesSet = ConnectivityChecker.partitionIntoMolecules(tmpMoleculeClone); tmpNonFGFragments = new ArrayList<>(tmpPartitionedMoietiesSet.getAtomContainerCount()); @@ -880,7 +799,7 @@ public List fragmentMolecule(IAtomContainer aMolecule) //post-processing of alkane fragments tmpContainer.setProperty(IMoleculeFragmenter.FRAGMENT_CATEGORY_PROPERTY_KEY, ErtlFunctionalGroupsFinderFragmenter.FRAGMENT_CATEGORY_ALKANE_VALUE); - if (this.fragmentSaturationSetting.get().equals(FragmentSaturationOption.HYDROGEN_SATURATION.name())) { + if (this.fragmentSaturationSetting.get().equals(FragmentSaturationOption.HYDROGEN_SATURATION)) { ChemUtil.saturateWithHydrogen(tmpContainer); } ChemUtil.checkAndCorrectElectronConfiguration(tmpContainer); @@ -892,10 +811,10 @@ public List fragmentMolecule(IAtomContainer aMolecule) } } else { //no FG identified - List tmpReturnList = new ArrayList(1); - if (this.returnedFragmentsSetting.get().equals(EFGFFragmenterReturnedFragmentsOption.ALL_FRAGMENTS.name()) - || this.returnedFragmentsSetting.get().equals(EFGFFragmenterReturnedFragmentsOption.ONLY_ALKANE_FRAGMENTS.name())) { - tmpReturnList.add(0, tmpMoleculeClone); + List tmpReturnList = new ArrayList<>(1); + if (this.returnedFragmentsSetting.get().equals(ErtlFunctionalGroupsFinderFragmenter.EFGFFragmenterReturnedFragmentsOption.ALL_FRAGMENTS) + || this.returnedFragmentsSetting.get().equals(ErtlFunctionalGroupsFinderFragmenter.EFGFFragmenterReturnedFragmentsOption.ONLY_ALKANE_FRAGMENTS)) { + tmpReturnList.addFirst(tmpMoleculeClone); tmpMoleculeClone.setProperty(IMoleculeFragmenter.FRAGMENT_CATEGORY_PROPERTY_KEY, ErtlFunctionalGroupsFinderFragmenter.FRAGMENT_CATEGORY_ALKANE_VALUE); } @@ -906,16 +825,20 @@ public List fragmentMolecule(IAtomContainer aMolecule) throw new IllegalArgumentException("An error occurred during fragmentation: " + anException.toString() + " Molecule Name: " + aMolecule.getProperty(Importer.MOLECULE_NAME_PROPERTY_KEY)); } List tmpFragments; - if (this.returnedFragmentsSetting.get().equals(EFGFFragmenterReturnedFragmentsOption.ALL_FRAGMENTS.name())) { - tmpFragments = new ArrayList(tmpFunctionalGroupFragments.size() + (tmpNonFGFragments == null ? 0 : tmpNonFGFragments.size())); + if (this.returnedFragmentsSetting.get().equals(ErtlFunctionalGroupsFinderFragmenter.EFGFFragmenterReturnedFragmentsOption.ALL_FRAGMENTS)) { + tmpFragments = new ArrayList<>(tmpFunctionalGroupFragments.size() + (tmpNonFGFragments == null ? 0 : tmpNonFGFragments.size())); tmpFragments.addAll(tmpFunctionalGroupFragments); tmpFragments.addAll(tmpNonFGFragments); - } else if (this.returnedFragmentsSetting.get().equals(EFGFFragmenterReturnedFragmentsOption.ONLY_FUNCTIONAL_GROUPS.name())) { - tmpFragments = new ArrayList(tmpFunctionalGroupFragments.size()); + } else if (this.returnedFragmentsSetting.get().equals(ErtlFunctionalGroupsFinderFragmenter.EFGFFragmenterReturnedFragmentsOption.ONLY_FUNCTIONAL_GROUPS)) { + tmpFragments = new ArrayList<>(tmpFunctionalGroupFragments.size()); tmpFragments.addAll(tmpFunctionalGroupFragments); - } else if (this.returnedFragmentsSetting.get().equals(EFGFFragmenterReturnedFragmentsOption.ONLY_ALKANE_FRAGMENTS.name())) { - tmpFragments = new ArrayList(tmpNonFGFragments.size()); - tmpFragments.addAll(tmpNonFGFragments); + } else if (this.returnedFragmentsSetting.get().equals(ErtlFunctionalGroupsFinderFragmenter.EFGFFragmenterReturnedFragmentsOption.ONLY_ALKANE_FRAGMENTS)) { + if (!Objects.isNull(tmpNonFGFragments)) { + tmpFragments = new ArrayList<>(tmpNonFGFragments.size()); + tmpFragments.addAll(tmpNonFGFragments); + } else { + tmpFragments = new ArrayList<>(0); + } } else { throw new IllegalStateException("Unknown return fragments setting option has been set."); } @@ -997,33 +920,33 @@ private void setAromaticityInstance(ElectronDonation anElectronDonation, CycleFi /** * Calling method needs to update the aromaticity model! */ - private void setCycleFinderInstance(CycleFinderOption anOption) throws NullPointerException { + private void setCycleFinderInstance(IMoleculeFragmenter.CycleFinderOption anOption) throws NullPointerException { //Developer comment: the switch way is used instead of having the CycleFinder objects as variables of the enum constants // to not have static objects becoming bottlenecks in parallelization. Objects.requireNonNull(anOption, "Given option is null."); switch (anOption) { - case ALL: + case IMoleculeFragmenter.CycleFinderOption.ALL: this.cycleFinderInstance = Cycles.or(Cycles.all(), ErtlFunctionalGroupsFinderFragmenter.AUXILIARY_CYCLE_FINDER); break; - case MCB: + case IMoleculeFragmenter.CycleFinderOption.MCB: this.cycleFinderInstance = Cycles.or(Cycles.mcb(), ErtlFunctionalGroupsFinderFragmenter.AUXILIARY_CYCLE_FINDER); break; - case RELEVANT: + case IMoleculeFragmenter.CycleFinderOption.RELEVANT: this.cycleFinderInstance = Cycles.or(Cycles.relevant(), ErtlFunctionalGroupsFinderFragmenter.AUXILIARY_CYCLE_FINDER); break; - case ESSENTIAL: + case IMoleculeFragmenter.CycleFinderOption.ESSENTIAL: this.cycleFinderInstance = Cycles.or(Cycles.essential(), ErtlFunctionalGroupsFinderFragmenter.AUXILIARY_CYCLE_FINDER); break; - case EDGE_SHORT: + case IMoleculeFragmenter.CycleFinderOption.EDGE_SHORT: this.cycleFinderInstance = Cycles.or(Cycles.edgeShort(), ErtlFunctionalGroupsFinderFragmenter.AUXILIARY_CYCLE_FINDER); break; - case VERTEX_SHORT: + case IMoleculeFragmenter.CycleFinderOption.VERTEX_SHORT: this.cycleFinderInstance = Cycles.or(Cycles.vertexShort(), ErtlFunctionalGroupsFinderFragmenter.AUXILIARY_CYCLE_FINDER); break; - case TRIPLET_SHORT: + case IMoleculeFragmenter.CycleFinderOption.TRIPLET_SHORT: this.cycleFinderInstance = Cycles.or(Cycles.tripletShort(), ErtlFunctionalGroupsFinderFragmenter.AUXILIARY_CYCLE_FINDER); break; - case CDK_AROMATIC_SET: + case IMoleculeFragmenter.CycleFinderOption.CDK_AROMATIC_SET: this.cycleFinderInstance = Cycles.cdkAromaticSet(); break; default: @@ -1034,21 +957,21 @@ private void setCycleFinderInstance(CycleFinderOption anOption) throws NullPoint /** * Calling method needs to update the aromaticity model! */ - private void setElectronDonationInstance(ElectronDonationModelOption anOption) throws NullPointerException { + private void setElectronDonationInstance(IMoleculeFragmenter.ElectronDonationModelOption anOption) throws NullPointerException { //Developer comment: the switch way is used instead of having the CycleFinder objects as variables of the enum constants // to not have static objects becoming bottlenecks in parallelization. Objects.requireNonNull(anOption, "Given option is null."); switch (anOption) { - case CDK: + case IMoleculeFragmenter.ElectronDonationModelOption.CDK: this.electronDonationInstance = ElectronDonation.cdk(); break; - case DAYLIGHT: + case IMoleculeFragmenter.ElectronDonationModelOption.DAYLIGHT: this.electronDonationInstance = ElectronDonation.daylight(); break; - case CDK_ALLOWING_EXOCYCLIC: + case IMoleculeFragmenter.ElectronDonationModelOption.CDK_ALLOWING_EXOCYCLIC: this.electronDonationInstance = ElectronDonation.cdkAllowingExocyclic(); break; - case PI_BONDS: + case IMoleculeFragmenter.ElectronDonationModelOption.PI_BONDS: this.electronDonationInstance = ElectronDonation.piBonds(); break; default: @@ -1059,9 +982,9 @@ private void setElectronDonationInstance(ElectronDonationModelOption anOption) t /** * Sets only the instance, not the property! So it is safe for the property to call this method when overriding set(). */ - private void setErtlFGFInstance(FGEnvOption anOption) throws NullPointerException { + private void setErtlFGFInstance(ErtlFunctionalGroupsFinderFragmenter.FGEnvOption anOption) throws NullPointerException { Objects.requireNonNull(anOption, "Given option is null."); - this.ertlFGFInstance = new ErtlFunctionalGroupsFinder(anOption.getAssociatedEFGFMode()); + this.ertlFGFInstance.setEnvMode(anOption.getAssociatedEFGFMode()); } // } diff --git a/src/main/java/de/unijena/cheminf/mortar/model/fragmentation/algorithm/IMoleculeFragmenter.java b/src/main/java/de/unijena/cheminf/mortar/model/fragmentation/algorithm/IMoleculeFragmenter.java index ce8f0f4a..8e73d245 100644 --- a/src/main/java/de/unijena/cheminf/mortar/model/fragmentation/algorithm/IMoleculeFragmenter.java +++ b/src/main/java/de/unijena/cheminf/mortar/model/fragmentation/algorithm/IMoleculeFragmenter.java @@ -25,8 +25,11 @@ package de.unijena.cheminf.mortar.model.fragmentation.algorithm; +import de.unijena.cheminf.mortar.message.Message; import de.unijena.cheminf.mortar.model.fragmentation.FragmentationService; +import de.unijena.cheminf.mortar.model.util.IDisplayEnum; import de.unijena.cheminf.mortar.model.util.SimpleEnumConstantNameProperty; +import de.unijena.cheminf.mortar.model.util.SimpleIDisplayEnumConstantProperty; import javafx.beans.property.Property; @@ -54,11 +57,13 @@ * {@link IMoleculeFragmenter#settingsProperties()}. Boolean settings must be implemented as * {@link javafx.beans.property.SimpleBooleanProperty}, integer settings as * {@link javafx.beans.property.SimpleIntegerProperty} etc. For settings where an option must be chosen from multiple - * available ones, a special Property class is implemented in MORTAR, {@link SimpleEnumConstantNameProperty}. The + * available ones, two special Property classes are implemented in MORTAR, {@link SimpleEnumConstantNameProperty} + * and {@link SimpleIDisplayEnumConstantProperty}. The * options to choose from must be implemented as enum constants and the setting property linked to the enum. If changes * to the settings done in the GUI must be tested, it is recommended to override the Property.set() method and implement * the parameter test logic there. Tooltip texts for the settings must be given in a HashMap with setting (property) names - * as keys and tooltip text as values (see {@link IMoleculeFragmenter#getSettingNameToTooltipTextMap()}). One setting that + * as keys and tooltip text as values (see {@link IMoleculeFragmenter#getSettingNameToTooltipTextMap()}). + * Similarly, names for the settings that are language-specific and can be displayed in the GUI must be given. One setting that * must be available is the fragment saturation setting that is already laid out in this interface, see below. *
*
More details can be found in the method documentations of this interface. @@ -70,22 +75,188 @@ * @version 1.0.0.0 */ public interface IMoleculeFragmenter { - // + // /** - * Enumeration of different ways to saturate free valences of returned fragment molecules + * Enumeration of different ways to saturate free valences of returned fragment molecules. */ - public static enum FragmentSaturationOption { + public static enum FragmentSaturationOption implements IDisplayEnum { /** * Do not saturate free valences or use default of the respective fragmenter. */ - NO_SATURATION, - + NO_SATURATION(Message.get("IMoleculeFragmenter.FragmentSaturationOption.noSaturation.displayName"), + Message.get("IMoleculeFragmenter.FragmentSaturationOption.noSaturation.tooltip")), /** * Saturate free valences with (implicit) hydrogen atoms. */ - HYDROGEN_SATURATION; + HYDROGEN_SATURATION(Message.get("IMoleculeFragmenter.FragmentSaturationOption.hydrogenSaturation.displayName"), + Message.get("IMoleculeFragmenter.FragmentSaturationOption.hydrogenSaturation.tooltip")); + /** + * Language-specific name for display in GUI. + */ + private final String displayName; + /** + * Language-specific tooltip text for display in GUI. + */ + private final String tooltip; + /** + * Constructor setting the display name and tooltip. + * + * @param aDisplayName display name + * @param aTooltip tooltip text + */ + private FragmentSaturationOption(String aDisplayName, String aTooltip) { + this.displayName = aDisplayName; + this.tooltip = aTooltip; + } + // + @Override + public String getDisplayName() { + return this.displayName; + } + // + @Override + public String getTooltipText() { + return this.tooltip; + } + } + // + // + // + /** + * Enum for available electron donation models that combined with a cycle finder algorithm is used to define an + * aromaticity model to use. Utility for defining the options in a GUI. The electron + * donation model specified in the constant name is used and a cycle finder algorithm set via the respective option. + */ + public static enum ElectronDonationModelOption implements IDisplayEnum { + /** + * Daylight electron donation model. + */ + DAYLIGHT(Message.get("IMoleculeFragmenter.ElectronDonationModelOption.daylight.displayName"), + Message.get("IMoleculeFragmenter.ElectronDonationModelOption.daylight.tooltip")), + /** + * CDK electron donation model. + */ + CDK(Message.get("IMoleculeFragmenter.ElectronDonationModelOption.cdk.displayName"), + Message.get("IMoleculeFragmenter.ElectronDonationModelOption.cdk.tooltip")), + /** + * CDK electron donation model that additionally allows exocyclic bonds to contribute electrons to the aromatic system. + */ + CDK_ALLOWING_EXOCYCLIC(Message.get("IMoleculeFragmenter.ElectronDonationModelOption.cdkAllowingExocyclic.displayName"), + Message.get("IMoleculeFragmenter.ElectronDonationModelOption.cdkAllowingExocyclic.tooltip")), + /** + * Pi bonds electron donation model. + */ + PI_BONDS(Message.get("IMoleculeFragmenter.ElectronDonationModelOption.piBonds.displayName"), + Message.get("IMoleculeFragmenter.ElectronDonationModelOption.piBonds.tooltip")); + /** + * Language-specific name for display in GUI. + */ + private final String displayName; + /** + * Language-specific tooltip text for display in GUI. + */ + private final String tooltip; + /** + * Constructor setting the display name and tooltip. + * + * @param aDisplayName display name + * @param aTooltip tooltip text + */ + private ElectronDonationModelOption(String aDisplayName, String aTooltip) { + this.displayName = aDisplayName; + this.tooltip = aTooltip; + } + // + @Override + public String getDisplayName() { + return this.displayName; + } + // + @Override + public String getTooltipText() { + return this.tooltip; + } } // + // + // + /** + * Enum for defining which cycle finder algorithm should be used to define an aromaticity model. The electron + * donation model is set via the respective option. See CDK class "Cycles" for more detailed descriptions of the + * available cycle finders. + */ + public static enum CycleFinderOption implements IDisplayEnum { + /** + * Algorithm that tries to find all possible rings in a given structure. Might cause IntractableException. + */ + ALL(Message.get("IMoleculeFragmenter.CycleFinderOption.all.displayName"), + Message.get("IMoleculeFragmenter.CycleFinderOption.all.tooltip")), + /** + * Algorithm that looks for cycles usually checked by the CDK when detecting aromaticity. + */ + CDK_AROMATIC_SET(Message.get("IMoleculeFragmenter.CycleFinderOption.cdkAromaticSet.displayName"), + Message.get("IMoleculeFragmenter.CycleFinderOption.cdkAromaticSet.tooltip")), + /** + * Gives the shortest cycles through each edge. + */ + EDGE_SHORT(Message.get("IMoleculeFragmenter.CycleFinderOption.edgeShort.displayName"), + Message.get("IMoleculeFragmenter.CycleFinderOption.edgeShort.tooltip")), + /** + * Unique set of essential cycles of a molecule. + */ + ESSENTIAL(Message.get("IMoleculeFragmenter.CycleFinderOption.essential.displayName"), + Message.get("IMoleculeFragmenter.CycleFinderOption.essential.tooltip")), + /** + * Minimum Cycle Basis (MCB, aka. SSSR - smallest set of smallest rings). + */ + MCB(Message.get("IMoleculeFragmenter.CycleFinderOption.mcb.displayName"), + Message.get("IMoleculeFragmenter.CycleFinderOption.mcb.tooltip")), + /** + * Union of all possible MCB cycle sets of a molecule. + */ + RELEVANT(Message.get("IMoleculeFragmenter.CycleFinderOption.relevant.displayName"), + Message.get("IMoleculeFragmenter.CycleFinderOption.relevant.tooltip")), + /** + * Shortest cycle through each triple of vertices. + */ + TRIPLET_SHORT(Message.get("IMoleculeFragmenter.CycleFinderOption.tripletShort.displayName"), + Message.get("IMoleculeFragmenter.CycleFinderOption.tripletShort.tooltip")), + /** + * Shortest cycles through each vertex. + */ + VERTEX_SHORT(Message.get("IMoleculeFragmenter.CycleFinderOption.vertexShort.displayName"), + Message.get("IMoleculeFragmenter.CycleFinderOption.vertexShort.tooltip")); + /** + * Language-specific name for display in GUI. + */ + private final String displayName; + /** + * Language-specific tooltip text for display in GUI. + */ + private final String tooltip; + /** + * Constructor setting the display name and tooltip. + * + * @param aDisplayName display name + * @param aTooltip tooltip text + */ + private CycleFinderOption(String aDisplayName, String aTooltip) { + this.displayName = aDisplayName; + this.tooltip = aTooltip; + } + // + @Override + public String getDisplayName() { + return this.displayName; + } + // + @Override + public String getTooltipText() { + return this.tooltip; + } + } + // + // // /** * Property key/name to assign a category to a fragment, represented by an IAtomContainer, e.g. 'aglycone' or @@ -105,7 +276,7 @@ public static enum FragmentSaturationOption { * * @return list of settings represented by properties */ - public List settingsProperties(); + public List> settingsProperties(); /** * Returns a map containing descriptive texts (values) for the settings with the given names (keys) to be used as @@ -116,42 +287,43 @@ public static enum FragmentSaturationOption { public Map getSettingNameToTooltipTextMap(); /** - * Returns a string representation of the algorithm name, e.g. "ErtlFunctionalGroupsFinder" or "Ertl algorithm". - * The given name must be unique among the available fragmentation algorithms! + * Returns a map containing language-specific names (values) for the settings with the given names (keys) to be used + * in the GUI. * - * @return algorithm name + * @return map with display names */ - public String getFragmentationAlgorithmName(); + public Map getSettingNameToDisplayNameMap(); /** - * Returns the currently set option for saturating free valences on returned fragment molecules. + * Returns a string representation of the algorithm name, e.g. "ErtlFunctionalGroupsFinder" or "Ertl algorithm". + * The given name must be unique among the available fragmentation algorithms! It is mostly used internal for + * persistence. For other functionalities, the display name (see below) is used. * - * @return the set option + * @return algorithm name */ - public String getFragmentSaturationSetting(); + public String getFragmentationAlgorithmName(); /** - * Returns the property representing the setting for fragment saturation. + * Returns a language-specific name of the fragmenter to be used in the GUI. + * The given name must be unique among the available fragmentation algorithms! * - * @return setting property for fragment saturation + * @return language-specific name for display in GUI */ - public SimpleEnumConstantNameProperty fragmentSaturationSettingProperty(); + public String getFragmentationAlgorithmDisplayName(); /** - * Returns the currently set fragment saturation option as the respective enum constant. + * Returns the currently set option for saturating free valences on returned fragment molecules. * - * @return fragment saturation setting enum constant + * @return the set option */ - public FragmentSaturationOption getFragmentSaturationSettingConstant(); + public FragmentSaturationOption getFragmentSaturationSetting(); /** - * Sets the option for saturating free valences on returned fragment molecules. + * Returns the property representing the setting for fragment saturation. * - * @param anOptionName constant name (use name()) from FragmentSaturationOption enum - * @throws NullPointerException if the given name is null - * @throws IllegalArgumentException if the given string does not represent an enum constant + * @return setting property for fragment saturation */ - public void setFragmentSaturationSetting(String anOptionName) throws NullPointerException, IllegalArgumentException; + public SimpleIDisplayEnumConstantProperty fragmentSaturationSettingProperty(); /** * Sets the option for saturating free valences on returned fragment molecules. diff --git a/src/main/java/de/unijena/cheminf/mortar/model/fragmentation/algorithm/ScaffoldGeneratorFragmenter.java b/src/main/java/de/unijena/cheminf/mortar/model/fragmentation/algorithm/ScaffoldGeneratorFragmenter.java index 7590b850..21c007d0 100644 --- a/src/main/java/de/unijena/cheminf/mortar/model/fragmentation/algorithm/ScaffoldGeneratorFragmenter.java +++ b/src/main/java/de/unijena/cheminf/mortar/model/fragmentation/algorithm/ScaffoldGeneratorFragmenter.java @@ -29,7 +29,8 @@ import de.unijena.cheminf.mortar.message.Message; import de.unijena.cheminf.mortar.model.util.BasicDefinitions; import de.unijena.cheminf.mortar.model.util.CollectionUtil; -import de.unijena.cheminf.mortar.model.util.SimpleEnumConstantNameProperty; +import de.unijena.cheminf.mortar.model.util.IDisplayEnum; +import de.unijena.cheminf.mortar.model.util.SimpleIDisplayEnumConstantProperty; import javafx.beans.property.Property; import javafx.beans.property.SimpleBooleanProperty; @@ -59,151 +60,247 @@ * @version 1.0.0.0 */ public class ScaffoldGeneratorFragmenter implements IMoleculeFragmenter { - - // + // /** - * Enum for available electron donation models that combined with a cycle finder algorithm is used to define an - * aromaticity model to use. Utility for defining the options in a GUI. The electron - * donation model specified in the constant name is used and a cycle finder algorithm set via the respective option. + * Enum for defining which SmiFlavor is used for the SmilesGenerator. + * The SmilesGenerator is used for the enumerative fragmentation. */ - public static enum ElectronDonationModelOption { + public static enum SmilesGeneratorOption implements IDisplayEnum { /** - * Daylight electron donation model. + * Output canonical SMILES without stereochemistry and without atomic masses. */ - DAYLIGHT, - + UNIQUE_WITHOUT_STEREO(Message.get("ScaffoldGeneratorFragmenter.SmilesGeneratorOption.UniqueWithoutStereo.displayName"), + Message.get("ScaffoldGeneratorFragmenter.SmilesGeneratorOption.UniqueWithoutStereo.tooltip")), /** - * CDK electron donation model. + * Output canonical SMILES with stereochemistry and without atomic masses. */ - CDK, - + UNIQUE_WITH_STEREO(Message.get("ScaffoldGeneratorFragmenter.SmilesGeneratorOption.UniqueWithStereo.displayName"), + Message.get("ScaffoldGeneratorFragmenter.SmilesGeneratorOption.UniqueWithStereo.tooltip")); /** - * CDK electron donation model that additionally allows exocyclic bonds to contribute electrons to the aromatic system. + * Language-specific name for each constant to display in the GUI. */ - CDK_ALLOWING_EXOCYCLIC, - + private final String displayName; /** - * Pi bonds electron donation model. + * Language-specific tooltip text for display in GUI. */ - PI_BONDS; + private final String tooltip; + /** + * Constructor setting the display name and tooltip. + * + * @param aDisplayName display name + * @param aTooltip tooltip text + */ + private SmilesGeneratorOption(String aDisplayName, String aTooltip) { + this.displayName = aDisplayName; + this.tooltip = aTooltip; + } + // + @Override + public String getDisplayName() { + return this.displayName; + } + // + @Override + public String getTooltipText() { + return this.tooltip; + } } // // - // + // /** - * Enum for defining which cycle finder algorithm should be used to define an aromaticity model. The electron - * donation model is set via the respective option. See CDK class "Cycles" for more detailed descriptions of the - * available cycle finders. + * Enum that defines whether only scaffolds, only side chains, or both are to be generated. */ - public static enum CycleFinderOption { + public static enum SideChainOption implements IDisplayEnum { /** - * Algorithm that tries to find all possible rings in a given structure. Might cause IntractableException. + * Generate only the scaffold without side chains. */ - ALL, - + ONLY_SCAFFOLDS(Message.get("ScaffoldGeneratorFragmenter.SideChainOption.OnlyScaffolds.displayName"), + Message.get("ScaffoldGeneratorFragmenter.SideChainOption.OnlyScaffolds.tooltip")), /** - * Algorithm that looks for cycles usually checked by the CDK when detecting aromaticity. + * Generate only the side chains without scaffolds. */ - CDK_AROMATIC_SET, - + ONLY_SIDE_CHAINS(Message.get("ScaffoldGeneratorFragmenter.SideChainOption.OnlySideChains.displayName"), + Message.get("ScaffoldGeneratorFragmenter.SideChainOption.OnlySideChains.tooltip")), /** - * Gives the shortest cycles through each edge. + * Generate scaffolds and side chains. */ - EDGE_SHORT, - + BOTH(Message.get("ScaffoldGeneratorFragmenter.SideChainOption.ScaffoldsAndSideChains.displayName"), + Message.get("ScaffoldGeneratorFragmenter.SideChainOption.ScaffoldsAndSideChains.tooltip")); /** - * Unique set of essential cycles of a molecule. + * Language-specific name for each constant to display in the GUI. */ - ESSENTIAL, - + private final String displayName; /** - * Minimum Cycle Basis (MCB, aka. SSSR - smallest set of smallest rings). + * Language-specific tooltip text for display in GUI. */ - MCB, - - /** - * Union of all possible MCB cycle sets of a molecule. - */ - RELEVANT, - + private final String tooltip; /** - * Shortest cycle through each triple of vertices. + * Constructor setting the display name and tooltip. + * + * @param aDisplayName display name + * @param aTooltip tooltip text */ - TRIPLET_SHORT, - - /** - * Shortest cycles through each vertex. - */ - VERTEX_SHORT; + private SideChainOption(String aDisplayName, String aTooltip) { + this.displayName = aDisplayName; + this.tooltip = aTooltip; + } + // + @Override + public String getDisplayName() { + return this.displayName; + } + // + @Override + public String getTooltipText() { + return this.tooltip; + } } // // - // + // /** - * Enum for defining which SmiFlavor is used for the SmilesGenerator. - * The SmilesGenerator is used for the enumerative fragmentation. + * Enum for defining which kind of fragmentation is used and shows how much fragmentation is to take place. */ - public static enum SmilesGeneratorOption { - + public static enum FragmentationTypeOption implements IDisplayEnum { /** - * Output canonical SMILES without stereochemistry and without atomic masses. + * {@link ScaffoldGenerator#applyEnumerativeRemoval(IAtomContainer)} is used. */ - UNIQUE_WITHOUT_STEREO, - + ENUMERATIVE(Message.get("ScaffoldGeneratorFragmenter.FragmentationTypeOption.Enumerative.displayName"), + Message.get("ScaffoldGeneratorFragmenter.FragmentationTypeOption.Enumerative.tooltip")), /** - * Output canonical SMILES with stereochemistry and without atomic masses. + * {@link ScaffoldGenerator#applySchuffenhauerRules(IAtomContainer)} is used. */ - UNIQUE_WITH_STEREO; - } - // - // - // - /** - * Enum that defines whether only scaffolds, only sidechains or both are to be generated. - */ - public static enum SideChainOption { - + SCHUFFENHAUER(Message.get("ScaffoldGeneratorFragmenter.FragmentationTypeOption.Schuffenhauer.displayName"), + Message.get("ScaffoldGeneratorFragmenter.FragmentationTypeOption.Schuffenhauer.tooltip")), /** - * Generate only the scaffold without sidechains. + * {@link ScaffoldGenerator#getScaffold(IAtomContainer, boolean)} is used. */ - ONLY_SCAFFOLDS, - + SCAFFOLD_ONLY(Message.get("ScaffoldGeneratorFragmenter.FragmentationTypeOption.Scaffold.displayName"), + Message.get("ScaffoldGeneratorFragmenter.FragmentationTypeOption.Scaffold.tooltip")), /** - * Generate only the sidechains without scaffolds. + * {@link ScaffoldGenerator#getRings(IAtomContainer, boolean)} is used. */ - ONLY_SIDECHAINS, - + RING_DISSECTION(Message.get("ScaffoldGeneratorFragmenter.FragmentationTypeOption.RingDissection.displayName"), + Message.get("ScaffoldGeneratorFragmenter.FragmentationTypeOption.RingDissection.tooltip")); /** - * Generate scaffolds and sidechains. + * Language-specific name for each constant to display in the GUI. */ - SCAFFOLDS_AND_SIDECHAINS; + private final String displayName; + /** + * Language-specific tooltip text for display in GUI. + */ + private final String tooltip; + /** + * Constructor setting the display name and tooltip. + * + * @param aDisplayName display name + * @param aTooltip tooltip text + */ + private FragmentationTypeOption(String aDisplayName, String aTooltip) { + this.displayName = aDisplayName; + this.tooltip = aTooltip; + } + // + @Override + public String getDisplayName() { + return this.displayName; + } + // + @Override + public String getTooltipText() { + return this.tooltip; + } } // // - // + // /** - * Enum for defining which kind of fragmentation is used and shows how much fragmentation is to take place. + * Enum with which the type of scaffolds to be generated can be set. It mirrors ScaffoldGenerator.ScaffoldModeOption + * to be able to have display names and tooltips for the options. */ - public static enum FragmentationTypeOption { + public enum SGFragmenterScaffoldModeOption implements IDisplayEnum { /** - * {@link ScaffoldGenerator#applyEnumerativeRemoval(IAtomContainer)} is used. + * Terminal side chains of the molecule are removed except for any atoms non-single bonded + * directly to linkers or rings, as it is e.g. defined in + * "The Scaffold Tree − Visualization of the Scaffold Universe by Hierarchical Scaffold Classification". */ - ENUMERATIVE_FRAGMENTATION, - + SCAFFOLD(ScaffoldGenerator.ScaffoldModeOption.SCAFFOLD, + Message.get("ScaffoldGeneratorFragmenter.SGFragmenterScaffoldModeOption.Scaffold.displayName"), + Message.get("ScaffoldGeneratorFragmenter.SGFragmenterScaffoldModeOption.Scaffold.tooltip")), /** - * {@link ScaffoldGenerator#applySchuffenhauerRules(IAtomContainer)} is used. + * Murcko frameworks are generated. Based on + * "The Properties of Known Drugs. 1. Molecular Frameworks" by Bemis and Murcko 1996. + * All terminal side chains are removed and only linkers and rings are retained. */ - SCHUFFENHAUER_FRAGMENTATION, - + MURCKO_FRAMEWORK(ScaffoldGenerator.ScaffoldModeOption.MURCKO_FRAMEWORK, + Message.get("ScaffoldGeneratorFragmenter.SGFragmenterScaffoldModeOption.Murcko.displayName"), + Message.get("ScaffoldGeneratorFragmenter.SGFragmenterScaffoldModeOption.Murcko.tooltip")), /** - * {@link ScaffoldGenerator#getScaffold(IAtomContainer, boolean)} is used. + * All side chains are removed, all bonds are converted into single bonds, and all atoms are converted into carbons. + * Naming is based on + * "Molecular Anatomy: a new multi‑dimensional hierarchical scaffold analysis tool" + * by Manelfi et al. 2021. */ - SCAFFOLD_ONLY, - + BASIC_WIRE_FRAME(ScaffoldGenerator.ScaffoldModeOption.BASIC_WIRE_FRAME, + Message.get("ScaffoldGeneratorFragmenter.SGFragmenterScaffoldModeOption.BasicWireFrame.displayName"), + Message.get("ScaffoldGeneratorFragmenter.SGFragmenterScaffoldModeOption.BasicWireFrame.tooltip")), /** - * {@link ScaffoldGenerator#getRings(IAtomContainer, boolean)} is used. + * All side chains are removed and multiple bonds are converted to single bonds, but the atomic elements remain. */ - RING_DISSECTION; + ELEMENTAL_WIRE_FRAME(ScaffoldGenerator.ScaffoldModeOption.ELEMENTAL_WIRE_FRAME, + Message.get("ScaffoldGeneratorFragmenter.SGFragmenterScaffoldModeOption.ElementalWireFrame.displayName"), + Message.get("ScaffoldGeneratorFragmenter.SGFragmenterScaffoldModeOption.ElementalWireFrame.tooltip")), + /** + * All side chains are removed and all atoms are converted into carbons. The order of the remaining bonds is not changed. + * Naming is based on + * "Molecular Anatomy: a new multi‑dimensional hierarchical scaffold analysis tool" + * by Manelfi et al. 2021. + */ + BASIC_FRAMEWORK(ScaffoldGenerator.ScaffoldModeOption.BASIC_FRAMEWORK, + Message.get("ScaffoldGeneratorFragmenter.SGFragmenterScaffoldModeOption.BasicFramework.displayName"), + Message.get("ScaffoldGeneratorFragmenter.SGFragmenterScaffoldModeOption.BasicFramework.tooltip")); + /** + * Mirrored scaffold mode option from ScaffoldGenerator. + */ + private final ScaffoldGenerator.ScaffoldModeOption scaffoldModeOption; + /** + * Language-specific name for each constant to display in the GUI. + */ + private final String displayName; + /** + * Language-specific tooltip text for display in GUI. + */ + private final String tooltip; + /** + * Constructor setting the display name and tooltip. + * + * @param aDisplayName display name + * @param aTooltip tooltip text + */ + private SGFragmenterScaffoldModeOption(ScaffoldGenerator.ScaffoldModeOption anOption, String aDisplayName, String aTooltip) { + this.scaffoldModeOption = anOption; + this.displayName = aDisplayName; + this.tooltip = aTooltip; + } + /** + * Returns the wrapped scaffold mode option from ScaffoldGenerator. + * + * @return scaffold mode option + */ + public ScaffoldGenerator.ScaffoldModeOption getScaffoldModeOption() { + return this.scaffoldModeOption; + } + // + @Override + public String getDisplayName() { + return this.displayName; + } + // + @Override + public String getTooltipText() { + return this.tooltip; + } } // // @@ -216,7 +313,8 @@ public static enum FragmentationTypeOption { /** * Default option for the cycle finder algorithm employed for aromaticity detection. */ - public static final ScaffoldGeneratorFragmenter.CycleFinderOption CYCLE_FINDER_OPTION_DEFAULT = ScaffoldGeneratorFragmenter.CycleFinderOption.CDK_AROMATIC_SET; + public static final IMoleculeFragmenter.CycleFinderOption CYCLE_FINDER_OPTION_DEFAULT = + IMoleculeFragmenter.CycleFinderOption.CDK_AROMATIC_SET; /** * Cycle finder algorithm that is used should the set option cause an IntractableException. @@ -226,23 +324,32 @@ public static enum FragmentationTypeOption { /** * Default electron donation model for aromaticity detection. */ - public static final ScaffoldGeneratorFragmenter.ElectronDonationModelOption Electron_Donation_MODEL_OPTION_DEFAULT - = ScaffoldGeneratorFragmenter.ElectronDonationModelOption.CDK; + public static final IMoleculeFragmenter.ElectronDonationModelOption Electron_Donation_MODEL_OPTION_DEFAULT + = IMoleculeFragmenter.ElectronDonationModelOption.CDK; /** * Default SmiFlavor for the default SmilesGenerator. */ - public static final ScaffoldGeneratorFragmenter.SmilesGeneratorOption SMILES_GENERATOR_OPTION_DEFAULT = SmilesGeneratorOption.UNIQUE_WITHOUT_STEREO; + public static final ScaffoldGeneratorFragmenter.SmilesGeneratorOption SMILES_GENERATOR_OPTION_DEFAULT = + ScaffoldGeneratorFragmenter.SmilesGeneratorOption.UNIQUE_WITHOUT_STEREO; /** * Default fragmentation type. */ - public static final FragmentationTypeOption FRAGMENTATION_TYPE_OPTION_DEFAULT = FragmentationTypeOption.SCHUFFENHAUER_FRAGMENTATION; + public static final ScaffoldGeneratorFragmenter.FragmentationTypeOption FRAGMENTATION_TYPE_OPTION_DEFAULT = + ScaffoldGeneratorFragmenter.FragmentationTypeOption.SCHUFFENHAUER; /** - * Default sidechain option. + * Default side chain option. */ - public static final SideChainOption SIDE_CHAIN_OPTION_DEFAULT = SideChainOption.ONLY_SCAFFOLDS; + public static final ScaffoldGeneratorFragmenter.SideChainOption SIDE_CHAIN_OPTION_DEFAULT = + ScaffoldGeneratorFragmenter.SideChainOption.ONLY_SCAFFOLDS; + + /** + * Default scaffold mode option. + */ + public static final ScaffoldGeneratorFragmenter.SGFragmenterScaffoldModeOption SCAFFOLD_MODE_OPTION_DEFAULT = + SGFragmenterScaffoldModeOption.SCAFFOLD; /** * Scaffolds will be assigned this value for the property with key IMoleculeFragmenter.FRAGMENT_CATEGORY_PROPERTY_KEY. @@ -250,9 +357,9 @@ public static enum FragmentationTypeOption { public static final String FRAGMENT_CATEGORY_SCAFFOLD_VALUE = "SGFragmenter.Scaffold"; /** - * Sidechains will be assigned this value for the property with key IMoleculeFragmenter.FRAGMENT_CATEGORY_PROPERTY_KEY. + * Side chains will be assigned this value for the property with key IMoleculeFragmenter.FRAGMENT_CATEGORY_PROPERTY_KEY. */ - public static final String FRAGMENT_CATEGORY_SIDECHAIN_VALUE = "SGFragmenter.Sidechain"; + public static final String FRAGMENT_CATEGORY_SIDE_CHAIN_VALUE = "SGFragmenter.Sidechain"; /** * Parent scaffolds will be assigned this value for the property with key IMoleculeFragmenter.FRAGMENT_CATEGORY_PROPERTY_KEY. @@ -275,52 +382,54 @@ public static enum FragmentationTypeOption { * A cycle finder instance for construction of the aromaticity model. */ private ElectronDonation electronDonationInstance; - - /** - * Instance of the ScaffoldGenerator class used to do generate fragments or scaffolds from the molecules. - */ - private ScaffoldGenerator scaffoldGeneratorInstance; // // // - private final SimpleEnumConstantNameProperty scaffoldModeSetting; + private final SimpleIDisplayEnumConstantProperty scaffoldModeSetting; private final SimpleBooleanProperty determineAromaticitySetting; - private final SimpleEnumConstantNameProperty smilesGeneratorSetting; + private final SimpleIDisplayEnumConstantProperty smilesGeneratorSetting; private final SimpleBooleanProperty ruleSevenAppliedSetting; private final SimpleBooleanProperty retainOnlyHybridisationsAtAromaticBondsSetting; - private final SimpleEnumConstantNameProperty sideChainSetting; + private final SimpleIDisplayEnumConstantProperty sideChainSetting; - private final SimpleEnumConstantNameProperty fragmentationTypeSetting; + private final SimpleIDisplayEnumConstantProperty fragmentationTypeSetting; - private final SimpleEnumConstantNameProperty cycleFinderSetting; + private final SimpleIDisplayEnumConstantProperty cycleFinderSetting; - /** - * A property that has a constant name from the IMoleculeFragmenter.FragmentSaturationOption enum as value. - */ - private final SimpleEnumConstantNameProperty fragmentSaturationSetting; + private final SimpleIDisplayEnumConstantProperty fragmentSaturationSetting; - private final SimpleEnumConstantNameProperty electronDonationModelSetting; + private final SimpleIDisplayEnumConstantProperty electronDonationModelSetting; /** * All settings of this fragmenter, encapsulated in JavaFX properties for binding in GUI. */ - private final List settings; + private final List> settings; /** * Map to store pairs of {@literal }. */ private final HashMap settingNameTooltipTextMap; + /** + * Map to store pairs of {@literal }. + */ + private final HashMap settingNameDisplayNameMap; + + /** + * Instance of the ScaffoldGenerator class used to do generate fragments or scaffolds from the molecules. + */ + private final ScaffoldGenerator scaffoldGeneratorInstance; + /** * Logger of this class. */ - private final Logger logger = Logger.getLogger(ScaffoldGeneratorFragmenter.class.getName()); + private static final Logger LOGGER = Logger.getLogger(ScaffoldGeneratorFragmenter.class.getName()); // // // @@ -333,17 +442,21 @@ public ScaffoldGeneratorFragmenter() { int tmpInitialCapacityForSettingNameTooltipTextMap = CollectionUtil.calculateInitialHashCollectionCapacity( tmpNumberOfSettingsForTooltipMapSize, BasicDefinitions.DEFAULT_HASH_COLLECTION_LOAD_FACTOR); - this.settingNameTooltipTextMap = new HashMap(tmpInitialCapacityForSettingNameTooltipTextMap, BasicDefinitions.DEFAULT_HASH_COLLECTION_LOAD_FACTOR); - this.fragmentSaturationSetting = new SimpleEnumConstantNameProperty(this, "Fragment saturation setting", - IMoleculeFragmenter.FRAGMENT_SATURATION_OPTION_DEFAULT.name(), IMoleculeFragmenter.FragmentSaturationOption.class) { + this.settingNameTooltipTextMap = new HashMap<>(tmpInitialCapacityForSettingNameTooltipTextMap, BasicDefinitions.DEFAULT_HASH_COLLECTION_LOAD_FACTOR); + this.settingNameDisplayNameMap = new HashMap<>(tmpInitialCapacityForSettingNameTooltipTextMap, BasicDefinitions.DEFAULT_HASH_COLLECTION_LOAD_FACTOR); + this.fragmentSaturationSetting = new SimpleIDisplayEnumConstantProperty(this, "Fragment saturation setting", + IMoleculeFragmenter.FRAGMENT_SATURATION_OPTION_DEFAULT, IMoleculeFragmenter.FragmentSaturationOption.class) { @Override - public void set(String newValue) throws NullPointerException, IllegalArgumentException { + public void set(IDisplayEnum newValue) throws NullPointerException, IllegalArgumentException { try { //call to super.set() for parameter checks super.set(newValue); } catch (NullPointerException | IllegalArgumentException anException) { - ScaffoldGeneratorFragmenter.this.logger.log(Level.WARNING, anException.toString(), anException); - GuiUtil.guiExceptionAlert("Illegal Argument", "Illegal Argument was set", anException.toString(), anException); + ScaffoldGeneratorFragmenter.LOGGER.log(Level.WARNING, anException.toString(), anException); + GuiUtil.guiExceptionAlert(Message.get("Fragmenter.IllegalSettingValue.Title"), + Message.get("Fragmenter.IllegalSettingValue.Header"), + anException.toString(), + anException); //re-throws the exception to properly reset the binding throw anException; } @@ -351,25 +464,31 @@ public void set(String newValue) throws NullPointerException, IllegalArgumentExc }; this.settingNameTooltipTextMap.put(this.fragmentSaturationSetting.getName(), Message.get("ScaffoldGeneratorFragmenter.fragmentSaturationSetting.tooltip")); - this.scaffoldModeSetting = new SimpleEnumConstantNameProperty(this, "Scaffold mode setting", - ScaffoldGenerator.SCAFFOLD_MODE_OPTION_DEFAULT.name(), ScaffoldGenerator.ScaffoldModeOption.class) { + this.settingNameDisplayNameMap.put(this.fragmentSaturationSetting.getName(), + Message.get("ScaffoldGeneratorFragmenter.fragmentSaturationSetting.displayName")); + this.scaffoldModeSetting = new SimpleIDisplayEnumConstantProperty(this, "Scaffold mode setting", + ScaffoldGeneratorFragmenter.SCAFFOLD_MODE_OPTION_DEFAULT, ScaffoldGeneratorFragmenter.SGFragmenterScaffoldModeOption.class) { @Override - public void set(String newValue) throws NullPointerException, IllegalArgumentException { + public void set(IDisplayEnum newValue) throws NullPointerException, IllegalArgumentException { try { - //valueOf() throws IllegalArgumentException - ScaffoldGenerator.ScaffoldModeOption tmpEnumConstant = ScaffoldGenerator.ScaffoldModeOption.valueOf(newValue); - ScaffoldGeneratorFragmenter.this.scaffoldGeneratorInstance.setScaffoldModeSetting(tmpEnumConstant); + super.set(newValue); + ScaffoldGeneratorFragmenter.this.scaffoldGeneratorInstance.setScaffoldModeSetting(((ScaffoldGeneratorFragmenter.SGFragmenterScaffoldModeOption) this.get()).getScaffoldModeOption()); } catch (IllegalArgumentException | NullPointerException anException) { - ScaffoldGeneratorFragmenter.this.logger.log(Level.WARNING, anException.toString(), anException); - GuiUtil.guiExceptionAlert("Illegal Argument", "Illegal Argument was set", anException.toString(), anException); + ScaffoldGeneratorFragmenter.LOGGER.log(Level.WARNING, anException.toString(), anException); + GuiUtil.guiExceptionAlert(Message.get("Fragmenter.IllegalSettingValue.Title"), + Message.get("Fragmenter.IllegalSettingValue.Header"), + anException.toString(), + anException); //re-throws the exception to properly reset the binding throw anException; } - super.set(newValue); + } }; this.settingNameTooltipTextMap.put(this.scaffoldModeSetting.getName(), Message.get("ScaffoldGeneratorFragmenter.scaffoldModeSetting.tooltip")); + this.settingNameDisplayNameMap.put(this.scaffoldModeSetting.getName(), + Message.get("ScaffoldGeneratorFragmenter.scaffoldModeSetting.displayName")); this.determineAromaticitySetting = new SimpleBooleanProperty(this, "Determine aromaticity setting", ScaffoldGenerator.DETERMINE_AROMATICITY_SETTING_DEFAULT) { @Override @@ -381,75 +500,93 @@ public void set(boolean newValue) { }; this.settingNameTooltipTextMap.put(this.determineAromaticitySetting.getName(), Message.get("ScaffoldGeneratorFragmenter.determineAromaticitySetting.tooltip")); + this.settingNameDisplayNameMap.put(this.determineAromaticitySetting.getName(), + Message.get("ScaffoldGeneratorFragmenter.determineAromaticitySetting.displayName")); //note: cycle finder and electron donation model have to be set prior to setting the aromaticity model! - this.cycleFinderSetting = new SimpleEnumConstantNameProperty(this, "Cycle finder algorithm setting", - ScaffoldGeneratorFragmenter.CYCLE_FINDER_OPTION_DEFAULT.name(), - ScaffoldGeneratorFragmenter.CycleFinderOption.class) { + this.cycleFinderSetting = new SimpleIDisplayEnumConstantProperty(this, "Cycle finder algorithm setting", + ScaffoldGeneratorFragmenter.CYCLE_FINDER_OPTION_DEFAULT, + IMoleculeFragmenter.CycleFinderOption.class) { @Override - public void set(String newValue) throws NullPointerException, IllegalArgumentException { + public void set(IDisplayEnum newValue) throws NullPointerException, IllegalArgumentException { try { //call to super.set() for parameter checks super.set(newValue); } catch (NullPointerException | IllegalArgumentException anException) { - ScaffoldGeneratorFragmenter.this.logger.log(Level.WARNING, anException.toString(), anException); - GuiUtil.guiExceptionAlert("Illegal Argument", "Illegal Argument was set", anException.toString(), anException); + ScaffoldGeneratorFragmenter.LOGGER.log(Level.WARNING, anException.toString(), anException); + GuiUtil.guiExceptionAlert(Message.get("Fragmenter.IllegalSettingValue.Title"), + Message.get("Fragmenter.IllegalSettingValue.Header"), + anException.toString(), + anException); //re-throws the exception to properly reset the binding throw anException; } //throws no exception if super.set() throws no exception - ScaffoldGeneratorFragmenter.this.setCycleFinderInstance(ScaffoldGeneratorFragmenter.CycleFinderOption.valueOf(newValue)); + ScaffoldGeneratorFragmenter.this.setCycleFinderInstance( + (IMoleculeFragmenter.CycleFinderOption) this.get()); Aromaticity tmpAromaticity = new Aromaticity(ScaffoldGeneratorFragmenter.this.electronDonationInstance, ScaffoldGeneratorFragmenter.this.cycleFinderInstance); ScaffoldGeneratorFragmenter.this.scaffoldGeneratorInstance.setAromaticityModelSetting(tmpAromaticity); } }; this.settingNameTooltipTextMap.put(this.cycleFinderSetting.getName(), Message.get("ScaffoldGeneratorFragmenter.cycleFinderSetting.tooltip")); - this.setCycleFinderInstance(ScaffoldGeneratorFragmenter.CycleFinderOption.valueOf(this.cycleFinderSetting.get())); - this.electronDonationModelSetting = new SimpleEnumConstantNameProperty(this, "Electron donation model setting", - ScaffoldGeneratorFragmenter.Electron_Donation_MODEL_OPTION_DEFAULT.name(), - ScaffoldGeneratorFragmenter.ElectronDonationModelOption.class) { + this.settingNameDisplayNameMap.put(this.cycleFinderSetting.getName(), + Message.get("ScaffoldGeneratorFragmenter.cycleFinderSetting.displayName")); + this.setCycleFinderInstance((IMoleculeFragmenter.CycleFinderOption) this.cycleFinderSetting.get()); + this.electronDonationModelSetting = new SimpleIDisplayEnumConstantProperty(this, "Electron donation model setting", + ScaffoldGeneratorFragmenter.Electron_Donation_MODEL_OPTION_DEFAULT, + IMoleculeFragmenter.ElectronDonationModelOption.class) { @Override - public void set(String newValue) throws NullPointerException, IllegalArgumentException { + public void set(IDisplayEnum newValue) throws NullPointerException, IllegalArgumentException { try { //call to super.set() for parameter checks super.set(newValue); } catch (NullPointerException | IllegalArgumentException anException) { - ScaffoldGeneratorFragmenter.this.logger.log(Level.WARNING, anException.toString(), anException); - GuiUtil.guiExceptionAlert("Illegal Argument", "Illegal Argument was set", anException.toString(), anException); + ScaffoldGeneratorFragmenter.LOGGER.log(Level.WARNING, anException.toString(), anException); + GuiUtil.guiExceptionAlert(Message.get("Fragmenter.IllegalSettingValue.Title"), + Message.get("Fragmenter.IllegalSettingValue.Header"), + anException.toString(), + anException); //re-throws the exception to properly reset the binding throw anException; } //throws no exception if super.set() throws no exception - ScaffoldGeneratorFragmenter.this.setElectronDonationInstance(ScaffoldGeneratorFragmenter.ElectronDonationModelOption.valueOf(newValue)); + ScaffoldGeneratorFragmenter.this.setElectronDonationInstance((IMoleculeFragmenter.ElectronDonationModelOption) this.get()); Aromaticity tmpAromaticity = new Aromaticity(ScaffoldGeneratorFragmenter.this.electronDonationInstance, ScaffoldGeneratorFragmenter.this.cycleFinderInstance); ScaffoldGeneratorFragmenter.this.scaffoldGeneratorInstance.setAromaticityModelSetting(tmpAromaticity); } }; this.settingNameTooltipTextMap.put(this.electronDonationModelSetting.getName(), Message.get("ScaffoldGeneratorFragmenter.electronDonationModelSetting.tooltip")); - this.setElectronDonationInstance(ScaffoldGeneratorFragmenter.ElectronDonationModelOption.valueOf(this.electronDonationModelSetting.get())); + this.settingNameDisplayNameMap.put(this.electronDonationModelSetting.getName(), + Message.get("ScaffoldGeneratorFragmenter.electronDonationModelSetting.displayName")); + this.setElectronDonationInstance((IMoleculeFragmenter.ElectronDonationModelOption) this.electronDonationModelSetting.get()); Aromaticity tmpAromaticity = new Aromaticity(ScaffoldGeneratorFragmenter.this.electronDonationInstance, ScaffoldGeneratorFragmenter.this.cycleFinderInstance); ScaffoldGeneratorFragmenter.this.scaffoldGeneratorInstance.setAromaticityModelSetting(tmpAromaticity); - this.smilesGeneratorSetting = new SimpleEnumConstantNameProperty(this, "Smiles generator setting", - ScaffoldGeneratorFragmenter.SMILES_GENERATOR_OPTION_DEFAULT.name(), ScaffoldGeneratorFragmenter.SmilesGeneratorOption.class) { + this.smilesGeneratorSetting = new SimpleIDisplayEnumConstantProperty(this, "SMILES generator setting", + ScaffoldGeneratorFragmenter.SMILES_GENERATOR_OPTION_DEFAULT, ScaffoldGeneratorFragmenter.SmilesGeneratorOption.class) { @Override - public void set(String newValue) throws NullPointerException, IllegalArgumentException { + public void set(IDisplayEnum newValue) throws NullPointerException, IllegalArgumentException { try { //call to super.set() for parameter checks super.set(newValue); } catch (NullPointerException | IllegalArgumentException anException) { - ScaffoldGeneratorFragmenter.this.logger.log(Level.WARNING, anException.toString(), anException); - GuiUtil.guiExceptionAlert("Illegal Argument", "Illegal Argument was set", anException.toString(), anException); + ScaffoldGeneratorFragmenter.LOGGER.log(Level.WARNING, anException.toString(), anException); + GuiUtil.guiExceptionAlert(Message.get("Fragmenter.IllegalSettingValue.Title"), + Message.get("Fragmenter.IllegalSettingValue.Header"), + anException.toString(), + anException); //re-throws the exception to properly reset the binding throw anException; } - ScaffoldGeneratorFragmenter.this.setSmilesGeneratorInstance(ScaffoldGeneratorFragmenter.SmilesGeneratorOption.valueOf(newValue)); + ScaffoldGeneratorFragmenter.this.setSmilesGeneratorInstance((ScaffoldGeneratorFragmenter.SmilesGeneratorOption) this.get()); ScaffoldGeneratorFragmenter.this.scaffoldGeneratorInstance.setSmilesGeneratorSetting(smilesGeneratorInstance); } }; this.settingNameTooltipTextMap.put(this.smilesGeneratorSetting.getName(), Message.get("ScaffoldGeneratorFragmenter.smilesGeneratorSetting.tooltip")); - this.setSmilesGeneratorInstance(ScaffoldGeneratorFragmenter.SmilesGeneratorOption.valueOf(this.smilesGeneratorSetting.get())); + this.settingNameDisplayNameMap.put(this.smilesGeneratorSetting.getName(), + Message.get("ScaffoldGeneratorFragmenter.smilesGeneratorSetting.displayName")); + this.setSmilesGeneratorInstance((ScaffoldGeneratorFragmenter.SmilesGeneratorOption) this.smilesGeneratorSetting.get()); ScaffoldGeneratorFragmenter.this.scaffoldGeneratorInstance.setSmilesGeneratorSetting(ScaffoldGeneratorFragmenter.this.smilesGeneratorInstance); this.ruleSevenAppliedSetting = new SimpleBooleanProperty(this, "Rule seven setting", ScaffoldGenerator.RULE_SEVEN_APPLIED_SETTING_DEFAULT) { @@ -462,6 +599,8 @@ public void set(boolean newValue) { }; this.settingNameTooltipTextMap.put(this.ruleSevenAppliedSetting.getName(), Message.get("ScaffoldGeneratorFragmenter.ruleSevenAppliedSetting.tooltip")); + this.settingNameDisplayNameMap.put(this.ruleSevenAppliedSetting.getName(), + Message.get("ScaffoldGeneratorFragmenter.ruleSevenAppliedSetting.displayName")); this.retainOnlyHybridisationsAtAromaticBondsSetting = new SimpleBooleanProperty(this, "Retain only hybridisations at aromatic bonds setting", ScaffoldGenerator.RETAIN_ONLY_HYBRIDISATIONS_AT_AROMATIC_BONDS_SETTING_DEFAULT) { @Override @@ -473,15 +612,20 @@ public void set(boolean newValue) { }; this.settingNameTooltipTextMap.put(this.retainOnlyHybridisationsAtAromaticBondsSetting.getName(), Message.get("ScaffoldGeneratorFragmenter.retainOnlyHybridisationsAtAromaticBondsSetting.tooltip")); - this.fragmentationTypeSetting = new SimpleEnumConstantNameProperty(this, "Fragmentation type setting", - ScaffoldGeneratorFragmenter.FRAGMENTATION_TYPE_OPTION_DEFAULT.name(), ScaffoldGeneratorFragmenter.FragmentationTypeOption.class) { + this.settingNameDisplayNameMap.put(this.retainOnlyHybridisationsAtAromaticBondsSetting.getName(), + Message.get("ScaffoldGeneratorFragmenter.retainOnlyHybridisationsAtAromaticBondsSetting.displayName")); + this.fragmentationTypeSetting = new SimpleIDisplayEnumConstantProperty(this, "Fragmentation type setting", + ScaffoldGeneratorFragmenter.FRAGMENTATION_TYPE_OPTION_DEFAULT, ScaffoldGeneratorFragmenter.FragmentationTypeOption.class) { @Override - public void set(String newValue) throws NullPointerException, IllegalArgumentException { + public void set(IDisplayEnum newValue) throws NullPointerException, IllegalArgumentException { try { super.set(newValue); } catch (NullPointerException | IllegalArgumentException anException) { - ScaffoldGeneratorFragmenter.this.logger.log(Level.WARNING, anException.toString(), anException); - GuiUtil.guiExceptionAlert("Illegal Argument", "Illegal Argument was set", anException.toString(), anException); + ScaffoldGeneratorFragmenter.LOGGER.log(Level.WARNING, anException.toString(), anException); + GuiUtil.guiExceptionAlert(Message.get("Fragmenter.IllegalSettingValue.Title"), + Message.get("Fragmenter.IllegalSettingValue.Header"), + anException.toString(), + anException); //re-throws the exception to properly reset the binding throw anException; } @@ -489,15 +633,20 @@ public void set(String newValue) throws NullPointerException, IllegalArgumentExc }; this.settingNameTooltipTextMap.put(this.fragmentationTypeSetting.getName(), Message.get("ScaffoldGeneratorFragmenter.fragmentationTypeSetting.tooltip")); - this.sideChainSetting = new SimpleEnumConstantNameProperty(this, "Side chain setting", - ScaffoldGeneratorFragmenter.SIDE_CHAIN_OPTION_DEFAULT.name(), ScaffoldGeneratorFragmenter.SideChainOption.class) { + this.settingNameDisplayNameMap.put(this.fragmentationTypeSetting.getName(), + Message.get("ScaffoldGeneratorFragmenter.fragmentationTypeSetting.displayName")); + this.sideChainSetting = new SimpleIDisplayEnumConstantProperty(this, "Side chain setting", + ScaffoldGeneratorFragmenter.SIDE_CHAIN_OPTION_DEFAULT, ScaffoldGeneratorFragmenter.SideChainOption.class) { @Override - public void set(String newValue) throws NullPointerException, IllegalArgumentException { + public void set(IDisplayEnum newValue) throws NullPointerException, IllegalArgumentException { try { super.set(newValue); } catch (NullPointerException | IllegalArgumentException anException) { - ScaffoldGeneratorFragmenter.this.logger.log(Level.WARNING, anException.toString(), anException); - GuiUtil.guiExceptionAlert("Illegal Argument", "Illegal Argument was set", anException.toString(), anException); + ScaffoldGeneratorFragmenter.LOGGER.log(Level.WARNING, anException.toString(), anException); + GuiUtil.guiExceptionAlert(Message.get("Fragmenter.IllegalSettingValue.Title"), + Message.get("Fragmenter.IllegalSettingValue.Header"), + anException.toString(), + anException); //re-throws the exception to properly reset the binding throw anException; } @@ -505,6 +654,8 @@ public void set(String newValue) throws NullPointerException, IllegalArgumentExc }; this.settingNameTooltipTextMap.put(this.sideChainSetting.getName(), Message.get("ScaffoldGeneratorFragmenter.sideChainSetting.tooltip")); + this.settingNameDisplayNameMap.put(this.sideChainSetting.getName(), + Message.get("ScaffoldGeneratorFragmenter.sideChainSetting.displayName")); this.settings = new ArrayList<>(10); this.settings.add(this.fragmentationTypeSetting); this.settings.add(this.fragmentSaturationSetting); @@ -523,27 +674,12 @@ public void set(String newValue) throws NullPointerException, IllegalArgumentExc /** * Sets the scaffold mode setting, defining which form of scaffold is to be created. * - * @param anOptionName name of a constant from the ScaffoldGenerator.ScaffoldModeOption enum - * @throws NullPointerException if the given string is null - * @throws IllegalArgumentException if the given string is not an enum constant name - */ - public void setScaffoldModeSetting(String anOptionName) throws NullPointerException, IllegalArgumentException { - Objects.requireNonNull(anOptionName, "Given option name is null."); - //throws IllegalArgumentException if the given name does not match a constant name in the enum - ScaffoldGenerator.ScaffoldModeOption tmpConstant = ScaffoldGenerator.ScaffoldModeOption.valueOf(anOptionName); - this.setScaffoldModeSetting(tmpConstant); - } - - /** - * Sets the scaffold mode setting, defining which form of scaffold is to be created. - * - * @param anOption a constant from the ScaffoldGenerator.ScaffoldModeOption enum + * @param anOption a constant from the ScaffoldGeneratorFragmenter.SGFragmenterScaffoldModeOption enum * @throws NullPointerException if the given parameter is null */ - public void setScaffoldModeSetting(ScaffoldGenerator.ScaffoldModeOption anOption) throws NullPointerException { + public void setScaffoldModeSetting(ScaffoldGeneratorFragmenter.SGFragmenterScaffoldModeOption anOption) throws NullPointerException { Objects.requireNonNull(anOption, "Given option name is null."); - //throws IllegalArgumentException if the given name does not match a constant name in the enum - this.scaffoldModeSetting.set(anOption.name()); + this.scaffoldModeSetting.set(anOption); } /** @@ -560,69 +696,25 @@ public void setDetermineAromaticitySetting(boolean aBoolean) { * Sets the electron donation model setting. The set electron donation model is used for aromaticity detection in * preprocessing together with the set cycle finder algorithm. * - * @param anOptionName name of a constant from the ElectronDonationModelOption enum - * @throws NullPointerException if the given string is null - * @throws IllegalArgumentException if the given string is not an enum constant name - */ - public void setElectronDonationModelSetting(String anOptionName) throws NullPointerException, IllegalArgumentException { - Objects.requireNonNull(anOptionName, "Given option name is null."); - //throws IllegalArgumentException if the given name does not match a constant name in the enum - ScaffoldGeneratorFragmenter.ElectronDonationModelOption tmpConstant = ScaffoldGeneratorFragmenter.ElectronDonationModelOption.valueOf(anOptionName); - this.setElectronDonationModelSetting(tmpConstant); - } - - /** - * Sets the electron donation model setting. The set electron donation model is used for aromaticity detection in - * preprocessing together with the set cycle finder algorithm. - * - * @param anOption a constant from the ElectronDonationModelOption enum + * @param anOption a constant from the IMoleculeFragmenter.ElectronDonationModelOption enum * @throws NullPointerException is the given parameter is null */ - public void setElectronDonationModelSetting(ScaffoldGeneratorFragmenter.ElectronDonationModelOption anOption) throws NullPointerException { + public void setElectronDonationModelSetting(IMoleculeFragmenter.ElectronDonationModelOption anOption) throws NullPointerException { Objects.requireNonNull(anOption, "Given option is null."); //synchronisation with aromaticity model instance done in overridden set() function of the property - this.electronDonationModelSetting.set(anOption.name()); - } - - /** - * Sets the cycle finder setting. The chosen cycle finder algorithm is used for aromaticity detection in - * preprocessing together with the set electron donation model. - * - * @param anOptionName name of a constant from the CycleFinderOption enum - * @throws NullPointerException if the given string is null - * @throws IllegalArgumentException if the given string is not an enum constant name - */ - public void setCycleFinderSetting(String anOptionName) throws NullPointerException, IllegalArgumentException { - Objects.requireNonNull(anOptionName, "Given option name is null."); - //throws IllegalArgumentException if the given name does not match a constant name in the enum - CycleFinderOption tmpConstant = CycleFinderOption.valueOf(anOptionName); - this.setCycleFinderSetting(tmpConstant); + this.electronDonationModelSetting.set(anOption); } /** * Sets the cycle finder setting. The chosen cycle finder algorithm is used for aromaticity detection in * preprocessing together with the set electron donation model. * - * @param anOption a constant from the CycleFinderOption enum + * @param anOption a constant from the IMoleculeFragmenter.CycleFinderOption enum * @throws NullPointerException if the given parameter is null */ - public void setCycleFinderSetting(CycleFinderOption anOption) throws NullPointerException { + public void setCycleFinderSetting(IMoleculeFragmenter.CycleFinderOption anOption) throws NullPointerException { Objects.requireNonNull(anOption, "Given option is null."); - this.cycleFinderSetting.set(anOption.name()); - } - - /** - * Sets the SMILES generator, defining which smiles generator should be used. - * - * @param anOptionName name of a constant from the SmilesGeneratorOption enum - * @throws NullPointerException if the given string is null - * @throws IllegalArgumentException if the given string is not an enum constant name - */ - public void setSmilesGeneratorSetting(String anOptionName) throws NullPointerException, IllegalArgumentException { - Objects.requireNonNull(anOptionName, "Given option name is null."); - //throws IllegalArgumentException if the given name does not match a constant name in the enum - SmilesGeneratorOption tmpConstant = SmilesGeneratorOption.valueOf(anOptionName); - this.setSmilesGeneratorSetting(tmpConstant); + this.cycleFinderSetting.set(anOption); } /** @@ -631,10 +723,9 @@ public void setSmilesGeneratorSetting(String anOptionName) throws NullPointerExc * @param anOption a constant from the SmilesGeneratorOption enum * @throws NullPointerException if the given parameter is null */ - public void setSmilesGeneratorSetting(SmilesGeneratorOption anOption) throws NullPointerException, IllegalArgumentException { + public void setSmilesGeneratorSetting(ScaffoldGeneratorFragmenter.SmilesGeneratorOption anOption) throws NullPointerException, IllegalArgumentException { Objects.requireNonNull(anOption, "Given option name is null."); - //throws IllegalArgumentException if the given name does not match a constant name in the enum - this.smilesGeneratorSetting.set(anOption.name()); + this.smilesGeneratorSetting.set(anOption); } /** @@ -657,54 +748,26 @@ public void setRetainOnlyHybridisationAtAromaticBondsSetting(boolean aBoolean) { this.retainOnlyHybridisationsAtAromaticBondsSetting.set(aBoolean); } - /** - * Sets the FragmentationType setting, defining which type of fragmentation is applied to the input molecule. - * - * @param anOptionName name of a constant from the FragmentationType enum - * @throws NullPointerException if the given string is null - * @throws IllegalArgumentException if the given string is not an enum constant name - */ - public void setFragmentationTypeSetting(String anOptionName) throws NullPointerException, IllegalArgumentException { - Objects.requireNonNull(anOptionName, "Given option name is null."); - //throws IllegalArgumentException if the given name does not match a constant name in the enum - FragmentationTypeOption tmpConstant = FragmentationTypeOption.valueOf(anOptionName); - this.setFragmentationTypeSetting(tmpConstant); - } - /** * Sets the FragmentationType setting, defining which type of fragmentation is applied to the input molecule. * * @param anOption a constant from the FragmentationType enum * @throws NullPointerException if the given parameter is null */ - public void setFragmentationTypeSetting(FragmentationTypeOption anOption) throws NullPointerException { + public void setFragmentationTypeSetting(ScaffoldGeneratorFragmenter.FragmentationTypeOption anOption) throws NullPointerException { Objects.requireNonNull(anOption, "Given type of fragmentation to remove is null."); - this.fragmentationTypeSetting.set(anOption.name()); + this.fragmentationTypeSetting.set(anOption); } /** - * Sets the SideChain setting, defining whether only scaffolds, only sidechains or both are to be generated. + *Sets the SideChain setting, defining whether only scaffolds, only side chains or both are to be generated. * - * @param anOptionName name of a constant from the SideChain enum - * @throws NullPointerException if the given string is null - * @throws IllegalArgumentException if the given string is not an enum constant name - */ - public void setSideChainSetting(String anOptionName) throws NullPointerException, IllegalArgumentException { - Objects.requireNonNull(anOptionName, "Given option name is null."); - //throws IllegalArgumentException if the given name does not match a constant name in the enum - SideChainOption tmpConstant = SideChainOption.valueOf(anOptionName); - this.setSideChainSetting(tmpConstant); - } - - /** - *Sets the SideChain setting, defining whether only scaffolds, only sidechains or both are to be generated. - * - * @param anOption a constant from the SideChain enum + * @param anOption a constant from the SideChainOption enum * @throws NullPointerException if the given parameter is null */ - public void setSideChainSetting(SideChainOption anOption) throws NullPointerException { + public void setSideChainSetting(ScaffoldGeneratorFragmenter.SideChainOption anOption) throws NullPointerException { Objects.requireNonNull(anOption, "Given type of side chain option is null."); - this.sideChainSetting.set(anOption.name()); + this.sideChainSetting.set(anOption); } // // @@ -730,61 +793,43 @@ public SimpleBooleanProperty retainOnlyHybridisationsAtAromaticBondsSetting() { } /** - * Returns the string representation of the currently set option for the sidechain. + * Returns the currently set option for the side chain. * - * @return enum constant name of the set option + * @return enum constant of the set option */ - public String getSideChainSetting() { - return this.sideChainSetting.get(); + public ScaffoldGeneratorFragmenter.SideChainOption getSideChainSetting() { + return (ScaffoldGeneratorFragmenter.SideChainOption) this.sideChainSetting.get(); } /** - * Returns the property object of the sidechain setting that can be used to configure this setting. - * Property that has a constant name from SideChainOption enum as value. + * Returns the property object of the side chain setting that can be used to configure this setting. + * Property that has a constant from SideChainOption enum as value. * - * @return property object of the returned sidechain setting + * @return property object of the returned side chain setting */ - public SimpleEnumConstantNameProperty sideChainSettingProperty() { + public SimpleIDisplayEnumConstantProperty sideChainSettingProperty() { return this.sideChainSetting; } /** - * Returns the enum constant currently set as option for the side chain. + * Returns the currently set option for the fragmentation type. * - * @return enum constant for side chain setting + * @return enum constant of the set option */ - public SideChainOption getSideChainSettingConstant() { - return SideChainOption.valueOf(this.sideChainSetting.get()); - } - - /** - * Returns the string representation of the currently set option for the fragmentation type. - * - * @return enum constant name of the set option - */ - public String getFragmentationTypeSetting() { - return this.fragmentationTypeSetting.get(); + public ScaffoldGeneratorFragmenter.FragmentationTypeOption getFragmentationTypeSetting() { + return (ScaffoldGeneratorFragmenter.FragmentationTypeOption) this.fragmentationTypeSetting.get(); } /** * Returns the property object of the fragmentation type setting that can be used to configure this setting. - * Property that has a constant name from FragmentationTypeOption enum as value. + * Property that has a constant from FragmentationTypeOption enum as value. * * @return property object of the returned fragmentation type setting */ - public SimpleEnumConstantNameProperty fragmentationTypeSettingProperty() { + public SimpleIDisplayEnumConstantProperty fragmentationTypeSettingProperty() { return this.fragmentationTypeSetting; } - /** - * Returns the enum constant currently set as option for the fragmentation type. - * - * @return enum constant for fragmentation type setting - */ - public FragmentationTypeOption getFragmentationTypeSettingConstant() { - return FragmentationTypeOption.valueOf(this.fragmentationTypeSetting.get()); - } - /** * Returns the current state of retain only hybridisation at aromatic bonds setting * @@ -825,12 +870,12 @@ public SimpleBooleanProperty ruleSevenAppliedSettingProperty() { } /** - * Returns the string representation of the currently set option for the SMILES generator. + * Returns the currently set option for the SMILES generator. * - * @return enum constant name of the set option + * @return enum constant of the set option */ - public String getSmilesGeneratorSetting() { - return this.smilesGeneratorSetting.get(); + public ScaffoldGeneratorFragmenter.SmilesGeneratorOption getSmilesGeneratorSetting() { + return (ScaffoldGeneratorFragmenter.SmilesGeneratorOption) this.smilesGeneratorSetting.get(); } /** @@ -838,19 +883,10 @@ public String getSmilesGeneratorSetting() { * * @return property object of the returned scaffold mode setting */ - public SimpleEnumConstantNameProperty smilesGeneratorSettingProperty() { + public SimpleIDisplayEnumConstantProperty smilesGeneratorSettingProperty() { return this.smilesGeneratorSetting; } - /** - * Returns the enum constant currently set as option for the SMILES generator. - * - * @return enum constant for smiles generator setting - */ - public SmilesGeneratorOption getSmilesGeneratorSettingConstant() { - return SmilesGeneratorOption.valueOf(this.smilesGeneratorSetting.get()); - } - /** * Returns the current state of determine aromaticity setting. * @@ -873,10 +909,10 @@ public SimpleBooleanProperty determineAromaticitySettingProperty() { /** * Returns the string representation of the currently set option for the returned scaffold mode. * - * @return enum constant name of the set option + * @return enum constant of the set option */ - public String getScaffoldModeSetting() { - return this.scaffoldModeSetting.get(); + public ScaffoldGeneratorFragmenter.SGFragmenterScaffoldModeOption getScaffoldModeSetting() { + return (SGFragmenterScaffoldModeOption) this.scaffoldModeSetting.get(); } /** @@ -884,27 +920,18 @@ public String getScaffoldModeSetting() { * * @return property object of the returned scaffold mode setting */ - public SimpleEnumConstantNameProperty scaffoldModeSettingProperty() { + public SimpleIDisplayEnumConstantProperty scaffoldModeSettingProperty() { return this.scaffoldModeSetting; } /** - * Returns the enum constant currently set as option for the returned scaffold mode setting. - * - * @return enum constant for returned scaffold mode setting - */ - public ScaffoldGenerator.ScaffoldModeOption getScaffoldModeSettingConstant() { - return ScaffoldGenerator.ScaffoldModeOption.valueOf(this.scaffoldModeSetting.get()); - } - - /** - * Returns the string representation of the currently set option for the electron donation model setting used for + * Returns the currently set option for the electron donation model setting used for * aromaticity detection together with the set cycle finder algorithm. * - * @return enum constant name of the set option + * @return enum constant of the set option */ - public String getElectronDonationModelSetting() { - return this.electronDonationModelSetting.get(); + public IMoleculeFragmenter.ElectronDonationModelOption getElectronDonationModelSetting() { + return (IMoleculeFragmenter.ElectronDonationModelOption) this.electronDonationModelSetting.get(); } /** @@ -912,54 +939,36 @@ public String getElectronDonationModelSetting() { * * @return property object of the electron donation model setting */ - public SimpleEnumConstantNameProperty electronDonationModelSettingProperty() { + public SimpleIDisplayEnumConstantProperty electronDonationModelSettingProperty() { return this.electronDonationModelSetting; } /** - * Returns the enum constant currently set as option for the electron donation model setting. - * - * @return enum constant for electron donation model setting - */ - public ElectronDonationModelOption getElectronDonationModelSettingConstant() { - return ElectronDonationModelOption.valueOf(this.electronDonationModelSetting.get()); - } - - /** - * Returns the string representation of the currently set option for the cycle finder setting used for aromaticity + * Returns the currently set option for the cycle finder setting used for aromaticity * detection together with the electron donation model setting. * - * @return enum constant name of the set option + * @return enum constant of the set option */ - public String getCycleFinderSetting() { - return this.cycleFinderSetting.get(); + public IMoleculeFragmenter.CycleFinderOption getCycleFinderSetting() { + return (IMoleculeFragmenter.CycleFinderOption) this.cycleFinderSetting.get(); } /** * Returns the property object of the cycle finder setting that can be used to configure this setting. - * A property that has a constant name from the CycleFinderOption enum as value. + * A property that has a constant from the IMoleculeFragmenter.CycleFinderOption enum as value. * * @return property object of the cycle finder setting */ - public SimpleEnumConstantNameProperty cycleFinderSettingProperty() { + public SimpleIDisplayEnumConstantProperty cycleFinderSettingProperty() { return this.cycleFinderSetting; } - - /** - * Returns the enum constant currently set as option for the cycle finder setting. - * - * @return enum constant for cycle finder setting - */ - public CycleFinderOption getCycleFinderSettingConstant() { - return CycleFinderOption.valueOf(this.cycleFinderSetting.get()); - } // // // //without the empty line, the code folding does not work properly here... @Override - public List settingsProperties() { + public List> settingsProperties() { return this.settings; } @@ -969,68 +978,65 @@ public Map getSettingNameToTooltipTextMap() { } @Override - public String getFragmentationAlgorithmName() { - return ScaffoldGeneratorFragmenter.ALGORITHM_NAME; + public Map getSettingNameToDisplayNameMap() { + return this.settingNameDisplayNameMap; } @Override - public String getFragmentSaturationSetting() { - return this.fragmentSaturationSetting.get(); + public String getFragmentationAlgorithmName() { + return ScaffoldGeneratorFragmenter.ALGORITHM_NAME; } @Override - public SimpleEnumConstantNameProperty fragmentSaturationSettingProperty() { - return this.fragmentSaturationSetting; + public String getFragmentationAlgorithmDisplayName() { + return Message.get("ScaffoldGeneratorFragmenter.displayName"); } @Override - public FragmentSaturationOption getFragmentSaturationSettingConstant() { - return FragmentSaturationOption.valueOf(this.fragmentSaturationSetting.get()); + public FragmentSaturationOption getFragmentSaturationSetting() { + return (IMoleculeFragmenter.FragmentSaturationOption) this.fragmentSaturationSetting.get(); } @Override - public void setFragmentSaturationSetting(String anOptionName) throws NullPointerException, IllegalArgumentException { - Objects.requireNonNull(anOptionName, "Given saturation option name is null."); - //throws IllegalArgumentException if the given name does not match a constant name in the enum - FragmentSaturationOption tmpConstant = FragmentSaturationOption.valueOf(anOptionName); - this.fragmentSaturationSetting.set(tmpConstant.name()); + public SimpleIDisplayEnumConstantProperty fragmentSaturationSettingProperty() { + return this.fragmentSaturationSetting; } @Override public void setFragmentSaturationSetting(FragmentSaturationOption anOption) throws NullPointerException { Objects.requireNonNull(anOption, "Given saturation option is null."); - this.fragmentSaturationSetting.set(anOption.name()); + this.fragmentSaturationSetting.set(anOption); } @Override public IMoleculeFragmenter copy() { ScaffoldGeneratorFragmenter tmpCopy = new ScaffoldGeneratorFragmenter(); - tmpCopy.setFragmentSaturationSetting(this.fragmentSaturationSetting.get()); - tmpCopy.setScaffoldModeSetting(this.scaffoldGeneratorInstance.getScaffoldModeSetting().name()); + tmpCopy.setFragmentSaturationSetting((IMoleculeFragmenter.FragmentSaturationOption) this.fragmentSaturationSetting.get()); + tmpCopy.setScaffoldModeSetting((SGFragmenterScaffoldModeOption) this.scaffoldModeSetting.get()); tmpCopy.setDetermineAromaticitySetting(this.determineAromaticitySetting.get()); - tmpCopy.setCycleFinderSetting(this.cycleFinderSetting.get()); - tmpCopy.setElectronDonationModelSetting(this.electronDonationModelSetting.get()); - tmpCopy.setSmilesGeneratorSetting(this.smilesGeneratorSetting.get()); + tmpCopy.setCycleFinderSetting((IMoleculeFragmenter.CycleFinderOption) this.cycleFinderSetting.get()); + tmpCopy.setElectronDonationModelSetting((IMoleculeFragmenter.ElectronDonationModelOption) this.electronDonationModelSetting.get()); + tmpCopy.setSmilesGeneratorSetting((ScaffoldGeneratorFragmenter.SmilesGeneratorOption) this.smilesGeneratorSetting.get()); tmpCopy.setRuleSevenAppliedSetting(this.ruleSevenAppliedSetting.get()); tmpCopy.setRetainOnlyHybridisationAtAromaticBondsSetting(this.retainOnlyHybridisationsAtAromaticBondsSetting.get()); - tmpCopy.setFragmentationTypeSetting(this.fragmentationTypeSetting.get()); - tmpCopy.setSideChainSetting(this.sideChainSetting.get()); + tmpCopy.setFragmentationTypeSetting((ScaffoldGeneratorFragmenter.FragmentationTypeOption) this.fragmentationTypeSetting.get()); + tmpCopy.setSideChainSetting((ScaffoldGeneratorFragmenter.SideChainOption) this.sideChainSetting.get()); return tmpCopy; } @Override public void restoreDefaultSettings() { - this.fragmentSaturationSetting.set(IMoleculeFragmenter.FRAGMENT_SATURATION_OPTION_DEFAULT.name()); - this.scaffoldModeSetting.set(ScaffoldGenerator.SCAFFOLD_MODE_OPTION_DEFAULT.name()); + this.fragmentSaturationSetting.set(IMoleculeFragmenter.FRAGMENT_SATURATION_OPTION_DEFAULT); + this.scaffoldModeSetting.set(ScaffoldGeneratorFragmenter.SCAFFOLD_MODE_OPTION_DEFAULT); this.determineAromaticitySetting.set(ScaffoldGenerator.DETERMINE_AROMATICITY_SETTING_DEFAULT); - this.cycleFinderSetting.set(ScaffoldGeneratorFragmenter.CYCLE_FINDER_OPTION_DEFAULT.name()); - this.setCycleFinderSetting(CycleFinderOption.valueOf(this.cycleFinderSetting.get())); - this.electronDonationModelSetting.set(ScaffoldGeneratorFragmenter.Electron_Donation_MODEL_OPTION_DEFAULT.name()); - this.smilesGeneratorSetting.set(ScaffoldGeneratorFragmenter.SMILES_GENERATOR_OPTION_DEFAULT.name()); + this.cycleFinderSetting.set(ScaffoldGeneratorFragmenter.CYCLE_FINDER_OPTION_DEFAULT); + this.setCycleFinderSetting(ScaffoldGeneratorFragmenter.CYCLE_FINDER_OPTION_DEFAULT); + this.electronDonationModelSetting.set(ScaffoldGeneratorFragmenter.Electron_Donation_MODEL_OPTION_DEFAULT); + this.smilesGeneratorSetting.set(ScaffoldGeneratorFragmenter.SMILES_GENERATOR_OPTION_DEFAULT); this.ruleSevenAppliedSetting.set(ScaffoldGenerator.RULE_SEVEN_APPLIED_SETTING_DEFAULT); this.retainOnlyHybridisationsAtAromaticBondsSetting.set(ScaffoldGenerator.RETAIN_ONLY_HYBRIDISATIONS_AT_AROMATIC_BONDS_SETTING_DEFAULT); - this.setFragmentationTypeSetting(ScaffoldGeneratorFragmenter.FRAGMENTATION_TYPE_OPTION_DEFAULT.name()); - this.setSideChainSetting(ScaffoldGeneratorFragmenter.SIDE_CHAIN_OPTION_DEFAULT.name()); + this.setFragmentationTypeSetting(ScaffoldGeneratorFragmenter.FRAGMENTATION_TYPE_OPTION_DEFAULT); + this.setSideChainSetting(ScaffoldGeneratorFragmenter.SIDE_CHAIN_OPTION_DEFAULT); } @Override @@ -1046,31 +1052,29 @@ public List fragmentMolecule(IAtomContainer aMolecule) throws Nu List tmpSideChainList = new ArrayList<>(); IAtomContainer tmpMoleculeClone = aMolecule.clone(); try { - //Hotfix for aromatic SMILES loader bug: - //Kekulization.kekulize(tmpMoleculeClone); - /*Generate Sidechains*/ - if(this.sideChainSetting.get().equals(SideChainOption.ONLY_SIDECHAINS.name()) || - this.sideChainSetting.get().equals(SideChainOption.SCAFFOLDS_AND_SIDECHAINS.name())) { - boolean tmpSaturateWithHydrogen = this.fragmentSaturationSetting.get().equals(FragmentSaturationOption.HYDROGEN_SATURATION.name()); + /*Generate side chains*/ + if (this.sideChainSetting.get().equals(ScaffoldGeneratorFragmenter.SideChainOption.ONLY_SIDE_CHAINS) || + this.sideChainSetting.get().equals(ScaffoldGeneratorFragmenter.SideChainOption.BOTH)) { + boolean tmpSaturateWithHydrogen = this.fragmentSaturationSetting.get().equals(FragmentSaturationOption.HYDROGEN_SATURATION); tmpSideChainList = this.scaffoldGeneratorInstance.getSideChains(tmpMoleculeClone, tmpSaturateWithHydrogen); - /*Add SideChain Property*/ - for(IAtomContainer tmpSideChain : tmpSideChainList) { + /*Add side chain Property*/ + for (IAtomContainer tmpSideChain : tmpSideChainList) { tmpSideChain.setProperty(IMoleculeFragmenter.FRAGMENT_CATEGORY_PROPERTY_KEY, - ScaffoldGeneratorFragmenter.FRAGMENT_CATEGORY_SIDECHAIN_VALUE); + ScaffoldGeneratorFragmenter.FRAGMENT_CATEGORY_SIDE_CHAIN_VALUE); } } - /*Return only the Sidechains*/ - if(this.sideChainSetting.get().equals(SideChainOption.ONLY_SIDECHAINS.name())) { + /*Return only the side chains*/ + if (this.sideChainSetting.get().equals(ScaffoldGeneratorFragmenter.SideChainOption.ONLY_SIDE_CHAINS)) { return tmpSideChainList; } /*Decomposition according to the Schuffenhauer rules*/ - if(this.fragmentationTypeSetting.get().equals(FragmentationTypeOption.SCHUFFENHAUER_FRAGMENTATION.name())) { + if (this.fragmentationTypeSetting.get().equals(ScaffoldGeneratorFragmenter.FragmentationTypeOption.SCHUFFENHAUER)) { List tmpFragmentList = this.scaffoldGeneratorInstance.applySchuffenhauerRules(tmpMoleculeClone); /*Set fragment category property*/ boolean tmpIsFirstFragment = true; - for(IAtomContainer tmpFragment : tmpFragmentList) { + for (IAtomContainer tmpFragment : tmpFragmentList) { /*First fragment is the scaffold*/ - if(tmpIsFirstFragment) { + if (tmpIsFirstFragment) { tmpIsFirstFragment = false; tmpFragment.setProperty(IMoleculeFragmenter.FRAGMENT_CATEGORY_PROPERTY_KEY, ScaffoldGeneratorFragmenter.FRAGMENT_CATEGORY_SCAFFOLD_VALUE); @@ -1083,7 +1087,7 @@ public List fragmentMolecule(IAtomContainer aMolecule) throws Nu tmpReturnList.addAll(tmpFragmentList); } /*Enumerative decomposition*/ - if(this.fragmentationTypeSetting.get().equals(FragmentationTypeOption.ENUMERATIVE_FRAGMENTATION.name())) { + if (this.fragmentationTypeSetting.get().equals(ScaffoldGeneratorFragmenter.FragmentationTypeOption.ENUMERATIVE)) { List tmpFragmentList = this.scaffoldGeneratorInstance.applyEnumerativeRemoval(tmpMoleculeClone); /*Set fragment category property*/ boolean tmpIsFirstFragment = true; @@ -1102,22 +1106,22 @@ public List fragmentMolecule(IAtomContainer aMolecule) throws Nu tmpReturnList.addAll(tmpFragmentList); } /*Generate the scaffold only*/ - if(this.fragmentationTypeSetting.get().equals(FragmentationTypeOption.SCAFFOLD_ONLY.name())) { - boolean tmpSaturateWithHydrogen = this.fragmentSaturationSetting.get().equals(FragmentSaturationOption.HYDROGEN_SATURATION.name()); + if (this.fragmentationTypeSetting.get().equals(ScaffoldGeneratorFragmenter.FragmentationTypeOption.SCAFFOLD_ONLY)) { + boolean tmpSaturateWithHydrogen = this.fragmentSaturationSetting.get().equals(FragmentSaturationOption.HYDROGEN_SATURATION); IAtomContainer tmpScaffold = this.scaffoldGeneratorInstance.getScaffold(tmpMoleculeClone, tmpSaturateWithHydrogen); //Set Scaffold Property tmpScaffold.setProperty(IMoleculeFragmenter.FRAGMENT_CATEGORY_PROPERTY_KEY, ScaffoldGeneratorFragmenter.FRAGMENT_CATEGORY_SCAFFOLD_VALUE); tmpReturnList.add(tmpScaffold); } - if(this.fragmentationTypeSetting.get().equals(FragmentationTypeOption.RING_DISSECTION.name())) { - boolean tmpSaturateWithHydrogen = this.fragmentSaturationSetting.get().equals(FragmentSaturationOption.HYDROGEN_SATURATION.name()); + if (this.fragmentationTypeSetting.get().equals(ScaffoldGeneratorFragmenter.FragmentationTypeOption.RING_DISSECTION)) { + boolean tmpSaturateWithHydrogen = this.fragmentSaturationSetting.get().equals(FragmentSaturationOption.HYDROGEN_SATURATION); IAtomContainer tmpScaffold = this.scaffoldGeneratorInstance.getScaffold(tmpMoleculeClone, tmpSaturateWithHydrogen); tmpScaffold.setProperty(IMoleculeFragmenter.FRAGMENT_CATEGORY_PROPERTY_KEY, ScaffoldGeneratorFragmenter.FRAGMENT_CATEGORY_SCAFFOLD_VALUE); tmpReturnList.add(tmpScaffold); List tmpFragmentList = this.scaffoldGeneratorInstance.getRings(tmpMoleculeClone, tmpSaturateWithHydrogen); - for(IAtomContainer tmpFragment : tmpFragmentList) { + for (IAtomContainer tmpFragment : tmpFragmentList) { tmpFragment.setProperty(IMoleculeFragmenter.FRAGMENT_CATEGORY_PROPERTY_KEY, ScaffoldGeneratorFragmenter.FRAGMENT_CATEGORY_PARENT_SCAFFOLD_VALUE); tmpReturnList.add(tmpFragment); @@ -1129,9 +1133,9 @@ public List fragmentMolecule(IAtomContainer aMolecule) throws Nu tmpReturnList.addAll(tmpSideChainList); /*Remove all empty fragments*/ if (!tmpReturnList.isEmpty()) { - for(int i = 0; i < tmpReturnList.size(); i++) { + for (int i = 0; i < tmpReturnList.size(); i++) { IAtomContainer tmpReturnMolecule = tmpReturnList.get(i); - if(tmpReturnMolecule.isEmpty()){ + if (tmpReturnMolecule.isEmpty()){ tmpReturnList.remove(tmpReturnMolecule); i--; } @@ -1180,33 +1184,33 @@ public IAtomContainer applyPreprocessing(IAtomContainer aMolecule) throws NullPo /** * Calling method needs to update the aromaticity model! */ - private void setCycleFinderInstance(ScaffoldGeneratorFragmenter.CycleFinderOption anOption) throws NullPointerException { + private void setCycleFinderInstance(IMoleculeFragmenter.CycleFinderOption anOption) throws NullPointerException { //Developer comment: the switch way is used instead of having the CycleFinder objects as variables of the enum constants // to not have static objects becoming bottlenecks in parallelization. Objects.requireNonNull(anOption, "Given option is null."); switch (anOption) { - case ALL: + case IMoleculeFragmenter.CycleFinderOption.ALL: this.cycleFinderInstance = Cycles.or(Cycles.all(), ScaffoldGeneratorFragmenter.AUXILIARY_CYCLE_FINDER); break; - case MCB: + case IMoleculeFragmenter.CycleFinderOption.MCB: this.cycleFinderInstance = Cycles.or(Cycles.mcb(), ScaffoldGeneratorFragmenter.AUXILIARY_CYCLE_FINDER); break; - case RELEVANT: + case IMoleculeFragmenter.CycleFinderOption.RELEVANT: this.cycleFinderInstance = Cycles.or(Cycles.relevant(), ScaffoldGeneratorFragmenter.AUXILIARY_CYCLE_FINDER); break; - case ESSENTIAL: + case IMoleculeFragmenter.CycleFinderOption.ESSENTIAL: this.cycleFinderInstance = Cycles.or(Cycles.essential(), ScaffoldGeneratorFragmenter.AUXILIARY_CYCLE_FINDER); break; - case EDGE_SHORT: + case IMoleculeFragmenter.CycleFinderOption.EDGE_SHORT: this.cycleFinderInstance = Cycles.or(Cycles.edgeShort(), ScaffoldGeneratorFragmenter.AUXILIARY_CYCLE_FINDER); break; - case VERTEX_SHORT: + case IMoleculeFragmenter.CycleFinderOption.VERTEX_SHORT: this.cycleFinderInstance = Cycles.or(Cycles.vertexShort(), ScaffoldGeneratorFragmenter.AUXILIARY_CYCLE_FINDER); break; - case TRIPLET_SHORT: + case IMoleculeFragmenter.CycleFinderOption.TRIPLET_SHORT: this.cycleFinderInstance = Cycles.or(Cycles.tripletShort(), ScaffoldGeneratorFragmenter.AUXILIARY_CYCLE_FINDER); break; - case CDK_AROMATIC_SET: + case IMoleculeFragmenter.CycleFinderOption.CDK_AROMATIC_SET: this.cycleFinderInstance = Cycles.cdkAromaticSet(); break; default: @@ -1217,21 +1221,21 @@ private void setCycleFinderInstance(ScaffoldGeneratorFragmenter.CycleFinderOptio /** * Calling method needs to update the aromaticity model! */ - private void setElectronDonationInstance(ScaffoldGeneratorFragmenter.ElectronDonationModelOption anOption) throws NullPointerException { + private void setElectronDonationInstance(IMoleculeFragmenter.ElectronDonationModelOption anOption) throws NullPointerException { //Developer comment: the switch way is used instead of having the CycleFinder objects as variables of the enum constants // to not have static objects becoming bottlenecks in parallelization. Objects.requireNonNull(anOption, "Given option is null."); switch (anOption) { - case CDK: + case IMoleculeFragmenter.ElectronDonationModelOption.CDK: this.electronDonationInstance = ElectronDonation.cdk(); break; - case DAYLIGHT: + case IMoleculeFragmenter.ElectronDonationModelOption.DAYLIGHT: this.electronDonationInstance = ElectronDonation.daylight(); break; - case CDK_ALLOWING_EXOCYCLIC: + case IMoleculeFragmenter.ElectronDonationModelOption.CDK_ALLOWING_EXOCYCLIC: this.electronDonationInstance = ElectronDonation.cdkAllowingExocyclic(); break; - case PI_BONDS: + case IMoleculeFragmenter.ElectronDonationModelOption.PI_BONDS: this.electronDonationInstance = ElectronDonation.piBonds(); break; default: @@ -1245,10 +1249,10 @@ private void setElectronDonationInstance(ScaffoldGeneratorFragmenter.ElectronDon private void setSmilesGeneratorInstance(ScaffoldGeneratorFragmenter.SmilesGeneratorOption anOption) throws NullPointerException { Objects.requireNonNull(anOption, "Given option is null."); switch (anOption) { - case UNIQUE_WITH_STEREO: + case ScaffoldGeneratorFragmenter.SmilesGeneratorOption.UNIQUE_WITH_STEREO: this.smilesGeneratorInstance = new SmilesGenerator(SmiFlavor.Unique | SmiFlavor.UseAromaticSymbols); break; - case UNIQUE_WITHOUT_STEREO: + case ScaffoldGeneratorFragmenter.SmilesGeneratorOption.UNIQUE_WITHOUT_STEREO: this.smilesGeneratorInstance = new SmilesGenerator(SmiFlavor.Unique); break; default: diff --git a/src/main/java/de/unijena/cheminf/mortar/model/fragmentation/algorithm/SugarRemovalUtilityFragmenter.java b/src/main/java/de/unijena/cheminf/mortar/model/fragmentation/algorithm/SugarRemovalUtilityFragmenter.java index d719940f..35a9a119 100644 --- a/src/main/java/de/unijena/cheminf/mortar/model/fragmentation/algorithm/SugarRemovalUtilityFragmenter.java +++ b/src/main/java/de/unijena/cheminf/mortar/model/fragmentation/algorithm/SugarRemovalUtilityFragmenter.java @@ -25,18 +25,14 @@ package de.unijena.cheminf.mortar.model.fragmentation.algorithm; -/** - * TODO: - * - - */ - import de.unijena.cheminf.deglycosylation.SugarRemovalUtility; import de.unijena.cheminf.mortar.gui.util.GuiUtil; import de.unijena.cheminf.mortar.message.Message; import de.unijena.cheminf.mortar.model.util.BasicDefinitions; import de.unijena.cheminf.mortar.model.util.ChemUtil; import de.unijena.cheminf.mortar.model.util.CollectionUtil; -import de.unijena.cheminf.mortar.model.util.SimpleEnumConstantNameProperty; +import de.unijena.cheminf.mortar.model.util.IDisplayEnum; +import de.unijena.cheminf.mortar.model.util.SimpleIDisplayEnumConstantProperty; import javafx.beans.property.Property; import javafx.beans.property.SimpleBooleanProperty; @@ -70,21 +66,50 @@ public class SugarRemovalUtilityFragmenter implements IMoleculeFragmenter { /** * Enum for options concerning the type of sugars to remove or detect. */ - public static enum SugarTypeToRemoveOption { + public static enum SugarTypeToRemoveOption implements IDisplayEnum { /** * Remove/detect only circular sugars. */ - CIRCULAR, - + CIRCULAR(Message.get("SugarRemovalUtilityFragmenter.SugarTypeToRemoveOption.Circular.displayName"), + Message.get("SugarRemovalUtilityFragmenter.SugarTypeToRemoveOption.Circular.tooltip")), /** * Remove/detect only linear sugars. */ - LINEAR, - + LINEAR(Message.get("SugarRemovalUtilityFragmenter.SugarTypeToRemoveOption.Linear.displayName"), + Message.get("SugarRemovalUtilityFragmenter.SugarTypeToRemoveOption.Linear.tooltip")), /** * Remove/detect both circular and linear sugars. */ - CIRCULAR_AND_LINEAR; + CIRCULAR_AND_LINEAR(Message.get("SugarRemovalUtilityFragmenter.SugarTypeToRemoveOption.Both.displayName"), + Message.get("SugarRemovalUtilityFragmenter.SugarTypeToRemoveOption.Both.tooltip")); + /** + * Language-specific name for display in GUI. + */ + private final String displayName; + /** + * Language-specific tooltip text for display in GUI. + */ + private final String tooltip; + /** + * Constructor. + * + * @param aDisplayName display name + * @param aTooltip tooltip text + */ + private SugarTypeToRemoveOption(String aDisplayName, String aTooltip) { + this.displayName = aDisplayName; + this.tooltip = aTooltip; + } + // + @Override + public String getDisplayName() { + return this.displayName; + } + // + @Override + public String getTooltipText() { + return this.tooltip; + } } // // @@ -93,21 +118,125 @@ public static enum SugarTypeToRemoveOption { * Enum for defining which fragments should be returned by the fragmentation methods, only the sugars, * only the aglycones, or both. */ - public static enum SRUFragmenterReturnedFragmentsOption { + public static enum SRUFragmenterReturnedFragmentsOption implements IDisplayEnum { /** * Option to return only the identified sugar moieties of a molecule after fragmentation. */ - ONLY_SUGAR_MOIETIES, - + ONLY_SUGAR_MOIETIES(Message.get("SugarRemovalUtilityFragmenter.SRUFragmenterReturnedFragmentsOption.OnlySugars.displayName"), + Message.get("SugarRemovalUtilityFragmenter.SRUFragmenterReturnedFragmentsOption.OnlySugars.tooltip")), /** * Option to return only the aglycone of a molecule after fragmentation. */ - ONLY_AGLYCONE, - + ONLY_AGLYCONE(Message.get("SugarRemovalUtilityFragmenter.SRUFragmenterReturnedFragmentsOption.OnlyAglycone.displayName"), + Message.get("SugarRemovalUtilityFragmenter.SRUFragmenterReturnedFragmentsOption.OnlyAglycone.tooltip")), /** * Option to return both, aglycone and sugar moieties, after fragmentation. */ - ALL_FRAGMENTS; + ALL_FRAGMENTS(Message.get("SugarRemovalUtilityFragmenter.SRUFragmenterReturnedFragmentsOption.All.displayName"), + Message.get("SugarRemovalUtilityFragmenter.SRUFragmenterReturnedFragmentsOption.All.tooltip")); + /** + * Language-specific name for display in GUI. + */ + private final String displayName; + /** + * Language-specific tooltip text for display in GUI. + */ + private final String tooltip; + /** + * Constructor. + * + * @param aDisplayName display name + * @param aTooltip tooltip text + */ + private SRUFragmenterReturnedFragmentsOption(String aDisplayName, String aTooltip) { + this.displayName = aDisplayName; + this.tooltip = aTooltip; + } + // + @Override + public String getDisplayName() { + return this.displayName; + } + // + @Override + public String getTooltipText() { + return this.tooltip; + } + } + // + // + // + /** + * Enum with options for how to determine whether a substructure that gets disconnected from the molecule during the + * removal of a sugar moiety should be preserved or can get removed along with the sugar. + * Wraps the enum PreservationModeOption from SugarRemovalUtility to be able to add display name and tooltip here. + */ + public static enum SRUFragmenterPreservationModeOption implements IDisplayEnum { + /** + * Specifies that all structures should be preserved. Note that if this option is combined with the removal of + * only terminal moieties, even the smallest attached structure will prevent the removal of a sugar. The most + * important consequence is that circular sugars with any hydroxy groups will not be removed because these are + * not considered as part of the sugar moiety. + */ + ALL(SugarRemovalUtility.PreservationModeOption.ALL, + Message.get("SugarRemovalUtilityFragmenter.SRUFragmenterPreservationModeOption.All.displayName"), + Message.get("SugarRemovalUtilityFragmenter.SRUFragmenterPreservationModeOption.All.tooltip")), + /** + * Specifies that whether a structure is worth preserving will be judged by its heavy atom count. The default + * threshold to preserve a structure is set to 5 heavy atoms (inclusive). + */ + HEAVY_ATOM_COUNT(SugarRemovalUtility.PreservationModeOption.HEAVY_ATOM_COUNT, + Message.get("SugarRemovalUtilityFragmenter.SRUFragmenterPreservationModeOption.HAC.displayName"), + Message.get("SugarRemovalUtilityFragmenter.SRUFragmenterPreservationModeOption.HAC.tooltip")), + /** + * Specifies that whether a structure is worth preserving will be judged by its molecular weight. The default + * threshold to preserve a structure is set to 60 Da (= 5 carbon atoms, inclusive). + */ + MOLECULAR_WEIGHT (SugarRemovalUtility.PreservationModeOption.MOLECULAR_WEIGHT, + Message.get("SugarRemovalUtilityFragmenter.SRUFragmenterPreservationModeOption.MW.displayName"), + Message.get("SugarRemovalUtilityFragmenter.SRUFragmenterPreservationModeOption.MW.tooltip")); + /** + * Wrapped enum constant from the analogous SRU enum. + */ + private final SugarRemovalUtility.PreservationModeOption wrappedOption; + /** + * Language-specific name for display in GUI. + */ + private final String displayName; + /** + * Language-specific tooltip text for display in GUI. + */ + private final String tooltip; + /** + * Constructor. + * + * @param anOption the wrapped enum constant from the original enum + * @param aDisplayName display name + * @param aTooltip tooltip text + */ + private SRUFragmenterPreservationModeOption(SugarRemovalUtility.PreservationModeOption anOption, String aDisplayName, String aTooltip) { + this.wrappedOption = anOption; + this.displayName = aDisplayName; + this.tooltip = aTooltip; + } + /** + * Returns the enum constant from the SRU PreservationModeOption enum that is wrapped in this instance. + * + * @return wrapped constant + */ + public SugarRemovalUtility.PreservationModeOption getWrappedSRUPreservationMode() { + return this.wrappedOption; + } + // + @Override + public String getDisplayName() { + return this.displayName; + } + // + @Override + public String getTooltipText() { + return this.tooltip; + } } // // @@ -130,36 +259,42 @@ public static enum SRUFragmenterReturnedFragmentsOption { /** * Default option for the sugar type to remove setting. */ - public static final SugarTypeToRemoveOption SUGAR_TYPE_TO_REMOVE_OPTION_DEFAULT = SugarTypeToRemoveOption.CIRCULAR_AND_LINEAR; + public static final SugarRemovalUtilityFragmenter.SugarTypeToRemoveOption SUGAR_TYPE_TO_REMOVE_OPTION_DEFAULT = + SugarRemovalUtilityFragmenter.SugarTypeToRemoveOption.CIRCULAR_AND_LINEAR; /** * Default returned fragments option. */ - public static final SRUFragmenterReturnedFragmentsOption RETURNED_FRAGMENTS_OPTION_DEFAULT = SRUFragmenterReturnedFragmentsOption.ALL_FRAGMENTS; + public static final SugarRemovalUtilityFragmenter.SRUFragmenterReturnedFragmentsOption RETURNED_FRAGMENTS_OPTION_DEFAULT = + SugarRemovalUtilityFragmenter.SRUFragmenterReturnedFragmentsOption.ALL_FRAGMENTS; + + /** + * Default preservation mode setting. + */ + public static final SugarRemovalUtilityFragmenter.SRUFragmenterPreservationModeOption PRESERVATION_MODE_DEFAULT = + SRUFragmenterPreservationModeOption.HEAVY_ATOM_COUNT; // // - // + // /** * Instance of the Sugar Removal Utility used internally to detect and remove the sugar moieties. */ - private SugarRemovalUtility sugarRUInstance; - // - // + private final SugarRemovalUtility sugarRUInstance; - private final SimpleEnumConstantNameProperty returnedFragmentsSetting; + private final SimpleIDisplayEnumConstantProperty returnedFragmentsSetting; - private final SimpleEnumConstantNameProperty sugarTypeToRemoveSetting; + private final SimpleIDisplayEnumConstantProperty sugarTypeToRemoveSetting; /** - * A property that has a constant name from the IMoleculeFragmenter.FragmentSaturationOption enum as value. + * A property that has a constant from the IMoleculeFragmenter.FragmentSaturationOption enum as value. */ - private final SimpleEnumConstantNameProperty fragmentSaturationSetting; + private final SimpleIDisplayEnumConstantProperty fragmentSaturationSetting; private final SimpleBooleanProperty detectCircularSugarsOnlyWithGlycosidicBondSetting; private final SimpleBooleanProperty removeOnlyTerminalSugarsSetting; - private final SimpleEnumConstantNameProperty preservationModeSetting; + private final SimpleIDisplayEnumConstantProperty preservationModeSetting; private final SimpleIntegerProperty preservationModeThresholdSetting; @@ -182,17 +317,22 @@ public static enum SRUFragmenterReturnedFragmentsOption { /** * All settings of this fragmenter, encapsulated in JavaFX properties for binding in GUI. */ - private final List settings; + private final List> settings; /** * Map to store pairs of {@literal }. */ private final HashMap settingNameTooltipTextMap; + /** + * Map to store pairs of {@literal }. + */ + private final HashMap settingNameDisplayNameMap; + /** * Logger of this class. */ - private final Logger logger = Logger.getLogger(SugarRemovalUtilityFragmenter.class.getName()); + private static final Logger LOGGER = Logger.getLogger(SugarRemovalUtilityFragmenter.class.getName()); // // // @@ -207,15 +347,19 @@ public SugarRemovalUtilityFragmenter() { tmpNumberOfSettings, BasicDefinitions.DEFAULT_HASH_COLLECTION_LOAD_FACTOR); this.settingNameTooltipTextMap = new HashMap<>(tmpInitialCapacityForSettingNameTooltipTextMap, BasicDefinitions.DEFAULT_HASH_COLLECTION_LOAD_FACTOR); - this.returnedFragmentsSetting = new SimpleEnumConstantNameProperty(this, "Returned fragments setting", - SugarRemovalUtilityFragmenter.RETURNED_FRAGMENTS_OPTION_DEFAULT.name(), SugarRemovalUtilityFragmenter.SRUFragmenterReturnedFragmentsOption.class) { + this.settingNameDisplayNameMap = new HashMap<>(tmpInitialCapacityForSettingNameTooltipTextMap, BasicDefinitions.DEFAULT_HASH_COLLECTION_LOAD_FACTOR); + this.returnedFragmentsSetting = new SimpleIDisplayEnumConstantProperty(this, "Returned fragments setting", + SugarRemovalUtilityFragmenter.RETURNED_FRAGMENTS_OPTION_DEFAULT, SugarRemovalUtilityFragmenter.SRUFragmenterReturnedFragmentsOption.class) { @Override - public void set(String newValue) throws NullPointerException, IllegalArgumentException { + public void set(IDisplayEnum newValue) throws NullPointerException, IllegalArgumentException { try { super.set(newValue); } catch (NullPointerException | IllegalArgumentException anException) { - SugarRemovalUtilityFragmenter.this.logger.log(Level.WARNING, anException.toString(), anException); - GuiUtil.guiExceptionAlert("Illegal Argument", "Illegal Argument was set", anException.toString(), anException); + SugarRemovalUtilityFragmenter.LOGGER.log(Level.WARNING, anException.toString(), anException); + GuiUtil.guiExceptionAlert(Message.get("Fragmenter.IllegalSettingValue.Title"), + Message.get("Fragmenter.IllegalSettingValue.Header"), + anException.toString(), + anException); //re-throws the exception to properly reset the binding throw anException; } @@ -224,15 +368,20 @@ public void set(String newValue) throws NullPointerException, IllegalArgumentExc this.settings.add(this.returnedFragmentsSetting); this.settingNameTooltipTextMap.put(this.returnedFragmentsSetting.getName(), Message.get("SugarRemovalUtilityFragmenter.returnedFragmentsSetting.tooltip")); - this.fragmentSaturationSetting = new SimpleEnumConstantNameProperty(this, "Fragment saturation setting", - IMoleculeFragmenter.FRAGMENT_SATURATION_OPTION_DEFAULT.name(), IMoleculeFragmenter.FragmentSaturationOption.class) { + this.settingNameDisplayNameMap.put(this.returnedFragmentsSetting.getName(), + Message.get("SugarRemovalUtilityFragmenter.returnedFragmentsSetting.displayName")); + this.fragmentSaturationSetting = new SimpleIDisplayEnumConstantProperty(this, "Fragment saturation setting", + IMoleculeFragmenter.FRAGMENT_SATURATION_OPTION_DEFAULT, IMoleculeFragmenter.FragmentSaturationOption.class) { @Override - public void set(String newValue) throws NullPointerException, IllegalArgumentException { + public void set(IDisplayEnum newValue) throws NullPointerException, IllegalArgumentException { try { super.set(newValue); } catch (NullPointerException | IllegalArgumentException anException) { - SugarRemovalUtilityFragmenter.this.logger.log(Level.WARNING, anException.toString(), anException); - GuiUtil.guiExceptionAlert("Illegal Argument", "Illegal Argument was set", anException.toString(), anException); + SugarRemovalUtilityFragmenter.LOGGER.log(Level.WARNING, anException.toString(), anException); + GuiUtil.guiExceptionAlert(Message.get("Fragmenter.IllegalSettingValue.Title"), + Message.get("Fragmenter.IllegalSettingValue.Header"), + anException.toString(), + anException); //re-throws the exception to properly reset the binding throw anException; } @@ -241,16 +390,21 @@ public void set(String newValue) throws NullPointerException, IllegalArgumentExc this.settings.add(this.fragmentSaturationSetting); this.settingNameTooltipTextMap.put(this.fragmentSaturationSetting.getName(), Message.get("SugarRemovalUtilityFragmenter.fragmentSaturationSetting.tooltip")); - this.sugarTypeToRemoveSetting = new SimpleEnumConstantNameProperty(this, "Sugar type to remove setting", - SugarRemovalUtilityFragmenter.SUGAR_TYPE_TO_REMOVE_OPTION_DEFAULT.name(), + this.settingNameDisplayNameMap.put(this.fragmentSaturationSetting.getName(), + Message.get("SugarRemovalUtilityFragmenter.fragmentSaturationSetting.displayName")); + this.sugarTypeToRemoveSetting = new SimpleIDisplayEnumConstantProperty(this, "Sugar type to remove setting", + SugarRemovalUtilityFragmenter.SUGAR_TYPE_TO_REMOVE_OPTION_DEFAULT, SugarRemovalUtilityFragmenter.SugarTypeToRemoveOption.class) { @Override - public void set(String newValue) throws NullPointerException, IllegalArgumentException { + public void set(IDisplayEnum newValue) throws NullPointerException, IllegalArgumentException { try { super.set(newValue); } catch (NullPointerException | IllegalArgumentException anException) { - SugarRemovalUtilityFragmenter.this.logger.log(Level.WARNING, anException.toString(), anException); - GuiUtil.guiExceptionAlert("Illegal Argument", "Illegal Argument was set", anException.toString(), anException); + SugarRemovalUtilityFragmenter.LOGGER.log(Level.WARNING, anException.toString(), anException); + GuiUtil.guiExceptionAlert(Message.get("Fragmenter.IllegalSettingValue.Title"), + Message.get("Fragmenter.IllegalSettingValue.Header"), + anException.toString(), + anException); //re-throws the exception to properly reset the binding throw anException; } @@ -259,6 +413,8 @@ public void set(String newValue) throws NullPointerException, IllegalArgumentExc this.settings.add(this.sugarTypeToRemoveSetting); this.settingNameTooltipTextMap.put(this.sugarTypeToRemoveSetting.getName(), Message.get("SugarRemovalUtilityFragmenter.sugarTypeToRemoveSetting.tooltip")); + this.settingNameDisplayNameMap.put(this.sugarTypeToRemoveSetting.getName(), + Message.get("SugarRemovalUtilityFragmenter.sugarTypeToRemoveSetting.displayName")); this.detectCircularSugarsOnlyWithGlycosidicBondSetting = new SimpleBooleanProperty(this, "Detect circular sugars only with glycosidic bond setting", this.sugarRUInstance.areOnlyCircularSugarsWithOGlycosidicBondDetected()) { @@ -272,6 +428,8 @@ public void set(boolean newValue) { this.settings.add(this.detectCircularSugarsOnlyWithGlycosidicBondSetting); this.settingNameTooltipTextMap.put(this.detectCircularSugarsOnlyWithGlycosidicBondSetting.getName(), Message.get("SugarRemovalUtilityFragmenter.detectCircularSugarsOnlyWithGlycosidicBondSetting.tooltip")); + this.settingNameDisplayNameMap.put(this.detectCircularSugarsOnlyWithGlycosidicBondSetting.getName(), + Message.get("SugarRemovalUtilityFragmenter.detectCircularSugarsOnlyWithGlycosidicBondSetting.displayName")); this.removeOnlyTerminalSugarsSetting = new SimpleBooleanProperty(this, "Remove only terminal sugars setting", this.sugarRUInstance.areOnlyTerminalSugarsRemoved()) { @Override @@ -284,20 +442,24 @@ public void set(boolean newValue) { this.settings.add(this.removeOnlyTerminalSugarsSetting); this.settingNameTooltipTextMap.put(this.removeOnlyTerminalSugarsSetting.getName(), Message.get("SugarRemovalUtilityFragmenter.removeOnlyTerminalSugarsSetting.tooltip")); - this.preservationModeSetting = new SimpleEnumConstantNameProperty(this, "Preservation mode setting", - this.sugarRUInstance.getPreservationModeSetting().name(), SugarRemovalUtility.PreservationModeOption.class) { + this.settingNameDisplayNameMap.put(this.removeOnlyTerminalSugarsSetting.getName(), + Message.get("SugarRemovalUtilityFragmenter.removeOnlyTerminalSugarsSetting.displayName")); + this.preservationModeSetting = new SimpleIDisplayEnumConstantProperty(this, "Preservation mode setting", + SugarRemovalUtilityFragmenter.PRESERVATION_MODE_DEFAULT, SugarRemovalUtilityFragmenter.SRUFragmenterPreservationModeOption.class) { @Override - public void set(String newValue) throws NullPointerException, IllegalArgumentException { + public void set(IDisplayEnum newValue) throws NullPointerException, IllegalArgumentException { try { - //valueOf() throws IllegalArgumentException - SugarRemovalUtility.PreservationModeOption tmpEnumConstant = SugarRemovalUtility.PreservationModeOption.valueOf(newValue); - SugarRemovalUtilityFragmenter.this.sugarRUInstance.setPreservationModeSetting(tmpEnumConstant); + SugarRemovalUtilityFragmenter.this.sugarRUInstance.setPreservationModeSetting( + ((SugarRemovalUtilityFragmenter.SRUFragmenterPreservationModeOption) newValue).getWrappedSRUPreservationMode()); //when the preservation mode is changed, the threshold is set to the default value of the chosen mode internally within the SRU! SugarRemovalUtilityFragmenter.this.preservationModeThresholdSetting.set( SugarRemovalUtilityFragmenter.this.sugarRUInstance.getPreservationModeThresholdSetting()); } catch (IllegalArgumentException | NullPointerException anException) { - SugarRemovalUtilityFragmenter.this.logger.log(Level.WARNING, anException.toString(), anException); - GuiUtil.guiExceptionAlert("Illegal Argument", "Illegal Argument was set", anException.toString(), anException); + SugarRemovalUtilityFragmenter.LOGGER.log(Level.WARNING, anException.toString(), anException); + GuiUtil.guiExceptionAlert(Message.get("Fragmenter.IllegalSettingValue.Title"), + Message.get("Fragmenter.IllegalSettingValue.Header"), + anException.toString(), + anException); //re-throws the exception to properly reset the binding throw anException; } @@ -307,6 +469,8 @@ public void set(String newValue) throws NullPointerException, IllegalArgumentExc this.settings.add(this.preservationModeSetting); this.settingNameTooltipTextMap.put(this.preservationModeSetting.getName(), Message.get("SugarRemovalUtilityFragmenter.preservationModeSetting.tooltip")); + this.settingNameDisplayNameMap.put(this.preservationModeSetting.getName(), + Message.get("SugarRemovalUtilityFragmenter.preservationModeSetting.displayName")); this.preservationModeThresholdSetting = new SimpleIntegerProperty(this, "Preservation mode threshold setting", this.sugarRUInstance.getPreservationModeThresholdSetting()) { @Override @@ -315,8 +479,11 @@ public void set(int newValue) throws IllegalArgumentException{ //throws IllegalArgumentException SugarRemovalUtilityFragmenter.this.sugarRUInstance.setPreservationModeThresholdSetting(newValue); }catch(IllegalArgumentException anException){ - SugarRemovalUtilityFragmenter.this.logger.log(Level.WARNING, anException.toString(), anException); - GuiUtil.guiExceptionAlert("Illegal Argument", "Illegal Argument was set", anException.toString(), anException); + SugarRemovalUtilityFragmenter.LOGGER.log(Level.WARNING, anException.toString(), anException); + GuiUtil.guiExceptionAlert(Message.get("Fragmenter.IllegalSettingValue.Title"), + Message.get("Fragmenter.IllegalSettingValue.Header"), + anException.toString(), + anException); //re-throws the exception to properly reset the binding throw anException; } @@ -326,6 +493,8 @@ public void set(int newValue) throws IllegalArgumentException{ this.settings.add(this.preservationModeThresholdSetting); this.settingNameTooltipTextMap.put(this.preservationModeThresholdSetting.getName(), Message.get("SugarRemovalUtilityFragmenter.preservationModeThresholdSetting.tooltip")); + this.settingNameDisplayNameMap.put(this.preservationModeThresholdSetting.getName(), + Message.get("SugarRemovalUtilityFragmenter.preservationModeThresholdSetting.displayName")); this.detectCircularSugarsOnlyWithEnoughExocyclicOxygenAtomsSetting = new SimpleBooleanProperty(this, "Detect circular sugars only with enough exocyclic oxygen atoms setting", this.sugarRUInstance.areOnlyCircularSugarsWithEnoughExocyclicOxygenAtomsDetected()) { @@ -339,6 +508,8 @@ public void set(boolean newValue) { this.settings.add(this.detectCircularSugarsOnlyWithEnoughExocyclicOxygenAtomsSetting); this.settingNameTooltipTextMap.put(this.detectCircularSugarsOnlyWithEnoughExocyclicOxygenAtomsSetting.getName(), Message.get("SugarRemovalUtilityFragmenter.detectCircularSugarsOnlyWithEnoughExocyclicOxygenAtomsSetting.tooltip")); + this.settingNameDisplayNameMap.put(this.detectCircularSugarsOnlyWithEnoughExocyclicOxygenAtomsSetting.getName(), + Message.get("SugarRemovalUtilityFragmenter.detectCircularSugarsOnlyWithEnoughExocyclicOxygenAtomsSetting.displayName")); this.exocyclicOxygenAtomsToAtomsInRingRatioThresholdSetting = new SimpleDoubleProperty(this, "Exocyclic oxygen atoms to atoms in ring ratio threshold setting", this.sugarRUInstance.getExocyclicOxygenAtomsToAtomsInRingRatioThresholdSetting()) { @@ -348,8 +519,11 @@ public void set(double newValue) throws IllegalArgumentException { //throws IllegalArgumentException SugarRemovalUtilityFragmenter.this.sugarRUInstance.setExocyclicOxygenAtomsToAtomsInRingRatioThresholdSetting(newValue); } catch (IllegalArgumentException anException) { - SugarRemovalUtilityFragmenter.this.logger.log(Level.WARNING, anException.toString(), anException); - GuiUtil.guiExceptionAlert("Illegal Argument", "Illegal Argument was set", anException.toString(), anException); + SugarRemovalUtilityFragmenter.LOGGER.log(Level.WARNING, anException.toString(), anException); + GuiUtil.guiExceptionAlert(Message.get("Fragmenter.IllegalSettingValue.Title"), + Message.get("Fragmenter.IllegalSettingValue.Header"), + anException.toString(), + anException); //re-throws the exception to properly reset the binding throw anException; } @@ -359,6 +533,8 @@ public void set(double newValue) throws IllegalArgumentException { this.settings.add(this.exocyclicOxygenAtomsToAtomsInRingRatioThresholdSetting); this.settingNameTooltipTextMap.put(this.exocyclicOxygenAtomsToAtomsInRingRatioThresholdSetting.getName(), Message.get("SugarRemovalUtilityFragmenter.exocyclicOxygenAtomsToAtomsInRingRatioThresholdSetting.tooltip")); + this.settingNameDisplayNameMap.put(this.exocyclicOxygenAtomsToAtomsInRingRatioThresholdSetting.getName(), + Message.get("SugarRemovalUtilityFragmenter.exocyclicOxygenAtomsToAtomsInRingRatioThresholdSetting.displayName")); this.detectLinearSugarsInRingsSetting = new SimpleBooleanProperty(this, "Detect linear sugars in rings setting", this.sugarRUInstance.areLinearSugarsInRingsDetected()) { @Override @@ -371,6 +547,8 @@ public void set(boolean newValue) { this.settings.add(this.detectLinearSugarsInRingsSetting); this.settingNameTooltipTextMap.put(this.detectLinearSugarsInRingsSetting.getName(), Message.get("SugarRemovalUtilityFragmenter.detectLinearSugarsInRingsSetting.tooltip")); + this.settingNameDisplayNameMap.put(this.detectLinearSugarsInRingsSetting.getName(), + Message.get("SugarRemovalUtilityFragmenter.detectLinearSugarsInRingsSetting.displayName")); this.linearSugarCandidateMinimumSizeSetting = new SimpleIntegerProperty(this, "Linear sugar candidate minimum size setting", this.sugarRUInstance.getLinearSugarCandidateMinSizeSetting()) { @@ -380,8 +558,11 @@ public void set(int newValue) { //throws IllegalArgumentException SugarRemovalUtilityFragmenter.this.sugarRUInstance.setLinearSugarCandidateMinSizeSetting(newValue); } catch (IllegalArgumentException anException) { - SugarRemovalUtilityFragmenter.this.logger.log(Level.WARNING, anException.toString(), anException); - GuiUtil.guiExceptionAlert("Illegal Argument", "Illegal Argument was set", anException.toString(), anException); + SugarRemovalUtilityFragmenter.LOGGER.log(Level.WARNING, anException.toString(), anException); + GuiUtil.guiExceptionAlert(Message.get("Fragmenter.IllegalSettingValue.Title"), + Message.get("Fragmenter.IllegalSettingValue.Header"), + anException.toString(), + anException); //re-throws the exception to properly reset the binding throw anException; } @@ -391,6 +572,8 @@ public void set(int newValue) { this.settings.add(this.linearSugarCandidateMinimumSizeSetting); this.settingNameTooltipTextMap.put(this.linearSugarCandidateMinimumSizeSetting.getName(), Message.get("SugarRemovalUtilityFragmenter.linearSugarCandidateMinimumSizeSetting.tooltip")); + this.settingNameDisplayNameMap.put(this.linearSugarCandidateMinimumSizeSetting.getName(), + Message.get("SugarRemovalUtilityFragmenter.linearSugarCandidateMinimumSizeSetting.displayName")); this.linearSugarCandidateMaximumSizeSetting = new SimpleIntegerProperty(this, "Linear sugar candidate maximum size setting", this.sugarRUInstance.getLinearSugarCandidateMaxSizeSetting()) { @@ -400,8 +583,11 @@ public void set(int newValue) { //throws IllegalArgumentException SugarRemovalUtilityFragmenter.this.sugarRUInstance.setLinearSugarCandidateMaxSizeSetting(newValue); } catch (IllegalArgumentException anException) { - SugarRemovalUtilityFragmenter.this.logger.log(Level.WARNING, anException.toString(), anException); - GuiUtil.guiExceptionAlert("Illegal Argument", "Illegal Argument was set", anException.toString(), anException); + SugarRemovalUtilityFragmenter.LOGGER.log(Level.WARNING, anException.toString(), anException); + GuiUtil.guiExceptionAlert(Message.get("Fragmenter.IllegalSettingValue.Title"), + Message.get("Fragmenter.IllegalSettingValue.Header"), + anException.toString(), + anException); //re-throws the exception to properly reset the binding throw anException; } @@ -411,6 +597,8 @@ public void set(int newValue) { this.settings.add(this.linearSugarCandidateMaximumSizeSetting); this.settingNameTooltipTextMap.put(this.linearSugarCandidateMaximumSizeSetting.getName(), Message.get("SugarRemovalUtilityFragmenter.linearSugarCandidateMaximumSizeSetting.tooltip")); + this.settingNameDisplayNameMap.put(this.linearSugarCandidateMaximumSizeSetting.getName(), + Message.get("SugarRemovalUtilityFragmenter.linearSugarCandidateMaximumSizeSetting.displayName")); this.detectLinearAcidicSugarsSetting = new SimpleBooleanProperty(this, "Detect linear acidic sugars setting", this.sugarRUInstance.areLinearAcidicSugarsDetected()) { @@ -424,6 +612,8 @@ public void set(boolean newValue) { this.settings.add(this.detectLinearAcidicSugarsSetting); this.settingNameTooltipTextMap.put(this.detectLinearAcidicSugarsSetting.getName(), Message.get("SugarRemovalUtilityFragmenter.detectLinearAcidicSugarsSetting.tooltip")); + this.settingNameDisplayNameMap.put(this.detectLinearAcidicSugarsSetting.getName(), + Message.get("SugarRemovalUtilityFragmenter.detectLinearAcidicSugarsSetting.displayName")); this.detectSpiroRingsAsCircularSugarsSetting = new SimpleBooleanProperty(this, "Detect spiro rings as circular sugars setting", this.sugarRUInstance.areSpiroRingsDetectedAsCircularSugars()) { @@ -437,6 +627,8 @@ public void set(boolean newValue) { this.settings.add(this.detectSpiroRingsAsCircularSugarsSetting); this.settingNameTooltipTextMap.put(this.detectSpiroRingsAsCircularSugarsSetting.getName(), Message.get("SugarRemovalUtilityFragmenter.detectSpiroRingsAsCircularSugarsSetting.tooltip")); + this.settingNameDisplayNameMap.put(this.detectSpiroRingsAsCircularSugarsSetting.getName(), + Message.get("SugarRemovalUtilityFragmenter.detectSpiroRingsAsCircularSugarsSetting.displayName")); this.detectCircularSugarsWithKetoGroupsSetting = new SimpleBooleanProperty(this, "Detect circular sugars with keto groups setting", this.sugarRUInstance.areCircularSugarsWithKetoGroupsDetected()) { @@ -450,66 +642,50 @@ public void set(boolean newValue) { this.settings.add(this.detectCircularSugarsWithKetoGroupsSetting); this.settingNameTooltipTextMap.put(this.detectCircularSugarsWithKetoGroupsSetting.getName(), Message.get("SugarRemovalUtilityFragmenter.detectCircularSugarsWithKetoGroupsSetting.tooltip")); + this.settingNameDisplayNameMap.put(this.detectCircularSugarsWithKetoGroupsSetting.getName(), + Message.get("SugarRemovalUtilityFragmenter.detectCircularSugarsWithKetoGroupsSetting.displayName")); } // // // /** - * Returns the string representation of the currently set option for the returned fragments setting + * Returns the currently set option for the returned fragments setting. * - * @return enum constant name of the set option + * @return enum constant of the set option */ - public String getReturnedFragmentsSetting() { - return this.returnedFragmentsSetting.get(); + public SugarRemovalUtilityFragmenter.SRUFragmenterReturnedFragmentsOption getReturnedFragmentsSetting() { + return (SugarRemovalUtilityFragmenter.SRUFragmenterReturnedFragmentsOption) this.returnedFragmentsSetting.get(); } /** * Returns the property object of the returned fragments setting that can be used to configure this setting. - * A property that has a constant name from SRUFragmenterReturnedFragmentsOption enum as value. + * A property that has a constant from SRUFragmenterReturnedFragmentsOption enum as value. * * @return property object of the returned fragments setting */ - public SimpleEnumConstantNameProperty returnedFragmentsSettingProperty() { + public SimpleIDisplayEnumConstantProperty returnedFragmentsSettingProperty() { return this.returnedFragmentsSetting; } /** - * Returns the enum constant currently set as option for the returned fragments setting. - * - * @return enum constant for returned fragments setting - */ - public SRUFragmenterReturnedFragmentsOption getReturnedFragmentsSettingConstant() { - return SRUFragmenterReturnedFragmentsOption.valueOf(this.returnedFragmentsSetting.get()); - } - - /** - * Returns the string representation of the currently set option for the sugar type to remove setting. + * Returns the currently set option for the sugar type to remove setting. * - * @return enum constant name of the set option + * @return enum constant of the set option */ - public String getSugarTypeToRemoveSetting() { - return this.sugarTypeToRemoveSetting.get(); + public SugarRemovalUtilityFragmenter.SugarTypeToRemoveOption getSugarTypeToRemoveSetting() { + return (SugarRemovalUtilityFragmenter.SugarTypeToRemoveOption) this.sugarTypeToRemoveSetting.get(); } /** * Returns the property object of the sugar type to remove setting that can be used to configure this setting. - * A property that has a constant name from SugarTypeToRemoveOption enum as value. + * A property that has a constant from SugarTypeToRemoveOption enum as value. * * @return property object of the sugar type to remove setting */ - public SimpleEnumConstantNameProperty sugarTypeToRemoveSettingProperty() { + public SimpleIDisplayEnumConstantProperty sugarTypeToRemoveSettingProperty() { return this.sugarTypeToRemoveSetting; } - /** - * Returns the enum constant currently set as option for the sugar type to remove setting. - * - * @return enum constant for sugar type to remove setting - */ - public SugarTypeToRemoveOption getSugarTypeToRemoveSettingConstant() { - return SugarTypeToRemoveOption.valueOf(this.sugarTypeToRemoveSetting.get()); - } - /** * Returns the current state of the detect circular sugars only with glycosidic bond setting. * @@ -548,32 +724,24 @@ public SimpleBooleanProperty removeOnlyTerminalSugarsSettingProperty() { } /** - * Returns the string representation of the currently set option for the preservation mode setting. + * Returns the currently set option for the preservation mode setting. * - * @return enum constant name of the set option + * @return enum constant of the set option */ - public String getPreservationModeSetting() { - return this.preservationModeSetting.get(); + public SugarRemovalUtilityFragmenter.SRUFragmenterPreservationModeOption getPreservationModeSetting() { + return (SugarRemovalUtilityFragmenter.SRUFragmenterPreservationModeOption) this.preservationModeSetting.get(); } /** * Returns the property object of the preservation mode setting that can be used to configure this setting. * It has a constant from the SugarRemovalUtility.PreservationModeOption enum as value. + * * @return property object of the preservation mode setting */ - public SimpleEnumConstantNameProperty preservationModeSettingProperty() { + public SimpleIDisplayEnumConstantProperty preservationModeSettingProperty() { return this.preservationModeSetting; } - /** - * Returns the enum constant currently set as option for the preservation mode setting. - * - * @return enum constant for preservation mode setting - */ - public SugarRemovalUtility.PreservationModeOption getPreservationModeSettingConstant() { - return SugarRemovalUtility.PreservationModeOption.valueOf(this.preservationModeSetting.get()); - } - /** * Returns the current value of the preservation mode threshold setting. * @@ -745,21 +913,6 @@ public SimpleBooleanProperty detectCircularSugarsWithKetoGroupsSettingProperty() // // // - /** - * Sets the returned fragments setting, defining whether only sugar moieties, only the aglycone, or both should - * be returned. - * - * @param anOptionName name of a constant from the SRUFragmenterReturnedFragmentsOption enum - * @throws NullPointerException if the given string is null - * @throws IllegalArgumentException if the given string is not an enum constant name - */ - public void setReturnedFragmentsSetting(String anOptionName) throws NullPointerException, IllegalArgumentException { - Objects.requireNonNull(anOptionName, "Given option name is null."); - //throws IllegalArgumentException if the given name does not match a constant name in the enum - SRUFragmenterReturnedFragmentsOption tmpConstant = SRUFragmenterReturnedFragmentsOption.valueOf(anOptionName); - this.setReturnedFragmentsSetting(tmpConstant); - } - /** * Sets the returned fragments setting, defining whether only sugar moieties, only the aglycone, or both should * be returned. @@ -767,24 +920,9 @@ public void setReturnedFragmentsSetting(String anOptionName) throws NullPointerE * @param anOption a constant from the SRUFragmenterReturnedFragmentsOption enum * @throws NullPointerException if the given parameter is null */ - public void setReturnedFragmentsSetting(SRUFragmenterReturnedFragmentsOption anOption) throws NullPointerException { + public void setReturnedFragmentsSetting(SugarRemovalUtilityFragmenter.SRUFragmenterReturnedFragmentsOption anOption) throws NullPointerException { Objects.requireNonNull(anOption, "Given option is null."); - this.returnedFragmentsSetting.set(anOption.name()); - } - - /** - * Sets the sugar type to remove setting, defining whether only circular, only linear, or both kinds of sugar - * moieties should be detected/removed. - * - * @param anOptionName name of a constant from the SugarTypeToRemoveOption enum - * @throws NullPointerException if the given string is null - * @throws IllegalArgumentException if the given string is not an enum constant name - */ - public void setSugarTypeToRemoveSetting(String anOptionName) throws NullPointerException, IllegalArgumentException { - Objects.requireNonNull(anOptionName, "Given option name is null."); - //throws IllegalArgumentException if the given name does not match a constant name in the enum - SugarTypeToRemoveOption tmpConstant = SugarTypeToRemoveOption.valueOf(anOptionName); - this.setSugarTypeToRemoveSetting(tmpConstant); + this.returnedFragmentsSetting.set(anOption); } /** @@ -794,9 +932,9 @@ public void setSugarTypeToRemoveSetting(String anOptionName) throws NullPointerE * @param aSugarTypeToRemoveOption a constant from the SugarTypeToRemoveOption enum * @throws NullPointerException if the given parameter is null */ - public void setSugarTypeToRemoveSetting(SugarTypeToRemoveOption aSugarTypeToRemoveOption) throws NullPointerException { + public void setSugarTypeToRemoveSetting(SugarRemovalUtilityFragmenter.SugarTypeToRemoveOption aSugarTypeToRemoveOption) throws NullPointerException { Objects.requireNonNull(aSugarTypeToRemoveOption, "Given type of sugars to remove is null."); - this.sugarTypeToRemoveSetting.set(aSugarTypeToRemoveOption.name()); + this.sugarTypeToRemoveSetting.set(aSugarTypeToRemoveOption); } /** @@ -819,21 +957,6 @@ public void setRemoveOnlyTerminalSugarsSetting(boolean aBoolean) { this.removeOnlyTerminalSugarsSetting.set(aBoolean); } - /** - * Sets the preservation mode setting, defining what molecular characteristic should be considered when judging - * whether a fragment is 'big enough' to be kept and not discarded. - * - * @param anOptionName name of a constant from the SugarRemovalUtility.PreservationModeOption enum - * @throws NullPointerException if the given string is null - * @throws IllegalArgumentException if the given string is not an enum constant name - */ - public void setPreservationModeSetting(String anOptionName) throws NullPointerException, IllegalArgumentException { - Objects.requireNonNull(anOptionName, "Given option name is null."); - //throws IllegalArgumentException if the given name does not match a constant name in the enum - SugarRemovalUtility.PreservationModeOption tmpConstant = SugarRemovalUtility.PreservationModeOption.valueOf(anOptionName); - this.setPreservationModeSetting(tmpConstant); - } - /** * Sets the preservation mode setting, defining what molecular characteristic should be considered when judging * whether a fragment is 'big enough' to be kept and not discarded. @@ -841,10 +964,10 @@ public void setPreservationModeSetting(String anOptionName) throws NullPointerEx * @param anOption a constant from the SugarRemovalUtility.PreservationModeOption enum * @throws NullPointerException if the given parameter is null */ - public void setPreservationModeSetting(SugarRemovalUtility.PreservationModeOption anOption) throws NullPointerException { + public void setPreservationModeSetting(SugarRemovalUtilityFragmenter.SRUFragmenterPreservationModeOption anOption) throws NullPointerException { Objects.requireNonNull(anOption, "Given option is null."); //synchronisation with SRU instance done in overridden set() function of the property - this.preservationModeSetting.set(anOption.name()); + this.preservationModeSetting.set(anOption); } /** @@ -960,7 +1083,7 @@ public void setDetectCircularSugarsWithKetoGroupsSetting(boolean aBoolean) { //without the empty line, the code folding does not work properly here... @Override - public List settingsProperties() { + public List> settingsProperties() { return this.settings; } @@ -970,48 +1093,45 @@ public Map getSettingNameToTooltipTextMap() { } @Override - public String getFragmentationAlgorithmName() { - return SugarRemovalUtilityFragmenter.ALGORITHM_NAME; + public Map getSettingNameToDisplayNameMap() { + return this.settingNameDisplayNameMap; } @Override - public String getFragmentSaturationSetting() { - return this.fragmentSaturationSetting.get(); + public String getFragmentationAlgorithmName() { + return SugarRemovalUtilityFragmenter.ALGORITHM_NAME; } @Override - public SimpleEnumConstantNameProperty fragmentSaturationSettingProperty() { - return this.fragmentSaturationSetting; + public String getFragmentationAlgorithmDisplayName() { + return Message.get("SugarRemovalUtilityFragmenter.displayName"); } @Override - public FragmentSaturationOption getFragmentSaturationSettingConstant() { - return FragmentSaturationOption.valueOf(this.fragmentSaturationSetting.get()); + public IMoleculeFragmenter.FragmentSaturationOption getFragmentSaturationSetting() { + return (IMoleculeFragmenter.FragmentSaturationOption) this.fragmentSaturationSetting.get(); } @Override - public void setFragmentSaturationSetting(String anOptionName) throws NullPointerException, IllegalArgumentException { - Objects.requireNonNull(anOptionName, "Given saturation option name is null."); - //throws IllegalArgumentException if the given name does not match a constant name in the enum - FragmentSaturationOption tmpConstant = FragmentSaturationOption.valueOf(anOptionName); - this.fragmentSaturationSetting.set(tmpConstant.name()); + public SimpleIDisplayEnumConstantProperty fragmentSaturationSettingProperty() { + return this.fragmentSaturationSetting; } @Override public void setFragmentSaturationSetting(FragmentSaturationOption anOption) throws NullPointerException { Objects.requireNonNull(anOption, "Given saturation option is null."); - this.fragmentSaturationSetting.set(anOption.name()); + this.fragmentSaturationSetting.set(anOption); } @Override public IMoleculeFragmenter copy() { SugarRemovalUtilityFragmenter tmpCopy = new SugarRemovalUtilityFragmenter(); - tmpCopy.setReturnedFragmentsSetting(this.returnedFragmentsSetting.get()); - tmpCopy.setSugarTypeToRemoveSetting(this.sugarTypeToRemoveSetting.get()); - tmpCopy.setFragmentSaturationSetting(this.fragmentSaturationSetting.get()); + tmpCopy.setReturnedFragmentsSetting((SRUFragmenterReturnedFragmentsOption) this.returnedFragmentsSetting.get()); + tmpCopy.setSugarTypeToRemoveSetting((SugarTypeToRemoveOption) this.sugarTypeToRemoveSetting.get()); + tmpCopy.setFragmentSaturationSetting((FragmentSaturationOption) this.fragmentSaturationSetting.get()); tmpCopy.setDetectCircularSugarsOnlyWithGlycosidicBondSetting(this.detectCircularSugarsOnlyWithEnoughExocyclicOxygenAtomsSetting.get()); tmpCopy.setRemoveOnlyTerminalSugarsSetting(this.removeOnlyTerminalSugarsSetting.get()); - tmpCopy.setPreservationModeSetting(this.preservationModeSetting.get()); + tmpCopy.setPreservationModeSetting((SRUFragmenterPreservationModeOption) this.preservationModeSetting.get()); tmpCopy.setPreservationModeThresholdSetting(this.preservationModeThresholdSetting.get()); tmpCopy.setDetectCircularSugarsOnlyWithEnoughExocyclicOxygenAtomsSetting(this.detectCircularSugarsOnlyWithEnoughExocyclicOxygenAtomsSetting.get()); tmpCopy.setExocyclicOxygenAtomsToAtomsInRingRatioThresholdSetting(this.exocyclicOxygenAtomsToAtomsInRingRatioThresholdSetting.get()); @@ -1026,13 +1146,13 @@ public IMoleculeFragmenter copy() { @Override public void restoreDefaultSettings() { - this.returnedFragmentsSetting.set(SugarRemovalUtilityFragmenter.RETURNED_FRAGMENTS_OPTION_DEFAULT.name()); - this.sugarTypeToRemoveSetting.set(SugarRemovalUtilityFragmenter.SUGAR_TYPE_TO_REMOVE_OPTION_DEFAULT.name()); - this.fragmentSaturationSetting.set(IMoleculeFragmenter.FRAGMENT_SATURATION_OPTION_DEFAULT.name()); + this.returnedFragmentsSetting.set(SugarRemovalUtilityFragmenter.RETURNED_FRAGMENTS_OPTION_DEFAULT); + this.sugarTypeToRemoveSetting.set(SugarRemovalUtilityFragmenter.SUGAR_TYPE_TO_REMOVE_OPTION_DEFAULT); + this.fragmentSaturationSetting.set(IMoleculeFragmenter.FRAGMENT_SATURATION_OPTION_DEFAULT); this.sugarRUInstance.restoreDefaultSettings(); this.detectCircularSugarsOnlyWithGlycosidicBondSetting.set(this.sugarRUInstance.areOnlyCircularSugarsWithOGlycosidicBondDetected()); this.removeOnlyTerminalSugarsSetting.set(this.sugarRUInstance.areOnlyTerminalSugarsRemoved()); - this.preservationModeSetting.set(this.sugarRUInstance.getPreservationModeSetting().name()); + this.preservationModeSetting.set(SugarRemovalUtilityFragmenter.PRESERVATION_MODE_DEFAULT); this.preservationModeThresholdSetting.set(this.sugarRUInstance.getPreservationModeThresholdSetting()); this.detectCircularSugarsOnlyWithEnoughExocyclicOxygenAtomsSetting.set(this.sugarRUInstance.areOnlyCircularSugarsWithEnoughExocyclicOxygenAtomsDetected()); this.exocyclicOxygenAtomsToAtomsInRingRatioThresholdSetting.set(this.sugarRUInstance.getExocyclicOxygenAtomsToAtomsInRingRatioThresholdSetting()); @@ -1048,8 +1168,8 @@ public void restoreDefaultSettings() { public List fragmentMolecule(IAtomContainer aMolecule) throws NullPointerException, IllegalArgumentException, CloneNotSupportedException { Objects.requireNonNull(aMolecule, "Given molecule is null."); if (aMolecule.isEmpty()) { - List tmpReturnList = new ArrayList(1); - tmpReturnList.add(0, aMolecule.clone()); + List tmpReturnList = new ArrayList<>(1); + tmpReturnList.addFirst(aMolecule.clone()); aMolecule.setProperty(IMoleculeFragmenter.FRAGMENT_CATEGORY_PROPERTY_KEY, SugarRemovalUtilityFragmenter.FRAGMENT_CATEGORY_DEGLYCOSYLATED_CORE_VALUE); return tmpReturnList; @@ -1059,31 +1179,28 @@ public List fragmentMolecule(IAtomContainer aMolecule) throws Nu throw new IllegalArgumentException("Given molecule cannot be fragmented but should be filtered or preprocessed first."); } List tmpFragments; - SugarTypeToRemoveOption tmpOption = SugarTypeToRemoveOption.valueOf(this.sugarTypeToRemoveSetting.get()); + SugarRemovalUtilityFragmenter.SugarTypeToRemoveOption tmpOption = (SugarRemovalUtilityFragmenter.SugarTypeToRemoveOption) this.sugarTypeToRemoveSetting.get(); try { - switch (tmpOption) { - case CIRCULAR: - tmpFragments = this.sugarRUInstance.removeAndReturnCircularSugars(aMolecule, true); - break; - case LINEAR: - tmpFragments = this.sugarRUInstance.removeAndReturnLinearSugars(aMolecule, true); - break; - case CIRCULAR_AND_LINEAR: - tmpFragments = this.sugarRUInstance.removeAndReturnCircularAndLinearSugars(aMolecule, true); - break; - default: - throw new IllegalStateException("Unexpected value: " + this.sugarTypeToRemoveSetting); - } + tmpFragments = switch (tmpOption) { + case SugarTypeToRemoveOption.CIRCULAR -> + this.sugarRUInstance.removeAndReturnCircularSugars(aMolecule, true); + case SugarTypeToRemoveOption.LINEAR -> + this.sugarRUInstance.removeAndReturnLinearSugars(aMolecule, true); + case SugarTypeToRemoveOption.CIRCULAR_AND_LINEAR -> + this.sugarRUInstance.removeAndReturnCircularAndLinearSugars(aMolecule, true); + default -> + throw new IllegalStateException("Unexpected value: " + this.sugarTypeToRemoveSetting.get()); + }; } catch (IllegalArgumentException | CloneNotSupportedException anException) { throw new IllegalArgumentException("An error occurred during fragmentation: " + anException.toString()); } //post-processing of aglycone, it is always saturated with implicit hydrogen atoms (might be empty) - IAtomContainer tmpAglycone = tmpFragments.get(0); + IAtomContainer tmpAglycone = tmpFragments.getFirst(); tmpAglycone.setProperty(IMoleculeFragmenter.FRAGMENT_CATEGORY_PROPERTY_KEY, SugarRemovalUtilityFragmenter.FRAGMENT_CATEGORY_DEGLYCOSYLATED_CORE_VALUE); boolean tmpSugarsWereDetected = (tmpFragments.size() > 1); - if (this.returnedFragmentsSetting.get().equals(SRUFragmenterReturnedFragmentsOption.ALL_FRAGMENTS.name()) - || this.returnedFragmentsSetting.get().equals(SRUFragmenterReturnedFragmentsOption.ONLY_AGLYCONE.name())) { + if (this.returnedFragmentsSetting.get().equals(SugarRemovalUtilityFragmenter.SRUFragmenterReturnedFragmentsOption.ALL_FRAGMENTS) + || this.returnedFragmentsSetting.get().equals(SugarRemovalUtilityFragmenter.SRUFragmenterReturnedFragmentsOption.ONLY_AGLYCONE)) { if (!tmpAglycone.isEmpty()) { if (!ConnectivityChecker.isConnected(tmpAglycone)) { List tmpAglyconeFragments = SugarRemovalUtility.partitionAndSortUnconnectedFragments(tmpAglycone); @@ -1091,25 +1208,24 @@ public List fragmentMolecule(IAtomContainer aMolecule) throws Nu tmpAglyconeFragment.setProperty(IMoleculeFragmenter.FRAGMENT_CATEGORY_PROPERTY_KEY, SugarRemovalUtilityFragmenter.FRAGMENT_CATEGORY_DEGLYCOSYLATED_CORE_VALUE); } - tmpFragments.remove(0); + tmpFragments.removeFirst(); tmpFragments.addAll(0, tmpAglyconeFragments); } } else { - tmpFragments.remove(0); + tmpFragments.removeFirst(); } //else: only sugars are returned, dispose of aglycone } else { - tmpFragments.remove(0); + tmpFragments.removeFirst(); } //sugars were detected, postprocessing if (tmpSugarsWereDetected) { - if (this.returnedFragmentsSetting.get().equals(SRUFragmenterReturnedFragmentsOption.ALL_FRAGMENTS.name()) - || this.returnedFragmentsSetting.get().equals(SRUFragmenterReturnedFragmentsOption.ONLY_SUGAR_MOIETIES.name())) { - for (int i = 0; i < tmpFragments.size(); i++) { - IAtomContainer tmpSugarFragment = tmpFragments.get(i); + if (this.returnedFragmentsSetting.get().equals(SugarRemovalUtilityFragmenter.SRUFragmenterReturnedFragmentsOption.ALL_FRAGMENTS) + || this.returnedFragmentsSetting.get().equals(SugarRemovalUtilityFragmenter.SRUFragmenterReturnedFragmentsOption.ONLY_SUGAR_MOIETIES)) { + for (IAtomContainer tmpSugarFragment : tmpFragments) { if (!Objects.isNull(tmpSugarFragment.getProperty(IMoleculeFragmenter.FRAGMENT_CATEGORY_PROPERTY_KEY)) - && ((String) tmpSugarFragment.getProperty(IMoleculeFragmenter.FRAGMENT_CATEGORY_PROPERTY_KEY)) - .equals(SugarRemovalUtilityFragmenter.FRAGMENT_CATEGORY_DEGLYCOSYLATED_CORE_VALUE)) { + && tmpSugarFragment.getProperty(IMoleculeFragmenter.FRAGMENT_CATEGORY_PROPERTY_KEY) + .equals(SugarRemovalUtilityFragmenter.FRAGMENT_CATEGORY_DEGLYCOSYLATED_CORE_VALUE)) { continue; } if (Objects.isNull(tmpSugarFragment.getProperty(IMoleculeFragmenter.FRAGMENT_CATEGORY_PROPERTY_KEY))) { @@ -1117,7 +1233,7 @@ public List fragmentMolecule(IAtomContainer aMolecule) throws Nu SugarRemovalUtilityFragmenter.FRAGMENT_CATEGORY_SUGAR_MOIETY_VALUE); } try { - if (this.fragmentSaturationSetting.get().equals(FragmentSaturationOption.HYDROGEN_SATURATION.name())) { + if (this.fragmentSaturationSetting.get().equals(FragmentSaturationOption.HYDROGEN_SATURATION)) { ChemUtil.saturateWithHydrogen(tmpSugarFragment); } ChemUtil.checkAndCorrectElectronConfiguration(tmpSugarFragment); @@ -1129,13 +1245,11 @@ public List fragmentMolecule(IAtomContainer aMolecule) throws Nu } else { for (int i = 0; i < tmpFragments.size(); i++) { IAtomContainer tmpFragment = tmpFragments.get(i); - if (!Objects.isNull(tmpFragment.getProperty(IMoleculeFragmenter.FRAGMENT_CATEGORY_PROPERTY_KEY)) - && ((String) tmpFragment.getProperty(IMoleculeFragmenter.FRAGMENT_CATEGORY_PROPERTY_KEY)) + if (Objects.isNull(tmpFragment.getProperty(IMoleculeFragmenter.FRAGMENT_CATEGORY_PROPERTY_KEY)) + || !tmpFragment.getProperty(IMoleculeFragmenter.FRAGMENT_CATEGORY_PROPERTY_KEY) .equals(SugarRemovalUtilityFragmenter.FRAGMENT_CATEGORY_DEGLYCOSYLATED_CORE_VALUE)) { - continue; - } else { - tmpFragments.remove(i); - i--; + tmpFragments.remove(i); + i--; } } } @@ -1153,9 +1267,7 @@ public boolean shouldBePreprocessed(IAtomContainer aMolecule) throws NullPointer Objects.requireNonNull(aMolecule, "Given molecule is null."); if (this.sugarRUInstance.areOnlyTerminalSugarsRemoved()) { boolean tmpIsConnected = ConnectivityChecker.isConnected(aMolecule); - if (!tmpIsConnected) { - return true; - } + return !tmpIsConnected; } return false; } @@ -1178,7 +1290,6 @@ public IAtomContainer applyPreprocessing(IAtomContainer aMolecule) throws NullPo if (!this.shouldBePreprocessed(aMolecule)) { return aMolecule.clone(); } - //Todo I (Jonas) would like to remove any preprocessing done by the fragmenters as soon as possible, i.e. as soon as we have central preprocessing functionalities available if (this.sugarRUInstance.areOnlyTerminalSugarsRemoved()) { boolean tmpIsConnected = ConnectivityChecker.isConnected(aMolecule); if (!tmpIsConnected) { diff --git a/src/main/java/de/unijena/cheminf/mortar/model/io/ChemFileTypes.java b/src/main/java/de/unijena/cheminf/mortar/model/io/ChemFileTypes.java index 45cc0a48..003b7722 100644 --- a/src/main/java/de/unijena/cheminf/mortar/model/io/ChemFileTypes.java +++ b/src/main/java/de/unijena/cheminf/mortar/model/io/ChemFileTypes.java @@ -32,11 +32,11 @@ */ public enum ChemFileTypes { /** - * enum value for SDF + * enum value for SDF. */ SDF, /** - * enum value for PDB + * enum value for PDB. */ - PDB + PDB; } diff --git a/src/main/java/de/unijena/cheminf/mortar/model/io/DynamicSMILESFileReader.java b/src/main/java/de/unijena/cheminf/mortar/model/io/DynamicSMILESFileReader.java index 07ff5ee6..c7a12e59 100644 --- a/src/main/java/de/unijena/cheminf/mortar/model/io/DynamicSMILESFileReader.java +++ b/src/main/java/de/unijena/cheminf/mortar/model/io/DynamicSMILESFileReader.java @@ -129,7 +129,7 @@ public static DynamicSMILESFileFormat detectFormat(File aFile) throws IOExceptio try ( // throws FileNotFoundException if file cannot be found, see catch block below FileReader tmpSmilesFileReader = new FileReader(aFile); - BufferedReader tmpSmilesFileBufferedReader = new BufferedReader(tmpSmilesFileReader, BasicDefinitions.BUFFER_SIZE); + BufferedReader tmpSmilesFileBufferedReader = new BufferedReader(tmpSmilesFileReader, BasicDefinitions.BUFFER_SIZE) ) { IChemObjectBuilder tmpBuilder = SilentChemObjectBuilder.getInstance(); // AtomContainer to save the parsed SMILES in @@ -274,7 +274,7 @@ public IAtomContainerSet readFile(File aFile, DynamicSMILESFileFormat aFormat) t } else { tmpSmiles = tmpSmilesFileCurrentLine.trim(); } - if (!tmpSmiles.isEmpty()) { + if (tmpSmiles != null && !tmpSmiles.isEmpty()) { //throws exception if SMILES string is null, goes to catch block tmpMolecule = tmpSmilesParser.parseSmiles(tmpSmiles); } else { @@ -282,11 +282,11 @@ public IAtomContainerSet readFile(File aFile, DynamicSMILESFileFormat aFormat) t } } catch (InvalidSmilesException | IndexOutOfBoundsException | NullPointerException anException) { this.skippedLinesCounter++; - DynamicSMILESFileReader.LOGGER.log(Level.WARNING, "Import failed for structure in line (starting at 0):\t" + tmpLineInFileCounter); + DynamicSMILESFileReader.LOGGER.log(Level.WARNING, String.format("Import failed for structure in line (starting at 0):\t%s", tmpLineInFileCounter)); continue; } //setting the name of the atom container - String tmpName = ""; + String tmpName; if (aFormat.hasIDColumn() && tmpProcessedLineArray.length > 1 && !tmpProcessedLineArray[tmpIDExpectedPosition].trim().isEmpty()) { tmpName = tmpProcessedLineArray[tmpIDExpectedPosition].trim(); } else { @@ -319,7 +319,6 @@ public IAtomContainerSet readFile(File aFile, DynamicSMILESFileFormat aFormat) t * . (disconnected parts), * (, ) (branches), * /, \ (cis/trans stereochemistry). - * * All other characters, including whitespace characters, are not allowed. * * @param aPotentialSMILESString the string to test diff --git a/src/main/java/de/unijena/cheminf/mortar/model/io/Exporter.java b/src/main/java/de/unijena/cheminf/mortar/model/io/Exporter.java index 13623b7d..1b0be06c 100644 --- a/src/main/java/de/unijena/cheminf/mortar/model/io/Exporter.java +++ b/src/main/java/de/unijena/cheminf/mortar/model/io/Exporter.java @@ -47,6 +47,8 @@ import de.unijena.cheminf.mortar.model.util.BasicDefinitions; import de.unijena.cheminf.mortar.model.util.ChemUtil; import de.unijena.cheminf.mortar.model.util.FileUtil; +import de.unijena.cheminf.mortar.model.util.IDisplayEnum; +import de.unijena.cheminf.mortar.model.util.MiscUtil; import javafx.collections.ObservableList; import javafx.embed.swing.SwingFXUtils; @@ -73,6 +75,7 @@ import java.io.PrintWriter; import java.nio.file.Files; import java.nio.file.Paths; +import java.text.DecimalFormat; import java.util.ArrayList; import java.util.LinkedList; import java.util.List; @@ -87,38 +90,130 @@ * @version 1.0.0.0 */ public class Exporter { - // + // /** - * Logger of this class + * Enum for different file types to export. */ - private static final Logger LOGGER = Logger.getLogger(Exporter.class.getName()); - + public enum ExportTypes { + /** + * enum value for item csv file. + */ + ITEM_CSV_FILE, + /** + * enum value for item pdf file. + */ + ITEM_PDF_FILE, + /** + * enum value for fragments csv file. + */ + FRAGMENT_CSV_FILE, + /** + * enum value for fragments pdf file. + */ + FRAGMENT_PDF_FILE, + /** + * enum value for single sd file. + */ + FRAGMENT_SINGLE_SD_FILE, + /** + * enum value for sd file. + */ + FRAGMENT_MULTIPLE_SD_FILES, + /** + * enum value for pdb file. + */ + FRAGMENT_PDB_FILE; + } + // + // + // /** - * Name of directory generated for exporting a stream of fragment files + * Enum for allowed CSV file export separator chars. */ - private static final String FRAGMENTS_EXPORT_DIRECTORY_NAME = "MORTAR_Fragments_Export"; - + public enum CSVSeparator implements IDisplayEnum { + /** + * Comma. + */ + COMMA(',', Message.get("Exporter.CSVSeparator.Comma.displayName"), Message.get("Exporter.CSVSeparator.Comma.tooltip")), + /** + * Semicolon. + */ + SEMICOLON(';', Message.get("Exporter.CSVSeparator.Semicolon.displayName"), Message.get("Exporter.CSVSeparator.Semicolon.tooltip")), + /** + * Tab. + */ + TAB('\t', Message.get("Exporter.CSVSeparator.Tab.displayName"), Message.get("Exporter.CSVSeparator.Tab.tooltip")), + /** + * Space. + */ + SPACE(' ', Message.get("Exporter.CSVSeparator.Space.displayName"), Message.get("Exporter.CSVSeparator.Space.tooltip")); + /** + * Character representation of the wrapped separator char. + */ + private final char separatorChar; + /** + * Language-specific name for display in GUI. + */ + private final String displayName; + /** + * Language-specific tooltip text for display in GUI. + */ + private final String tooltip; + /** + * Constructor setting the wrapped separator char, display name, and tooltip text. + * + * @param aSeparatorChar CSV separator character to use when this option is selected + * @param aDisplayName display name + * @param aTooltipText tooltip text + */ + private CSVSeparator(char aSeparatorChar, String aDisplayName, String aTooltipText) { + this.separatorChar = aSeparatorChar; + this.displayName = aDisplayName; + this.tooltip = aTooltipText; + } + /** + * Returns the character representation of this separator. + * + * @return CSV separator char + */ + public char getSeparatorChar() { + return this.separatorChar; + } + // + @Override + public String getDisplayName() { + return this.displayName; + } + // + @Override + public String getTooltipText() { + return this.tooltip; + } + } + // + // + // /** - * Font for cells in exported PDF files + * Logger of this class. */ - private final Font fontFactory = FontFactory.getFont(FontFactory.TIMES, 12, Font.BOLD); - // + private static final Logger LOGGER = Logger.getLogger(Exporter.class.getName()); // - // /** - * Container for general settings for managing, preserving, and reloading application settings + * Name of directory generated for exporting a stream of fragment files. */ - private SettingsContainer settingsContainer; + private static final String FRAGMENTS_EXPORT_DIRECTORY_NAME = "MORTAR_Fragments_Export"; // /** - * PDF Document for export + * Font for cells in exported PDF files. */ - private Document document; + private static final Font PDF_CELL_FONT = FontFactory.getFont(FontFactory.TIMES, 12, Font.BOLD); + // // + // /** - * Destination file for export + * Container for general settings for managing, preserving, and reloading application settings. */ - private File file; + private final SettingsContainer settingsContainer; // // // @@ -138,223 +233,258 @@ public Exporter(SettingsContainer aSettingsContainer) throws NullPointerExceptio // // /** - * Opens FileChooser for export destination file + * Opens FileChooser to enable the user to choose an export destination file. * * @param aParentStage Stage * @param anExportType enum ExportType specifies file type - * @param aFragmentationName String for name of fragmentation + * @param aFragmentationName String for name of fragmentation to be used as file name proposal + * @return the chosen file or directory or null if the user chose to cancel the export in the file chooser dialog */ - public void saveFile(Stage aParentStage, ExportTypes anExportType, String aFragmentationName){ + public File openFileChooserForExportFileOrDir(Stage aParentStage, ExportTypes anExportType, String aFragmentationName){ Objects.requireNonNull(aParentStage, "aParentStage must not be null"); - this.file = null; - String tmpFileName = ""; + File tmpFile; + String tmpFileName; String tmpFragmentationName = aFragmentationName.replaceAll("\\s+", "_"); - switch (anExportType) { - case FRAGMENT_CSV_FILE: + tmpFile = switch (anExportType) { + case ExportTypes.FRAGMENT_CSV_FILE -> { tmpFileName = "Fragments_" + tmpFragmentationName; - this.file = this.saveFile(aParentStage, "CSV", "*.csv", tmpFileName); - break; - case PDB_FILE: - case SD_FILE: - this.file = this.chooseDirectory(aParentStage); - break; - case FRAGMENT_PDF_FILE: + yield this.chooseFile(aParentStage, "CSV", "*.csv", tmpFileName); + } + case ExportTypes.FRAGMENT_PDB_FILE, ExportTypes.FRAGMENT_MULTIPLE_SD_FILES -> + this.chooseDirectory(aParentStage); + case ExportTypes.FRAGMENT_PDF_FILE -> { tmpFileName = "Fragments_" + tmpFragmentationName; - this.file = this.saveFile(aParentStage, "PDF", "*.pdf", tmpFileName); - break; - case SINGLE_SD_FILE: + yield this.chooseFile(aParentStage, "PDF", "*.pdf", tmpFileName); + } + case ExportTypes.FRAGMENT_SINGLE_SD_FILE -> { tmpFileName = "Fragments_Export_" + tmpFragmentationName; - this.file = this.saveFile(aParentStage, "SD-File", "*.sdf", tmpFileName); - break; - case ITEM_CSV_FILE: + yield this.chooseFile(aParentStage, "SD-File", "*.sdf", tmpFileName); + } + case ExportTypes.ITEM_CSV_FILE -> { tmpFileName = "Items_" + tmpFragmentationName; - this.file = this.saveFile(aParentStage, "CSV", "*.csv", tmpFileName); - break; - case ITEM_PDF_FILE: + yield this.chooseFile(aParentStage, "CSV", "*.csv", tmpFileName); + } + case ExportTypes.ITEM_PDF_FILE -> { tmpFileName = "Items_" + tmpFragmentationName; - this.file = this.saveFile(aParentStage, "PDF", "*.pdf", tmpFileName); - break; - } + yield this.chooseFile(aParentStage, "PDF", "*.pdf", tmpFileName); + } + default -> + throw new UnsupportedOperationException(String.format("Unsupported export type: %s", anExportType)); + }; + return tmpFile; } // /** - * Exports in a new thread depending on aTabName the fragmentation results as displayed on the Itemisation tab or - * on the Fragments tab as a CSV file. - * Returns a list containing SMILES of the molecules that cause an error when exported + * Exports the fragmentation results as displayed on the Itemisation tab or + * on the Fragments tab, depending on aTabName, to a CSV file. + * Returns a list containing SMILES of the molecules that cause an error when exported. * + * @param aFile the file to export to; method returns null if the file is null * @param aMoleculeDataModelList a list of MoleculeDataModel instances to export along with their fragments * @param aFragmentationName fragmentation name to retrieve the specific set of fragments from the molecule data models * @param aSeparator the separator for the csv file * @param aTabName TabName to identify type of tab - * @return List {@literal <}String {@literal >} + * @return List {@literal <}String {@literal >} SMILES codes of the molecules that caused an error + * @throws FileNotFoundException if the given file cannot be found */ - public List exportCsvFile(List aMoleculeDataModelList, String aFragmentationName, String aSeparator, TabNames aTabName) { - try { - if (this.file == null) - return null; - if (aTabName.equals(TabNames.FRAGMENTS)) { - //can throw FileNotFoundException, gets handled in setOnFailed() - this.createFragmentationTabCsvFile(this.file, aMoleculeDataModelList, aSeparator); - } else if (aTabName.equals(TabNames.ITEMIZATION)) { - //can throw FileNotFoundException, gets handled in setOnFailed() - this.createItemizationTabCsvFile(this.file, aMoleculeDataModelList, aFragmentationName, aSeparator); - } - } catch (Exception anException) { - Exporter.LOGGER.log(Level.SEVERE, anException.toString(), anException); + public List exportCsvFile(File aFile, List aMoleculeDataModelList, String aFragmentationName, char aSeparator, TabNames aTabName) + throws FileNotFoundException { + if (aFile == null) { + return null; + } + if (aTabName.equals(TabNames.FRAGMENTS)) { + //can throw FileNotFoundException, gets handled in setOnFailed() + return this.createFragmentsTabCsvFile(aFile, aMoleculeDataModelList, aSeparator); + } else if (aTabName.equals(TabNames.ITEMIZATION)) { + //can throw FileNotFoundException, gets handled in setOnFailed() + return this.createItemizationTabCsvFile(aFile, aMoleculeDataModelList, aFragmentationName, aSeparator); } return new ArrayList<>(0); } // - /** - * Exports in a new thread depending on aTabName the fragmentation results as displayed on the Itemisation tab or on the Fragments tab as a CSV file. - * Returns a list containing SMILES of the molecules that cause an error when exported + * Exports depending on aTabName the fragmentation results as displayed on the Itemisation tab or on the Fragments tab as a CSV file. + * Returns a list containing SMILES of the molecules that caused an error when exported. * + * @param aFile the file to export to * @param aFragmentDataModelList a list of FragmentDataModel instances to export - * @param aMoleculeDataModelList a list MoleculeDataModel needed for the fragmentation report at the head of the exported document + * @param aMoleculeDataModelList a list MoleculeDataModel to export items * @param aFragmentationName fragmentation name to be displayed in the header of the PDF file + * @param anImportedFileName name of the input file whose molecules were fragmented * @param aTabName TabName to identify type of tab - * @return List {@literal <}String {@literal >} + * @return List {@literal <}String {@literal >} SMILES codes of the molecules that caused an error + * @throws FileNotFoundException if the given file cannot be found */ - public List exportPdfFile(List aFragmentDataModelList, ObservableList aMoleculeDataModelList, String aFragmentationName, TabNames aTabName) { - try { - if (this.file == null) - return null; - if (aTabName.equals(TabNames.FRAGMENTS)) { - //throws FileNotFoundException, gets handled in setOnFailed() - return this.createFragmentationTabPdfFile(this.file, aFragmentDataModelList, aMoleculeDataModelList, aFragmentationName); - } else if (aTabName.equals(TabNames.ITEMIZATION)) { - //throws FileNotFoundException, gets handled in setOnFailed() - return this.createItemizationTabPdfFile(this.file, aFragmentDataModelList.size(), aMoleculeDataModelList, aFragmentationName); - } - } catch (Exception anException) { - Exporter.LOGGER.log(Level.SEVERE, anException.toString(), anException); + public List exportPdfFile(File aFile, + List aFragmentDataModelList, + ObservableList aMoleculeDataModelList, + String aFragmentationName, + String anImportedFileName, + TabNames aTabName) throws FileNotFoundException { + if (aFile == null) { + return null; + } + if (aTabName.equals(TabNames.FRAGMENTS)) { + //throws FileNotFoundException, gets handled in setOnFailed() + return this.createFragmentsTabPdfFile(aFile, aFragmentDataModelList, aMoleculeDataModelList.size(), aFragmentationName, anImportedFileName); + } else if (aTabName.equals(TabNames.ITEMIZATION)) { + //throws FileNotFoundException, gets handled in setOnFailed() + return this.createItemizationTabPdfFile(aFile, aFragmentDataModelList.size(), aMoleculeDataModelList, aFragmentationName, anImportedFileName); } return null; } // /** - * Exports in a new thread depending on aFragmentationName the results as displayed on the Itemisation tab or on the Fragments tab as a chemical file. - * Returns a list containing SMILES of the molecules that cause an error when exported + * Exports depending on aFragmentationName the results as displayed on the Itemisation tab or on the Fragments tab as a chemical file. + * Returns a list containing SMILES of the molecules that caused an error when exported. The fragments are exported into separate files. * + * @param aFile the file/directory to export to * @param aFragmentDataModelList list of FragmentDataModel instances - * @param aFragmentationName name of fragmentation * @param aChemFileType ChemFileTypes specifies which file type should be exported * @param generate2dAtomCoordinates boolean value whether to generate 2D coordinates - * @return List {@literal <}String {@literal >} + * @return List {@literal <}String {@literal >} SMILES codes of the molecules that caused an error + * @throws IOException if sth goes wrong */ - public List exportFragmentsAsChemicalFile(List aFragmentDataModelList, String aFragmentationName, ChemFileTypes aChemFileType, boolean generate2dAtomCoordinates) { - return this.exportFragmentsAsChemicalFile(aFragmentDataModelList, aFragmentationName, aChemFileType, generate2dAtomCoordinates, false); + public List exportFragmentsAsChemicalFile(File aFile, List aFragmentDataModelList, ChemFileTypes aChemFileType, boolean generate2dAtomCoordinates) + throws IOException { + return this.exportFragmentsAsChemicalFile(aFile, aFragmentDataModelList, aChemFileType, generate2dAtomCoordinates, false); } // /** - * Exports in a new thread depending on aFragmentationName the results as displayed on the Itemisation tab or on the Fragments tab as a chemical file. - * Returns a list containing SMILES of the molecules that cause an error when exported + * Exports in a new thread the results as displayed on the Itemisation tab or on the Fragments tab as a chemical file. + * Returns a list containing SMILES of the molecules that caused an error when exported. * + * @param aFile the file/directory to export to * @param aFragmentDataModelList list of FragmentDataModel instances - * @param aFragmentationName name of fragmentation * @param aChemFileType ChemFileTypes specifies which file type should be exported * @param generate2dAtomCoordinates boolean value whether to generate 2D coordinates - * @param isSingleExport boolean if fragments should be exported in one file or seperated, one file each fragment - * @return List {@literal <}String {@literal >} + * @param isSingleExport true if fragments should be exported into one single file; false if separated, one file for each fragment + * @return List {@literal <}String {@literal >} SMILES codes of the molecules that caused an error + * @throws IOException if sth goes wrong */ - public List exportFragmentsAsChemicalFile(List aFragmentDataModelList, String aFragmentationName, ChemFileTypes aChemFileType, boolean generate2dAtomCoordinates, boolean isSingleExport) { - try { - if (this.file == null) - return null; - if (aChemFileType == ChemFileTypes.SDF && isSingleExport) { - return this.createFragmentationTabSingleSDFile(this.file, aFragmentDataModelList, generate2dAtomCoordinates); - } else if (aChemFileType == ChemFileTypes.SDF) { - return this.createFragmentationTabSeparateSDFiles(this.file, aFragmentDataModelList, generate2dAtomCoordinates); - } else if (aChemFileType == ChemFileTypes.PDB) { - return this.createFragmentationTabPDBFiles(this.file, aFragmentDataModelList, generate2dAtomCoordinates); - } - } catch (Exception anException) { - Exporter.LOGGER.log(Level.SEVERE, anException.toString(), anException); + public List exportFragmentsAsChemicalFile(File aFile, + List aFragmentDataModelList, + ChemFileTypes aChemFileType, + boolean generate2dAtomCoordinates, + boolean isSingleExport) throws IOException { + if (aFile == null) { + return null; } - return null; + List tmpReturnedList; + if (aChemFileType == ChemFileTypes.SDF && isSingleExport) { + tmpReturnedList = this.createFragmentationTabSingleSDFile(aFile, aFragmentDataModelList, generate2dAtomCoordinates); + } else if (aChemFileType == ChemFileTypes.SDF) { + tmpReturnedList = this.createFragmentationTabSeparateSDFiles(aFile, aFragmentDataModelList, generate2dAtomCoordinates); + } else if (aChemFileType == ChemFileTypes.PDB) { + tmpReturnedList = this.createFragmentationTabPDBFiles(aFile, aFragmentDataModelList, generate2dAtomCoordinates); + } else { + tmpReturnedList = null; + } + return tmpReturnedList; } // // - // + // /** * Exports the fragmentation results as they are displayed on the itemization tab as a CSV file. * * @param aMoleculeDataModelList a list of MoleculeDataModel instances to export along with their fragments * @param aFragmentationName fragmentation name to retrieve the specific set of fragments from the molecule data models * @param aSeparator the separator for the csv file - * @throws FileNotFoundException + * @return List {@literal <}String {@literal >} SMILES codes of the molecules that caused an error + * @throws FileNotFoundException if given file cannot be found * @author Betül Sevindik */ - private void createItemizationTabCsvFile(File aCsvFile, + private List createItemizationTabCsvFile(File aCsvFile, List aMoleculeDataModelList, String aFragmentationName, - String aSeparator) - throws FileNotFoundException { + char aSeparator) throws FileNotFoundException { if (aCsvFile == null || aMoleculeDataModelList == null || aFragmentationName == null) { - return; + return null; } - PrintWriter tmpWriter = new PrintWriter(aCsvFile.getPath()); - StringBuilder tmpCsvHeader = new StringBuilder(); - tmpCsvHeader.append(Message.get("Exporter.itemsTab.csvHeader.moleculeName") + aSeparator + - Message.get("Exporter.itemsTab.csvHeader.smilesOfStructure") + aSeparator + - Message.get("Exporter.itemsTab.csvHeader.smilesOfFragmentsAndFrequency") + "\n"); - tmpWriter.write(tmpCsvHeader.toString()); - for (MoleculeDataModel tmpMoleculeDataModel : aMoleculeDataModelList) { - if(Thread.currentThread().isInterrupted()){ - return; - } - tmpWriter.printf("%s" + aSeparator + "%s", tmpMoleculeDataModel.getName(), tmpMoleculeDataModel.getUniqueSmiles()); - if(!tmpMoleculeDataModel.hasMoleculeUndergoneSpecificFragmentation(aFragmentationName)){ - continue; - } - List tmpFragmentList = tmpMoleculeDataModel.getFragmentsOfSpecificAlgorithm(aFragmentationName); - for (FragmentDataModel tmpFragmentDataModel : tmpFragmentList) { - if(Thread.currentThread().isInterrupted() || - !tmpMoleculeDataModel.hasMoleculeUndergoneSpecificFragmentation(aFragmentationName) - ){ - return; + List tmpFailedExportFragments = new LinkedList<>(); + try (PrintWriter tmpWriter = new PrintWriter(aCsvFile.getPath())) { + String tmpCsvHeader = Message.get("Exporter.itemsTab.csvHeader.moleculeName") + aSeparator + + Message.get("Exporter.itemsTab.csvHeader.smilesOfStructure") + aSeparator + + Message.get("Exporter.itemsTab.csvHeader.smilesOfFragment") + aSeparator + + Message.get("Exporter.itemsTab.csvHeader.frequencyOfFragment"); + tmpWriter.write(tmpCsvHeader); + for (MoleculeDataModel tmpMoleculeDataModel : aMoleculeDataModelList) { + if (Thread.currentThread().isInterrupted()) { + return null; + } + try { + tmpWriter.printf("%n%s%s%s", tmpMoleculeDataModel.getName(), aSeparator, tmpMoleculeDataModel.getUniqueSmiles()); + } catch (Exception anException) { + Logger.getLogger(MoleculeDataModel.class.getName()).log(Level.SEVERE, String.format("%s molecule name: %s", anException.toString(), tmpMoleculeDataModel.getName()), anException); + tmpFailedExportFragments.add(tmpMoleculeDataModel.getUniqueSmiles()); + continue; + } + if (!tmpMoleculeDataModel.hasMoleculeUndergoneSpecificFragmentation(aFragmentationName)) { + continue; + } + List tmpFragmentList = tmpMoleculeDataModel.getFragmentsOfSpecificFragmentation(aFragmentationName); + for (FragmentDataModel tmpFragmentDataModel : tmpFragmentList) { + if (Thread.currentThread().isInterrupted()) { + return null; + } + tmpWriter.append(aSeparator); + try { + tmpWriter.printf("%s%s%s", + tmpFragmentDataModel.getUniqueSmiles(), + aSeparator, + tmpMoleculeDataModel.getFragmentFrequencyOfSpecificFragmentation(aFragmentationName).get(tmpFragmentDataModel.getUniqueSmiles()).toString()); + } catch (Exception anException) { + Logger.getLogger(MoleculeDataModel.class.getName()).log(Level.SEVERE, String.format("%s molecule name: %s", anException.toString(), tmpFragmentDataModel.getName()), anException); + tmpFailedExportFragments.add(tmpFragmentDataModel.getUniqueSmiles()); + //continue; + } } - tmpWriter.append(aSeparator); - tmpWriter.printf("%s" + aSeparator + "%s", tmpFragmentDataModel.getUniqueSmiles(), tmpMoleculeDataModel.getFragmentFrequencyOfSpecificAlgorithm(aFragmentationName).get(tmpFragmentDataModel.getUniqueSmiles()).toString()); } - tmpWriter.append("\n"); + return tmpFailedExportFragments; } - tmpWriter.close(); } // /** * Exports the fragmentation results as they are displayed on the fragments tab as a CSV file. * - * @param aList a list of FragmentDataModel instances to export + * @param aList a list of FragmentDataModel instances to export * @param aSeparator the separator for the csv file - * @throws FileNotFoundException + * @return List {@literal <}String {@literal >} SMILES codes of the molecules that caused an error + * @throws FileNotFoundException if given file cannot be found * @author Betül Sevindik */ - private void createFragmentationTabCsvFile(File aCsvFile, List aList, String aSeparator) + private List createFragmentsTabCsvFile(File aCsvFile, List aList, char aSeparator) throws FileNotFoundException { if (aCsvFile == null || aList == null) { - return; + return null; } - PrintWriter tmpWriter = new PrintWriter(aCsvFile.getPath()); - StringBuilder tmpFragmentationCsvHeader = new StringBuilder(); - tmpFragmentationCsvHeader.append(Message.get("Exporter.fragmentationTab.csvHeader.smiles") + aSeparator + - Message.get("Exporter.fragmentationTab.csvHeader.frequency") + aSeparator + - Message.get("Exporter.fragmentationTab.csvHeader.percentage") + aSeparator + - Message.get("Exporter.fragmentationTab.csvHeader.moleculeFrequency") + aSeparator + - Message.get("Exporter.fragmentationTab.csvHeader.moleculePercentage") + ("\n")); - tmpWriter.write(tmpFragmentationCsvHeader.toString()); - for (MoleculeDataModel tmpDataModel : aList) { - if(Thread.currentThread().isInterrupted()){ - return; + List tmpFailedExportFragments = new LinkedList<>(); + try (PrintWriter tmpWriter = new PrintWriter(aCsvFile.getPath())) { + String tmpFragmentationCsvHeader = Message.get("Exporter.fragmentationTab.csvHeader.smiles") + aSeparator + + Message.get("Exporter.fragmentationTab.csvHeader.frequency") + aSeparator + + Message.get("Exporter.fragmentationTab.csvHeader.percentage") + aSeparator + + Message.get("Exporter.fragmentationTab.csvHeader.moleculeFrequency") + aSeparator + + Message.get("Exporter.fragmentationTab.csvHeader.moleculePercentage"); + tmpWriter.write(tmpFragmentationCsvHeader); + for (MoleculeDataModel tmpDataModel : aList) { + if (Thread.currentThread().isInterrupted()) { + return null; + } + try { + FragmentDataModel tmpFragmentDataModel = (FragmentDataModel) tmpDataModel; + tmpWriter.printf("%n%s%s%d%s%.4f%s%d%s%.4f", + tmpFragmentDataModel.getUniqueSmiles(), aSeparator, tmpFragmentDataModel.getAbsoluteFrequency(), aSeparator, + tmpFragmentDataModel.getAbsolutePercentage(), aSeparator, tmpFragmentDataModel.getMoleculeFrequency(), aSeparator, + tmpFragmentDataModel.getMoleculePercentage()); + } catch (Exception anException) { + Logger.getLogger(MoleculeDataModel.class.getName()).log(Level.SEVERE, String.format("%s molecule name: %s", anException.toString(), tmpDataModel.getName()), anException); + tmpFailedExportFragments.add(tmpDataModel.getUniqueSmiles()); + //continue; + } } - FragmentDataModel tmpFragmentDataModel = (FragmentDataModel) tmpDataModel; - tmpWriter.printf("%s" + aSeparator + "%d" + aSeparator + "%.3f" + aSeparator + "%d" + aSeparator + "%.2f\n", - tmpFragmentDataModel.getUniqueSmiles(), tmpFragmentDataModel.getAbsoluteFrequency(), - tmpFragmentDataModel.getAbsolutePercentage(), tmpFragmentDataModel.getMoleculeFrequency(), - tmpFragmentDataModel.getMoleculePercentage()); + return tmpFailedExportFragments; } - tmpWriter.close(); } // /** @@ -362,95 +492,98 @@ private void createFragmentationTabCsvFile(File aCsvFile, List} SMILES codes of the molecules that caused an error + * @throws FileNotFoundException if given file cannot be found + * @throws DocumentException if something goes wrong writing the document * @author Betül Sevindik */ - private List createFragmentationTabPdfFile(File aPdfFile, - List aFragmentDataModelList, - ObservableList aMoleculeDataModelList, - String aFragmentationName) throws FileNotFoundException, DocumentException { - if (aPdfFile == null || aFragmentDataModelList == null || aMoleculeDataModelList == null || - aFragmentationName == null) { + private List createFragmentsTabPdfFile(File aPdfFile, + List aFragmentDataModelList, + int aMoleculeDataModelListSize, + String aFragmentationName, + String anImportedFileName) throws FileNotFoundException, DocumentException { + if (aPdfFile == null || aFragmentDataModelList == null || aMoleculeDataModelListSize == 0 || + aFragmentationName == null || anImportedFileName == null) { return null; } - List tmpFailedExportFragments = new LinkedList<>(); - this.document = new Document(PageSize.A4); - this.document.setPageSize(this.document.getPageSize().rotate()); - PdfWriter.getInstance(this.document, new FileOutputStream(aPdfFile.getPath())); - this.document.open(); - float tmpCellLength[] = {70f, 120f, 50f, 50f, 55f, 55f}; // relative sizes - PdfPTable tmpFragmentationTable = new PdfPTable(tmpCellLength); - PdfPCell tmpSmilesStringCell = new PdfPCell(new Paragraph(Message.get("Exporter.fragmentationTab.pdfCellHeader.smiles"), fontFactory)); - PdfPCell tmpFrequencyCell = new PdfPCell(new Paragraph(Message.get("Exporter.fragmentationTab.pdfCellHeader.frequency"), this.fontFactory)); - PdfPCell tmpPercentageCell = new PdfPCell(new Paragraph(Message.get("Exporter.fragmentationTab.pdfCellHeader.percentage"), this.fontFactory)); - PdfPCell tmpMolFrequencyCell = new PdfPCell(new Paragraph(Message.get("Exporter.fragmentationTab.pdfCellHeader.moleculeFrequency"), this.fontFactory)); - PdfPCell tmpMolPercentageCell = new PdfPCell(new Paragraph(Message.get("Exporter.fragmentationTab.pdfCellHeader.moleculePercentage"), this.fontFactory)); - PdfPCell tmpFragmentCell = new PdfPCell(new Paragraph(Message.get("Exporter.fragmentationTab.pdfCellHeader.fragment"), this.fontFactory)); - Chunk tmpHeader = new Chunk(Message.get("Exporter.fragmentationTab.pdfCellHeader.header"), - FontFactory.getFont(FontFactory.TIMES_ROMAN, 18, Font.UNDERLINE)); - Paragraph tmpSpace = new Paragraph(" "); - tmpFragmentationTable.addCell(tmpFragmentCell); - tmpFragmentationTable.addCell(tmpSmilesStringCell); - tmpFragmentationTable.addCell(tmpFrequencyCell); - tmpFragmentationTable.addCell(tmpPercentageCell); - tmpFragmentationTable.addCell(tmpMolFrequencyCell); - tmpFragmentationTable.addCell(tmpMolPercentageCell); - for (MoleculeDataModel tmpModel : aFragmentDataModelList) { - if(Thread.currentThread().isInterrupted()){ - return null; - } - FragmentDataModel tmpFragmentDataModel = (FragmentDataModel) tmpModel; - int tmpAbsoluteFrequency = tmpFragmentDataModel.getAbsoluteFrequency(); - String tmpStringAbsoluteFrequency = String.format("%d", tmpAbsoluteFrequency); - double tmpAbsolutePercentage = tmpFragmentDataModel.getAbsolutePercentage(); - int tmpMoleculeFrequency = tmpFragmentDataModel.getMoleculeFrequency(); - String tmpStringMoleculeFrequency = String.format("%d", tmpMoleculeFrequency); - String tmpStringAbsolutePercentage = String.format("%.3f", tmpAbsolutePercentage); - double tmpMoleculePercentage = tmpFragmentDataModel.getMoleculePercentage(); - String tmpStringMoleculePercentage = String.format("%.2f", tmpMoleculePercentage); - //creates an image of the fragment - PdfPCell tmpImageFragmentCell = new PdfPCell(); - tmpImageFragmentCell.setFixedHeight(85f); - IAtomContainer tmpStructureOfFragment; - try { - tmpStructureOfFragment = tmpFragmentDataModel.getAtomContainer(); - } catch (CDKException anException) { - Exporter.LOGGER.getLogger(MoleculeDataModel.class.getName()).log(Level.SEVERE, anException.toString() + "_" + tmpFragmentDataModel.getName(), anException); - tmpFailedExportFragments.add(tmpFragmentDataModel.getUniqueSmiles()); - continue; + try (Document tmpPDFDocument = new Document(PageSize.A4)) { + List tmpFailedExportFragments = new LinkedList<>(); + tmpPDFDocument.setPageSize(tmpPDFDocument.getPageSize().rotate()); + PdfWriter.getInstance(tmpPDFDocument, new FileOutputStream(aPdfFile.getPath())); + tmpPDFDocument.open(); + float[] tmpCellLength = {70f, 120f, 50f, 50f, 55f, 55f}; // relative sizes, magic numbers + PdfPTable tmpFragmentationTable = new PdfPTable(tmpCellLength); + PdfPCell tmpSmilesStringCell = new PdfPCell(new Paragraph(Message.get("Exporter.fragmentationTab.pdfCellHeader.smiles"), Exporter.PDF_CELL_FONT)); + PdfPCell tmpFrequencyCell = new PdfPCell(new Paragraph(Message.get("Exporter.fragmentationTab.pdfCellHeader.frequency"), Exporter.PDF_CELL_FONT)); + PdfPCell tmpPercentageCell = new PdfPCell(new Paragraph(Message.get("Exporter.fragmentationTab.pdfCellHeader.percentage"), Exporter.PDF_CELL_FONT)); + PdfPCell tmpMolFrequencyCell = new PdfPCell(new Paragraph(Message.get("Exporter.fragmentationTab.pdfCellHeader.moleculeFrequency"), Exporter.PDF_CELL_FONT)); + PdfPCell tmpMolPercentageCell = new PdfPCell(new Paragraph(Message.get("Exporter.fragmentationTab.pdfCellHeader.moleculePercentage"), Exporter.PDF_CELL_FONT)); + PdfPCell tmpFragmentCell = new PdfPCell(new Paragraph(Message.get("Exporter.fragmentationTab.pdfCellHeader.fragment"), Exporter.PDF_CELL_FONT)); + Chunk tmpHeader = new Chunk(Message.get("Exporter.fragmentationTab.pdfCellHeader.header"), + FontFactory.getFont(FontFactory.TIMES_ROMAN, 18, Font.UNDERLINE)); + Paragraph tmpSpace = new Paragraph(" "); + tmpFragmentationTable.addCell(tmpFragmentCell); + tmpFragmentationTable.addCell(tmpSmilesStringCell); + tmpFragmentationTable.addCell(tmpFrequencyCell); + tmpFragmentationTable.addCell(tmpPercentageCell); + tmpFragmentationTable.addCell(tmpMolFrequencyCell); + tmpFragmentationTable.addCell(tmpMolPercentageCell); + DecimalFormat tmpPercentageForm = new DecimalFormat("#.##%"); + for (MoleculeDataModel tmpModel : aFragmentDataModelList) { + if (Thread.currentThread().isInterrupted()) { + return null; + } + FragmentDataModel tmpFragmentDataModel = (FragmentDataModel) tmpModel; + int tmpAbsoluteFrequency = tmpFragmentDataModel.getAbsoluteFrequency(); + String tmpStringAbsoluteFrequency = String.format("%d", tmpAbsoluteFrequency); + double tmpAbsolutePercentage = tmpFragmentDataModel.getAbsolutePercentage(); + int tmpMoleculeFrequency = tmpFragmentDataModel.getMoleculeFrequency(); + String tmpStringMoleculeFrequency = String.format("%d", tmpMoleculeFrequency); + String tmpStringAbsolutePercentage = tmpPercentageForm.format(tmpAbsolutePercentage); + double tmpMoleculePercentage = tmpFragmentDataModel.getMoleculePercentage(); + String tmpStringMoleculePercentage = tmpPercentageForm.format(tmpMoleculePercentage); + //creates an image of the fragment + PdfPCell tmpImageFragmentCell = new PdfPCell(); + tmpImageFragmentCell.setFixedHeight(85f); + IAtomContainer tmpStructureOfFragment; + try { + tmpStructureOfFragment = tmpFragmentDataModel.getAtomContainer(); + } catch (CDKException anException) { + Logger.getLogger(MoleculeDataModel.class.getName()).log(Level.SEVERE, String.format("%s molecule name: %s", anException.toString(), tmpFragmentDataModel.getName()), anException); + tmpFailedExportFragments.add(tmpFragmentDataModel.getUniqueSmiles()); + continue; + } + //cannot be imported because com.lowagie.text.Image is already imported + javafx.scene.image.Image tmpImageStructureOfFragment = DepictionUtil.depictImageWithZoom(tmpStructureOfFragment, 4.0); + BufferedImage tmpBufferedImageFragment = SwingFXUtils.fromFXImage(tmpImageStructureOfFragment, null); + Image tmpImageFragment = this.convertToITextImage(tmpBufferedImageFragment); + //inserts the data into the table + PdfPCell tmpCellOfFrequency = new PdfPCell(new Paragraph(tmpStringAbsoluteFrequency)); + tmpCellOfFrequency.setHorizontalAlignment(Element.ALIGN_RIGHT); + PdfPCell tmpCellOfPercentage = new PdfPCell(new Paragraph(tmpStringAbsolutePercentage)); + tmpCellOfPercentage.setHorizontalAlignment(Element.ALIGN_RIGHT); + PdfPCell tmpCellOfMolFrequency = new PdfPCell(new Paragraph(tmpStringMoleculeFrequency)); + tmpCellOfMolFrequency.setHorizontalAlignment(Element.ALIGN_RIGHT); + PdfPCell tmpCellOfMolPercentage = new PdfPCell(new Paragraph(tmpStringMoleculePercentage)); + tmpCellOfMolPercentage.setHorizontalAlignment(Element.ALIGN_RIGHT); + tmpImageFragmentCell.addElement(tmpImageFragment); + tmpFragmentationTable.addCell(tmpImageFragmentCell); + tmpFragmentationTable.addCell(tmpFragmentDataModel.getUniqueSmiles()); + tmpFragmentationTable.addCell(tmpCellOfFrequency); + tmpFragmentationTable.addCell(tmpCellOfPercentage); + tmpFragmentationTable.addCell(tmpCellOfMolFrequency); + tmpFragmentationTable.addCell(tmpCellOfMolPercentage); } - javafx.scene.image.Image tmpImageStructureOfFragment = DepictionUtil.depictImageWithZoom(tmpStructureOfFragment, - 4.0); - BufferedImage tmpBufferedImageFragment = SwingFXUtils.fromFXImage(tmpImageStructureOfFragment, null); - Image tmpImageFragment = this.getITextImage(tmpBufferedImageFragment); - //inserts the data into the table - PdfPCell tmpCellOfFrequency = new PdfPCell(new Paragraph(tmpStringAbsoluteFrequency)); - tmpCellOfFrequency.setHorizontalAlignment(Element.ALIGN_RIGHT); - PdfPCell tmpCellOfPercentage = new PdfPCell(new Paragraph(tmpStringAbsolutePercentage)); - tmpCellOfPercentage.setHorizontalAlignment(Element.ALIGN_RIGHT); - PdfPCell tmpCellOfMolFrequency = new PdfPCell(new Paragraph(tmpStringMoleculeFrequency)); - tmpCellOfMolFrequency.setHorizontalAlignment(Element.ALIGN_RIGHT); - PdfPCell tmpCellOfMolPercentage = new PdfPCell(new Paragraph(tmpStringMoleculePercentage)); - tmpCellOfMolPercentage.setHorizontalAlignment(Element.ALIGN_RIGHT); - tmpImageFragmentCell.addElement(tmpImageFragment); - tmpFragmentationTable.addCell(tmpImageFragmentCell); - tmpFragmentationTable.addCell(tmpFragmentDataModel.getUniqueSmiles()); - tmpFragmentationTable.addCell(tmpCellOfFrequency); - tmpFragmentationTable.addCell(tmpCellOfPercentage); - tmpFragmentationTable.addCell(tmpCellOfMolFrequency); - tmpFragmentationTable.addCell(tmpCellOfMolPercentage); + tmpPDFDocument.add(tmpHeader); + tmpPDFDocument.add(tmpSpace); + tmpPDFDocument.add(this.createHeaderTable(aFragmentDataModelList.size(), aMoleculeDataModelListSize, aFragmentationName, anImportedFileName)); + tmpPDFDocument.add(tmpSpace); + tmpPDFDocument.add(tmpFragmentationTable); + return tmpFailedExportFragments; } - this.document.add(tmpHeader); - this.document.add(tmpSpace); - this.document.add(this.createHeaderTable(aFragmentDataModelList.size(), aMoleculeDataModelList.size(), aFragmentationName)); - this.document.add(tmpSpace); - this.document.add(tmpFragmentationTable); - this.document.close(); - return tmpFailedExportFragments; } // /** @@ -460,123 +593,133 @@ private List createFragmentationTabPdfFile(File aPdfFile, * @param aFragmentDataModelListSize size of list of FragmentDataModel instances to export * @param aMoleculeDataModelList a list MoleculeDataModel needed for the fragmentation report at the head of the exported document * @param aFragmentationName fragmentation name to retrieve the specific set of fragments from the molecule data models - * @throws FileNotFoundException - * @throws DocumentException + * @param anImportedFileName name of the input file whose molecules were fragmented + * @return List {@literal <}String {@literal >} SMILES codes of the molecules that caused an error + * @throws FileNotFoundException if given file cannot be found + * @throws DocumentException if something goes wrong writing the document * @author Betül Sevindik */ private List createItemizationTabPdfFile(File aPdfFile, int aFragmentDataModelListSize, ObservableList aMoleculeDataModelList, - String aFragmentationName) throws FileNotFoundException, DocumentException { + String aFragmentationName, + String anImportedFileName) throws FileNotFoundException, DocumentException { if (aPdfFile == null || aFragmentDataModelListSize == 0 || - aMoleculeDataModelList == null || aMoleculeDataModelList.size() == 0 || - aFragmentationName == null || aFragmentationName.isEmpty()) { + aMoleculeDataModelList == null || aMoleculeDataModelList.isEmpty() || + aFragmentationName == null || aFragmentationName.isEmpty() || + anImportedFileName == null || anImportedFileName.isEmpty()) { return null; } - List tmpFailedExportFragments = new LinkedList<>(); - this.document = new Document(PageSize.A4); - PdfWriter.getInstance(this.document, new FileOutputStream(aPdfFile.getPath())); - this.document.open(); - // creates the pdf table - Chunk tmpItemizationTabHeader = new Chunk(Message.get("Exporter.itemsTab.pdfCellHeader.header"), - FontFactory.getFont(FontFactory.TIMES_ROMAN, 18, Font.UNDERLINE)); - Paragraph tmpSpace = new Paragraph(" "); - this.document.add(tmpItemizationTabHeader); - this.document.add(tmpSpace); - this.document.add(this.createHeaderTable(aFragmentDataModelListSize, aMoleculeDataModelList.size(), aFragmentationName)); - this.document.add(tmpSpace); - for (MoleculeDataModel tmpMoleculeDataModel : aMoleculeDataModelList) { - if(Thread.currentThread().isInterrupted()){ - return null; - } - PdfPTable tmpTable = new PdfPTable(2); - PdfPTable tmpFragmentTable = new PdfPTable(1); - tmpTable.setWidths(new int[]{40, 80}); - PdfPCell tmpNameCell = new PdfPCell(new Paragraph(Message.get("Exporter.itemsTab.pdfCellHeader.name"), this.fontFactory)); - tmpNameCell.setFixedHeight(55f); - PdfPCell tmpStructureCell = new PdfPCell(new Paragraph(Message.get("Exporter.itemsTab.pdfCellHeader.structure"), this.fontFactory)); - tmpStructureCell.setFixedHeight(120f); - tmpTable.addCell(tmpNameCell); - String tmpName = tmpMoleculeDataModel.getName(); - tmpTable.addCell(tmpName); - tmpTable.addCell(tmpStructureCell); - // Image of molecule - IAtomContainer tmpMoleculeStructure; - try { - tmpMoleculeStructure = tmpMoleculeDataModel.getAtomContainer(); - } catch (CDKException anException) { - Exporter.LOGGER.getLogger(MoleculeDataModel.class.getName()).log(Level.SEVERE, anException.toString() + "_" + tmpMoleculeDataModel.getName(), anException); - tmpFailedExportFragments.add(tmpMoleculeDataModel.getUniqueSmiles()); - continue; - } - PdfPCell tmpMoleculeStructureCell = new PdfPCell(); - tmpMoleculeStructureCell.setFixedHeight(120f); - javafx.scene.image.Image tmpMoleculeImage = DepictionUtil.depictImageWithZoom(tmpMoleculeStructure, - 3.0); - BufferedImage tmpBufferedImageOfMolecule = SwingFXUtils.fromFXImage(tmpMoleculeImage, null); - Image tmpMolecule = this.getITextImage(tmpBufferedImageOfMolecule); - tmpMoleculeStructureCell.addElement(tmpMolecule); - tmpTable.addCell(tmpMoleculeStructureCell); - PdfPCell tmpCellOfFragment = new PdfPCell(new Paragraph(Message.get("Exporter.itemsTab.pdfCellHeader.fragments"), this.fontFactory)); - tmpCellOfFragment.setHorizontalAlignment(Element.ALIGN_CENTER); - tmpFragmentTable.addCell(tmpCellOfFragment); - this.document.add(tmpTable); - this.document.add(tmpFragmentTable); - if(!tmpMoleculeDataModel.hasMoleculeUndergoneSpecificFragmentation(aFragmentationName)){ - continue; - } - List tmpFragmentList = tmpMoleculeDataModel.getFragmentsOfSpecificAlgorithm(aFragmentationName); - PdfPTable tmpFragmentationTable2 = new PdfPTable(3); - for (int tmpFragmentNumber = 0; tmpFragmentNumber < tmpFragmentList.size(); ) { - if(Thread.currentThread().isInterrupted()){ + try (Document tmpPDFDocument = new Document(PageSize.A4)) { + List tmpFailedExportFragments = new LinkedList<>(); + PdfWriter.getInstance(tmpPDFDocument, new FileOutputStream(aPdfFile.getPath())); + tmpPDFDocument.open(); + // creates the pdf table + Chunk tmpItemizationTabHeader = new Chunk(Message.get("Exporter.itemsTab.pdfCellHeader.header"), + FontFactory.getFont(FontFactory.TIMES_ROMAN, 18, Font.UNDERLINE)); + Paragraph tmpSpace = new Paragraph(" "); + tmpPDFDocument.add(tmpItemizationTabHeader); + tmpPDFDocument.add(tmpSpace); + tmpPDFDocument.add(this.createHeaderTable(aFragmentDataModelListSize, aMoleculeDataModelList.size(), aFragmentationName, anImportedFileName)); + tmpPDFDocument.add(tmpSpace); + for (MoleculeDataModel tmpMoleculeDataModel : aMoleculeDataModelList) { + if (Thread.currentThread().isInterrupted()) { return null; } - ArrayList tmpCell = new ArrayList(3); //magic number, see line 487 (loop below): "for (; tmpImagesNumbers < 3; tmpImagesNumbers++){" - int tmpImagesNumbers = 0; - for (; tmpImagesNumbers < 3; tmpImagesNumbers++) { - if(Thread.currentThread().isInterrupted()){ - return null; - } - if (tmpFragmentNumber >= tmpFragmentList.size()) { - break; - } - FragmentDataModel tmpFragmentDatModel = tmpFragmentList.get(tmpFragmentNumber); - IAtomContainer tmpFragmentStructure; - try { - tmpFragmentStructure = tmpFragmentDatModel.getAtomContainer(); - } catch (CDKException anException) { - Exporter.LOGGER.getLogger(MoleculeDataModel.class.getName()).log(Level.SEVERE, anException.toString() + "_" + tmpFragmentDatModel.getName(), anException); - tmpFailedExportFragments.add(tmpFragmentDatModel.getUniqueSmiles()); - continue; - } - if(!tmpMoleculeDataModel.hasMoleculeUndergoneSpecificFragmentation(aFragmentationName)){ - continue; - } - String tmpFrequency = tmpMoleculeDataModel.getFragmentFrequencyOfSpecificAlgorithm(aFragmentationName).get(tmpFragmentDatModel.getUniqueSmiles()).toString(); - javafx.scene.image.Image tmpFragmentImage = DepictionUtil.depictImageWithText(tmpFragmentStructure, 3.0, BasicDefinitions.DEFAULT_IMAGE_WIDTH_DEFAULT, BasicDefinitions.DEFAULT_IMAGE_HEIGHT_DEFAULT, tmpFrequency); - BufferedImage tmpBufferedImageOfFragment = SwingFXUtils.fromFXImage(tmpFragmentImage, null); - Image tmpFragment = this.getITextImage(tmpBufferedImageOfFragment); - PdfPCell cell = new PdfPCell(); - cell.addElement(tmpFragment); - tmpCell.add(cell); - tmpFragmentNumber++; + PdfPTable tmpTable = new PdfPTable(2); + PdfPTable tmpFragmentTable = new PdfPTable(1); + tmpTable.setWidths(new int[]{40, 80}); + PdfPCell tmpNameCell = new PdfPCell(new Paragraph(Message.get("Exporter.itemsTab.pdfCellHeader.name"), Exporter.PDF_CELL_FONT)); + tmpNameCell.setFixedHeight(55f); + PdfPCell tmpStructureCell = new PdfPCell(new Paragraph(Message.get("Exporter.itemsTab.pdfCellHeader.structure"), Exporter.PDF_CELL_FONT)); + tmpStructureCell.setFixedHeight(120f); + tmpTable.addCell(tmpNameCell); + String tmpName = tmpMoleculeDataModel.getName(); + tmpTable.addCell(tmpName); + tmpTable.addCell(tmpStructureCell); + // Image of molecule + IAtomContainer tmpMoleculeStructure; + try { + tmpMoleculeStructure = tmpMoleculeDataModel.getAtomContainer(); + } catch (CDKException anException) { + Logger.getLogger(MoleculeDataModel.class.getName()).log(Level.SEVERE, String.format("%s molecule name: %s", anException.toString(), tmpMoleculeDataModel.getName()), anException); + tmpFailedExportFragments.add(tmpMoleculeDataModel.getUniqueSmiles()); + continue; + } + PdfPCell tmpMoleculeStructureCell = new PdfPCell(); + tmpMoleculeStructureCell.setFixedHeight(120f); + //cannot be imported because com.lowagie.text.Image is already imported + javafx.scene.image.Image tmpMoleculeImage = DepictionUtil.depictImageWithZoom(tmpMoleculeStructure, 3.0); + BufferedImage tmpBufferedImageOfMolecule = SwingFXUtils.fromFXImage(tmpMoleculeImage, null); + Image tmpMolecule = this.convertToITextImage(tmpBufferedImageOfMolecule); + tmpMoleculeStructureCell.addElement(tmpMolecule); + tmpTable.addCell(tmpMoleculeStructureCell); + PdfPCell tmpCellOfFragment = new PdfPCell(new Paragraph(Message.get("Exporter.itemsTab.pdfCellHeader.fragments"), Exporter.PDF_CELL_FONT)); + tmpCellOfFragment.setHorizontalAlignment(Element.ALIGN_CENTER); + tmpFragmentTable.addCell(tmpCellOfFragment); + tmpPDFDocument.add(tmpTable); + tmpPDFDocument.add(tmpFragmentTable); + if (!tmpMoleculeDataModel.hasMoleculeUndergoneSpecificFragmentation(aFragmentationName)) { + continue; } - for (int tmpCellIterator = 0; tmpCellIterator < 3; tmpCellIterator++) { - if(Thread.currentThread().isInterrupted()){ + List tmpFragmentList = tmpMoleculeDataModel.getFragmentsOfSpecificFragmentation(aFragmentationName); + int tmpFragmentsPerLine = 3; //magic number + PdfPTable tmpFragmentationTable2 = new PdfPTable(tmpFragmentsPerLine); + for (int tmpFragmentNumber = 0; tmpFragmentNumber < tmpFragmentList.size(); ) { + if (Thread.currentThread().isInterrupted()) { return null; } - if (tmpCellIterator < tmpImagesNumbers) { - tmpFragmentationTable2.addCell(tmpCell.get(tmpCellIterator)); - } else { - tmpFragmentationTable2.addCell(new Paragraph("")); + ArrayList tmpCell = new ArrayList<>(3); + int tmpImagesNumbers = 0; + for (; tmpImagesNumbers < tmpFragmentsPerLine; tmpImagesNumbers++) { + if (Thread.currentThread().isInterrupted()) { + return null; + } + if (tmpFragmentNumber >= tmpFragmentList.size()) { + break; + } + FragmentDataModel tmpFragmentDatModel = tmpFragmentList.get(tmpFragmentNumber); + IAtomContainer tmpFragmentStructure; + try { + tmpFragmentStructure = tmpFragmentDatModel.getAtomContainer(); + } catch (CDKException anException) { + Logger.getLogger(MoleculeDataModel.class.getName()).log(Level.SEVERE, String.format("%s molecule name: %s", anException.toString(), tmpMoleculeDataModel.getName()), anException); + tmpFailedExportFragments.add(tmpFragmentDatModel.getUniqueSmiles()); + continue; + } + if (!tmpMoleculeDataModel.hasMoleculeUndergoneSpecificFragmentation(aFragmentationName)) { + continue; + } + String tmpFrequency = tmpMoleculeDataModel.getFragmentFrequencyOfSpecificFragmentation(aFragmentationName).get(tmpFragmentDatModel.getUniqueSmiles()).toString(); + javafx.scene.image.Image tmpFragmentImage = DepictionUtil.depictImageWithText( + tmpFragmentStructure, + 3.0, + BasicDefinitions.DEFAULT_IMAGE_WIDTH_DEFAULT, + BasicDefinitions.DEFAULT_IMAGE_HEIGHT_DEFAULT, + tmpFrequency); + BufferedImage tmpBufferedImageOfFragment = SwingFXUtils.fromFXImage(tmpFragmentImage, null); + Image tmpFragment = this.convertToITextImage(tmpBufferedImageOfFragment); + PdfPCell cell = new PdfPCell(); + cell.addElement(tmpFragment); + tmpCell.add(cell); + tmpFragmentNumber++; + } + for (int tmpCellIterator = 0; tmpCellIterator < tmpFragmentsPerLine; tmpCellIterator++) { + if(Thread.currentThread().isInterrupted()){ + return null; + } + if (tmpCellIterator < tmpImagesNumbers) { + tmpFragmentationTable2.addCell(tmpCell.get(tmpCellIterator)); + } else { + tmpFragmentationTable2.addCell(new Paragraph("")); + } } } + tmpPDFDocument.add(tmpFragmentationTable2); + tmpPDFDocument.newPage(); } - this.document.add(tmpFragmentationTable2); - this.document.newPage(); + return tmpFailedExportFragments; } - this.document.close(); - return tmpFailedExportFragments; } // /** @@ -594,88 +737,81 @@ private List createItemizationTabPdfFile(File aPdfFile, * @param aFile File to save fragments * @param aFragmentDataModelList list of FragmentDataModel instances * @param generate2DCoordinates boolean value whether to generate 2D coordinates + * @return List {@literal <}String {@literal >} SMILES codes of the molecules that caused an error + * @throws IOException if sth goes wrong * @author Samuel Behr */ private List createFragmentationTabSingleSDFile(File aFile, List aFragmentDataModelList, - boolean generate2DCoordinates) { - if (aFragmentDataModelList == null) { + boolean generate2DCoordinates) throws IOException { + if (aFile == null || aFragmentDataModelList == null) { return null; } - try { - if (aFile != null) { - List tmpFailedExportFragments = new LinkedList<>(); - int tmpExportedFragmentsCounter = 0; - int tmpFailedFragmentExportCounter = 0; - try ( - PrintWriter tmpWriter = new PrintWriter(aFile.getPath()); - BufferedWriter tmpBufferedWriter = new BufferedWriter(tmpWriter); - SDFWriter tmpSDFWriter = new SDFWriter(tmpBufferedWriter); - ) { - //specifying format of export - //setting whether to always use MDL V3000 format - tmpSDFWriter.setAlwaysV3000(this.settingsContainer.getAlwaysMDLV3000FormatAtExportSetting()); - //accessing the WriteAromaticBondType setting - tmpSDFWriter.getSetting(MDLV2000Writer.OptWriteAromaticBondTypes).setSetting("true"); - //iterating through the fragments held by the list of fragments - for (MoleculeDataModel tmpFragmentDataModel : aFragmentDataModelList) { - if(Thread.currentThread().isInterrupted()){ - return null; - } - IAtomContainer tmpFragment; - try { - tmpFragment = tmpFragmentDataModel.getAtomContainer(); - } catch (CDKException anException) { - Exporter.LOGGER.log(Level.SEVERE, anException.toString() + "_" + tmpFragmentDataModel.getName(), anException); - tmpFailedExportFragments.add(tmpFragmentDataModel.getUniqueSmiles()); - tmpFailedFragmentExportCounter++; - continue; + List tmpFailedExportFragments = new LinkedList<>(); + int tmpExportedFragmentsCounter = 0; + int tmpFailedFragmentExportCounter = 0; + try ( + PrintWriter tmpWriter = new PrintWriter(aFile.getPath()); + BufferedWriter tmpBufferedWriter = new BufferedWriter(tmpWriter); + SDFWriter tmpSDFWriter = new SDFWriter(tmpBufferedWriter); + ) { + //specifying format of export + //setting whether to always use MDL V3000 format + tmpSDFWriter.setAlwaysV3000(this.settingsContainer.getAlwaysMDLV3000FormatAtExportSetting()); + //accessing the WriteAromaticBondType setting + try { + tmpSDFWriter.getSetting(MDLV2000Writer.OptWriteAromaticBondTypes).setSetting("true"); + } catch (CDKException anException) { + Exporter.LOGGER.log(Level.WARNING, "Exporting fragments with aromatic bond types not possible", anException); + } + //iterating through the fragments held by the list of fragments + for (MoleculeDataModel tmpFragmentDataModel : aFragmentDataModelList) { + if (Thread.currentThread().isInterrupted()) { + return null; + } + IAtomContainer tmpFragment; + try { + tmpFragment = tmpFragmentDataModel.getAtomContainer(); + IAtomContainer tmpFragmentClone = null; + boolean tmpPoint3dAvailable = ChemUtil.has3DCoordinates(tmpFragmentDataModel); + boolean tmpPoint2dAvailable = ChemUtil.has2DCoordinates(tmpFragmentDataModel); + if (!tmpPoint3dAvailable) { + tmpFragmentClone = this.handleFragmentWithNo3dInformationAvailable(tmpFragment, + tmpPoint2dAvailable, generate2DCoordinates); + } //else: given 3D info is used + //writing to file + try { + if (tmpPoint3dAvailable) { + tmpSDFWriter.write(tmpFragment); + } else { + tmpSDFWriter.write(tmpFragmentClone); } - IAtomContainer tmpFragmentClone = null; - boolean tmpPoint3dAvailable = ChemUtil.has3DCoordinates(tmpFragmentDataModel); - boolean tmpPoint2dAvailable = ChemUtil.has2DCoordinates(tmpFragmentDataModel); - if (!tmpPoint3dAvailable) { - tmpFragmentClone = this.handleFragmentWithNo3dInformationAvailable(tmpFragment, - tmpPoint2dAvailable, generate2DCoordinates); - } //else: given 3D info is used - //writing to file - try { - if (tmpPoint3dAvailable) { - tmpSDFWriter.write(tmpFragment); - } else { - tmpSDFWriter.write(tmpFragmentClone); - } - tmpExportedFragmentsCounter++; - } catch (CDKException anException) { - //retrying with a kekulized clone of the fragment - try { - if (tmpPoint3dAvailable) { - tmpFragmentClone = tmpFragment.clone(); - } - Kekulization.kekulize(tmpFragmentClone); - tmpSDFWriter.write(tmpFragmentClone); - tmpExportedFragmentsCounter++; - } catch (CDKException | CloneNotSupportedException anInnerException) { - Exporter.LOGGER.log(Level.SEVERE, anInnerException.toString(), anInnerException); - tmpFailedExportFragments.add(tmpFragmentDataModel.getUniqueSmiles()); - tmpFailedFragmentExportCounter++; - } + tmpExportedFragmentsCounter++; + } catch (CDKException anException) { + //retrying with a kekulized clone of the fragment - going to main catch block if sth goes wrong + if (tmpPoint3dAvailable) { + tmpFragmentClone = tmpFragment.clone(); } + Kekulization.kekulize(tmpFragmentClone); + tmpSDFWriter.write(tmpFragmentClone); + tmpExportedFragmentsCounter++; } + } catch (CDKException | CloneNotSupportedException anException) { + Exporter.LOGGER.log(Level.SEVERE, String.format("%s molecule name: %s", anException.toString(), tmpFragmentDataModel.getName()), anException); + tmpFailedExportFragments.add(tmpFragmentDataModel.getUniqueSmiles()); + tmpFailedFragmentExportCounter++; + //continue; } - Exporter.LOGGER.log(Level.INFO, String.format("Exported %d fragments as single SD file " + - "(export of %d fragments failed). File name: %s", tmpExportedFragmentsCounter, - tmpFailedFragmentExportCounter, aFile.getName())); - return tmpFailedExportFragments; } - } catch (NullPointerException | IOException | CDKException anException) { - Exporter.LOGGER.log(Level.SEVERE, anException.toString(), anException); - return null; + int finalTmpExportedFragmentsCounter = tmpExportedFragmentsCounter; + int finalTmpFailedFragmentExportCounter = tmpFailedFragmentExportCounter; + Exporter.LOGGER.log(Level.INFO, () -> String.format("Exported %d fragments as single SD file " + + "(export of %d fragments failed). File name: %s", finalTmpExportedFragmentsCounter, + finalTmpFailedFragmentExportCounter, aFile.getName())); + return tmpFailedExportFragments; } - return null; } // - /** * Exports the chemical data of the given fragments as separate MDL SD files to an * empty folder generated at the chosen path. The molecular formula of each fragment is used as name for each respective @@ -692,96 +828,93 @@ private List createFragmentationTabSingleSDFile(File aFile, * @param aDirectory directory to save fragments * @param aFragmentDataModelList list of FragmentDataModel instances * @param generate2DCoordinates boolean value whether to generate 2D coordinates + * @return List {@literal <}String {@literal >} SMILES codes of the molecules that caused an error + * @throws IOException if sth goes wrong * @author Samuel Behr */ private List createFragmentationTabSeparateSDFiles(File aDirectory, List aFragmentDataModelList, - boolean generate2DCoordinates) { - if (aFragmentDataModelList == null) { + boolean generate2DCoordinates) throws IOException { + if (aDirectory == null || !aDirectory.isDirectory() || aFragmentDataModelList == null) { return null; } - try { - if (aDirectory != null && aDirectory.isDirectory()) { - List tmpFailedExportFragments = new LinkedList<>(); - String tmpSDFFilesDirectoryPathName = aDirectory - + File.separator - + FRAGMENTS_EXPORT_DIRECTORY_NAME + "_" + FileUtil.getTimeStampFileNameExtension(); - String tmpFinalSDFilesDirectoryPathName = FileUtil.getNonExistingFilePath(tmpSDFFilesDirectoryPathName, File.separator); - File tmpSDFilesDirectory = Files.createDirectory(Paths.get(tmpFinalSDFilesDirectoryPathName)).toFile(); - int tmpExportedFragmentsCounter = 0; - int tmpFailedFragmentExportCounter = 0; - //iterating through the fragments held by the list of fragments - for (MoleculeDataModel tmpFragmentDataModel : aFragmentDataModelList) { - if(Thread.currentThread().isInterrupted()){ - return null; - } - IAtomContainer tmpFragment; + List tmpFailedExportFragments = new LinkedList<>(); + String tmpSDFFilesDirectoryPathName = aDirectory + + File.separator + + Exporter.FRAGMENTS_EXPORT_DIRECTORY_NAME + "_" + FileUtil.getTimeStampFileNameExtension(); + // this is not how this FileUtil method should be used, but I'll allow it + String tmpFinalSDFilesDirectoryPathName = FileUtil.getNonExistingFilePath(tmpSDFFilesDirectoryPathName, File.separator); + File tmpSDFilesDirectory = Files.createDirectory(Paths.get(tmpFinalSDFilesDirectoryPathName)).toFile(); + int tmpExportedFragmentsCounter = 0; + int tmpFailedFragmentExportCounter = 0; + //iterating through the fragments held by the list of fragments + for (MoleculeDataModel tmpFragmentDataModel : aFragmentDataModelList) { + if (Thread.currentThread().isInterrupted()) { + return null; + } + IAtomContainer tmpFragment; + try { + tmpFragment = tmpFragmentDataModel.getAtomContainer(); + IAtomContainer tmpFragmentClone = null; + boolean tmpPoint3dAvailable = ChemUtil.has3DCoordinates(tmpFragmentDataModel); + boolean tmpPoint2dAvailable = ChemUtil.has2DCoordinates(tmpFragmentDataModel); + //checking whether 3D information are available + if (!tmpPoint3dAvailable) { + tmpFragmentClone = this.handleFragmentWithNo3dInformationAvailable(tmpFragment, + tmpPoint2dAvailable, generate2DCoordinates); + } //else: given 3D info is used + //generating file + String tmpMolecularFormula = ChemUtil.generateMolecularFormula(tmpFragment); + String tmpSDFilePathName = FileUtil.getNonExistingFilePath(tmpSDFilesDirectory + + File.separator + tmpMolecularFormula, ".sdf"); + File tmpSDFile = new File(tmpSDFilePathName); + //writing to file + try ( + PrintWriter tmpWriter = new PrintWriter(tmpSDFile); + BufferedWriter tmpBufferedWriter = new BufferedWriter(tmpWriter); + SDFWriter tmpSDFWriter = new SDFWriter(tmpBufferedWriter); + ) { + //specifying format of export + //setting whether to always use MDL V3000 format + tmpSDFWriter.setAlwaysV3000(this.settingsContainer.getAlwaysMDLV3000FormatAtExportSetting()); + //accessing the WriteAromaticBondType setting try { - tmpFragment = tmpFragmentDataModel.getAtomContainer(); + tmpSDFWriter.getSetting(MDLV2000Writer.OptWriteAromaticBondTypes).setSetting("true"); } catch (CDKException anException) { - Exporter.LOGGER.log(Level.SEVERE, anException.toString() + "_" + tmpFragmentDataModel.getName(), anException); - tmpFailedExportFragments.add(tmpFragmentDataModel.getUniqueSmiles()); - tmpFailedFragmentExportCounter++; - continue; + Exporter.LOGGER.log(Level.WARNING, "Exporting fragments with aromatic bond types not possible", anException); } - IAtomContainer tmpFragmentClone = null; - boolean tmpPoint3dAvailable = ChemUtil.has3DCoordinates(tmpFragmentDataModel); - boolean tmpPoint2dAvailable = ChemUtil.has2DCoordinates(tmpFragmentDataModel); - if (!tmpPoint3dAvailable) { - tmpFragmentClone = this.handleFragmentWithNo3dInformationAvailable(tmpFragment, - tmpPoint2dAvailable, generate2DCoordinates); - } - //generating file - String tmpMolecularFormula = ChemUtil.generateMolecularFormula(tmpFragment); - String tmpSDFilePathName = FileUtil.getNonExistingFilePath(tmpSDFilesDirectory - + File.separator + tmpMolecularFormula, ".sdf"); - File tmpSDFile = new File(tmpSDFilePathName); - //writing to file - try ( - PrintWriter tmpWriter = new PrintWriter(tmpSDFile); - BufferedWriter tmpBufferedWriter = new BufferedWriter(tmpWriter); - SDFWriter tmpSDFWriter = new SDFWriter(tmpBufferedWriter); - ) { - try { - //specifying format of export - tmpSDFWriter.setAlwaysV3000(this.settingsContainer.getAlwaysMDLV3000FormatAtExportSetting()); //setting whether to always use MDL V3000 format - tmpSDFWriter.getSetting(MDLV2000Writer.OptWriteAromaticBondTypes).setSetting("true"); //accessing the WriteAromaticBondType setting - if (tmpPoint3dAvailable) { - tmpSDFWriter.write(tmpFragment); - } else { - tmpSDFWriter.write(tmpFragmentClone); - } - tmpExportedFragmentsCounter++; - } catch (CDKException anException) { - //retrying with a kekulized clone of the fragment - try { - if (tmpPoint3dAvailable) { - tmpFragmentClone = tmpFragment.clone(); - } - Kekulization.kekulize(tmpFragmentClone); - tmpSDFWriter.write(tmpFragmentClone); - tmpExportedFragmentsCounter++; - } catch (CDKException | CloneNotSupportedException anInnerException) { - Exporter.LOGGER.log(Level.SEVERE, anInnerException.toString(), anInnerException); - tmpFailedExportFragments.add(tmpFragmentDataModel.getUniqueSmiles()); - tmpFailedFragmentExportCounter++; - } + try { + if (tmpPoint3dAvailable) { + tmpSDFWriter.write(tmpFragment); + } else { + tmpSDFWriter.write(tmpFragmentClone); + } + tmpExportedFragmentsCounter++; + } catch (CDKException anException) { + //retrying with a kekulized clone of the fragment - going to main catch block if sth goes wrong + if (tmpPoint3dAvailable) { + tmpFragmentClone = tmpFragment.clone(); } + Kekulization.kekulize(tmpFragmentClone); + tmpSDFWriter.write(tmpFragmentClone); + tmpExportedFragmentsCounter++; } } - Exporter.LOGGER.log(Level.INFO, String.format("Exported %d fragments as separate SD files " + - "(export of %d fragments failed). Folder name: %s", tmpExportedFragmentsCounter, - tmpFailedFragmentExportCounter, tmpSDFilesDirectory.getName())); - return tmpFailedExportFragments; + } catch (CDKException | CloneNotSupportedException anException) { + Exporter.LOGGER.log(Level.SEVERE, String.format("%s molecule name: %s", anException.toString(), tmpFragmentDataModel.getName()), anException); + tmpFailedExportFragments.add(tmpFragmentDataModel.getUniqueSmiles()); + tmpFailedFragmentExportCounter++; + //continue; } - } catch (NullPointerException | IOException | IllegalArgumentException anException) { - Exporter.LOGGER.log(Level.SEVERE, anException.toString(), anException); - return null; } - return null; + int finalTmpExportedFragmentsCounter = tmpExportedFragmentsCounter; + int finalTmpFailedFragmentExportCounter = tmpFailedFragmentExportCounter; + Exporter.LOGGER.log(Level.INFO, () -> String.format("Exported %d fragments as separate SD files " + + "(export of %d fragments failed). Folder name: %s", finalTmpExportedFragmentsCounter, + finalTmpFailedFragmentExportCounter, tmpSDFilesDirectory.getName())); + return tmpFailedExportFragments; } // - /** * Opens a directory chooser and exports the chemical data of the given fragments as PDB files to an empty folder * generated at the chosen path. The molecular formula of each fragment is used as name for the associated file. In @@ -793,55 +926,51 @@ private List createFragmentationTabSeparateSDFiles(File aDirectory, * @param aDirectory directory to save fragments * @param aFragmentDataModelList list of FragmentDataModel instances * @param generate2DCoordinates boolean value whether to generate 2D coordinates + * @return List {@literal <}String {@literal >} SMILES codes of the molecules that caused an error + * @throws IOException if sth goes wrong * @author Samuel Behr */ private List createFragmentationTabPDBFiles(File aDirectory, List aFragmentDataModelList, - boolean generate2DCoordinates) { - if (aFragmentDataModelList == null) { + boolean generate2DCoordinates) throws IOException { + if (aDirectory == null || !aDirectory.isDirectory() || aFragmentDataModelList == null) { return null; } - try { - if (aDirectory != null && aDirectory.isDirectory()) { - List tmpFailedExportFragments = new LinkedList<>(); - String tmpPDBFilesDirectoryPathName = aDirectory - + File.separator - + FRAGMENTS_EXPORT_DIRECTORY_NAME + "_" + FileUtil.getTimeStampFileNameExtension(); - String tmpFinalPDBFilesDirectoryPathName = FileUtil.getNonExistingFilePath(tmpPDBFilesDirectoryPathName, File.separator); - File tmpPDBFilesDirectory = Files.createDirectory(Paths.get(tmpFinalPDBFilesDirectoryPathName)).toFile(); - int tmpExportedFragmentsCounter = 0; - int tmpFailedFragmentExportCounter = 0; - //iterating through the fragments held by the list of fragments - for (MoleculeDataModel tmpFragmentDataModel : aFragmentDataModelList) { - if(Thread.currentThread().isInterrupted()){ - return null; - } - IAtomContainer tmpFragment; + List tmpFailedExportFragments = new LinkedList<>(); + String tmpPDBFilesDirectoryPathName = aDirectory + + File.separator + + Exporter.FRAGMENTS_EXPORT_DIRECTORY_NAME + "_" + FileUtil.getTimeStampFileNameExtension(); + // this is not how this FileUtil method should be used, but I'll allow it + String tmpFinalPDBFilesDirectoryPathName = FileUtil.getNonExistingFilePath(tmpPDBFilesDirectoryPathName, File.separator); + File tmpPDBFilesDirectory = Files.createDirectory(Paths.get(tmpFinalPDBFilesDirectoryPathName)).toFile(); + int tmpExportedFragmentsCounter = 0; + int tmpFailedFragmentExportCounter = 0; + //iterating through the fragments held by the list of fragments + for (MoleculeDataModel tmpFragmentDataModel : aFragmentDataModelList) { + if (Thread.currentThread().isInterrupted()) { + return null; + } + IAtomContainer tmpFragment; + try { + tmpFragment = tmpFragmentDataModel.getAtomContainer(); + IAtomContainer tmpFragmentClone = null; + boolean tmpPoint3dAvailable = ChemUtil.has3DCoordinates(tmpFragmentDataModel); + boolean tmpPoint2dAvailable = ChemUtil.has2DCoordinates(tmpFragmentDataModel); + //checking whether 3D information are available + if (!tmpPoint3dAvailable) { + tmpFragmentClone = this.handleFragmentWithNo3dInformationAvailable(tmpFragment, + tmpPoint2dAvailable, generate2DCoordinates); + } //else: given 3D info is used + //generating file + String tmpMolecularFormula = ChemUtil.generateMolecularFormula(tmpFragment); + String tmpPDBFilePathName = FileUtil.getNonExistingFilePath(tmpPDBFilesDirectory + + File.separator + tmpMolecularFormula, ".pdb"); + File tmpPDBFile = new File(tmpPDBFilePathName); + //writing to file + try ( + PDBWriter tmpPDBWriter = new PDBWriter(new FileOutputStream(tmpPDBFile)); + ) { try { - tmpFragment = tmpFragmentDataModel.getAtomContainer(); - } catch (CDKException anException) { - Exporter.LOGGER.log(Level.SEVERE, anException.toString() + "_" + tmpFragmentDataModel.getName(), anException); - tmpFailedExportFragments.add(tmpFragmentDataModel.getUniqueSmiles()); - tmpFailedFragmentExportCounter++; - continue; - } - IAtomContainer tmpFragmentClone = null; - boolean tmpPoint3dAvailable = ChemUtil.has3DCoordinates(tmpFragmentDataModel); - boolean tmpPoint2dAvailable = ChemUtil.has2DCoordinates(tmpFragmentDataModel); - //checking whether 3D information are available - if (!tmpPoint3dAvailable) { - tmpFragmentClone = this.handleFragmentWithNo3dInformationAvailable(tmpFragment, - tmpPoint2dAvailable, generate2DCoordinates); - } - //generating file - String tmpMolecularFormula = ChemUtil.generateMolecularFormula(tmpFragment); - String tmpPDBFilePathName = FileUtil.getNonExistingFilePath(tmpPDBFilesDirectory - + File.separator + tmpMolecularFormula, ".pdb"); - File tmpPDBFile = new File(tmpPDBFilePathName); - //writing to file - try ( - PDBWriter tmpPDBWriter = new PDBWriter(new FileOutputStream(tmpPDBFile)); - ) { if (tmpPoint3dAvailable) { tmpPDBWriter.writeMolecule(tmpFragment); } else { @@ -849,31 +978,37 @@ private List createFragmentationTabPDBFiles(File aDirectory, } tmpExportedFragmentsCounter++; } catch (CDKException anException) { - Exporter.LOGGER.log(Level.SEVERE, anException.toString(), anException); - tmpFailedExportFragments.add(tmpFragmentDataModel.getUniqueSmiles()); - tmpFailedFragmentExportCounter++; + //retrying with a kekulized clone of the fragment - going to main catch block if sth goes wrong + if (tmpPoint3dAvailable) { + tmpFragmentClone = tmpFragment.clone(); + } + Kekulization.kekulize(tmpFragmentClone); + tmpPDBWriter.write(tmpFragmentClone); + tmpExportedFragmentsCounter++; } } - Exporter.LOGGER.log(Level.INFO, String.format("Exported %d fragments as PDB files " + - "(export of %d fragments failed). Folder name: %s", tmpExportedFragmentsCounter, - tmpFailedFragmentExportCounter, tmpPDBFilesDirectory.getName())); - return tmpFailedExportFragments; + } catch (CDKException | CloneNotSupportedException anException) { + Exporter.LOGGER.log(Level.SEVERE, String.format("%s molecule name: %s", anException.toString(), tmpFragmentDataModel.getName()), anException); + tmpFailedExportFragments.add(tmpFragmentDataModel.getUniqueSmiles()); + tmpFailedFragmentExportCounter++; + //continue; } - } catch (NullPointerException | IOException | IllegalArgumentException anException) { - Exporter.LOGGER.log(Level.SEVERE, anException.toString(), anException); - return null; } - return null; + int finalTmpFailedFragmentExportCounter = tmpFailedFragmentExportCounter; + int finalTmpExportedFragmentsCounter = tmpExportedFragmentsCounter; + Exporter.LOGGER.log(Level.INFO, () -> String.format("Exported %d fragments as PDB files " + + "(export of %d fragments failed). Folder name: %s", finalTmpExportedFragmentsCounter, + finalTmpFailedFragmentExportCounter, tmpPDBFilesDirectory.getName())); + return tmpFailedExportFragments; } // - /** - * Converts a buffered image into a com.lowagie.text image (necessary for pdf export with iText) and returns it. + * Converts a buffered image into a com.lowagie.text.Image (necessary for pdf export with iText) and returns it. * * @param aBufferedImage buffered image to be converted - * @return com.lowagie.text image + * @return com.lowagie.text.Image */ - private Image getITextImage(BufferedImage aBufferedImage) { + private Image convertToITextImage(BufferedImage aBufferedImage) { try { ByteArrayOutputStream tmpByteArrayOS = new ByteArrayOutputStream(); ImageIO.write(aBufferedImage, "png", tmpByteArrayOS); @@ -885,43 +1020,49 @@ private Image getITextImage(BufferedImage aBufferedImage) { } } // - /** * Creates a header with general information for the PDf files. * * @param aFragmentDataModelListSize size of list of fragments * @param aMoleculeDataModelListSize size of list of molecules - * @param anAlgorithmName name of the used algorithm + * @param aFragmentationName name of the fragmentation task to display in the header + * @param anImportedFileName name of the input file whose molecules were fragmented * @return fragmentation report table for a PDF file header * @author Betül Sevindik */ private PdfPTable createHeaderTable( int aFragmentDataModelListSize, int aMoleculeDataModelListSize, - String anAlgorithmName) { - int tmpFragmentNumbers = aFragmentDataModelListSize; - int tmpMoleculeNumbers = aMoleculeDataModelListSize; + String aFragmentationName, + String anImportedFileName) { //creates the header - float tmpCellLengthIntro[] = {60f, 60f}; // relative sizes + float[] tmpCellLengthIntro = {60f, 60f}; // relative sizes PdfPTable tmpTableIntro = new PdfPTable(tmpCellLengthIntro); - PdfPCell tmpIntroCell1 = new PdfPCell(new Paragraph(Message.get("Exporter.pdfHeader.algorithmUsed"), this.fontFactory)); - PdfPCell tmpIntroCell2 = new PdfPCell(new Paragraph(anAlgorithmName)); - PdfPCell tmpIntroCell3 = new PdfPCell(new Paragraph(Message.get("Exporter.pdfHeader.numberOfMolecules"), this.fontFactory)); - PdfPCell tmpIntroCell4 = new PdfPCell(new Paragraph(String.valueOf(tmpMoleculeNumbers))); - PdfPCell tmpIntroCell5 = new PdfPCell(new Paragraph(Message.get("Exporter.pdfHeader.numberOfFragments"), this.fontFactory)); - PdfPCell tmpIntroCell6 = new PdfPCell(new Paragraph(String.valueOf(tmpFragmentNumbers))); + PdfPCell tmpIntroCell1 = new PdfPCell(new Paragraph(Message.get("Exporter.pdfHeader.fragmentationName"), Exporter.PDF_CELL_FONT)); + PdfPCell tmpIntroCell2 = new PdfPCell(new Paragraph(aFragmentationName)); + PdfPCell tmpIntroCell3 = new PdfPCell(new Paragraph(Message.get("Exporter.pdfHeader.numberOfMolecules"), Exporter.PDF_CELL_FONT)); + PdfPCell tmpIntroCell4 = new PdfPCell(new Paragraph(String.valueOf(aMoleculeDataModelListSize))); + PdfPCell tmpIntroCell5 = new PdfPCell(new Paragraph(Message.get("Exporter.pdfHeader.numberOfFragments"), Exporter.PDF_CELL_FONT)); + PdfPCell tmpIntroCell6 = new PdfPCell(new Paragraph(String.valueOf(aFragmentDataModelListSize))); + PdfPCell tmpIntroCell7 = new PdfPCell(new Paragraph(Message.get("Exporter.pdfHeader.fileName"), Exporter.PDF_CELL_FONT)); + PdfPCell tmpIntroCell8 = new PdfPCell(new Paragraph(anImportedFileName)); + PdfPCell tmpIntroCell9 = new PdfPCell(new Paragraph(Message.get("Exporter.pdfHeader.timeStamp"), Exporter.PDF_CELL_FONT)); + PdfPCell tmpIntroCell10 = new PdfPCell(new Paragraph(MiscUtil.getTimestampInStandardFormat())); tmpTableIntro.addCell(tmpIntroCell1); tmpTableIntro.addCell(tmpIntroCell2); tmpTableIntro.addCell(tmpIntroCell3); tmpTableIntro.addCell(tmpIntroCell4); tmpTableIntro.addCell(tmpIntroCell5); tmpTableIntro.addCell(tmpIntroCell6); + tmpTableIntro.addCell(tmpIntroCell7); + tmpTableIntro.addCell(tmpIntroCell8); + tmpTableIntro.addCell(tmpIntroCell9); + tmpTableIntro.addCell(tmpIntroCell10); return tmpTableIntro; } // - /** - * Opens a FileChooser to be able to save a file. + * Opens a FileChooser to be able to save a file. Returns null if the user cancelled the dialog. * * @param aParentStage Stage where the FileChooser should be shown * @param aDescription file type description to be used in the dialog (not the file extension) @@ -931,17 +1072,17 @@ private PdfPTable createHeaderTable( * @throws NullPointerException if the given stage is null * @author Betül Sevindik */ - private File saveFile(Stage aParentStage, String aDescription, String anExtension, String aFileName) throws NullPointerException { + private File chooseFile(Stage aParentStage, String aDescription, String anExtension, String aFileName) throws NullPointerException { Objects.requireNonNull(aParentStage, "aParentStage (instance of Stage) is null"); FileChooser tmpFileChooser = new FileChooser(); tmpFileChooser.setTitle((Message.get("Exporter.fileChooser.title"))); - FileChooser.ExtensionFilter tmpExtensionfilter2 = new FileChooser.ExtensionFilter(aDescription, anExtension); - tmpFileChooser.getExtensionFilters().addAll(tmpExtensionfilter2); + FileChooser.ExtensionFilter tmpExtensionFilter2 = new FileChooser.ExtensionFilter(aDescription, anExtension); + tmpFileChooser.getExtensionFilters().addAll(tmpExtensionFilter2); tmpFileChooser.setInitialFileName(aFileName); tmpFileChooser.setInitialDirectory(new File(this.settingsContainer.getRecentDirectoryPathSetting())); File tmpFile = tmpFileChooser.showSaveDialog(aParentStage); if (tmpFile != null) { - this.settingsContainer.setRecentDirectoryPathSetting(tmpFile.getParent()); + this.settingsContainer.setRecentDirectoryPathSetting(tmpFile.getParent() + File.separator); } return tmpFile; } @@ -974,14 +1115,14 @@ private File chooseDirectory(Stage aParentStage) throws NullPointerException { /** * Optionally completes 2D coordinates of a given fragment by setting all z-coordinates to 0 or generates new * pseudo-3D-coordinates for it using a structure diagram generator. As a third option, all coordinates of the given - * atoms can be set to 0 (or if an exception occurs at coordinate generation. Which option is applied depends on the + * atoms can be set to 0 (or if an exception occurs at coordinate generation). Which option is applied depends on the * given parameters. Initially, the given fragment is cloned but if this fails, the original, given atom container * is processed and the exception logged. * * @param aFragment atom container of a fragment to handle * @param aPoint2dAvailable whether 2D atom coordinates are available; this is not checked by this method! * @param aGenerate2dAtomCoordinates whether 2D atom coordinates should be generated (if the first parameter is true, - * this parameter does not matter + * this parameter does not matter) * @return a clone of the given fragment with 3D atom coordinates created according to the given parameters * @author Samuel Behr */ @@ -1020,60 +1161,4 @@ private IAtomContainer handleFragmentWithNo3dInformationAvailable( return tmpFragmentClone; } // - // - // - /** - * Returns Document for pdf export - * - * @return Document - */ - public Document getDocument(){ - return this.document; - } - // - /** - * Returns export destination file - * - * @return destination File - */ - public File getFile() { - return this.file; - } - // - // - // - /** - * Enum for different file types to export - */ - public enum ExportTypes { - /** - * enum value for item csv file - */ - ITEM_CSV_FILE, - /** - * enum value for item pdf file - */ - ITEM_PDF_FILE, - /** - * enum value for fragments csv file - */ - FRAGMENT_CSV_FILE, - /** - * enum value for fragments pdf file - */ - FRAGMENT_PDF_FILE, - /** - * enum value for single sd file - */ - SINGLE_SD_FILE, - /** - * enum value for sd file - */ - SD_FILE, - /** - * enum value for pdb file - */ - PDB_FILE - } - // } diff --git a/src/main/java/de/unijena/cheminf/mortar/model/io/Importer.java b/src/main/java/de/unijena/cheminf/mortar/model/io/Importer.java index 34ec2032..77661b98 100644 --- a/src/main/java/de/unijena/cheminf/mortar/model/io/Importer.java +++ b/src/main/java/de/unijena/cheminf/mortar/model/io/Importer.java @@ -28,8 +28,8 @@ import de.unijena.cheminf.mortar.gui.util.GuiUtil; import de.unijena.cheminf.mortar.message.Message; import de.unijena.cheminf.mortar.model.settings.SettingsContainer; -import de.unijena.cheminf.mortar.model.util.BasicDefinitions; import de.unijena.cheminf.mortar.model.util.FileUtil; +import de.unijena.cheminf.mortar.model.util.LogUtil; import javafx.stage.FileChooser; import javafx.stage.Stage; @@ -60,10 +60,10 @@ import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; -import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; import java.util.Objects; +import java.util.Optional; import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; @@ -79,7 +79,7 @@ public class Importer { /** * Property key that is used to store the detected molecule names on the imported atom containers. */ - public static final String MOLECULE_NAME_PROPERTY_KEY = "NAME"; + public static final String MOLECULE_NAME_PROPERTY_KEY = "MORTAR_IMPORTER_NAME"; // // // @@ -97,7 +97,7 @@ public class Importer { /** * Container of general MORTAR settings, providing the recent directory path and other import-related settings. */ - private SettingsContainer settingsContainer; + private final SettingsContainer settingsContainer; // // // @@ -122,7 +122,8 @@ public Importer(SettingsContainer aSettingsContainer) throws NullPointerExceptio // // /** - * Imports a molecule file, user can choose between four types - mol, sdf, pdb, and smi. A text file will be treated + * Imports a molecule file, user can choose between three types - mol, sdf, and smi. A text file (.txt) and a + * CSV file will be treated * as a SMILES file. If the respective setting is activated, incomplete valences of the imported atoms are filled * with implicit hydrogen atoms. If no molecule name or ID is given in the input file, the file name with an appended * counter is used as such and added to the returned atom containers as a property. @@ -133,9 +134,8 @@ public Importer(SettingsContainer aSettingsContainer) throws NullPointerExceptio * @throws CDKException if the given file cannot be parsed * @throws IOException if the given file cannot be found or read * @throws NullPointerException if the given file is null - * @throws Exception if something goes wrong */ - public IAtomContainerSet importMoleculeFile(File aFile) throws NullPointerException, Exception { + public IAtomContainerSet importMoleculeFile(File aFile) throws NullPointerException, IOException, CDKException { Objects.requireNonNull(aFile, "aFile is null"); String tmpRecentDirFromContainer = this.settingsContainer.getRecentDirectoryPathSetting(); if(tmpRecentDirFromContainer == null || tmpRecentDirFromContainer.isEmpty()) { @@ -145,7 +145,7 @@ public IAtomContainerSet importMoleculeFile(File aFile) throws NullPointerExcept String tmpFilePath = aFile.getPath(); String tmpFileExtension = FileUtil.getFileExtension(tmpFilePath); this.fileName = aFile.getName(); - IAtomContainerSet tmpImportedMoleculesSet = null; + IAtomContainerSet tmpImportedMoleculesSet; switch (tmpFileExtension) { case ".mol": tmpImportedMoleculesSet = this.importMolFile(aFile); @@ -161,7 +161,7 @@ public IAtomContainerSet importMoleculeFile(File aFile) throws NullPointerExcept tmpImportedMoleculesSet = this.importSMILESFile(aFile); break; default: - tmpImportedMoleculesSet = null; + return null; } this.preprocessMoleculeSet(tmpImportedMoleculesSet); return tmpImportedMoleculesSet; @@ -190,28 +190,27 @@ public File openFile(Stage aParentStage) throws NullPointerException { //to make PDB available, add "*.pdb" here tmpFileChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter("Molecules", "*.mol", "*.sdf", "*.smi", "*.txt", "*.csv")); File tmpRecentDirectory = new File(this.settingsContainer.getRecentDirectoryPathSetting()); - if(!tmpRecentDirectory.isDirectory()) { + if (!tmpRecentDirectory.isDirectory()) { tmpRecentDirectory = new File(SettingsContainer.RECENT_DIRECTORY_PATH_SETTING_DEFAULT); this.settingsContainer.setRecentDirectoryPathSetting(SettingsContainer.RECENT_DIRECTORY_PATH_SETTING_DEFAULT); Importer.LOGGER.log(Level.INFO, "Recent directory could not be read, resetting to default."); } tmpFileChooser.setInitialDirectory(tmpRecentDirectory); - File tmpFile = null; + File tmpFile; try { tmpFile = tmpFileChooser.showOpenDialog(aParentStage); if (tmpFile != null) { - this.settingsContainer.setRecentDirectoryPathSetting(tmpFile.getParent()); + this.settingsContainer.setRecentDirectoryPathSetting(tmpFile.getParent() + File.separator); } + return tmpFile; } catch (Exception anException){ Importer.LOGGER.log(Level.SEVERE, anException.toString(), anException); GuiUtil.guiExceptionAlert( Message.get("Error.ExceptionAlert.Title"), Message.get("Importer.FileImportExceptionAlert.Header"), - Message.get("Importer.FileImportExceptionAlert.Text") + "\n" + - FileUtil.getAppDirPath() + File.separator + BasicDefinitions.LOG_FILES_DIRECTORY + File.separator, + Message.get("Importer.FileImportExceptionAlert.Text") + "\n" + LogUtil.getLogFileDirectoryPath(), anException); - } finally { - return tmpFile; + return null; } } // @@ -229,33 +228,38 @@ public File openFile(Stage aParentStage) throws NullPointerException { */ private IAtomContainerSet importMolFile(File aFile) throws IOException, CDKException { IAtomContainerSet tmpAtomContainerSet = new AtomContainerSet(); - BufferedInputStream tmpInputStream = new BufferedInputStream(new FileInputStream(aFile)); - FormatFactory tmpFactory = new FormatFactory(); - IChemFormat tmpFormat = tmpFactory.guessFormat(tmpInputStream); - IAtomContainer tmpAtomContainer; - if(tmpFormat.getFormatName().equalsIgnoreCase(MDLV2000Format.getInstance().getFormatName())){ - MDLV2000Reader tmpReader = new MDLV2000Reader(new FileInputStream(aFile), IChemObjectReader.Mode.RELAXED); - tmpAtomContainer = tmpReader.read(new AtomContainer()); + IChemFormat tmpFormat; + try (BufferedInputStream tmpInputStream = new BufferedInputStream(new FileInputStream(aFile))) { + FormatFactory tmpFactory = new FormatFactory(); + tmpFormat = tmpFactory.guessFormat(tmpInputStream); } - else if(tmpFormat.getFormatName().equalsIgnoreCase(MDLV3000Format.getInstance().getFormatName())){ - MDLV3000Reader tmpReader = new MDLV3000Reader(new FileInputStream(aFile), IChemObjectReader.Mode.RELAXED); - tmpAtomContainer = tmpReader.read(new AtomContainer()); + if (tmpFormat == null) { + throw new CDKException("The given file type could not be determined"); } - else{ + IAtomContainer tmpAtomContainer; + if (tmpFormat.getFormatName().equalsIgnoreCase(MDLV2000Format.getInstance().getFormatName())) { + try (MDLV2000Reader tmpReader = new MDLV2000Reader(new FileInputStream(aFile), IChemObjectReader.Mode.RELAXED)) { + tmpAtomContainer = tmpReader.read(new AtomContainer()); + } + } else if (tmpFormat.getFormatName().equalsIgnoreCase(MDLV3000Format.getInstance().getFormatName())) { + try (MDLV3000Reader tmpReader = new MDLV3000Reader(new FileInputStream(aFile), IChemObjectReader.Mode.RELAXED)) { + tmpAtomContainer = tmpReader.read(new AtomContainer()); + } + } else { throw new CDKException("The mol file does not correspond to either the MDLV2000 or the MDLV3000 format and " + "therefore cannot be imported."); } String tmpName = this.findMoleculeName(tmpAtomContainer); - if(tmpName == null){ - BufferedReader tmpBufferedReader = new BufferedReader(new FileReader(aFile)); - tmpName = tmpBufferedReader.readLine(); - if(tmpName == null || tmpName.isBlank() || tmpName.isEmpty()) - tmpName = FileUtil.getFileNameWithoutExtension(aFile); - tmpBufferedReader.close(); + if (tmpName == null) { + try (BufferedReader tmpBufferedReader = new BufferedReader(new FileReader(aFile))) { + tmpName = tmpBufferedReader.readLine(); + if (tmpName == null || tmpName.isBlank() || tmpName.isEmpty()) { + tmpName = FileUtil.getFileNameWithoutExtension(aFile); + } + } } tmpAtomContainer.setProperty(Importer.MOLECULE_NAME_PROPERTY_KEY, tmpName); tmpAtomContainerSet.addAtomContainer(tmpAtomContainer); - tmpInputStream.close(); return tmpAtomContainerSet; } // @@ -267,43 +271,43 @@ else if(tmpFormat.getFormatName().equalsIgnoreCase(MDLV3000Format.getInstance(). * * @param aFile sdf * @return the imported molecules in an IAtomContainerSet - * @throws FileNotFoundException if a file input stream cannot be opened for the given file + * @throws IOException if a file input stream cannot be opened or closed for the given file */ - private IAtomContainerSet importSDFile(File aFile) throws FileNotFoundException { + private IAtomContainerSet importSDFile(File aFile) throws IOException { IAtomContainerSet tmpAtomContainerSet = new AtomContainerSet(); /*the IteratingSDFReader is not set to skip erroneous input molecules in its constructor to be able to log them*/ - IteratingSDFReader tmpSDFReader = new IteratingSDFReader(new FileInputStream(aFile), SilentChemObjectBuilder.getInstance()); - int tmpCounter = 0; - while (!Thread.currentThread().isInterrupted()) { - //end of file or encountered erroneous entry - if (!tmpSDFReader.hasNext()) { - //skip if it is an erroneous entry - tmpSDFReader.setSkip(true); + try (IteratingSDFReader tmpSDFReader = new IteratingSDFReader(new FileInputStream(aFile), SilentChemObjectBuilder.getInstance())) { + int tmpCounter = 0; + while (!Thread.currentThread().isInterrupted()) { + //end of file or encountered erroneous entry if (!tmpSDFReader.hasNext()) { - // there is no next, end of file! - break; + //skip if it is an erroneous entry + tmpSDFReader.setSkip(true); + if (!tmpSDFReader.hasNext()) { + // there is no next, end of file! + break; + } + // molecule just could not be read and has therefore been skipped, restore skip setting for next iteration + tmpSDFReader.setSkip(false); + Importer.LOGGER.log(Level.WARNING, "Import failed for structure: {0} (index of structure in file).", tmpCounter); + tmpCounter++; + } + IAtomContainer tmpAtomContainer = tmpSDFReader.next(); + String tmpName = this.findMoleculeName(tmpAtomContainer); + if (tmpName == null || tmpName.isBlank()) { + // the counter here equals the index of the structure in the file + tmpName = FileUtil.getFileNameWithoutExtension(aFile) + tmpCounter; } - // molecule just could not be read and has therefore been skipped, restore skip setting for next iteration - tmpSDFReader.setSkip(false); - Importer.LOGGER.log(Level.WARNING, "Import failed for structure:\t" + tmpCounter + " (index of structure in file)."); + tmpAtomContainer.setProperty(Importer.MOLECULE_NAME_PROPERTY_KEY, tmpName); + tmpAtomContainerSet.addAtomContainer(tmpAtomContainer); tmpCounter++; } - IAtomContainer tmpAtomContainer = tmpSDFReader.next(); - String tmpName = this.findMoleculeName(tmpAtomContainer); - if(tmpName == null || tmpName.isBlank()) { - // the counter here equals the index of the structure in the file - tmpName = FileUtil.getFileNameWithoutExtension(aFile) + tmpCounter; + int tmpFailedImportsCount = tmpCounter - tmpAtomContainerSet.getAtomContainerCount(); + if (tmpFailedImportsCount > 0) { + Importer.LOGGER.log(Level.WARNING, "The import from SD file failed for a total of {0} structure(s).", tmpFailedImportsCount); } - tmpAtomContainer.setProperty(Importer.MOLECULE_NAME_PROPERTY_KEY, tmpName); - tmpAtomContainerSet.addAtomContainer(tmpAtomContainer); - tmpCounter++; - } - int tmpFailedImportsCount = tmpCounter - tmpAtomContainerSet.getAtomContainerCount(); - if (tmpFailedImportsCount > 0) { - Importer.LOGGER.log(Level.WARNING, "The import from SD file failed for a total of " + tmpFailedImportsCount + - " structure(s)."); + return tmpAtomContainerSet; } - return tmpAtomContainerSet; } // /** @@ -326,53 +330,55 @@ private IAtomContainerSet importSMILESFile(File aFile) throws IOException { // checks whether thread has been interrupted, logs faulty structures, and assigns names like the other methods IAtomContainerSet tmpAtomContainerSet = tmpReader.readFile(aFile, tmpFormat); if (tmpReader.getSkippedLinesCounter() > 0) { - Importer.LOGGER.log(Level.WARNING, "The import from SMILES file failed for a total of " - + tmpReader.getSkippedLinesCounter() + " structures."); + Importer.LOGGER.log(Level.WARNING, "The import from SMILES file failed for a total of {0} structures.", + tmpReader.getSkippedLinesCounter()); } return tmpAtomContainerSet; } // - // Currently out of use! Needs more work before it can be made available. See importMoleculeFile() and loadFile() /** * Imports a PDB file. * * @param aFile PDB file * @return the imported molecules in an IAtomContainerSet * @throws CDKException if the given PDB file cannot be read - * @throws FileNotFoundException if a file input stream cannot be opened for the given file + * @throws IOException if a file input stream cannot be opened or closed for the given file + * @deprecated Currently out of use! Needs more work before it can be made available. See importMoleculeFile() and loadFile() */ - private IAtomContainerSet importPDBFile(File aFile) throws FileNotFoundException, CDKException { + @Deprecated + private IAtomContainerSet importPDBFile(File aFile) throws IOException, CDKException { IAtomContainerSet tmpAtomContainerSet = new AtomContainerSet(); - PDBReader tmpPDBReader = new PDBReader(new FileInputStream(aFile)); - for (IOSetting setting : tmpPDBReader.getIOSettings()) { - if (setting.getName().equals("UseRebondTool")) { - //default false - //CDK seems unable to read all info in the "CONECT" block, and often it is not there at all; therefore, we - // re-bond the whole molecule based on the atom distances with this setting - // BUT this is unable to re-create double bonds! - setting.setSetting("true"); - } - if (setting.getName().equals("ReadConnectSection")) { - //default true - } - if (setting.getName().equals("UseHetDictionary")) { - //default false + try (PDBReader tmpPDBReader = new PDBReader(new FileInputStream(aFile))) { + for (IOSetting setting : tmpPDBReader.getIOSettings()) { + if (setting.getName().equals("UseRebondTool")) { + //default false + //CDK seems unable to read all info in the "CONECT" block, and often it is not there at all; therefore, we + // re-bond the whole molecule based on the atom distances with this setting + // BUT this is unable to re-create double bonds! + setting.setSetting("true"); + } + if (setting.getName().equals("ReadConnectSection")) { + //default true + } + if (setting.getName().equals("UseHetDictionary")) { + //default false + } } - } - ChemFile tmpChemFile = tmpPDBReader.read(new ChemFile()); - int tmpCounter = 0; - for (IAtomContainer tmpAtomContainer : ChemFileManipulator.getAllAtomContainers(tmpChemFile)) { - if (Thread.currentThread().isInterrupted()) { - break; + ChemFile tmpChemFile = tmpPDBReader.read(new ChemFile()); + int tmpCounter = 0; + for (IAtomContainer tmpAtomContainer : ChemFileManipulator.getAllAtomContainers(tmpChemFile)) { + if (Thread.currentThread().isInterrupted()) { + break; + } + String tmpName = this.findMoleculeName(tmpAtomContainer); + if(tmpName == null || tmpName.isBlank() || tmpName.isEmpty()) + tmpName = FileUtil.getFileNameWithoutExtension(aFile) + tmpCounter; + tmpAtomContainer.setProperty(Importer.MOLECULE_NAME_PROPERTY_KEY, tmpName); + tmpAtomContainerSet.addAtomContainer(tmpAtomContainer); + tmpCounter++; } - String tmpName = this.findMoleculeName(tmpAtomContainer); - if(tmpName == null || tmpName.isBlank() || tmpName.isEmpty()) - tmpName = FileUtil.getFileNameWithoutExtension(aFile) + tmpCounter; - tmpAtomContainer.setProperty(Importer.MOLECULE_NAME_PROPERTY_KEY, tmpName); - tmpAtomContainerSet.addAtomContainer(tmpAtomContainer); - tmpCounter++; + return tmpAtomContainerSet; } - return tmpAtomContainerSet; } // /** @@ -387,16 +393,23 @@ private IAtomContainerSet importPDBFile(File aFile) throws FileNotFoundException private String findMoleculeName(IAtomContainer anAtomContainer) { String tmpName = anAtomContainer.getTitle(); Set keySet = (Set)(Set)anAtomContainer.getProperties().keySet(); - if(tmpName == null && keySet.stream().anyMatch(k -> (k.toLowerCase().contains("name")) && !k.equalsIgnoreCase("Database_Name"))){ - String key = keySet.stream().filter(k -> k.toLowerCase().contains("name")).findFirst().get(); - tmpName = anAtomContainer.getProperty(key); + if (tmpName == null && keySet.stream().anyMatch(k -> (k.toLowerCase().contains("name")) && !k.equalsIgnoreCase("Database_Name"))) { + Optional tmpKeyOptional = keySet.stream().filter(k -> k.toLowerCase().contains("name")).findFirst(); + if (tmpKeyOptional.isPresent()) { + String key = tmpKeyOptional.get(); + tmpName = anAtomContainer.getProperty(key); + } } - if((tmpName == null || tmpName.equalsIgnoreCase("None")) && keySet.stream().anyMatch(k -> k.toLowerCase().contains("id"))){ - String key = keySet.stream().filter(k -> k.toLowerCase().contains("id")).findFirst().get(); - tmpName = anAtomContainer.getProperty(key); + if ((tmpName == null || tmpName.equalsIgnoreCase("None")) && keySet.stream().anyMatch(k -> k.toLowerCase().contains("id"))) { + Optional tmpKeyOptional = keySet.stream().filter(k -> k.toLowerCase().contains("id")).findFirst(); + if (tmpKeyOptional.isPresent()) { + String key = tmpKeyOptional.get(); + tmpName = anAtomContainer.getProperty(key); + } } - if(tmpName != null && (tmpName.equalsIgnoreCase("None"))) + if (tmpName != null && (tmpName.equalsIgnoreCase("None"))) { tmpName = null; + } return tmpName; } // @@ -405,6 +418,9 @@ private String findMoleculeName(IAtomContainer anAtomContainer) { * (kekulization) and suppressing explicit hydrogen atoms that can also be represented as an implicit hydrogen count * on the respective atom. If the respective setting is activated, empty valences on the atom are completed with implicit * hydrogen atoms as well. Molecules that cause an exception in the routine are logged but remain in the given set. + * note: Things like assigning bond orders and atom types here is redundant if the atom containers + * are discarded after molecule set import and molecular information only represented by SMILES codes in + * the molecule data models. Nevertheless, it is done here to ensure that the generated SMILES codes are correct. * * @param aMoleculeSet the molecule set to process; may be empty but not null * @throws NullPointerException if the given molecule set is null @@ -414,10 +430,6 @@ private void preprocessMoleculeSet(IAtomContainerSet aMoleculeSet) throws NullPo if (aMoleculeSet.isEmpty()) { return; } - /* note: Things like assigning bond orders and atom types here is redundant if the atom containers - are discarded after molecule set import and molecular information only represented by SMILES codes in - the molecule data models. Nevertheless, it is done here to ensure that the generated SMILES codes are correct. - */ int tmpExceptionsCounter = 0; for (IAtomContainer tmpMolecule : aMoleculeSet.atomContainers()) { try { @@ -433,12 +445,13 @@ private void preprocessMoleculeSet(IAtomContainerSet aMoleculeSet) throws NullPo //might throw exceptions if the implicit hydrogen count is unset or kekulization is impossible Kekulization.kekulize(tmpMolecule); } catch (Exception anException) { - Importer.LOGGER.log(Level.WARNING, anException.toString() + " molecule name: " - + tmpMolecule.getProperty(Importer.MOLECULE_NAME_PROPERTY_KEY), anException); + Importer.LOGGER.log(Level.WARNING, + String.format("%s molecule name: %s", anException.toString(), tmpMolecule.getProperty(Importer.MOLECULE_NAME_PROPERTY_KEY)), + anException); tmpExceptionsCounter++; } } - Importer.LOGGER.log(Level.INFO, "Imported and preprocessed molecule set. " + tmpExceptionsCounter + " exceptions occurred while processing."); + Importer.LOGGER.log(Level.INFO, "Imported and preprocessed molecule set. {0} exceptions occurred while processing.", tmpExceptionsCounter); } // } diff --git a/src/main/java/de/unijena/cheminf/mortar/model/settings/SettingsContainer.java b/src/main/java/de/unijena/cheminf/mortar/model/settings/SettingsContainer.java index 66bf895a..a38fdc05 100644 --- a/src/main/java/de/unijena/cheminf/mortar/model/settings/SettingsContainer.java +++ b/src/main/java/de/unijena/cheminf/mortar/model/settings/SettingsContainer.java @@ -25,28 +25,20 @@ package de.unijena.cheminf.mortar.model.settings; -/** - * Important note for developers: When adding a new setting represented by a string, also consider the - * SingleTermPreference class input restrictions when testing whether an input is valid! - */ - import de.unijena.cheminf.mortar.gui.util.GuiUtil; import de.unijena.cheminf.mortar.message.Message; +import de.unijena.cheminf.mortar.model.io.Exporter; import de.unijena.cheminf.mortar.model.util.BasicDefinitions; import de.unijena.cheminf.mortar.model.util.CollectionUtil; import de.unijena.cheminf.mortar.model.util.FileUtil; -import de.unijena.cheminf.mortar.model.util.SimpleEnumConstantNameProperty; -import de.unijena.cheminf.mortar.preference.BooleanPreference; -import de.unijena.cheminf.mortar.preference.IPreference; +import de.unijena.cheminf.mortar.model.util.IDisplayEnum; +import de.unijena.cheminf.mortar.model.util.SimpleIDisplayEnumConstantProperty; import de.unijena.cheminf.mortar.preference.PreferenceContainer; import de.unijena.cheminf.mortar.preference.PreferenceUtil; -import de.unijena.cheminf.mortar.preference.SingleIntegerPreference; -import de.unijena.cheminf.mortar.preference.SingleNumberPreference; import de.unijena.cheminf.mortar.preference.SingleTermPreference; import javafx.beans.property.Property; import javafx.beans.property.SimpleBooleanProperty; -import javafx.beans.property.SimpleDoubleProperty; import javafx.beans.property.SimpleIntegerProperty; import javafx.beans.property.SimpleStringProperty; import javafx.scene.control.Alert; @@ -55,7 +47,6 @@ import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; -import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Objects; @@ -66,19 +57,18 @@ * Container for general settings in MORTAR, capable of managing, preserving, and reloading application settings. * Externally, the settings can be accessed via JavaFX properties and internally, they are managed via * {@link de.unijena.cheminf.mortar.preference.IPreference} objects for persistence. + * IMPORTANT NOTE for developers: When adding a new setting represented by a string, also consider the + * SingleTermPreference class input restrictions when testing whether an input is valid! * * @author Jonas Schaub * @version 1.0.0.0 */ public class SettingsContainer { - // + // /** - * Logger of this class. + * Name of the settings container file that persists the global settings. */ - private static final Logger LOGGER = Logger.getLogger(SettingsContainer.class.getName()); - // - // - // + public static final String SETTINGS_CONTAINER_FILE_NAME = "MORTAR_Settings"; /** * Maximum available threads on the given machine. */ @@ -110,9 +100,9 @@ public class SettingsContainer { public static final boolean ALWAYS_MDLV3000_FORMAT_AT_EXPORT_SETTING_DEFAULT = false; /** - * Default string for separator for the csv export + * Default separator for the csv export. */ - public static final String CSV_EXPORT_SEPARATOR_SETTING_DEFAULT = ","; + public static final Exporter.CSVSeparator CSV_EXPORT_SEPARATOR_SETTING_DEFAULT = Exporter.CSVSeparator.COMMA; /** * Default value of whether to keep last fragment. @@ -120,12 +110,19 @@ public class SettingsContainer { public static final boolean KEEP_LAST_FRAGMENT_SETTING_DEFAULT = false; // // + // + /** + * Logger of this class. + */ + private static final Logger LOGGER = Logger.getLogger(SettingsContainer.class.getName()); + // + // // /** * Default value of the number of parallel tasks to use for fragmentation, determined based on the maximum available * threads on this machine in the constructor. */ - private final int NR_OF_TASKS_FOR_FRAGMENTATION_SETTING_DEFAULT; + private final int nrOfTasksForFragmentationSettingDefault; // // // @@ -142,7 +139,7 @@ public class SettingsContainer { private SimpleBooleanProperty alwaysMDLV3000FormatAtExportSetting; - private SimpleStringProperty csvExportSeparatorSetting; + private SimpleIDisplayEnumConstantProperty csvExportSeparatorSetting; private SimpleBooleanProperty keepLastFragmentSetting; @@ -150,12 +147,17 @@ public class SettingsContainer { * List of setting to display in the general settings dialogue; excludes recent directory path because this is only * for internal use, not intended to be changed by the user via this dialogue. */ - private List settings; + private List> settings; /** * Map to store pairs of {@literal }. */ private HashMap settingNameTooltipTextMap; + // + /** + * Map to store pairs of {@literal }. + */ + private HashMap settingNameDisplayNameMap; // // // @@ -164,12 +166,12 @@ public class SettingsContainer { */ public SettingsContainer() { if (SettingsContainer.MAX_AVAILABLE_THREADS == 1) { - this.NR_OF_TASKS_FOR_FRAGMENTATION_SETTING_DEFAULT = 1; + this.nrOfTasksForFragmentationSettingDefault = 1; } else if (SettingsContainer.MAX_AVAILABLE_THREADS < 4) { - this.NR_OF_TASKS_FOR_FRAGMENTATION_SETTING_DEFAULT = 2; + this.nrOfTasksForFragmentationSettingDefault = 2; } else { //max available threads equal or higher than 4 - this.NR_OF_TASKS_FOR_FRAGMENTATION_SETTING_DEFAULT = 4; + this.nrOfTasksForFragmentationSettingDefault = 4; } this.initialiseSettings(); try { @@ -191,7 +193,7 @@ public SettingsContainer() { * * @return list of settings as properties */ - public List settingsProperties() { + public List> settingsProperties() { return this.settings; } @@ -206,7 +208,17 @@ public Map getSettingNameToTooltipTextMap() { } /** - * Returns the current value of the rows or molecules per page setting. + * Returns a map containing language-specific names for display (values) for the settings with the given names (keys) + * to be used in the GUI. + * + * @return map with display names + */ + public Map getSettingNameToDisplayNameMap() { + return this.settingNameDisplayNameMap; + } + + /** + * Returns the current value of the rows, or rather molecules, per page setting. * * @return rows per page setting value */ @@ -219,7 +231,7 @@ public int getRowsPerPageSetting() { * * @return rows per page setting property */ - public Property rowsPerPageSettingProperty() { + public SimpleIntegerProperty rowsPerPageSettingProperty() { return this.rowsPerPageSetting; } @@ -237,7 +249,7 @@ public int getNumberOfTasksForFragmentationSetting() { * * @return number of tasks for fragmentation setting property */ - public Property numberOfTasksForFragmentationSettingProperty() { + public SimpleIntegerProperty numberOfTasksForFragmentationSettingProperty() { return this.numberOfTasksForFragmentationSetting; } @@ -248,7 +260,7 @@ public Property numberOfTasksForFragmentationSettingProperty() { * @return default value of number of tasks for fragmentation setting */ public int getNumberOfTasksForFragmentationSettingDefault() { - return this.NR_OF_TASKS_FOR_FRAGMENTATION_SETTING_DEFAULT; + return this.nrOfTasksForFragmentationSettingDefault; } /** @@ -265,7 +277,7 @@ public String getRecentDirectoryPathSetting() { * * @return recent directory path setting property */ - public Property recentDirectoryPathSettingProperty() { + public SimpleStringProperty recentDirectoryPathSettingProperty() { return this.recentDirectoryPathSetting; } @@ -283,7 +295,7 @@ public boolean getAddImplicitHydrogensAtImportSetting() { * * @return add implicit hydrogens at import setting property */ - public Property addImplicitHydrogensAtImportSettingProperty() { + public SimpleBooleanProperty addImplicitHydrogensAtImportSettingProperty() { return this.addImplicitHydrogensAtImportSetting; } @@ -291,9 +303,10 @@ public Property addImplicitHydrogensAtImportSettingProperty() { * Returns the current value of the keep atom container in data model setting. * * @return keep atom container in data model setting value + * @deprecated currently not in use, returns always false */ + @Deprecated public boolean getKeepAtomContainerInDataModelSetting() { - //DEPRECATED //return this.keepAtomContainerInDataModelSetting.get(); return false; } @@ -302,8 +315,10 @@ public boolean getKeepAtomContainerInDataModelSetting() { * Returns the property wrapping the keep atom container in data model setting. * * @return keep atom container in data model setting property + * @deprecated currently not in use, returns always false */ - public Property keepAtomContainerInDataModelSettingProperty() { + @Deprecated + public SimpleBooleanProperty keepAtomContainerInDataModelSettingProperty() { return this.keepAtomContainerInDataModelSetting; } @@ -321,7 +336,7 @@ public boolean getAlwaysMDLV3000FormatAtExportSetting() { * * @return always MDLV3000 format at export setting property */ - public Property alwaysMDLV3000FormatAtExportSettingProperty() { + public SimpleBooleanProperty alwaysMDLV3000FormatAtExportSettingProperty() { return this.alwaysMDLV3000FormatAtExportSetting; } @@ -330,8 +345,8 @@ public Property alwaysMDLV3000FormatAtExportSettingProperty() { * * @return csv export separator value */ - public String getCsvExportSeparatorSetting() { - return this.csvExportSeparatorSetting.get(); + public Exporter.CSVSeparator getCsvExportSeparatorSetting() { + return (Exporter.CSVSeparator) this.csvExportSeparatorSetting.get(); } /** @@ -339,16 +354,25 @@ public String getCsvExportSeparatorSetting() { * * @return csv export separator setting property */ - public Property csvExportSeparatorSettingProperty() { + public SimpleIDisplayEnumConstantProperty csvExportSeparatorSettingProperty() { return this.csvExportSeparatorSetting; } + /** + * Returns the currently set CSV export separator character. + * + * @return CSV separator char + */ + public char getCsvExportSeparatorSettingCharacter() { + return ((Exporter.CSVSeparator) this.csvExportSeparatorSetting.get()).getSeparatorChar(); + } + /** * Returns the current value of the keep last fragment setting. * * @return keep last fragment setting value */ - public boolean isKeepLastFragmentSetting(){ + public boolean isKeepLastFragmentSetting() { return this.keepLastFragmentSetting.get(); } @@ -357,7 +381,7 @@ public boolean isKeepLastFragmentSetting(){ * * @return keep last fragment setting property */ - public Property keepLastFragmentSettingProperty(){ + public SimpleBooleanProperty keepLastFragmentSettingProperty() { return this.keepLastFragmentSetting; } @@ -424,11 +448,12 @@ public void setAddImplicitHydrogensAtImportSetting(boolean aBoolean) { * Sets the setting for whether to keep the atom container in the molecule/fragment data model. * * @param aBoolean whether to keep the atom container in the molecule/fragment data model + * @deprecated setting is currently unused */ - //DEPRECATED - /*public void setKeepAtomContainerInDataModelSetting(boolean aBoolean) { + @Deprecated + public void setKeepAtomContainerInDataModelSetting(boolean aBoolean) { this.keepAtomContainerInDataModelSetting.set(aBoolean); - }*/ + } /** * Sets the setting for whether to always use MDL V3000 format for file export. Per default, this is set to false and @@ -443,13 +468,14 @@ public void setAlwaysMDLV3000FormatAtExportSetting(boolean aBoolean) { } /** - * Sets the setting for the separator for the csv export. For now, only "," and ";" are allowed. + * Sets the setting for the separator for the csv export. Param must be an enum constant of + * the Exporter CSV separator enum. * - * @param aSeparator String for separator - * @throws IllegalArgumentException if the string is null, empty, blank or not valid. + * @param aSeparator enum constant + * @throws IllegalArgumentException if the param is null or not valid */ - public void setCsvExportSeparatorSetting(String aSeparator) throws IllegalArgumentException { - if(this.isLegalCsvExportSeparator(aSeparator)){ + public void setCsvExportSeparatorSetting(Exporter.CSVSeparator aSeparator) throws IllegalArgumentException { + if (this.isLegalCsvExportSeparator(aSeparator.getSeparatorChar())) { this.csvExportSeparatorSetting.set(aSeparator); } else { throw new IllegalArgumentException("Given separator for csv export is null, empty, blank or not valid"); @@ -471,7 +497,7 @@ public void setKeepLastFragmentSetting(boolean aBoolean){ */ public void restoreDefaultSettings() { this.rowsPerPageSetting.set(SettingsContainer.ROWS_PER_PAGE_SETTING_DEFAULT); - this.numberOfTasksForFragmentationSetting.set(this.NR_OF_TASKS_FOR_FRAGMENTATION_SETTING_DEFAULT); + this.numberOfTasksForFragmentationSetting.set(this.nrOfTasksForFragmentationSettingDefault); this.recentDirectoryPathSetting.set(SettingsContainer.RECENT_DIRECTORY_PATH_SETTING_DEFAULT); this.addImplicitHydrogensAtImportSetting.set(SettingsContainer.ADD_IMPLICIT_HYDROGENS_AT_IMPORT_SETTING_DEFAULT); //DEPRECATED @@ -489,10 +515,11 @@ public void restoreDefaultSettings() { public void preserveSettings() { String tmpSettingsDirectoryPathName = FileUtil.getSettingsDirPath(); File tmpSettingsDirectoryFile = new File(tmpSettingsDirectoryPathName); + boolean tmpMKDirsSuccessful = true; if (!tmpSettingsDirectoryFile.exists()) { - tmpSettingsDirectoryFile.mkdirs(); + tmpMKDirsSuccessful = tmpSettingsDirectoryFile.mkdirs(); } - if (!tmpSettingsDirectoryFile.canWrite()) { + if (!tmpSettingsDirectoryFile.canWrite() || !tmpMKDirsSuccessful) { SettingsContainer.LOGGER.log(Level.WARNING, "Global settings persistence went wrong, cannot write to settings directory."); GuiUtil.guiMessageAlert(Alert.AlertType.ERROR, Message.get("Error.ExceptionAlert.Title"), Message.get("Error.ExceptionAlert.Header"), @@ -500,23 +527,22 @@ public void preserveSettings() { return; } String tmpPreferenceContainerFilePathName = tmpSettingsDirectoryPathName - + BasicDefinitions.SETTINGS_CONTAINER_FILE_NAME + + SettingsContainer.SETTINGS_CONTAINER_FILE_NAME + BasicDefinitions.PREFERENCE_CONTAINER_FILE_EXTENSION; - List tmpSettings = new ArrayList<>(6); + List> tmpSettings = new ArrayList<>(6); tmpSettings.addAll(this.settings); tmpSettings.add(this.recentDirectoryPathSetting); try { PreferenceContainer tmpPrefContainer = PreferenceUtil.translateJavaFxPropertiesToPreferences(tmpSettings, tmpPreferenceContainerFilePathName); tmpPrefContainer.writeRepresentation(); } catch (NullPointerException | IllegalArgumentException | IOException | SecurityException anException) { - SettingsContainer.LOGGER.log(Level.WARNING, "Global settings persistence went wrong, exception: " + anException.toString(), anException); + SettingsContainer.LOGGER.log(Level.WARNING, String.format("Global settings persistence went wrong, exception: %s", anException.toString()), anException); GuiUtil.guiExceptionAlert(Message.get("Error.ExceptionAlert.Title"), Message.get("Error.ExceptionAlert.Header"), Message.get("SettingsContainer.Error.settingsPersistence"), anException); - return; + //return; } - } /** @@ -526,13 +552,13 @@ public void reloadGlobalSettings() { String tmpSettingsDirectoryPathName = FileUtil.getSettingsDirPath(); File tmpSettingsDirectoryFile = new File(tmpSettingsDirectoryPathName); String tmpPreferenceContainerFilePathName = tmpSettingsDirectoryPathName - + BasicDefinitions.SETTINGS_CONTAINER_FILE_NAME + + SettingsContainer.SETTINGS_CONTAINER_FILE_NAME + BasicDefinitions.PREFERENCE_CONTAINER_FILE_EXTENSION; File tmpPreferenceContainerFile = new File(tmpPreferenceContainerFilePathName); if (!tmpSettingsDirectoryFile.exists()) { FileUtil.createDirectory(tmpSettingsDirectoryFile.getAbsolutePath()); SettingsContainer.LOGGER.info("No persisted global settings could be found, all set to default."); - return; + //return; } else { boolean tmpExists = tmpPreferenceContainerFile.exists(); boolean tmpIsFile = tmpPreferenceContainerFile.isFile(); @@ -540,48 +566,19 @@ public void reloadGlobalSettings() { if (!tmpExists || !tmpIsFile || !tmpCanRead) { SettingsContainer.LOGGER.warning("Preference container file does not exist or cannot be read. " + "A new one is initialised."); - return; + //return; } else { - PreferenceContainer tmpContainer; + PreferenceContainer tmpDePersistedContainer; try { - tmpContainer = new PreferenceContainer(tmpPreferenceContainerFile); + tmpDePersistedContainer = new PreferenceContainer(tmpPreferenceContainerFile); } catch (IOException | SecurityException anException) { - SettingsContainer.LOGGER.log(Level.SEVERE, "Unable to reload global settings: " + anException.toString(), anException); + SettingsContainer.LOGGER.log(Level.SEVERE, String.format("Unable to reload global settings: %s", anException.toString()), anException); return; } - List tmpSettings = new ArrayList<>(6); + List> tmpSettings = new ArrayList<>(8); tmpSettings.addAll(this.settings); tmpSettings.add(this.recentDirectoryPathSetting); - for (Property tmpSettingProperty : tmpSettings) { - String tmpPropertyName = tmpSettingProperty.getName(); - if (tmpContainer.containsPreferenceName(tmpPropertyName)) { - IPreference[] tmpPreferences = tmpContainer.getPreferences(tmpPropertyName); - try { - if (tmpSettingProperty instanceof SimpleBooleanProperty) { - BooleanPreference tmpBooleanPreference = (BooleanPreference) tmpPreferences[0]; - tmpSettingProperty.setValue(tmpBooleanPreference.getContent()); - } else if (tmpSettingProperty instanceof SimpleIntegerProperty) { - SingleIntegerPreference tmpIntPreference = (SingleIntegerPreference) tmpPreferences[0]; - tmpSettingProperty.setValue(tmpIntPreference.getContent()); - } else if (tmpSettingProperty instanceof SimpleDoubleProperty) { - SingleNumberPreference tmpDoublePreference = (SingleNumberPreference) tmpPreferences[0]; - tmpSettingProperty.setValue(tmpDoublePreference.getContent()); - } else if (tmpSettingProperty instanceof SimpleEnumConstantNameProperty || tmpSettingProperty instanceof SimpleStringProperty) { - SingleTermPreference tmpStringPreference = (SingleTermPreference) tmpPreferences[0]; - tmpSettingProperty.setValue(tmpStringPreference.getContent()); - } else { - //setting will remain in default - SettingsContainer.LOGGER.log(Level.WARNING, "Setting " + tmpPropertyName + " is of unknown type."); - } - } catch (ClassCastException | IllegalArgumentException anException) { - //setting will remain in default - SettingsContainer.LOGGER.log(Level.WARNING, anException.toString(), anException); - } - } else { - //setting will remain in default - SettingsContainer.LOGGER.log(Level.WARNING, "No persisted settings for " + tmpPropertyName + " available."); - } - } + PreferenceUtil.updatePropertiesFromPreferences(tmpSettings, tmpDePersistedContainer); } } } @@ -593,12 +590,14 @@ public void reloadGlobalSettings() { * to the list of settings for display to the user. */ private void initialiseSettings() { - int tmpNumberOfSettings = 8; - int tmpInitialCapacityForSettingNameTooltipTextMap = CollectionUtil.calculateInitialHashCollectionCapacity( + int tmpNumberOfSettings = 6; + int tmpInitialCapacityForSettingNameMaps = CollectionUtil.calculateInitialHashCollectionCapacity( tmpNumberOfSettings, BasicDefinitions.DEFAULT_HASH_COLLECTION_LOAD_FACTOR); - this.settingNameTooltipTextMap = new HashMap(tmpInitialCapacityForSettingNameTooltipTextMap, BasicDefinitions.DEFAULT_HASH_COLLECTION_LOAD_FACTOR); + this.settingNameTooltipTextMap = new HashMap<>(tmpInitialCapacityForSettingNameMaps, BasicDefinitions.DEFAULT_HASH_COLLECTION_LOAD_FACTOR); + this.settingNameDisplayNameMap = new HashMap<>(tmpInitialCapacityForSettingNameMaps, BasicDefinitions.DEFAULT_HASH_COLLECTION_LOAD_FACTOR); this.rowsPerPageSetting = new SimpleIntegerProperty(this, + // note: these names are for persistence and de-persistence! The map values are for display "Rows per page setting", SettingsContainer.ROWS_PER_PAGE_SETTING_DEFAULT) { @Override @@ -607,7 +606,7 @@ public void set(int newValue) throws IllegalArgumentException { super.set(newValue); } else { IllegalArgumentException tmpException = new IllegalArgumentException("An illegal rows per page number was given: " + newValue); - SettingsContainer.this.LOGGER.log(Level.WARNING, tmpException.toString(), tmpException); + SettingsContainer.LOGGER.log(Level.WARNING, tmpException.toString(), tmpException); GuiUtil.guiExceptionAlert(Message.get("SettingsContainer.Error.invalidSettingArgument.Title"), Message.get("SettingsContainer.Error.invalidSettingArgument.Header"), tmpException.toString(), @@ -618,16 +617,17 @@ public void set(int newValue) throws IllegalArgumentException { } }; this.settingNameTooltipTextMap.put(this.rowsPerPageSetting.getName(), Message.get("SettingsContainer.rowsPerPageSetting.tooltip")); + this.settingNameDisplayNameMap.put(this.rowsPerPageSetting.getName(), Message.get("SettingsContainer.rowsPerPageSetting.displayName")); this.numberOfTasksForFragmentationSetting = new SimpleIntegerProperty(this, "Nr of tasks for fragmentation setting", - this.NR_OF_TASKS_FOR_FRAGMENTATION_SETTING_DEFAULT) { + this.nrOfTasksForFragmentationSettingDefault) { @Override public void set(int newValue) throws IllegalArgumentException { if (SettingsContainer.this.isLegalNumberOfTasksForFragmentationSetting(newValue)) { super.set(newValue); } else { IllegalArgumentException tmpException = new IllegalArgumentException("An illegal number of tasks for fragmentation was given: " + newValue); - SettingsContainer.this.LOGGER.log(Level.WARNING, tmpException.toString(), tmpException); + SettingsContainer.LOGGER.log(Level.WARNING, tmpException.toString(), tmpException); GuiUtil.guiExceptionAlert(Message.get("SettingsContainer.Error.invalidSettingArgument.Title"), Message.get("SettingsContainer.Error.invalidSettingArgument.Header"), tmpException.toString(), @@ -637,7 +637,10 @@ public void set(int newValue) throws IllegalArgumentException { } } }; - this.settingNameTooltipTextMap.put(this.numberOfTasksForFragmentationSetting.getName(), String.format(Message.get("SettingsContainer.numberOfTasksForFragmentationSetting.tooltip"), SettingsContainer.MAX_AVAILABLE_THREADS)); + this.settingNameTooltipTextMap.put(this.numberOfTasksForFragmentationSetting.getName(), + String.format(Message.get("SettingsContainer.numberOfTasksForFragmentationSetting.tooltip"), SettingsContainer.MAX_AVAILABLE_THREADS)); + this.settingNameDisplayNameMap.put(this.numberOfTasksForFragmentationSetting.getName(), + Message.get("SettingsContainer.numberOfTasksForFragmentationSetting.displayName")); this.recentDirectoryPathSetting = new SimpleStringProperty(this, "Recent directory path setting", SettingsContainer.RECENT_DIRECTORY_PATH_SETTING_DEFAULT) { @@ -647,7 +650,7 @@ public void set(String newValue) throws IllegalArgumentException { super.set(newValue); } else { IllegalArgumentException tmpException = new IllegalArgumentException("An illegal number of tasks for fragmentation was given: " + newValue); - SettingsContainer.this.LOGGER.log(Level.WARNING, tmpException.toString(), tmpException); + SettingsContainer.LOGGER.log(Level.WARNING, tmpException.toString(), tmpException); //no GUI alert here because this is an internal setting //re-throws the exception to properly reset the binding throw tmpException; @@ -656,13 +659,9 @@ public void set(String newValue) throws IllegalArgumentException { }; this.addImplicitHydrogensAtImportSetting = new SimpleBooleanProperty(this, "Add implicit hydrogens at import setting", - SettingsContainer.ADD_IMPLICIT_HYDROGENS_AT_IMPORT_SETTING_DEFAULT) { - @Override - public void set(boolean newValue) { - super.set(newValue); - } - }; + SettingsContainer.ADD_IMPLICIT_HYDROGENS_AT_IMPORT_SETTING_DEFAULT); this.settingNameTooltipTextMap.put(this.addImplicitHydrogensAtImportSetting.getName(), Message.get("SettingsContainer.addImplicitHydrogensAtImportSetting.tooltip")); + this.settingNameDisplayNameMap.put(this.addImplicitHydrogensAtImportSetting.getName(), Message.get("SettingsContainer.addImplicitHydrogensAtImportSetting.displayName")); //DEPRECATED /*this.keepAtomContainerInDataModelSetting = new SimpleBooleanProperty(this, "Keep AtomContainers in the DataModels setting", @@ -683,23 +682,19 @@ public void set(boolean newValue) { }; this.alwaysMDLV3000FormatAtExportSetting = new SimpleBooleanProperty(this, "Always MDL V3000 format at export setting", - SettingsContainer.ALWAYS_MDLV3000_FORMAT_AT_EXPORT_SETTING_DEFAULT) { - @Override - public void set(boolean newValue) { - super.set(newValue); - } - }; + SettingsContainer.ALWAYS_MDLV3000_FORMAT_AT_EXPORT_SETTING_DEFAULT); this.settingNameTooltipTextMap.put(this.alwaysMDLV3000FormatAtExportSetting.getName(), Message.get("SettingsContainer.alwaysMDLV3000FormatAtExportSetting.tooltip")); - this.csvExportSeparatorSetting = new SimpleStringProperty( this, - "Csv export separator setting", - SettingsContainer.CSV_EXPORT_SEPARATOR_SETTING_DEFAULT) { + this.settingNameDisplayNameMap.put(this.alwaysMDLV3000FormatAtExportSetting.getName(), Message.get("SettingsContainer.alwaysMDLV3000FormatAtExportSetting.displayName")); + this.csvExportSeparatorSetting = new SimpleIDisplayEnumConstantProperty(this, + "Csv export separator setting", SettingsContainer.CSV_EXPORT_SEPARATOR_SETTING_DEFAULT, + Exporter.CSVSeparator.class) { @Override - public void set(String newValue) throws IllegalArgumentException { - if(SettingsContainer.this.isLegalCsvExportSeparator(newValue)) { + public void set(IDisplayEnum newValue) throws IllegalArgumentException { + if (SettingsContainer.this.isLegalCsvExportSeparator(((Exporter.CSVSeparator) newValue).getSeparatorChar())) { super.set(newValue); } else { IllegalArgumentException tmpException = new IllegalArgumentException("An illegal value for the separator for the csv export was given: " + newValue); - SettingsContainer.this.LOGGER.log(Level.WARNING, tmpException.toString(), tmpException); + SettingsContainer.LOGGER.log(Level.WARNING, tmpException.toString(), tmpException); GuiUtil.guiExceptionAlert(Message.get("SettingsContainer.Error.invalidSettingArgument.Title"), Message.get("SettingsContainer.Error.invalidSettingArgument.Header"), tmpException.toString(), @@ -710,65 +705,38 @@ public void set(String newValue) throws IllegalArgumentException { } }; this.settingNameTooltipTextMap.put(this.csvExportSeparatorSetting.getName(), Message.get("SettingsContainer.csvExportSeparatorSetting.tooltip")); + this.settingNameDisplayNameMap.put(this.csvExportSeparatorSetting.getName(), Message.get("SettingsContainer.csvExportSeparatorSetting.displayName")); this.keepLastFragmentSetting = new SimpleBooleanProperty(this, "Keep last fragment in pipelining", - SettingsContainer.KEEP_LAST_FRAGMENT_SETTING_DEFAULT){ - @Override - public void set(boolean newValue){ - super.set(newValue); - } - }; + SettingsContainer.KEEP_LAST_FRAGMENT_SETTING_DEFAULT); this.settingNameTooltipTextMap.put(this.keepLastFragmentSetting.getName(), Message.get("SettingsContainer.keepLastFragmentSetting.tooltip")); - this.settings = new ArrayList(6); + this.settingNameDisplayNameMap.put(this.keepLastFragmentSetting.getName(), Message.get("SettingsContainer.keepLastFragmentSetting.displayName")); + this.settings = new ArrayList<>(tmpNumberOfSettings); this.settings.add(this.rowsPerPageSetting); this.settings.add(this.numberOfTasksForFragmentationSetting); this.settings.add(this.addImplicitHydrogensAtImportSetting); //DEPRECATED //this.settings.add(this.keepAtomContainerInDataModelSetting); this.settings.add(this.alwaysMDLV3000FormatAtExportSetting); - this.settings.add(this.csvExportSeparatorSetting); this.settings.add(this.keepLastFragmentSetting); + this.settings.add(this.csvExportSeparatorSetting); //note: recent directory path is only internal, all settings in the list are for the user } /** * Checks the settings for restrictions imposed by persistence. Throws an exception if * anything does not meet the requirements. + * - setting names must be singletons + * - setting names and values must adhere to the preference input restrictions + * - setting values are only tested for their current state, not the entire possible input space! It is tested again at persistence + * + * @throws UnsupportedOperationException if a setting does not fulfil the requirements */ - private void checkSettings() throws Exception { - //setting names must be singletons - //setting names and values must adhere to the preference input restrictions - //setting values are only tested for their current state, not the entire possible input space! It is tested again at persistence - List tmpSettingsList = this.settings; - int tmpSettingNamesSetInitCapacity = CollectionUtil.calculateInitialHashCollectionCapacity(tmpSettingsList.size(), BasicDefinitions.DEFAULT_HASH_COLLECTION_LOAD_FACTOR); - HashSet tmpSettingNamesSet = new HashSet<>(tmpSettingNamesSetInitCapacity, BasicDefinitions.DEFAULT_HASH_COLLECTION_LOAD_FACTOR); - for (Property tmpSetting : tmpSettingsList) { - if (!PreferenceUtil.isValidName(tmpSetting.getName())) { - throw new Exception("Setting " + tmpSetting.getName() + " has an invalid name."); - } - if (tmpSettingNamesSet.contains(tmpSetting.getName())) { - throw new Exception("Setting name " + tmpSetting.getName() + " is used multiple times."); - } else { - tmpSettingNamesSet.add(tmpSetting.getName()); - } - if (tmpSetting instanceof SimpleBooleanProperty) { - //nothing to do here, booleans cannot have invalid values - } else if (tmpSetting instanceof SimpleIntegerProperty) { - if (!SingleIntegerPreference.isValidContent(Integer.toString(((SimpleIntegerProperty) tmpSetting).get()))) { - throw new Exception("Setting value " + ((SimpleIntegerProperty) tmpSetting).get() + " of setting name " + tmpSetting.getName() + " is invalid."); - } - } else if (tmpSetting instanceof SimpleDoubleProperty) { - if (!SingleNumberPreference.isValidContent(((SimpleDoubleProperty) tmpSetting).get())) { - throw new Exception("Setting value " + ((SimpleDoubleProperty) tmpSetting).get() + " of setting name " + tmpSetting.getName() + " is invalid."); - } - } else if (tmpSetting instanceof SimpleEnumConstantNameProperty || tmpSetting instanceof SimpleStringProperty) { - if (!SingleTermPreference.isValidContent(((SimpleStringProperty) tmpSetting).get())) { - throw new Exception("Setting value " + ((SimpleStringProperty) tmpSetting).get() + " of setting name " + tmpSetting.getName() + " is invalid."); - } - } else { - throw new Exception("Setting " + tmpSetting.getName() + " is of an invalid type."); - } - } + private void checkSettings() throws UnsupportedOperationException { + List> tmpSettings = new ArrayList<>(8); + tmpSettings.addAll(this.settings); + tmpSettings.add(this.recentDirectoryPathSetting); + PreferenceUtil.checkPropertiesForPreferenceRestrictions(tmpSettings); } /** @@ -779,7 +747,7 @@ private void checkSettings() throws Exception { * @return true if the given parameter is a legal value for the setting */ private boolean isLegalRowsPerPageSetting(int anInteger) { - return !(anInteger <= 0); + return anInteger > 0; } /** @@ -813,56 +781,23 @@ private boolean isLegalRecentDirectoryPath(String aPath) { boolean tmpExists = tmpFile.exists(); boolean tmpIsDirectory = tmpFile.isDirectory(); boolean tmpCanRead = tmpFile.canRead(); - if (tmpIsEmpty || !tmpExists || !tmpIsDirectory || !tmpCanRead) { - return false; - } else { - return true; - } + return !tmpIsEmpty && tmpExists && tmpIsDirectory && tmpCanRead; } /** - * Tests whether a string is an allowed separator for csv export. For this, it must be not null, not empty, not blank - * and an allowed ASCII char. For now, only "," and ";" are allowed. + * Tests whether a character is an allowed separator for csv export. For this, it must be not null, not empty, not blank + * and must be findable in the exporter csv separator enum. * * @param aSeparator the separator to test * @return true if the given parameter is a legal value for the setting */ - private boolean isLegalCsvExportSeparator(String aSeparator){ - if(Objects.isNull(aSeparator)){ - return false; - } - if(aSeparator.isEmpty()){ - return false; - } - if(aSeparator.isBlank()){ - return false; - } - if(aSeparator.length()>1){ - return false; - } - switch (aSeparator){ - case ",": - case ";": + private boolean isLegalCsvExportSeparator(char aSeparator){ + for (Exporter.CSVSeparator tmpEnumConstant : Exporter.CSVSeparator.values()) { + if (aSeparator == tmpEnumConstant.getSeparatorChar()) { return true; - //some characters defined in the SMILES syntax - case ".": - case "=": - case "#": - case "(": - case ")": - case "{": - case "}": - case "[": - case "]": - case "-": - case "+": - case "@": - case "/": - case "\\": - case " ": - default: - return false; + } } + return false; } // } diff --git a/src/main/java/de/unijena/cheminf/mortar/model/util/BasicDefinitions.java b/src/main/java/de/unijena/cheminf/mortar/model/util/BasicDefinitions.java index 15fdb8a4..d10ddb16 100644 --- a/src/main/java/de/unijena/cheminf/mortar/model/util/BasicDefinitions.java +++ b/src/main/java/de/unijena/cheminf/mortar/model/util/BasicDefinitions.java @@ -28,13 +28,13 @@ import java.util.regex.Pattern; /** - * Basic definitions of the model or application + * Basic definitions of the model or application. * * @author Jonas Schaub, Achim Zielesny * @version 1.0.0.0 */ public final class BasicDefinitions { - // + // /** * Private parameter-less constructor. * Introduced because javadoc build complained about classes without declared default constructor. @@ -43,156 +43,92 @@ private BasicDefinitions() { } // // - // + // /** - * Minimum java version to run MORTAR + * Minimum java version to run MORTAR. */ - public static final String MINIMUM_JAVA_VERSION = "17.0.4"; + public static final String MINIMUM_JAVA_VERSION = "21.0.1"; // // // /** - * Regex pattern for non-word or non-numeric characters + * Regex pattern for non-word or non-numeric characters. */ public static final Pattern NON_WORDNUMERIC_CHARACTER_PATTERN = Pattern.compile("[^a-zA-Z0-9]"); - /** - * Standard timestamp format + * Standard timestamp format. */ public static final String STANDARD_TIMESTAMP_FORMAT = "yyyy/MM/dd - HH:mm:ss"; - /** - * Timestamp format for filename extensions + * Timestamp format for filename extensions. */ public static final String FILENAME_TIMESTAMP_FORMAT = "uuuu_MM_dd_HH_mm"; - /** - * Upper limit of bytes used in the log-file folder for entering the managing routine + * Upper limit of bytes used in the log-file folder for entering the managing routine (1 MByte = 1048576 Byte). */ - public static final int LIMIT_OF_BYTES_USED_BY_LOG_FILES = 1000000; - + public static final int LIMIT_OF_BYTES_USED_BY_LOG_FILES = 1048576; /** - * Upper limit of files stored in the log-file folder for entering the managing routine + * Upper limit of files stored in the log-file folder for entering the managing routine. */ public static final int UPPER_LIMIT_OF_LOG_FILES = 64; - /** - * Lower limit of files stored in the log-file folder for entering the managing routine + * Lower limit of files stored in the log-file folder for entering the managing routine. */ public static final int LOWER_LIMIT_OF_LOG_FILES = 32; - /** - * Factor by which to trim the log-file folder + * Factor by which to trim the log-file folder. */ public static final double FACTOR_TO_TRIM_LOG_FILE_FOLDER = 0.2; // // // /** - * Buffer size (64 kByte = 65536, 256 kByte = 262144, 512 kByte = 524288, 1 - * MByte = 1048576 Byte) + * Buffer size (64 kByte = 65536, 256 kByte = 262144, 512 kByte = 524288, 1 MByte = 1048576 Byte). */ public static final int BUFFER_SIZE = 65536; // // // - /** - * Vendor name of application to include in data directory path - */ - public static final String MORTAR_VENDOR = "MORTAR"; - - /** - * Directory name for MORTAR data - */ - public static final String MORTAR_DATA_DIRECTORY = "MORTAR_Data"; - - /** - * Directory name for storing log files - */ - public static final String LOG_FILES_DIRECTORY = "Logs"; - - /** - * Name for Log files - */ - public static final String LOG_FILE_NAME = "MORTAR_Log"; - - /** - * Name extension (denoting the file type) of log files - */ - public static final String LOG_FILE_NAME_EXTENSION = ".txt"; - - /** - * Name of the folder where all settings (global, fragmenter, pipeline) are persisted. - */ - public static final String SETTINGS_CONTAINER_FILE_DIRECTORY = "Settings"; - - /** - * Name of the settings container file that persists the global settings. - */ - public static final String SETTINGS_CONTAINER_FILE_NAME = "MORTAR_Settings"; - /** * Note, the file extension (.txt or .gzip) defines whether the preference container file is compressed or not. + * For the moment, we are not using compression here in MORTAR for persisting settings. */ public static final String PREFERENCE_CONTAINER_FILE_EXTENSION = ".txt"; // // - // + // /** - * MORTAR session start log entry + * MORTAR session start log entry. */ public static final String MORTAR_SESSION_START_FORMAT = "MORTAR %s session start"; - /** - * MORTAR session end log entry + * MORTAR session end log entry. */ public static final String MORTAR_SESSION_END = "MORTAR session end"; // // - // + // /** - * Version of application + * Version of application. */ public static final String MORTAR_VERSION = "1.1.1.0"; // // - // - /** - * URL which links to GitHub repository of MORTAR - */ - public static final String GITHUB_REPOSITORY_URL = "https://github.com/FelixBaensch/MORTAR"; - // - // - // - /** - * URL which links to MORTAR tutorial on GitHub repository of MORTAR - */ - public static final String MORTAR_TUTORIAL_URL = "https://github.com/FelixBaensch/MORTAR/blob/master/Tutorial/MORTAR_Tutorial.pdf"; - // - // - // - /** - * File path to the tutorial file in the root directory - * Note: Does not work when started from IDE, only in built version started from batch file - */ - public static final String MORTAR_TUTORIAL_RELATIVE_FILE_PATH = "../tutorial/MORTAR_Tutorial.pdf"; - // - // - // + // /** - * Default initial capacity for fragment maps + * Default initial capacity for fragment maps. */ public static final int DEFAULT_INITIAL_MAP_CAPACITY = 25; /** - * Default width for images + * Default width for images. */ public static final double DEFAULT_IMAGE_WIDTH_DEFAULT = 250.0; /** - * Default height for images + * Default height for images. */ public static final double DEFAULT_IMAGE_HEIGHT_DEFAULT = 250.0; /** - * Default distance between image and text + * Default distance between image and text. */ public static final int DEFAULT_IMAGE_TEXT_DISTANCE = 15; /** diff --git a/src/main/java/de/unijena/cheminf/mortar/model/util/ChemUtil.java b/src/main/java/de/unijena/cheminf/mortar/model/util/ChemUtil.java index 31cc4a93..a0a63ad3 100644 --- a/src/main/java/de/unijena/cheminf/mortar/model/util/ChemUtil.java +++ b/src/main/java/de/unijena/cheminf/mortar/model/util/ChemUtil.java @@ -31,7 +31,6 @@ import org.openscience.cdk.CDKConstants; import org.openscience.cdk.aromaticity.Kekulization; import org.openscience.cdk.exception.CDKException; -import org.openscience.cdk.exception.InvalidSmilesException; import org.openscience.cdk.interfaces.IAtom; import org.openscience.cdk.interfaces.IAtomContainer; import org.openscience.cdk.interfaces.IMolecularFormula; @@ -66,7 +65,8 @@ public final class ChemUtil { */ private static final Logger LOGGER = Logger.getLogger(ChemUtil.class.getName()); // - // + // + // /** * Private parameter-less constructor. * Introduced because javadoc build complained about classes without declared default constructor. @@ -75,12 +75,12 @@ private ChemUtil() { } // // - // // /** * Creates a unique SMILES string out of the given atom container or returns null, if the creation was not possible. * If the SMILES could not be created in the first place, it is retried with a kekulized clone of the given atom - * container. Aromaticity information is encoded in the returned SMILES string, if there is any given. + * container. Aromaticity information is encoded in the returned SMILES string, if there is any given. Unique SMILES + * codes do NOT encode stereochemistry! * * @param anAtomContainer atom container the unique SMILES should be created of * @return unique SMILES of the given atom container or 'null' if no creation was possible @@ -92,16 +92,12 @@ public static String createUniqueSmiles(IAtomContainer anAtomContainer) { try { tmpSmiles = tmpSmilesGen.create(anAtomContainer); } catch (CDKException anException){ - try { - IAtomContainer tmpAtomContainer = anAtomContainer.clone(); - Kekulization.kekulize(tmpAtomContainer); - tmpSmiles = tmpSmilesGen.create(tmpAtomContainer); - } catch (CDKException anInnerException){ - throw anInnerException; - } + IAtomContainer tmpAtomContainer = anAtomContainer.clone(); + Kekulization.kekulize(tmpAtomContainer); + tmpSmiles = tmpSmilesGen.create(tmpAtomContainer); } } catch (CDKException | NullPointerException | IllegalArgumentException | CloneNotSupportedException anException){ - ChemUtil.LOGGER.log(Level.SEVERE, anException.toString() + "; molecule name: " + anAtomContainer.getProperty(Importer.MOLECULE_NAME_PROPERTY_KEY), anException); + ChemUtil.LOGGER.log(Level.SEVERE, String.format("%s; molecule name: %s", anException.toString(), anAtomContainer.getProperty(Importer.MOLECULE_NAME_PROPERTY_KEY)), anException); } return tmpSmiles; } @@ -116,12 +112,11 @@ public static String createUniqueSmiles(IAtomContainer anAtomContainer) { * does not affect aromaticity flags * @param shouldAtomTypesBePerceived whether atom types should be perceived and configured * @return IAtomContainer atom container of the molecule - * @throws InvalidSmilesException if the given SMILES is invalid - * @throws CDKException if kekulization or atom type matching fails + * @throws CDKException if the given SMILES is invalid or if kekulization or atom type matching fails */ public static IAtomContainer parseSmilesToAtomContainer(String aSmilesCode, boolean shouldBeKekulized, boolean shouldAtomTypesBePerceived) - throws InvalidSmilesException, CDKException { - //no checks because .parseSmiles() checks and throws InvalidSmilesException if the SMILES cannot be parsed + throws CDKException { + //no checks because .parseSmiles() checks and throws InvalidSmilesException (subclass of CDKException) if the SMILES cannot be parsed IAtomContainer tmpAtomContainer; SmilesParser tmpSmiPar = new SmilesParser(SilentChemObjectBuilder.getInstance()); tmpSmiPar.kekulise(false); @@ -144,10 +139,9 @@ public static IAtomContainer parseSmilesToAtomContainer(String aSmilesCode, bool * * @param aSmilesCode SMILES representation * @return IAtomContainer atom container of the molecule - * @throws InvalidSmilesException if the given SMILES is invalid - * @throws CDKException if kekulization or atom type matching fails + * @throws CDKException if the given SMILES is invalid or if kekulization or atom type matching fails */ - public static IAtomContainer parseSmilesToAtomContainer(String aSmilesCode) throws InvalidSmilesException, CDKException { + public static IAtomContainer parseSmilesToAtomContainer(String aSmilesCode) throws CDKException { return ChemUtil.parseSmilesToAtomContainer(aSmilesCode, true, true); } @@ -160,7 +154,7 @@ public static IAtomContainer parseSmilesToAtomContainer(String aSmilesCode) thro * @author Samuel Behr */ public static String generateMolecularFormula(IAtomContainer anAtomContainer) { - IAtomContainer tmpAtomContainerClone = null; + IAtomContainer tmpAtomContainerClone; String tmpMolecularFormulaString = null; try { tmpAtomContainerClone = anAtomContainer.clone(); @@ -168,8 +162,8 @@ public static String generateMolecularFormula(IAtomContainer anAtomContainer) { IMolecularFormula tmpMolecularFormula = MolecularFormulaManipulator.getMolecularFormula(tmpAtomContainerClone); tmpMolecularFormulaString = MolecularFormulaManipulator.getString(tmpMolecularFormula); } catch (CloneNotSupportedException anException) { - ChemUtil.LOGGER.log(Level.WARNING, anException.toString() + " molecule name: " - + anAtomContainer.getProperty(Importer.MOLECULE_NAME_PROPERTY_KEY), anException); + ChemUtil.LOGGER.log(Level.WARNING, String.format("%s molecule name: %s", anException.toString(), + anAtomContainer.getProperty(Importer.MOLECULE_NAME_PROPERTY_KEY)), anException); } return tmpMolecularFormulaString; } @@ -180,19 +174,17 @@ public static String generateMolecularFormula(IAtomContainer anAtomContainer) { * @param aMolecule to check for 3D coordinates * @return true if 3D coordinates are set for ALL atoms in the given molecule */ - public static boolean has3DCoordinates(MoleculeDataModel aMolecule){ + public static boolean has3DCoordinates(MoleculeDataModel aMolecule) { IAtomContainer tmpFragment; - try{ + try { tmpFragment = aMolecule.getAtomContainer(); } catch(CDKException anException){ - ChemUtil.LOGGER.log(Level.SEVERE, anException.toString() + "_" + aMolecule.getName(), anException); + ChemUtil.LOGGER.log(Level.SEVERE, String.format("%s molecule name: %s", anException.toString(), aMolecule.getName()), anException); return false; } boolean tmpHas3DCoords = true; - for(IAtom tmpAtom : tmpFragment.atoms()){ - if(tmpAtom.getPoint3d() != null){ - continue; - } else { + for (IAtom tmpAtom : tmpFragment.atoms()) { + if (tmpAtom.getPoint3d() == null) { tmpHas3DCoords = false; break; } @@ -206,19 +198,17 @@ public static boolean has3DCoordinates(MoleculeDataModel aMolecule){ * @param aMolecule to check for 2D coordinates * @return true if 2D coordinates are set for ALL atoms in the given molecule */ - public static boolean has2DCoordinates(MoleculeDataModel aMolecule){ + public static boolean has2DCoordinates(MoleculeDataModel aMolecule) { IAtomContainer tmpFragment; - try{ + try { tmpFragment = aMolecule.getAtomContainer(); } catch(CDKException anException){ - ChemUtil.LOGGER.log(Level.SEVERE, anException.toString() + "_" + aMolecule.getName(), anException); + ChemUtil.LOGGER.log(Level.SEVERE, String.format("%s molecule name: %s", anException.toString(), aMolecule.getName()), anException); return false; } boolean tmpHas2DCoords = true; - for(IAtom tmpAtom : tmpFragment.atoms()){ - if(tmpAtom.getPoint2d() != null){ - continue; - } else { + for (IAtom tmpAtom : tmpFragment.atoms()) { + if (tmpAtom.getPoint2d() == null) { tmpHas2DCoords = false; break; } @@ -233,13 +223,13 @@ public static boolean has2DCoordinates(MoleculeDataModel aMolecule){ * @param aListOfMolecules to check for 2D or 3D coordinates * @return true if 2D or 3D coordinates are set for EVERY atom in EVERY molecule in the given list */ - public static boolean checkMoleculeListForCoordinates(List aListOfMolecules){ - if(aListOfMolecules == null || aListOfMolecules.size() == 0){ + public static boolean checkMoleculeListForCoordinates(List aListOfMolecules) { + if (aListOfMolecules == null || aListOfMolecules.isEmpty()) { return false; } boolean tmpHasCoords = true; - for(MoleculeDataModel tmpMolecule : aListOfMolecules){ - if(!ChemUtil.has3DCoordinates(tmpMolecule) && !ChemUtil.has2DCoordinates(tmpMolecule)){ + for (MoleculeDataModel tmpMolecule : aListOfMolecules) { + if (!ChemUtil.has3DCoordinates(tmpMolecule) && !ChemUtil.has2DCoordinates(tmpMolecule)) { tmpHasCoords = false; break; } @@ -331,7 +321,7 @@ public static void saturateWithHydrogen(IAtomContainer aMolecule) throws NullPoi /** * Checks whether atoms in the given molecule have free atom pairs correctly assigned if chemically needed. If not, - * they are added. + * they are added. Note: it does not work really well on most fragments... * * @param aMolecule to check for missing free electron pairs * @throws NullPointerException if the given molecule is null diff --git a/src/main/java/de/unijena/cheminf/mortar/model/util/CollectionUtil.java b/src/main/java/de/unijena/cheminf/mortar/model/util/CollectionUtil.java index adadece4..c6c84e6a 100644 --- a/src/main/java/de/unijena/cheminf/mortar/model/util/CollectionUtil.java +++ b/src/main/java/de/unijena/cheminf/mortar/model/util/CollectionUtil.java @@ -38,7 +38,7 @@ * @version 1.0.2.0 */ public final class CollectionUtil { - // + // /** * Private parameter-less constructor. * Introduced because javadoc build complained about classes without declared default constructor. @@ -67,7 +67,7 @@ public static void sortGivenFragmentListByPropertyAndSortType(List 1.0f) { throw new IllegalArgumentException("Load factor must be higher than 0 and not bigger than 1.0 but is " + aLoadFactor); } - float tmpInitialSize = (float) aNumberOfElements * (1.0f / aLoadFactor) + 2.0f; + float tmpInitialSize = aNumberOfElements * (1.0f / aLoadFactor) + 2.0f; return (int) tmpInitialSize; } // diff --git a/src/main/java/de/unijena/cheminf/mortar/model/util/FileUtil.java b/src/main/java/de/unijena/cheminf/mortar/model/util/FileUtil.java index 69b8a61d..519fae0e 100644 --- a/src/main/java/de/unijena/cheminf/mortar/model/util/FileUtil.java +++ b/src/main/java/de/unijena/cheminf/mortar/model/util/FileUtil.java @@ -25,7 +25,12 @@ package de.unijena.cheminf.mortar.model.util; +import de.unijena.cheminf.mortar.configuration.Configuration; +import de.unijena.cheminf.mortar.configuration.IConfiguration; + import java.io.File; +import java.io.IOException; +import java.nio.file.Files; import java.time.DateTimeException; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; @@ -34,7 +39,7 @@ import java.util.logging.Logger; /** - * File utility + * File utility. * * @author Achim Zielesny, Jonas Schaub, Felix Baensch * @version 1.0.0.0 @@ -42,7 +47,7 @@ public final class FileUtil { // /** - * Cache String for app dir path + * Cache String for app dir path. */ private static String appDirPath = null; // @@ -52,9 +57,23 @@ public final class FileUtil { * Logger of this class. */ private static final Logger LOGGER = Logger.getLogger(FileUtil.class.getName()); + // + /** + * Configuration class to read resource file paths from. + */ + private static final IConfiguration CONFIGURATION; + static { + try { + CONFIGURATION = Configuration.getInstance(); + } catch (IOException anIOException) { + //when MORTAR is run via MainApp.start(), the correct initialization of Configuration is checked there before + // FileUtil is accessed and this static initializer called + throw new NullPointerException("Configuration could not be initialized"); + } + } // // - // + // /** * Private parameter-less constructor. * Introduced because javadoc build complained about classes without declared default constructor. @@ -93,7 +112,7 @@ public static String getFileExtension(String aFilePathname) throws NullPointerEx /** * Returns the name of the file without the file extension. * - * @param aFile whose name should ne return without extension + * @param aFile whose name should be returned without extension * @return file name without extension * @throws NullPointerException if given file is 'null' */ @@ -101,11 +120,11 @@ public static String getFileNameWithoutExtension(File aFile) throws NullPointerE // Objects.requireNonNull(aFile, "Given file is 'null'."); // - return aFile.getName().replaceFirst("[.][^.]+$", ""); //cuts the + return aFile.getName().replaceFirst("[.][^.]+$", ""); } /** - * Deletes single file + * Deletes single file. * * @param aFilePathname Full pathname of file to be deleted (may be null * then false is returned) @@ -113,19 +132,18 @@ public static String getFileNameWithoutExtension(File aFile) throws NullPointerE */ public static boolean deleteSingleFile(String aFilePathname) { // - if (aFilePathname == null || - aFilePathname.isEmpty() - ) { + if (aFilePathname == null || aFilePathname.isEmpty()) { return false; } // try { File tmpFile = new File(aFilePathname); - if (!tmpFile.isFile()) { - return true; - } else { - return tmpFile.delete(); + if (tmpFile.isFile()) { + Files.delete(tmpFile.toPath()); } + //if it is not a file, it does not exist and therefore must not be deleted + //if delete fails, it goes to catch, logs the detailed exception and returns false + return true; } catch (Exception anException) { FileUtil.LOGGER.log(Level.SEVERE, anException.toString(), anException); return false; @@ -147,30 +165,27 @@ public static boolean deleteAllFilesInDirectory(String aDirectoryPath) { return false; } // + boolean tmpAllFilesDeletedSuccessfully = true; try { File tmpDirectory = new File(aDirectoryPath); if (!tmpDirectory.isDirectory()) { return false; } File[] tmpFilesArray = tmpDirectory.listFiles(); - boolean tmpAllFilesDeletedSuccessfully = true; for (File tmpFile : tmpFilesArray) { if (tmpFile.isFile()) { - boolean tmpFileDeleted = tmpFile.delete(); - if (!tmpFileDeleted) { - tmpAllFilesDeletedSuccessfully = false; - } + Files.delete(tmpFile.toPath()); } } - return tmpAllFilesDeletedSuccessfully; } catch (Exception anException) { FileUtil.LOGGER.log(Level.SEVERE, anException.toString(), anException); - return false; + tmpAllFilesDeletedSuccessfully = false; } + return tmpAllFilesDeletedSuccessfully; } /** - * Creates directory and all non-existent ancestor directories if necessary + * Creates directory and all non-existent ancestor directories if necessary. * * @param aDirectoryPath Full directory path to be created * @return true: Directory already existed or was successfully created, @@ -197,7 +212,7 @@ public static boolean createDirectory(String aDirectoryPath) { } /** - * Creates empty file + * Creates empty file. * * @param aFilePathname Full file pathname * @return True: Empty file was created, false: Otherwise @@ -228,7 +243,7 @@ public static boolean createEmptyFile(String aFilePathname) { * path cannot be determined or data directory cannot be created */ public static String getAppDirPath() throws SecurityException { - if(FileUtil.appDirPath != null){ + if (FileUtil.appDirPath != null) { return FileUtil.appDirPath; } String tmpAppDir; @@ -246,10 +261,10 @@ else if (tmpOS.contains("NUX") || tmpOS.contains("NIX") || tmpOS.contains("AIX") throw new SecurityException("AppData (Windows) or user home directory path " + tmpAppDir + " is either no directory or does not exist."); if (tmpOS.contains("MAC")) tmpAppDir += File.separator + "Library" + File.separator + "Application Support"; - tmpAppDir += File.separator + BasicDefinitions.MORTAR_VENDOR + File.separator + BasicDefinitions.MORTAR_DATA_DIRECTORY; + tmpAppDir += File.separator + FileUtil.CONFIGURATION.getProperty("mortar.vendor.name") + File.separator + FileUtil.CONFIGURATION.getProperty("mortar.dataDirectory.name"); tmpAppDirFile = new File(tmpAppDir); boolean tmpSuccessful = true; - if(!tmpAppDirFile.exists()) + if (!tmpAppDirFile.exists()) tmpSuccessful = tmpAppDirFile.mkdirs(); if (!tmpSuccessful) throw new SecurityException("Unable to create application data directory"); @@ -265,20 +280,23 @@ else if (tmpOS.contains("NUX") || tmpOS.contains("NIX") || tmpOS.contains("AIX") * path cannot be determined or data directory cannot be created */ public static String getSettingsDirPath() throws SecurityException { - return FileUtil.getAppDirPath() + File.separator + BasicDefinitions.SETTINGS_CONTAINER_FILE_DIRECTORY + File.separator; + return FileUtil.getAppDirPath() + File.separator + FileUtil.CONFIGURATION.getProperty("mortar.settingsDirectory.name") + File.separator; } /** * Returns a timestamp to add to a filename. * - * @throws DateTimeException if the time cannot be determined or formatted - * @return timestamp filename extension + * @return timestamp filename extension or a placeholder string if time stamp creation failed */ public static String getTimeStampFileNameExtension() throws DateTimeException { - LocalDateTime tmpDateTime = LocalDateTime.now(); - String tmpDateTimeAddition = tmpDateTime.format(DateTimeFormatter.ofPattern( - BasicDefinitions.FILENAME_TIMESTAMP_FORMAT)); - return tmpDateTimeAddition; + try { + LocalDateTime tmpDateTime = LocalDateTime.now(); + String tmpDateTimeAddition = tmpDateTime.format(DateTimeFormatter.ofPattern(BasicDefinitions.FILENAME_TIMESTAMP_FORMAT)); + return tmpDateTimeAddition; + } catch (Exception e) { + FileUtil.LOGGER.log(Level.WARNING, e.toString(), e); + return BasicDefinitions.FILENAME_TIMESTAMP_FORMAT; + } } /** @@ -299,16 +317,15 @@ public static String getNonExistingFilePath(String aFilePath, String aFileExtens if (tmpLastChar == File.separatorChar) throw new IllegalArgumentException("Given file path is a directory."); // - String tmpFilePath = aFilePath; String tmpFileExtension = aFileExtension; if (Objects.isNull(tmpFileExtension)) { tmpFileExtension = ""; } int tmpFilesInThisMinuteCounter = 1; - File tmpFile = new File(tmpFilePath+ tmpFileExtension); + File tmpFile = new File(aFilePath + tmpFileExtension); if (tmpFile.exists()) { - while (tmpFilesInThisMinuteCounter <= Integer.MAX_VALUE) { - tmpFile = new File(tmpFilePath + "(" + tmpFilesInThisMinuteCounter + ")" + tmpFileExtension); + while (true) { + tmpFile = new File(aFilePath + "(" + tmpFilesInThisMinuteCounter + ")" + tmpFileExtension); if (!tmpFile.exists()) { break; } @@ -320,7 +337,35 @@ public static String getNonExistingFilePath(String aFilePath, String aFileExtens String tmpNonExistingFilePath = tmpFile.getPath(); return tmpNonExistingFilePath; } else { - return tmpFilePath + tmpFileExtension; + return aFilePath + tmpFileExtension; + } + } + // + /** + * Opens given path in OS depending explorer equivalent. + * + * @param aPath path to open + * @throws IllegalArgumentException if the given path is empty, blank, or null + * @throws SecurityException if the directory could not be opened + */ + public static void openFilePathInExplorer(String aPath) throws SecurityException { + if (Objects.isNull(aPath) || aPath.isEmpty() || aPath.isBlank()) { + throw new IllegalArgumentException("Given file path is null or empty."); + } + String tmpOS = System.getProperty("os.name").toUpperCase(); + try { + if (tmpOS.contains("WIN")) { + Runtime.getRuntime().exec(new String[]{"explorer", "/open,", aPath}); + } else if (tmpOS.contains("MAC")) { + Runtime.getRuntime().exec(new String[]{"open", "-R", aPath}); + } else if (tmpOS.contains("NUX") || tmpOS.contains("NIX") || tmpOS.contains("AIX")) { + Runtime.getRuntime().exec(new String[]{"gio", "open", aPath}); + } else { + throw new SecurityException("OS name " + tmpOS + " unknown."); + } + } catch (IOException anException) { + FileUtil.LOGGER.log(Level.SEVERE, anException.toString(), anException); + throw new SecurityException("Could not open directory path"); } } // diff --git a/src/main/java/de/unijena/cheminf/mortar/model/util/IDisplayEnum.java b/src/main/java/de/unijena/cheminf/mortar/model/util/IDisplayEnum.java new file mode 100644 index 00000000..2974b5c2 --- /dev/null +++ b/src/main/java/de/unijena/cheminf/mortar/model/util/IDisplayEnum.java @@ -0,0 +1,44 @@ +/* + * MORTAR - MOlecule fRagmenTAtion fRamework + * Copyright (C) 2024 Felix Baensch, Jonas Schaub (felix.baensch@w-hs.de, jonas.schaub@uni-jena.de) + * + * Source code is available at + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package de.unijena.cheminf.mortar.model.util; + +/** + * Interface for enums to extend in order to provide a language-specific display name and a tooltip text for their constants. + */ +public interface IDisplayEnum { + /** + * Returns the language-specific display name of the enum constant for using it in the GUI. + * + * @return String display name + */ + public String getDisplayName(); + /** + * Returns the language-specific tooltip text of the enum constant for using it in the GUI. + * + * @return String tooltip text + */ + public String getTooltipText(); +} diff --git a/src/main/java/de/unijena/cheminf/mortar/model/util/LogUtil.java b/src/main/java/de/unijena/cheminf/mortar/model/util/LogUtil.java index 600ca8d0..f4b19f24 100644 --- a/src/main/java/de/unijena/cheminf/mortar/model/util/LogUtil.java +++ b/src/main/java/de/unijena/cheminf/mortar/model/util/LogUtil.java @@ -25,13 +25,14 @@ package de.unijena.cheminf.mortar.model.util; +import de.unijena.cheminf.mortar.configuration.Configuration; +import de.unijena.cheminf.mortar.configuration.IConfiguration; import de.unijena.cheminf.mortar.gui.util.GuiUtil; import de.unijena.cheminf.mortar.message.Message; import javafx.scene.control.Alert; import java.io.File; -import java.io.FilenameFilter; import java.io.IOException; import java.nio.file.DirectoryStream; import java.nio.file.Files; @@ -57,15 +58,34 @@ public final class LogUtil { // /** - * Root logger + * Root logger. */ private static final Logger ROOT_LOGGER = LogManager.getLogManager().getLogger(""); - /** * Logger of this class. */ private static final Logger LOGGER = Logger.getLogger(LogUtil.class.getName()); - + /** + * Configuration class to read resource file paths from. + */ + private static final IConfiguration CONFIGURATION; + static { + try { + CONFIGURATION = Configuration.getInstance(); + } catch (IOException anIOException) { + //when MORTAR is run via MainApp.start(), the correct initialization of Configuration is checked there before + // LogUtil is accessed and this static initializer called + throw new NullPointerException("Configuration could not be initialized"); + } + } + /** + * Name for Log files. + */ + private static final String LOG_FILE_NAME = "MORTAR_Log"; + /** + * Name extension (denoting the file type) of log files. + */ + private static final String LOG_FILE_NAME_EXTENSION = ".txt"; /** * Uncaught exception handler to be used in MORTAR. IMPORTANT: Threads running parallel to the JavaFX GUI thread * must be assigned this uncaught exception handler manually. BUT this handler tries to display an exception @@ -97,31 +117,29 @@ public final class LogUtil { Message.get("Error.UnexpectedError.Header"), Message.get("Error.UnexpectedError.Content"), (Exception) aThrowable); - } else { - //logging is enough in this case - } + } //else: logging is enough in this case, done in first line of this method } }; // // // /** - * File handler added to the root logger + * File handler added to the root logger. */ private static FileHandler fileHandler; /** - * Log file that is currently logged in + * Log file that is currently logged in. */ private static File logFile; /** - * Storage for exceptions thrown in the process of managing the log-files' folder + * Storage for exceptions thrown in the process of managing the log files folder. */ private static ArrayList storedExceptions; // // - // + // /** * Private parameter-less constructor. * Introduced because javadoc build complained about classes without declared default constructor. @@ -154,23 +172,23 @@ public static synchronized boolean initializeLoggingEnvironment() { //Messages of levels INFO, WARNING and SEVERE will be logged only LogUtil.ROOT_LOGGER.setLevel(Level.INFO); String tmpLoggingDirectoryPathName = FileUtil.getAppDirPath() + File.separator - + BasicDefinitions.LOG_FILES_DIRECTORY + File.separator; + + LogUtil.CONFIGURATION.getProperty("mortar.logDirectory.name") + File.separator; File tmpLoggingDirectoryFile = new File(tmpLoggingDirectoryPathName); //If the directories do not exist already they are created if (!tmpLoggingDirectoryFile.exists()) { FileUtil.createDirectory(tmpLoggingDirectoryFile.getAbsolutePath()); } - String tmpLogFilePathName = tmpLoggingDirectoryPathName + BasicDefinitions.LOG_FILE_NAME + String tmpLogFilePathName = tmpLoggingDirectoryPathName + LogUtil.LOG_FILE_NAME + "_" + FileUtil.getTimeStampFileNameExtension(); - String tmpFinalLogFilePathName = FileUtil.getNonExistingFilePath(tmpLogFilePathName, BasicDefinitions.LOG_FILE_NAME_EXTENSION); + String tmpFinalLogFilePathName = FileUtil.getNonExistingFilePath(tmpLogFilePathName, LogUtil.LOG_FILE_NAME_EXTENSION); File tmpLogFile = new File(tmpFinalLogFilePathName); boolean tmpFileWasCreated = FileUtil.createEmptyFile(tmpLogFile.getAbsolutePath()); if (!tmpFileWasCreated) { - throw new Exception("Log file " + tmpFinalLogFilePathName + " could not be created."); + throw new IOException("Log file " + tmpFinalLogFilePathName + " could not be created."); } if (!tmpLogFile.isFile() || !tmpLogFile.canWrite()) { - throw new Exception("The designated log file " + tmpFinalLogFilePathName + " is not a file or can not be written to."); + throw new IOException("The designated log file " + tmpFinalLogFilePathName + " is not a file or can not be written to."); } LogUtil.logFile = tmpLogFile; LogUtil.fileHandler = new FileHandler(tmpFinalLogFilePathName, true); @@ -241,12 +259,13 @@ public static synchronized boolean resetLogFile() { * @author Samuel Behr */ public static void manageLogFilesFolderIfExists() { - Path tmpLogFileDirectory = Paths.get(FileUtil.getAppDirPath() + File.separator + BasicDefinitions.LOG_FILES_DIRECTORY); + Path tmpLogFileDirectory = Paths.get(FileUtil.getAppDirPath() + File.separator + + LogUtil.CONFIGURATION.getProperty("mortar.logDirectory.name") + File.separator); if (!(Files.exists(tmpLogFileDirectory) && Files.isDirectory(tmpLogFileDirectory))) { return; } LogUtil.storedExceptions = new ArrayList<>(); - //deleting all of the *.txt.lck files out of the log-files' folder + //deleting all of the *.txt.lck files out of the log files folder try (DirectoryStream tmpLCKFilePaths = Files.newDirectoryStream(tmpLogFileDirectory, "*.txt.lck")) { for (Path tmpLCKFilePath : tmpLCKFilePaths) { try { @@ -264,7 +283,7 @@ public static void manageLogFilesFolderIfExists() { } int tmpTotalOfBytesUsed = 0; for (File tmpLogFile : tmpLogFiles) { - tmpTotalOfBytesUsed += tmpLogFile.length(); + tmpTotalOfBytesUsed += (int) tmpLogFile.length(); } //managing the log-files if the limits are exceeded //the parameters of this if statement's condition should be changed with caution or otherwise an infinite loop is risked @@ -297,12 +316,7 @@ public static boolean checkForLCKFileInLogDir() { if (!tmpLoggingDirFile.exists() || !tmpLoggingDirFile.isDirectory()) { return false; } - return tmpLoggingDirFile.listFiles(new FilenameFilter() { - @Override - public boolean accept(File dir, String name) { - return FileUtil.getFileExtension(dir + File.separator + name).equals(".lck"); - } - }).length > 0; + return tmpLoggingDirFile.listFiles((dir, name) -> FileUtil.getFileExtension(dir + File.separator + name).equals(".lck")).length > 0; } // // @@ -312,9 +326,9 @@ public boolean accept(File dir, String name) { * * @return path (String) to log file directory */ - public static String getLogFileDirectoryPath(){ + public static String getLogFileDirectoryPath() { return FileUtil.getAppDirPath() + File.separator - + BasicDefinitions.LOG_FILES_DIRECTORY + File.separator; + + LogUtil.CONFIGURATION.getProperty("mortar.logDirectory.name") + File.separator; } diff --git a/src/main/java/de/unijena/cheminf/mortar/model/util/MORTARException.java b/src/main/java/de/unijena/cheminf/mortar/model/util/MORTARException.java new file mode 100644 index 00000000..2f101d17 --- /dev/null +++ b/src/main/java/de/unijena/cheminf/mortar/model/util/MORTARException.java @@ -0,0 +1,54 @@ +/* + * MORTAR - MOlecule fRagmenTAtion fRamework + * Copyright (C) 2024 Felix Baensch, Jonas Schaub (felix.baensch@w-hs.de, jonas.schaub@uni-jena.de) + * + * Source code is available at + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package de.unijena.cheminf.mortar.model.util; + +/** + * A checked exception that MORTAR classes can throw when no predefined Java exception fits the case. + * Inspired by {@link org.openscience.cdk.exception.CDKException}. + * + * @author Jonas Schaub + * @version 1.0.0.0 + */ +public class MORTARException extends Exception { + /** + * Constructs a new MORTARException with the given message. Calls super class constructor. + * + * @param aMessage for the constructed exception + */ + public MORTARException(String aMessage) { + super(aMessage); + } + // + /** + * Constructs a new MORTARException with the given message and the Throwable as cause. Calls super class constructor. + * + * @param aMessage for the constructed exception + * @param aCause the Throwable that triggered this MORTARException + */ + public MORTARException(String aMessage, Throwable aCause) { + super(aMessage, aCause); + } +} diff --git a/src/main/java/de/unijena/cheminf/mortar/model/util/MiscUtil.java b/src/main/java/de/unijena/cheminf/mortar/model/util/MiscUtil.java index 76cd2ab5..148e32eb 100644 --- a/src/main/java/de/unijena/cheminf/mortar/model/util/MiscUtil.java +++ b/src/main/java/de/unijena/cheminf/mortar/model/util/MiscUtil.java @@ -34,7 +34,7 @@ import java.util.logging.Logger; /** - * Miscellaneous utilities + * Miscellaneous utilities. * * @author Achim Zielesny, Jonas Schaub * @version 1.0.0.0 @@ -47,7 +47,7 @@ public final class MiscUtil { private static final Logger LOGGER = Logger.getLogger(MiscUtil.class.getName()); // // - // + // /** * Private parameter-less constructor. * Introduced because javadoc build complained about classes without declared default constructor. @@ -58,7 +58,7 @@ private MiscUtil() { // // /** - * String with globally unique ID + * String with globally unique ID. * * @return String with globally unique ID */ @@ -67,10 +67,9 @@ public static String getGloballyUniqueID() { } // /** - * Returns current timestamp in standard form (see code) + * Returns current timestamp in standard form (see code). * - * @return Current timestamp in standard form or null if none could be - * created + * @return Current timestamp in standard form or a placeholder string if none could be created */ public static String getTimestampInStandardFormat() { try { @@ -80,7 +79,7 @@ public static String getTimestampInStandardFormat() { return tmpSimpleDateFormat.format(tmpDate); } catch (Exception anException) { MiscUtil.LOGGER.log(Level.SEVERE, anException.toString(), anException); - return null; + return BasicDefinitions.STANDARD_TIMESTAMP_FORMAT; } } // @@ -103,7 +102,7 @@ public static int compareVersions(String aVersionString1, String aVersionString2 } String[] tmpSeparateNumbersV1 = aVersionString1.split("\\."); String[] tmpSeparateNumbersV2 = aVersionString2.split("\\."); - int tmpIterations = tmpSeparateNumbersV1.length < tmpSeparateNumbersV2.length ? tmpSeparateNumbersV1.length : tmpSeparateNumbersV2.length; + int tmpIterations = Math.min(tmpSeparateNumbersV1.length, tmpSeparateNumbersV2.length); for (int i = 0; i < tmpIterations; i++) { int tmpV1Int = Integer.parseInt(tmpSeparateNumbersV1[i]); int tmpV2Int = Integer.parseInt(tmpSeparateNumbersV2[i]); diff --git a/src/main/java/de/unijena/cheminf/mortar/model/util/SimpleEnumConstantNameProperty.java b/src/main/java/de/unijena/cheminf/mortar/model/util/SimpleEnumConstantNameProperty.java index 60a2654f..b0b57e82 100644 --- a/src/main/java/de/unijena/cheminf/mortar/model/util/SimpleEnumConstantNameProperty.java +++ b/src/main/java/de/unijena/cheminf/mortar/model/util/SimpleEnumConstantNameProperty.java @@ -25,8 +25,6 @@ package de.unijena.cheminf.mortar.model.util; -import javafx.beans.property.SimpleStringProperty; - import java.util.Objects; /** @@ -37,14 +35,7 @@ * @author Jonas Schaub * @version 1.0.0.0 */ -public class SimpleEnumConstantNameProperty extends SimpleStringProperty { - // - /** - * The enum class the constant name belongs to. - */ - private final Class associatedEnum; - // - // +public class SimpleEnumConstantNameProperty extends SimpleEnumConstantPropertyBase { // /** * Constructor with all parameters. @@ -59,23 +50,11 @@ public class SimpleEnumConstantNameProperty extends SimpleStringProperty { */ public SimpleEnumConstantNameProperty(Object bean, String name, String initialValue, Class associatedEnum) throws NullPointerException, IllegalArgumentException { - super(bean, name, initialValue); - Objects.requireNonNull(associatedEnum, "Given enum class is null."); - Objects.requireNonNull(initialValue, "Given initial value is null."); - Objects.requireNonNull(name, "Given name is null."); - Objects.requireNonNull(bean, "Given bean is null."); - if (!associatedEnum.isEnum()) { - throw new IllegalArgumentException("Given class must be an enum."); - } - Enum[] tmpEnumConstants = (Enum[]) associatedEnum.getEnumConstants(); - if (tmpEnumConstants.length == 0) { - throw new IllegalArgumentException("The given enum class has no constants declared in it."); - } - this.associatedEnum = associatedEnum; + super(bean, name, initialValue, associatedEnum); //throws IllegalArgumentException if initial value is no enum constant name Enum.valueOf(associatedEnum, initialValue); } - + // /** * Constructor without an initial value. * @@ -87,22 +66,11 @@ public SimpleEnumConstantNameProperty(Object bean, String name, String initialVa */ public SimpleEnumConstantNameProperty(Object bean, String name, Class associatedEnum) throws NullPointerException, IllegalArgumentException { - super(bean, name); - Objects.requireNonNull(associatedEnum, "Given enum class is null."); - Objects.requireNonNull(name, "Given name is null."); - Objects.requireNonNull(bean, "Given bean is null."); - if (!associatedEnum.isEnum()) { - throw new IllegalArgumentException("Given class must be an enum."); - } - Enum[] tmpEnumConstants = (Enum[]) associatedEnum.getEnumConstants(); - if (tmpEnumConstants.length == 0) { - throw new IllegalArgumentException("The given enum class has no constants declared in it."); - } - this.associatedEnum = associatedEnum; + super(bean, name, associatedEnum); } - + // /** - * Constructor without bean and name. + * Constructor without bean and property name. * * @param initialValue the initial value of the wrapped value * @param associatedEnum the enum class of which a constant name should be wrapped @@ -112,23 +80,13 @@ public SimpleEnumConstantNameProperty(Object bean, String name, Class associated */ public SimpleEnumConstantNameProperty(String initialValue, Class associatedEnum) throws NullPointerException, IllegalArgumentException { - super(initialValue); - Objects.requireNonNull(associatedEnum, "Given enum class is null."); - Objects.requireNonNull(initialValue, "Given initial value is null."); - if (!associatedEnum.isEnum()) { - throw new IllegalArgumentException("Given class must be an enum."); - } - Enum[] tmpEnumConstants = (Enum[]) associatedEnum.getEnumConstants(); - if (tmpEnumConstants.length == 0) { - throw new IllegalArgumentException("The given enum class has no constants declared in it."); - } - this.associatedEnum = associatedEnum; + super(initialValue, associatedEnum); //throws IllegalArgumentException if initial value is no enum constant name Enum.valueOf(associatedEnum, initialValue); } - + // /** - * Constructor without bean, name, and initial value. Only the associated enum class must be given. + * Constructor without bean, property name, and initial value. Only the associated enum class must be given. * * @param associatedEnum the enum class of which a constant name should be wrapped * @throws NullPointerException if a parameter is null @@ -136,16 +94,7 @@ public SimpleEnumConstantNameProperty(String initialValue, Class associatedEnum) */ public SimpleEnumConstantNameProperty(Class associatedEnum) throws NullPointerException, IllegalArgumentException { - super(); - Objects.requireNonNull(associatedEnum, "Given enum class is null."); - if (!associatedEnum.isEnum()) { - throw new IllegalArgumentException("Given class must be an enum."); - } - Enum[] tmpEnumConstants = (Enum[]) associatedEnum.getEnumConstants(); - if (tmpEnumConstants.length == 0) { - throw new IllegalArgumentException("The given enum class has no constants declared in it."); - } - this.associatedEnum = associatedEnum; + super(associatedEnum); } // // @@ -172,55 +121,23 @@ public void set(String newValue) throws NullPointerException, IllegalArgumentExc * * @param v the new value * @throws NullPointerException if the parameter is null - * @throws IllegalArgumentException if the associated enum class has no constant with the name specified in newValue + * @throws IllegalArgumentException if the associated enum class has no constant with the name specified in v */ @Override public void setValue(String v) throws NullPointerException, IllegalArgumentException { - Objects.requireNonNull(v, "Given value is null."); - //throws IllegalArgumentException if value is no enum constant name - Enum.valueOf(this.associatedEnum, v); - super.setValue(v); + this.set(v); } - /** - * Convenience method that accepts an enum constant object directly instead of its name as new value for this property. - * - * @param newValue the new value will be the name of the given enum constant object - * @throws NullPointerException if the parameter is null - * @throws IllegalArgumentException if the given enum constant object does not belong to the associated enum class of - * this property - */ + @Override public void setEnumValue(Enum newValue) throws NullPointerException, IllegalArgumentException { this.set(newValue.name()); } - /** - * Convenience method that returns an enum constant object directly instead of its name for the wrapped value. - * - * @return the enum constant object whose name is currently wrapped in this property - */ + @Override public Enum getEnumValue() { return Enum.valueOf(this.associatedEnum, this.get()); } - /** - * Returns the enum class of which a constant name is wrapped in this property. - * - * @return the associated enum class - */ - public Class getAssociatedEnum() { - return this.associatedEnum; - } - - /** - * Convenience method that returns an array containing all enum constants of the associated enum class. - * - * @return all constants of the associated enum - */ - public Enum[] getAssociatedEnumConstants() { - return (Enum[]) this.associatedEnum.getEnumConstants(); - } - /** * Convenience method that returns an array containing the names of all enum constants of the associated enum class. * Therefore, this array represents all possible values for the wrapped value of this property. diff --git a/src/main/java/de/unijena/cheminf/mortar/model/util/SimpleEnumConstantPropertyBase.java b/src/main/java/de/unijena/cheminf/mortar/model/util/SimpleEnumConstantPropertyBase.java new file mode 100644 index 00000000..3cc90f55 --- /dev/null +++ b/src/main/java/de/unijena/cheminf/mortar/model/util/SimpleEnumConstantPropertyBase.java @@ -0,0 +1,181 @@ +/* + * MORTAR - MOlecule fRagmenTAtion fRamework + * Copyright (C) 2024 Felix Baensch, Jonas Schaub (felix.baensch@w-hs.de, jonas.schaub@uni-jena.de) + * + * Source code is available at + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package de.unijena.cheminf.mortar.model.util; + +import javafx.beans.property.SimpleStringProperty; + +import java.util.Objects; + +/** + * Base class for custom JavaFX properties that wrap a value associated with an enum, e.g. its constant name or some + * defined string(!) property of each constant of the associated enum, e.g. the display names or enums implementing the IDisplayEnum + * interface. This base class associates the property with an enum but does not impose any restrictions on the wrapped string(!) + * value. + */ +public abstract class SimpleEnumConstantPropertyBase extends SimpleStringProperty { + // + /** + * The enum class the constant name belongs to. + */ + protected Class associatedEnum; + // + // + // + /** + * Constructor with all parameters. + * + * @param bean the bean of this property + * @param name the name of this property + * @param initialValue the initial value of the wrapped value + * @param associatedEnum the enum class of which a constant should be wrapped + * @throws NullPointerException if a parameter is null + * @throws IllegalArgumentException if the given class is no enum or it contains no constants + */ + protected SimpleEnumConstantPropertyBase(Object bean, String name, String initialValue, Class associatedEnum) + throws NullPointerException, IllegalArgumentException { + super(bean, name, initialValue); + Objects.requireNonNull(associatedEnum, "Given enum class is null."); + Objects.requireNonNull(initialValue, "Given initial value is null."); + Objects.requireNonNull(name, "Given name is null."); + Objects.requireNonNull(bean, "Given bean is null."); + if (!associatedEnum.isEnum()) { + throw new IllegalArgumentException("Given class must be an enum."); + } + Enum[] tmpEnumConstants = (Enum[]) associatedEnum.getEnumConstants(); + if (tmpEnumConstants.length == 0) { + throw new IllegalArgumentException("The given enum class has no constants declared in it."); + } + this.associatedEnum = associatedEnum; + } + // + /** + * Constructor without an initial value. + * + * @param bean the bean of this property + * @param name the name of this property + * @param associatedEnum the enum class of which a constant should be wrapped + * @throws NullPointerException if a parameter is null + * @throws IllegalArgumentException if the given class is no enum or it contains no constants + */ + protected SimpleEnumConstantPropertyBase(Object bean, String name, Class associatedEnum) + throws NullPointerException, IllegalArgumentException { + super(bean, name); + Objects.requireNonNull(associatedEnum, "Given enum class is null."); + Objects.requireNonNull(name, "Given name is null."); + Objects.requireNonNull(bean, "Given bean is null."); + if (!associatedEnum.isEnum()) { + throw new IllegalArgumentException("Given class must be an enum."); + } + Enum[] tmpEnumConstants = (Enum[]) associatedEnum.getEnumConstants(); + if (tmpEnumConstants.length == 0) { + throw new IllegalArgumentException("The given enum class has no constants declared in it."); + } + this.associatedEnum = associatedEnum; + } + // + /** + * Constructor without bean and property name. + * + * @param initialValue the initial value of the wrapped value + * @param associatedEnum the enum class of which a constant should be wrapped + * @throws NullPointerException if a parameter is null + * @throws IllegalArgumentException if the given class is no enum, or it contains no constants + */ + protected SimpleEnumConstantPropertyBase(String initialValue, Class associatedEnum) + throws NullPointerException, IllegalArgumentException { + super(initialValue); + Objects.requireNonNull(associatedEnum, "Given enum class is null."); + Objects.requireNonNull(initialValue, "Given initial value is null."); + if (!associatedEnum.isEnum()) { + throw new IllegalArgumentException("Given class must be an enum."); + } + Enum[] tmpEnumConstants = (Enum[]) associatedEnum.getEnumConstants(); + if (tmpEnumConstants.length == 0) { + throw new IllegalArgumentException("The given enum class has no constants declared in it."); + } + this.associatedEnum = associatedEnum; + } + // + /** + * Constructor without bean, property name, and initial value. Only the associated enum class must be given. + * + * @param associatedEnum the enum class of which a constant should be wrapped + * @throws NullPointerException if a parameter is null + * @throws IllegalArgumentException if the given class is no enum or it contains no constants + */ + protected SimpleEnumConstantPropertyBase(Class associatedEnum) + throws NullPointerException, IllegalArgumentException { + super(); + Objects.requireNonNull(associatedEnum, "Given enum class is null."); + if (!associatedEnum.isEnum()) { + throw new IllegalArgumentException("Given class must be an enum."); + } + Enum[] tmpEnumConstants = (Enum[]) associatedEnum.getEnumConstants(); + if (tmpEnumConstants.length == 0) { + throw new IllegalArgumentException("The given enum class has no constants declared in it."); + } + this.associatedEnum = associatedEnum; + } + // + // + // + /** + * Returns the enum class of which a constant name is wrapped in this property. + * + * @return the associated enum class + */ + public Class getAssociatedEnum() { + return this.associatedEnum; + } + // + /** + * Convenience method that returns an array containing all enum constants of the associated enum class. + * + * @return all constants of the associated enum + */ + public Enum[] getAssociatedEnumConstants() { + return (Enum[]) this.associatedEnum.getEnumConstants(); + } + // + /** + * Convenience method that accepts an enum constant object directly as new value for this property. The wrapped value + * associated with the enum in this property is extracted internally. + * + * @param newValue the new value will be extracted from the given enum constant object + * @throws NullPointerException if the parameter is null + * @throws IllegalArgumentException if the given enum constant object does not belong to the associated enum class of + * this property + */ + public abstract void setEnumValue(Enum newValue) throws NullPointerException, IllegalArgumentException; + // + /** + * Convenience method that returns an enum constant object directly instead of its associated wrapped value. + * + * @return the enum constant object whose property is currently wrapped in this instance + */ + public abstract Enum getEnumValue(); + // +} diff --git a/src/main/java/de/unijena/cheminf/mortar/model/util/SimpleIDisplayEnumConstantProperty.java b/src/main/java/de/unijena/cheminf/mortar/model/util/SimpleIDisplayEnumConstantProperty.java new file mode 100644 index 00000000..21758c61 --- /dev/null +++ b/src/main/java/de/unijena/cheminf/mortar/model/util/SimpleIDisplayEnumConstantProperty.java @@ -0,0 +1,239 @@ +/* + * MORTAR - MOlecule fRagmenTAtion fRamework + * Copyright (C) 2024 Felix Baensch, Jonas Schaub (felix.baensch@w-hs.de, jonas.schaub@uni-jena.de) + * + * Source code is available at + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package de.unijena.cheminf.mortar.model.util; + +import javafx.beans.property.SimpleObjectProperty; + +import java.util.Objects; + +/** + * A JavaFX property for wrapping a constant from one specified enum class that implements the IDisplayEnum interface. + * The specific enum class is set in the constructor and cannot be changed. All values the property might be given later + * must represent a constant from this specific enum class. + * + * @author Jonas Schaub + * @version 1.0.0.0 + */ +public class SimpleIDisplayEnumConstantProperty extends SimpleObjectProperty { + // + /** + * The enum class the constant belongs to. + */ + protected Class associatedEnum; + // + /** + * Constructor with all parameters. + * + * @param bean the bean of this property + * @param name the name of this property + * @param initialValue the initial value of the wrapped value + * @param associatedEnum the enum class of which a constant should be wrapped; it MUST implement IDisplayEnum + * @throws NullPointerException if a parameter is null + * @throws IllegalArgumentException if the given class is no enum, it contains no constants, it does not implement IDisplayEnum, + * or the given initial value does not represent a constant from the given enum class + */ + public SimpleIDisplayEnumConstantProperty(Object bean, String name, IDisplayEnum initialValue, Class associatedEnum) + throws NullPointerException, IllegalArgumentException { + super(bean, name, initialValue); + Objects.requireNonNull(associatedEnum, "Given enum class is null."); + Objects.requireNonNull(initialValue, "Given initial value is null."); + Objects.requireNonNull(name, "Given name is null."); + Objects.requireNonNull(bean, "Given bean is null."); + if (!associatedEnum.isEnum()) { + throw new IllegalArgumentException("Given class must be an enum."); + } + Enum[] tmpEnumConstants = (Enum[]) associatedEnum.getEnumConstants(); + if (tmpEnumConstants.length == 0) { + throw new IllegalArgumentException("The given enum class has no constants declared in it."); + } + if (!(tmpEnumConstants[0] instanceof IDisplayEnum)) { + throw new IllegalArgumentException("Given enum class does not implement IDisplayEnum: " + associatedEnum); + } + //throws IllegalArgumentException if the specified enum class has no constant with the specified name, or the specified class object does not represent an enum class + Enum.valueOf(associatedEnum, ((Enum) initialValue).name()); + this.associatedEnum = associatedEnum; + } + // + /** + * Constructor without an initial value. + * + * @param bean the bean of this property + * @param name the name of this property + * @param associatedEnum the enum class of which a constant should be wrapped; it MUST implement IDisplayEnum + * @throws NullPointerException if a parameter is null + * @throws IllegalArgumentException if the given class is no enum, it does not implement IDisplayEnum, or it contains no constants + */ + public SimpleIDisplayEnumConstantProperty(Object bean, String name, Class associatedEnum) + throws NullPointerException, IllegalArgumentException { + super(bean, name); + Objects.requireNonNull(associatedEnum, "Given enum class is null."); + Objects.requireNonNull(name, "Given name is null."); + Objects.requireNonNull(bean, "Given bean is null."); + if (!associatedEnum.isEnum()) { + throw new IllegalArgumentException("Given class must be an enum."); + } + Enum[] tmpEnumConstants = (Enum[]) associatedEnum.getEnumConstants(); + if (tmpEnumConstants.length == 0) { + throw new IllegalArgumentException("The given enum class has no constants declared in it."); + } + if (!(tmpEnumConstants[0] instanceof IDisplayEnum)) { + throw new IllegalArgumentException("Given enum class does not implement IDisplayEnum: " + associatedEnum); + } + this.associatedEnum = associatedEnum; + } + // + /** + * Constructor without bean and property name. + * + * @param initialValue the initial value of the wrapped value + * @param associatedEnum the enum class of which a constant should be wrapped; it MUST implement IDisplayEnum + * @throws NullPointerException if a parameter is null + * @throws IllegalArgumentException if the given class is no enum, it does not implement IDisplayEnum, it contains no constants, or the given initial + * values does not represent a constant from the given enum class + */ + public SimpleIDisplayEnumConstantProperty(IDisplayEnum initialValue, Class associatedEnum) + throws NullPointerException, IllegalArgumentException { + super(initialValue); + Objects.requireNonNull(associatedEnum, "Given enum class is null."); + Objects.requireNonNull(initialValue, "Given initial value is null."); + if (!associatedEnum.isEnum()) { + throw new IllegalArgumentException("Given class must be an enum."); + } + Enum[] tmpEnumConstants = (Enum[]) associatedEnum.getEnumConstants(); + if (tmpEnumConstants.length == 0) { + throw new IllegalArgumentException("The given enum class has no constants declared in it."); + } + if (!(tmpEnumConstants[0] instanceof IDisplayEnum)) { + throw new IllegalArgumentException("Given enum class does not implement IDisplayEnum: " + associatedEnum); + } + //throws IllegalArgumentException if the specified enum class has no constant with the specified name, or the specified class object does not represent an enum class + Enum.valueOf(associatedEnum, ((Enum) initialValue).name()); + this.associatedEnum = associatedEnum; + } + // + /** + * Constructor without bean, property name, and initial value. Only the associated enum class must be given. + * + * @param associatedEnum the enum class of which a constant should be wrapped; it MUST implement IDisplayEnum + * @throws NullPointerException if a parameter is null + * @throws IllegalArgumentException if the given class is no enum, it does not implement IDisplayEnum, or it contains no constants + */ + public SimpleIDisplayEnumConstantProperty(Class associatedEnum) + throws NullPointerException, IllegalArgumentException { + super(); + Objects.requireNonNull(associatedEnum, "Given enum class is null."); + if (!associatedEnum.isEnum()) { + throw new IllegalArgumentException("Given class must be an enum."); + } + Enum[] tmpEnumConstants = (Enum[]) associatedEnum.getEnumConstants(); + if (tmpEnumConstants.length == 0) { + throw new IllegalArgumentException("The given enum class has no constants declared in it."); + } + if (!(tmpEnumConstants[0] instanceof IDisplayEnum)) { + throw new IllegalArgumentException("Given enum class does not implement IDisplayEnum: " + associatedEnum); + } + this.associatedEnum = associatedEnum; + } + // + // + // + /** + * Set the wrapped enum constant. + * + * @param newValue the new value + * @throws NullPointerException if the parameter is null + * @throws IllegalArgumentException if the associated enum class has no constant specified in newValue + */ + @Override + public void set(IDisplayEnum newValue) throws NullPointerException, IllegalArgumentException { + Objects.requireNonNull(newValue, "Given value is null."); + //throws IllegalArgumentException if the specified enum class has no constant with the specified name + Enum.valueOf(this.associatedEnum, ((Enum) newValue).name()); + super.set(newValue); + } + + /** + * Set the wrapped enum constant. + * + * @param v the new value + * @throws NullPointerException if the parameter is null + * @throws IllegalArgumentException if the associated enum class has no constant specified in v + */ + @Override + public void setValue(IDisplayEnum v) throws NullPointerException, IllegalArgumentException { + this.set(v); + } + + /** + * Returns an array containing the display names of all enum constants of the associated enum class. + * + * @return all associated enum constants display names + */ + public String[] getAssociatedEnumConstantDisplayNames() { + IDisplayEnum[] tmpConstants = (IDisplayEnum[]) this.associatedEnum.getEnumConstants(); + String[] tmpNames = new String[tmpConstants.length]; + for (int i = 0; i < tmpConstants.length; i++) { + tmpNames[i] = tmpConstants[i].getDisplayName(); + } + return tmpNames; + } + + /** + * Returns the enum constant object with the given display name from the associated enum class. + * + * @param anEnumConstantDisplayName display name of a constant of the associated enum class + * @return the corresponding enum constant object + * @throws NullPointerException if parameter is null + * @throws IllegalArgumentException if the associated enum class has no constant with the given display name + */ + public Enum translateDisplayNameToEnumConstant(String anEnumConstantDisplayName) throws NullPointerException, IllegalArgumentException { + Objects.requireNonNull(anEnumConstantDisplayName, "Given enum constant name is null."); + for (IDisplayEnum tmpConstant : (IDisplayEnum[]) this.getAssociatedEnumConstants()) { + if (tmpConstant.getDisplayName().equals(anEnumConstantDisplayName)) { + return (Enum) tmpConstant; + } + } + throw new IllegalArgumentException("Given string does not represent an enum constant display name: " + anEnumConstantDisplayName); + } + // + /** + * Convenience method that returns an array containing all enum constants of the associated enum class. + * + * @return all constants of the associated enum + */ + public Enum[] getAssociatedEnumConstants() { + return (Enum[]) this.associatedEnum.getEnumConstants(); + } + /** + * Returns the enum class of which a constant is wrapped in this property. + * + * @return the associated enum class + */ + public Class getAssociatedEnum() { + return this.associatedEnum; + } + // +} diff --git a/src/main/java/de/unijena/cheminf/mortar/model/util/StringSortWrapper.java b/src/main/java/de/unijena/cheminf/mortar/model/util/StringSortWrapper.java index 9c586e61..38691358 100644 --- a/src/main/java/de/unijena/cheminf/mortar/model/util/StringSortWrapper.java +++ b/src/main/java/de/unijena/cheminf/mortar/model/util/StringSortWrapper.java @@ -26,7 +26,6 @@ package de.unijena.cheminf.mortar.model.util; import java.util.Objects; -import java.util.logging.Logger; /** * A generic sort wrapper that can be compared to another object (e.g. for sorting) via a specified sort string. @@ -35,13 +34,8 @@ * @author Jonas Schaub * @version 1.0.0.0 */ -public class StringSortWrapper implements Comparable{ +public class StringSortWrapper implements Comparable>{ // - /** - * Logger of this class. - */ - private static final Logger LOGGER = Logger.getLogger(StringSortWrapper.class.getName()); - /** * Seed for hashCode() method. */ @@ -140,7 +134,7 @@ public boolean equals(Object anObject) { if (anObject == null || (anObject.getClass() != this.getClass())) { return false; } - StringSortWrapper tmpStringSortWrapper = (StringSortWrapper) anObject; + StringSortWrapper tmpStringSortWrapper = (StringSortWrapper) anObject; return this.hashCode() == tmpStringSortWrapper.hashCode(); } diff --git a/src/main/java/de/unijena/cheminf/mortar/preference/BasePreference.java b/src/main/java/de/unijena/cheminf/mortar/preference/BasePreference.java index 932ea375..015e6192 100644 --- a/src/main/java/de/unijena/cheminf/mortar/preference/BasePreference.java +++ b/src/main/java/de/unijena/cheminf/mortar/preference/BasePreference.java @@ -26,7 +26,6 @@ package de.unijena.cheminf.mortar.preference; import java.util.Objects; -import java.util.logging.Logger; import java.util.regex.Pattern; /** @@ -43,11 +42,11 @@ public abstract class BasePreference implements IPreference { // /** * Preference name regex pattern. - * Allowed characters: 1-9, a-z, A-z, -, [], (), {}, ., space (ASCII 32, hexadecimal value: 20, see below) and ,. + * Allowed characters: 1-9, a-z, A-z, -, [], (), {}, ., space (ASCII 32, hexadecimal value: 20, see below), and ,. * The first character must be a capital letter. * '#' is reserved for comments in text files for persisting objects. */ - private static final Pattern PREFERENCE_NAME_PATTERN = Pattern.compile("\\A[A-Z]{1}+[0-9a-zA-Z\\.\\,\\-\\[\\]\\(\\)\\{\\}\\x20]*+\\z"); + private static final Pattern PREFERENCE_NAME_PATTERN = Pattern.compile("\\A[A-Z][0-9a-zA-Z.,\\-\\[\\](){}\\x20]*+\\z"); /** * Seed for hashCode() method. @@ -58,11 +57,6 @@ public abstract class BasePreference implements IPreference { * Factor for including GUID string in hash code produced by hashCode() method. */ private static final int HASH_FACTOR_GUID = 919; - - /** - * Logger of this class. - */ - private static final Logger LOGGER = Logger.getLogger(BasePreference.class.getName()); // // // @@ -88,14 +82,13 @@ protected BasePreference() { // // // - @Override - public abstract IPreference clone() throws CloneNotSupportedException; @Override public abstract String toString(); // // // + @Override public String getName() { return this.name; @@ -109,6 +102,7 @@ public String getGUID() { // // // + @Override public int compareTo(IPreference aPreference) throws NullPointerException { Objects.requireNonNull(aPreference, "aPreference is 'null'"); @@ -131,7 +125,7 @@ public boolean equals(Object anObject) { @Override public int hashCode() { int tmpHash = BasePreference.HASH_SEED; - tmpHash = BasePreference.HASH_FACTOR_GUID * tmpHash + Objects.hashCode(this.guid); + tmpHash = BasePreference.HASH_FACTOR_GUID * tmpHash + this.guid.hashCode(); return tmpHash; } // diff --git a/src/main/java/de/unijena/cheminf/mortar/preference/BooleanPreference.java b/src/main/java/de/unijena/cheminf/mortar/preference/BooleanPreference.java index 126230c1..65f172af 100644 --- a/src/main/java/de/unijena/cheminf/mortar/preference/BooleanPreference.java +++ b/src/main/java/de/unijena/cheminf/mortar/preference/BooleanPreference.java @@ -25,11 +25,6 @@ package de.unijena.cheminf.mortar.preference; -/** - * TODO: - * - Implement clone() - */ - import de.unijena.cheminf.mortar.model.util.MiscUtil; import java.io.BufferedReader; @@ -64,7 +59,7 @@ public class BooleanPreference extends BasePreference { // // /** - * Boolean content of this preference + * Boolean content of this preference. */ private boolean content; // @@ -107,7 +102,7 @@ public BooleanPreference(BufferedReader aReader) throws IOException { //... //break; default: - throw new Exception("Invalid version."); + throw new IOException("Invalid version."); } } catch (Exception anException) { BooleanPreference.LOGGER.log(Level.SEVERE, anException.toString(), anException); @@ -117,6 +112,7 @@ public BooleanPreference(BufferedReader aReader) throws IOException { // // // + @Override public String getContentRepresentative() { return Boolean.toString(this.content); @@ -154,15 +150,11 @@ public void setContent(boolean aBoolean) { // // // - @Override - public IPreference clone() throws CloneNotSupportedException { - throw new UnsupportedOperationException("Not supported yet."); - } @Override public BooleanPreference copy() { - BooleanPreference tmpCopy = new BooleanPreference(new String(this.name), Boolean.valueOf(this.content)); - tmpCopy.guid = new String(this.guid); + BooleanPreference tmpCopy = new BooleanPreference(this.name, this.content); + tmpCopy.guid = this.guid; return tmpCopy; } @@ -171,12 +163,12 @@ public void writeRepresentation(PrintWriter aPrintWriter) { aPrintWriter.println(BooleanPreference.VERSION); aPrintWriter.println(this.name); aPrintWriter.println(this.guid); - aPrintWriter.println(Boolean.toString(this.content)); + aPrintWriter.println(this.content); } @Override public String toString() { - return this.getClass().getName() + "_'" + this.name + "'_" + "Content:" + Boolean.toString(this.content); + return this.getClass().getName() + "_'" + this.name + "'_" + "Content:" + this.content; } // // @@ -184,7 +176,7 @@ public String toString() { /** * (Re-)instantiates a new BooleanPreference object of version 1.0.0.0 from a line-based text file. */ - private void reloadVersion1000(BufferedReader aReader) throws Exception { + private void reloadVersion1000(BufferedReader aReader) throws IOException { this.name = aReader.readLine(); this.guid = aReader.readLine(); this.content = Boolean.parseBoolean(aReader.readLine()); diff --git a/src/main/java/de/unijena/cheminf/mortar/preference/IPreference.java b/src/main/java/de/unijena/cheminf/mortar/preference/IPreference.java index 10731457..47931006 100644 --- a/src/main/java/de/unijena/cheminf/mortar/preference/IPreference.java +++ b/src/main/java/de/unijena/cheminf/mortar/preference/IPreference.java @@ -25,12 +25,6 @@ package de.unijena.cheminf.mortar.preference; -/** - * TODO: - * - Implement preference container reference (GUID) to ensure that no preference is added to multiple preference - * containers? - */ - import java.io.PrintWriter; /** @@ -39,7 +33,7 @@ * @author Jonas Schaub * @version 1.0.0.0 */ -public interface IPreference extends Comparable, Cloneable { +public interface IPreference extends Comparable { // /** * Returns the name of this preference. @@ -126,16 +120,7 @@ public interface IPreference extends Comparable, Cloneable { public int hashCode(); // // - // - /** - * Returns a clone of this preference object. When implementing this method the compiler recognizes it as an - * override of the clone method in class Object. - * - * @return a clone of this preference object - * @throws CloneNotSupportedException if cloning this specific object fails - */ - public IPreference clone() throws CloneNotSupportedException; - + // /** * Returns a deep copy of this object. * diff --git a/src/main/java/de/unijena/cheminf/mortar/preference/PreferenceContainer.java b/src/main/java/de/unijena/cheminf/mortar/preference/PreferenceContainer.java index 958746be..ce12158d 100644 --- a/src/main/java/de/unijena/cheminf/mortar/preference/PreferenceContainer.java +++ b/src/main/java/de/unijena/cheminf/mortar/preference/PreferenceContainer.java @@ -25,11 +25,6 @@ package de.unijena.cheminf.mortar.preference; -/** - * TODO: - * - Add lz4 compression (and zip compression?) - */ - import de.unijena.cheminf.mortar.model.util.BasicDefinitions; import de.unijena.cheminf.mortar.model.util.FileUtil; import de.unijena.cheminf.mortar.model.util.MiscUtil; @@ -39,13 +34,17 @@ import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; +import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintWriter; +import java.nio.charset.Charset; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; +import java.util.Map; import java.util.Objects; import java.util.concurrent.ConcurrentSkipListMap; import java.util.concurrent.ConcurrentSkipListSet; @@ -154,11 +153,11 @@ public class PreferenceContainer implements Comparable { private ConcurrentSkipListMap preferenceTypeMapCache; // // - // + // /** * Initializes an empty preference container with the given file pathname. * - * @param aContainerFilePathname pathname denoting the file where the container will write its persistent representation to; + * @param aContainerFilePathname pathname denoting the file where the container will write its persistent representation to * @throws IllegalArgumentException if aContainerFilePathname is not valid */ public PreferenceContainer(String aContainerFilePathname) throws IllegalArgumentException { @@ -187,8 +186,7 @@ public PreferenceContainer(String aContainerFilePathname) throws IllegalArgument * method denies read access to the file */ public PreferenceContainer(File aFile) throws IOException, SecurityException { - BufferedReader tmpReader = this.constructDecompressedBufferedReaderForContainerFile(aFile.getPath()); - try { + try (BufferedReader tmpReader = this.constructDecompressedBufferedReaderForContainerFile(aFile.getPath())) { this.containerFile = aFile; //Can throw IOException String tmpVersion = tmpReader.readLine(); @@ -200,12 +198,10 @@ public PreferenceContainer(File aFile) throws IOException, SecurityException { //... //break; default: - throw new Exception("Invalid version."); + throw new IOException("Invalid version."); } - tmpReader.close(); } catch (Exception anException) { PreferenceContainer.LOGGER.log(Level.SEVERE, anException.toString(), anException); - tmpReader.close(); throw new IOException("Preference container can not be instantiated from given reader."); } } @@ -313,7 +309,7 @@ public IPreference[] getPreferences() { return new IPreference[0]; } if (Objects.isNull(this.allPreferencesCache)) { - this.allPreferencesCache = this.preferenceMasterMap.values().toArray(new IPreference[this.preferenceMasterMap.size()]); + this.allPreferencesCache = this.preferenceMasterMap.values().toArray(new IPreference[0]); } return this.allPreferencesCache; } @@ -337,10 +333,7 @@ public IPreference[] getPreferences(String aPreferenceName) throws NullPointerEx return new IPreference[0]; } if (this.containsPreferenceName(aPreferenceName)) { - if (!this.preferenceNameMapCache.containsKey(aPreferenceName)) { - ConcurrentSkipListSet tmpSet = this.preferenceNameMap.get(aPreferenceName); - this.preferenceNameMapCache.put(aPreferenceName, tmpSet.toArray(new IPreference[tmpSet.size()])); - } + this.preferenceNameMapCache.computeIfAbsent(aPreferenceName, v -> this.preferenceNameMap.get(aPreferenceName).toArray(new IPreference[0])); return this.preferenceNameMapCache.get(aPreferenceName); } else { return new IPreference[0]; @@ -365,10 +358,7 @@ public IPreference[] getPreferences(PreferenceType aType) throws NullPointerExce return new IPreference[0]; } if (this.preferenceTypeMap.containsKey(aType)) { - if (!this.preferenceTypeMapCache.containsKey(aType)) { - ConcurrentSkipListSet tmpSet = this.preferenceTypeMap.get(aType); - this.preferenceTypeMapCache.put(aType, tmpSet.toArray(new IPreference[tmpSet.size()])); - } + this.preferenceTypeMapCache.computeIfAbsent(aType, v -> this.preferenceTypeMap.get(aType).toArray(new IPreference[0])); return this.preferenceTypeMapCache.get(aType); } else { return new IPreference[0]; @@ -394,12 +384,12 @@ public IPreference[] getPreferencesSortedNameAscending() { if (!Objects.isNull(this.preferencesSortedNameDescendingCache)) { List tmpDescendingList = Arrays.asList(this.preferencesSortedNameDescendingCache); Collections.reverse(tmpDescendingList); - this.preferencesSortedNameAscendingCache = tmpDescendingList.toArray(new IPreference[tmpDescendingList.size()]); + this.preferencesSortedNameAscendingCache = tmpDescendingList.toArray(new IPreference[0]); } else { - StringSortWrapper[] tmpArray = this.preferenceNameWrapperSet.toArray(new StringSortWrapper[this.preferenceNameWrapperSet.size()]); + StringSortWrapper[] tmpArray = this.preferenceNameWrapperSet.toArray(new StringSortWrapper[0]); IPreference[] tmpPreferenceArray = new IPreference[tmpArray.length]; for (int i = 0; i < tmpArray.length; i++) { - tmpPreferenceArray[i] = (IPreference) tmpArray[i].getWrappedObject(); + tmpPreferenceArray[i] = tmpArray[i].getWrappedObject(); } this.preferencesSortedNameAscendingCache = tmpPreferenceArray; } @@ -429,15 +419,14 @@ public IPreference[] getPreferencesSortedNameDescending() { if (!Objects.isNull(this.preferencesSortedNameAscendingCache)) { List tmpAscendingList = Arrays.asList(this.preferencesSortedNameAscendingCache); Collections.reverse(tmpAscendingList); - this.preferencesSortedNameDescendingCache = tmpAscendingList.toArray(new IPreference[tmpAscendingList.size()]); + this.preferencesSortedNameDescendingCache = tmpAscendingList.toArray(new IPreference[0]); } else { - //TODO: Is there a better way to do this? - List tmpWrapperList = Arrays.asList(this.preferenceNameWrapperSet.toArray(new StringSortWrapper[this.preferenceNameWrapperSet.size()])); + List> tmpWrapperList = new ArrayList<>(this.preferenceNameWrapperSet); Collections.reverse(tmpWrapperList); - StringSortWrapper[] tmpArray = tmpWrapperList.toArray(new StringSortWrapper[tmpWrapperList.size()]); + StringSortWrapper[] tmpArray = tmpWrapperList.toArray(new StringSortWrapper[0]); IPreference[] tmpPreferenceArray = new IPreference[tmpArray.length]; for (int i = 0; i < tmpArray.length; i++) { - tmpPreferenceArray[i] = (IPreference) tmpArray[i].getWrappedObject(); + tmpPreferenceArray[i] = tmpArray[i].getWrappedObject(); } this.preferencesSortedNameDescendingCache = tmpPreferenceArray; } @@ -469,7 +458,7 @@ public boolean add(IPreference aPreference) throws NullPointerException { * Replaces a preference in this container with another one. *
The central preference management of this class is backed by a ConcurrentSkipListMap. *
The time consumption of this method scales log(size) on average (worst case O(size) for both the addition - * operation and the deletion operation. + * operation and the deletion operation). * * @param anOldPreference the preference to delete from this container * @param aNewPreference the preference to add to this container in place of the first parameter @@ -492,7 +481,7 @@ public boolean replace(IPreference anOldPreference, IPreference aNewPreference) * Replaces a preference in this container with another one. *
The central preference management of this class is backed by a ConcurrentSkipListMap. *
The time consumption of this method scales log(size) on average (worst case O(size) for both the addition - * operation and the deletion operation. + * operation and the deletion operation). * * @param aPreferenceGUID GUID string of the preference to delete from this container * @param aPreference the preference to add to this container in place of the preference with the given GUID string @@ -525,7 +514,7 @@ public boolean delete(String aPreferenceGUID) throws NullPointerException { if (!this.contains(aPreferenceGUID)) { return false; } - boolean tmpWasDeletionSuccessful = delete(aPreferenceGUID); + boolean tmpWasDeletionSuccessful = this.delete(this.preferenceMasterMap.get(aPreferenceGUID)); return tmpWasDeletionSuccessful; } @@ -540,7 +529,7 @@ public boolean delete(String aPreferenceGUID) throws NullPointerException { * @throws NullPointerException if aPreference is 'null' */ public boolean delete(IPreference aPreference) throws NullPointerException { - Objects.requireNonNull(aPreference, "aPrference is 'null'."); + Objects.requireNonNull(aPreference, "aPreference is 'null'."); if (!this.contains(aPreference.getGUID())) { return false; } @@ -619,7 +608,7 @@ public boolean containsPreferenceName(String aPreferenceName) { } boolean tmpContains = this.preferenceNameMap.containsKey(aPreferenceName); if (tmpContains) { - ConcurrentSkipListSet tmpSet = this.preferenceNameMap.get(aPreferenceName); + ConcurrentSkipListSet tmpSet = this.preferenceNameMap.get(aPreferenceName); if (tmpSet.isEmpty()) { tmpContains = false; } @@ -635,7 +624,7 @@ public boolean containsPreferenceName(String aPreferenceName) { * * @param aPreferenceType the preference type to check for * @return true, if this container contains one or more preference(s) with the given type; false, if otherwise or if - * apreferenceType is 'null' + * aPreferenceType is 'null' */ public boolean containsPreferenceType(PreferenceType aPreferenceType) { if (Objects.isNull(aPreferenceType)) { @@ -646,7 +635,7 @@ public boolean containsPreferenceType(PreferenceType aPreferenceType) { } boolean tmpContains = this.preferenceTypeMap.containsKey(aPreferenceType); if (tmpContains) { - ConcurrentSkipListSet tmpSet = this.preferenceTypeMap.get(aPreferenceType); + ConcurrentSkipListSet tmpSet = this.preferenceTypeMap.get(aPreferenceType); if (tmpSet.isEmpty()) { tmpContains = false; } @@ -685,9 +674,9 @@ public void clearAll() { * object */ public PreferenceContainer copy() throws CloneNotSupportedException { - PreferenceContainer tmpCopy = new PreferenceContainer(new String(this.containerFile.getPath())); - tmpCopy.guid = new String(this.guid); - tmpCopy.timeStamp = new String(this.timeStamp); + PreferenceContainer tmpCopy = new PreferenceContainer(this.containerFile.getPath()); + tmpCopy.guid = this.guid; + tmpCopy.timeStamp = this.timeStamp; if (this.isEmpty()) { return tmpCopy; } @@ -729,8 +718,7 @@ public void writeRepresentation() throws IOException, SecurityException { * denies read access to the file or directory */ public void writeRepresentationTo(String aFilePathname) throws IOException, SecurityException { - PrintWriter tmpPrintWriter = this.constructCompressedPrintWriterForContainerFile(aFilePathname); - try { + try (PrintWriter tmpPrintWriter = this.constructCompressedPrintWriterForContainerFile(aFilePathname)) { tmpPrintWriter.println(PreferenceContainer.VERSION); tmpPrintWriter.println(this.guid); tmpPrintWriter.println(this.timeStamp); @@ -738,16 +726,13 @@ public void writeRepresentationTo(String aFilePathname) throws IOException, Secu tmpPrintWriter.println(PreferenceContainer.CONTAINER_END); return; } - for (String tmpKey : this.preferenceMasterMap.keySet()) { - IPreference tmpPreference = this.preferenceMasterMap.get(tmpKey); - tmpPrintWriter.println(tmpPreference.getType().name()); - tmpPreference.writeRepresentation(tmpPrintWriter); + for (Map.Entry tmpEntry : this.preferenceMasterMap.entrySet()) { + tmpPrintWriter.println(tmpEntry.getValue().getType().name()); + tmpEntry.getValue().writeRepresentation(tmpPrintWriter); } tmpPrintWriter.println(PreferenceContainer.CONTAINER_END); - tmpPrintWriter.close(); } catch (Exception anException) { PreferenceContainer.LOGGER.log(Level.SEVERE, anException.toString(), anException); - tmpPrintWriter.close(); throw new IOException("Project object can not be written to file."); } } @@ -870,9 +855,10 @@ public static boolean isValidContainerFilePathname(String aFilePathname) { private boolean addWithoutChecks(IPreference aPreference) { this.preferenceMasterMap.put(aPreference.getGUID(), aPreference); PreferenceType tmpType = aPreference.getType(); + boolean tmpDidAdditionToPreferenceTypeMapFail = false; if (this.preferenceTypeMap.containsKey(tmpType)) { ConcurrentSkipListSet tmpSet = this.preferenceTypeMap.get(tmpType); - tmpSet.add(aPreference); + tmpDidAdditionToPreferenceTypeMapFail = !tmpSet.add(aPreference); } else { ConcurrentSkipListSet tmpNewSet = new ConcurrentSkipListSet<>(); tmpNewSet.add(aPreference); @@ -880,15 +866,16 @@ private boolean addWithoutChecks(IPreference aPreference) { } this.preferenceNameWrapperSet.add(new StringSortWrapper<>(aPreference, aPreference.getName())); String tmpName = aPreference.getName(); + boolean tmpDidAdditionToPreferenceNameMapFail = false; if (this.preferenceNameMap.containsKey(tmpName)) { ConcurrentSkipListSet tmpSet = this.preferenceNameMap.get(tmpName); - tmpSet.add(aPreference); + tmpDidAdditionToPreferenceNameMapFail = !tmpSet.add(aPreference); } else { ConcurrentSkipListSet tmpNewSet = new ConcurrentSkipListSet<>(); tmpNewSet.add(aPreference); this.preferenceNameMap.put(tmpName, tmpNewSet); } - return true; + return !(tmpDidAdditionToPreferenceTypeMapFail || tmpDidAdditionToPreferenceNameMapFail); } /** @@ -906,7 +893,7 @@ private void initializeCollections() { /** * (Re-)instantiates a new PreferenceContainer object of version 1.0.0.0 from a line-based text file. */ - private void reloadVersion1000(BufferedReader aReader) throws Exception { + private void reloadVersion1000(BufferedReader aReader) throws IOException { this.initializeCollections(); this.clearCache(); this.guid = aReader.readLine(); @@ -920,7 +907,7 @@ private void reloadVersion1000(BufferedReader aReader) throws Exception { IPreference tmpPreference = PreferenceFactory.reinitializePreference(tmpPreferenceTypeOrContainerEnd, aReader); boolean tmpWasLoadingSuccessful = this.addWithoutChecks(tmpPreference); if (!tmpWasLoadingSuccessful) { - throw new Exception(); + throw new IOException(); } tmpPreferenceTypeOrContainerEnd = aReader.readLine(); } @@ -928,6 +915,7 @@ private void reloadVersion1000(BufferedReader aReader) throws Exception { /** * Constructs a compressing (if necessary) PrintWriter object that can be used to write a preference container file. + * Note that calling code must take care of closing the PrintWriter! * * @param aFilePathname the destined file * @return a compressed (if necessary) PrintWriter object @@ -955,7 +943,12 @@ private PrintWriter constructCompressedPrintWriterForContainerFile(String aFileP if (!tmpContainerFile.canWrite()) { throw new IOException("Unable to modify the destined container file."); } - FileOutputStream tmpFileOut = new FileOutputStream(tmpContainerFile, false); + FileOutputStream tmpFileOut; + try { + tmpFileOut = new FileOutputStream(tmpContainerFile, false); + } catch (FileNotFoundException e) { + throw new IOException("Container file cannot be found."); + } BufferedOutputStream tmpBufOut; if (tmpFileExtension.equals(PreferenceContainer.VALID_FILE_EXTENSIONS[0])) { tmpBufOut = new BufferedOutputStream(tmpFileOut, BasicDefinitions.BUFFER_SIZE); @@ -963,7 +956,11 @@ private PrintWriter constructCompressedPrintWriterForContainerFile(String aFileP GZIPOutputStream tmpGzipOut = new GZIPOutputStream(tmpFileOut, BasicDefinitions.BUFFER_SIZE, false); tmpBufOut = new BufferedOutputStream(tmpGzipOut, BasicDefinitions.BUFFER_SIZE); } else { - tmpFileOut.close(); + try { + tmpFileOut.close(); + } catch (IOException e) { + //exception thrown in the next line anyway + } throw new IOException("Invalid file extension."); } PrintWriter tmpPrintWriter = new PrintWriter(tmpBufOut, false); @@ -972,6 +969,7 @@ private PrintWriter constructCompressedPrintWriterForContainerFile(String aFileP /** * Constructs a decompressing (if necessary) BufferedReader object that can be used to open and read container files. + * Note that calling code must take care of closing the BufferedReader! * * @param aFilePathname representing the persisted container file * @return buffered reader reading from the container file @@ -991,17 +989,24 @@ private BufferedReader constructDecompressedBufferedReaderForContainerFile(Strin if (!tmpIsFile || !tmpCanRead) { throw new IOException("Given file does not exist, does not represent a file or can not be read."); } - FileInputStream tmpFileIn = new FileInputStream(tmpContainerFile); + FileInputStream tmpFileIn; + try { + tmpFileIn = new FileInputStream(tmpContainerFile); + } catch (FileNotFoundException e) { + throw new IOException("Container file cannot be found."); + } InputStreamReader tmpInStreamReader; if (tmpFileExtension.equals(PreferenceContainer.VALID_FILE_EXTENSIONS[0])) { - //TODO: Specify charset? Uses class of nio package... - tmpInStreamReader = new InputStreamReader(tmpFileIn, System.getProperty("file.encoding")); + tmpInStreamReader = new InputStreamReader(tmpFileIn, Charset.defaultCharset().displayName()); } else if (tmpFileExtension.equals(PreferenceContainer.VALID_FILE_EXTENSIONS[1])) { GZIPInputStream tmpGzipIn = new GZIPInputStream(tmpFileIn, BasicDefinitions.BUFFER_SIZE); - //TODO: Specify charset? Uses class of nio package... - tmpInStreamReader = new InputStreamReader(tmpGzipIn, System.getProperty("file.encoding")); + tmpInStreamReader = new InputStreamReader(tmpGzipIn, Charset.defaultCharset().displayName()); } else { - tmpFileIn.close(); + try { + tmpFileIn.close(); + } catch (IOException e) { + //exception thrown in next line anyway + } throw new IOException("Invalid file extension."); } BufferedReader tmpReader = new BufferedReader(tmpInStreamReader, BasicDefinitions.BUFFER_SIZE); diff --git a/src/main/java/de/unijena/cheminf/mortar/preference/PreferenceFactory.java b/src/main/java/de/unijena/cheminf/mortar/preference/PreferenceFactory.java index 8aa1ea66..5faf2129 100644 --- a/src/main/java/de/unijena/cheminf/mortar/preference/PreferenceFactory.java +++ b/src/main/java/de/unijena/cheminf/mortar/preference/PreferenceFactory.java @@ -25,18 +25,13 @@ package de.unijena.cheminf.mortar.preference; -/** - * TODO: - * - implement method that initializes a whole new IPreference object? - * - newly added preference classes need to be added to 'reinitializePreference()' method - */ - import java.io.BufferedReader; import java.util.logging.Level; import java.util.logging.Logger; /** - * Utility class for creating IPreference objects. + * Utility class for creating IPreference objects. Note for developers: newly added preference classes need to be added + * to 'reinitializePreference()' method. * * @author Jonas Schaub * @version 1.0.0.0 @@ -49,7 +44,7 @@ public final class PreferenceFactory { private static final Logger LOGGER = Logger.getLogger(PreferenceFactory.class.getName()); //
// - // + // /** * Private parameter-less constructor. * Introduced because javadoc build complained about classes without declared default constructor. diff --git a/src/main/java/de/unijena/cheminf/mortar/preference/PreferenceType.java b/src/main/java/de/unijena/cheminf/mortar/preference/PreferenceType.java index 17599468..f168811c 100644 --- a/src/main/java/de/unijena/cheminf/mortar/preference/PreferenceType.java +++ b/src/main/java/de/unijena/cheminf/mortar/preference/PreferenceType.java @@ -37,22 +37,18 @@ public enum PreferenceType { * Constant associated with class BooleanPreference. */ BOOLEAN, - /** * Constant associated with class RGBColorPreference. */ RGB_COLOR, - /** * Constant associated with class SingleIntegerPreference. */ SINGLE_INTEGER, - /** * Constant associated with class SingleNumberPreference. */ SINGLE_NUMBER, - /** * Constant associated with class SingleTermPreference. */ diff --git a/src/main/java/de/unijena/cheminf/mortar/preference/PreferenceUtil.java b/src/main/java/de/unijena/cheminf/mortar/preference/PreferenceUtil.java index ab8a3b7f..bd6b1340 100644 --- a/src/main/java/de/unijena/cheminf/mortar/preference/PreferenceUtil.java +++ b/src/main/java/de/unijena/cheminf/mortar/preference/PreferenceUtil.java @@ -25,7 +25,10 @@ package de.unijena.cheminf.mortar.preference; -import de.unijena.cheminf.mortar.model.util.SimpleEnumConstantNameProperty; +import de.unijena.cheminf.mortar.model.util.BasicDefinitions; +import de.unijena.cheminf.mortar.model.util.CollectionUtil; +import de.unijena.cheminf.mortar.model.util.IDisplayEnum; +import de.unijena.cheminf.mortar.model.util.SimpleIDisplayEnumConstantProperty; import javafx.beans.property.Property; import javafx.beans.property.SimpleBooleanProperty; @@ -33,6 +36,7 @@ import javafx.beans.property.SimpleIntegerProperty; import javafx.beans.property.SimpleStringProperty; +import java.util.HashSet; import java.util.List; import java.util.Objects; import java.util.logging.Level; @@ -52,7 +56,7 @@ public final class PreferenceUtil { private static final Logger LOGGER = Logger.getLogger(PreferenceUtil.class.getName()); // // - // + // /** * Private parameter-less constructor. * Introduced because javadoc build complained about classes without declared default constructor. @@ -85,7 +89,7 @@ public static boolean isValidName(String aName) { * @throws NullPointerException if a given argument is null * @throws IllegalArgumentException if the given file path name is invalid */ - public static PreferenceContainer translateJavaFxPropertiesToPreferences(List aPropertiesList, String aContainerFilePathname) throws NullPointerException, IllegalArgumentException { + public static PreferenceContainer translateJavaFxPropertiesToPreferences(List> aPropertiesList, String aContainerFilePathname) throws NullPointerException, IllegalArgumentException { Objects.requireNonNull(aPropertiesList); Objects.requireNonNull(aContainerFilePathname); if (!PreferenceContainer.isValidContainerFilePathname(aContainerFilePathname)) { @@ -96,31 +100,170 @@ public static PreferenceContainer translateJavaFxPropertiesToPreferences(List { + BooleanPreference tmpBooleanPreference = new BooleanPreference(simpleBooleanProperty.getName(), + simpleBooleanProperty.get()); + tmpContainer.add(tmpBooleanPreference); + } + case SimpleIntegerProperty simpleIntegerProperty -> { + SingleIntegerPreference tmpIntPreference = new SingleIntegerPreference(simpleIntegerProperty.getName(), + simpleIntegerProperty.get()); + tmpContainer.add(tmpIntPreference); + } + case SimpleDoubleProperty simpleDoubleProperty -> { + SingleNumberPreference tmpDoublePreference = new SingleNumberPreference(simpleDoubleProperty.getName(), + simpleDoubleProperty.get()); + tmpContainer.add(tmpDoublePreference); + } + case SimpleIDisplayEnumConstantProperty simpleIDisplayEnumConstantProperty -> { + SingleTermPreference tmpStringPreference = new SingleTermPreference(simpleIDisplayEnumConstantProperty.getName(), + ((Enum) simpleIDisplayEnumConstantProperty.get()).name()); + tmpContainer.add(tmpStringPreference); + } + case SimpleStringProperty simpleStringProperty -> { + // includes SimpleEnumConstantNameProperty + SingleTermPreference tmpStringPreference = new SingleTermPreference(simpleStringProperty.getName(), + simpleStringProperty.get()); + tmpContainer.add(tmpStringPreference); + } + default -> + PreferenceUtil.LOGGER.log(Level.WARNING, "Unknown property type {0} was given.", + tmpProperty.getClass().getSimpleName()); } } catch (IllegalArgumentException anException) { - PreferenceUtil.LOGGER.log(Level.WARNING, "Setting translation to property went wrong, exception: " + anException.toString(), anException); - continue; + PreferenceUtil.LOGGER.log(Level.WARNING, + String.format("Setting translation to property went wrong, exception: %s", anException.toString()), + anException); + //continue; } } return tmpContainer; } + // + /** + * Sets the values of the given JFX properties (settings) according to the preferences in the given container with the same name. + * If no matching preference for a given property is found, the value will remain in its default setting. Intended use is to update + * JFX properties that represent settings based on de-persisted MORTAR preference objects. If no preference with the same name + * can be found for a given property, the case is logged with the property name and the property remains in the same + * value. + * + * @param aPropertiesList properties to update + * @param aPreferenceContainer preferences to take the values from + */ + public static void updatePropertiesFromPreferences(List> aPropertiesList, PreferenceContainer aPreferenceContainer) { + Objects.requireNonNull(aPropertiesList, "Given JFX properties list is null"); + Objects.requireNonNull(aPreferenceContainer, "Given preference container is null"); + if (aPreferenceContainer.isEmpty() || aPropertiesList.isEmpty()) { + PreferenceUtil.LOGGER.log(Level.WARNING, "Preference container or properties list is empty."); + return; + } + for (Property tmpSettingProperty : aPropertiesList) { + String tmpPropertyName = tmpSettingProperty.getName(); + if (aPreferenceContainer.containsPreferenceName(tmpPropertyName)) { + IPreference[] tmpPreferences = aPreferenceContainer.getPreferences(tmpPropertyName); + try { + switch (tmpSettingProperty) { + case SimpleBooleanProperty tmpSimpleBooleanProperty -> { + BooleanPreference tmpBooleanPreference = (BooleanPreference) tmpPreferences[0]; + tmpSimpleBooleanProperty.setValue(tmpBooleanPreference.getContent()); + } + case SimpleIntegerProperty tmpSimpleIntegerProperty -> { + SingleIntegerPreference tmpIntPreference = (SingleIntegerPreference) tmpPreferences[0]; + tmpSimpleIntegerProperty.setValue(tmpIntPreference.getContent()); + } + case SimpleDoubleProperty tmpSimpleDoubleProperty -> { + SingleNumberPreference tmpDoublePreference = (SingleNumberPreference) tmpPreferences[0]; + tmpSimpleDoubleProperty.setValue(tmpDoublePreference.getContent()); + } + case SimpleIDisplayEnumConstantProperty tmpSimpleIDisplayEnumConstantProperty -> { + SingleTermPreference tmpStringPreference = (SingleTermPreference) tmpPreferences[0]; + tmpSimpleIDisplayEnumConstantProperty.setValue((IDisplayEnum) Enum.valueOf(tmpSimpleIDisplayEnumConstantProperty.getAssociatedEnum(), tmpStringPreference.getContent())); + } + case SimpleStringProperty tmpSimpleStringProperty -> { + //includes SimpleEnumConstantNameProperty since it extends tmpSimpleStringProperty + SingleTermPreference tmpStringPreference = (SingleTermPreference) tmpPreferences[0]; + tmpSimpleStringProperty.setValue(tmpStringPreference.getContent()); + } + default -> { + //setting will remain in default + PreferenceUtil.LOGGER.log(Level.WARNING, "Setting {0} is of unknown type.", tmpPropertyName); + } + } + } catch (ClassCastException | IllegalArgumentException anException) { + //setting will remain in default + PreferenceUtil.LOGGER.log(Level.WARNING, anException.toString(), anException); + } + } else { + //setting will remain in default + PreferenceUtil.LOGGER.log(Level.WARNING, "No persisted settings for {0} available.", tmpPropertyName); + } + } + } + // + /** + * Checks the given list of JavaFx property objects for whether they adhere with the restrictions imposed by the MORTAR + * preferences classes that are used for persisting the properties. Property names must be singletons within the given list. + * Property names and values must adhere to the preference input restrictions. Property values are only tested for + * their current state, not the entire possible input space! It is therefore recommended to call this method again before + * attempting to persist the properties as preferences. An UnsupportedOperationException is thrown if something does + * not adhere to the given requirements. + * + * @param aPropertiesList the list of properties, e.g. the settings of a specific fragmenter class + * @throws UnsupportedOperationException if sth does not fit the restrictions + */ + public static void checkPropertiesForPreferenceRestrictions(List> aPropertiesList) throws UnsupportedOperationException { + int tmpSettingNamesSetInitCapacity = CollectionUtil.calculateInitialHashCollectionCapacity(aPropertiesList.size(), + BasicDefinitions.DEFAULT_HASH_COLLECTION_LOAD_FACTOR); + HashSet tmpSettingNames = new HashSet<>(tmpSettingNamesSetInitCapacity, + BasicDefinitions.DEFAULT_HASH_COLLECTION_LOAD_FACTOR); + for (Property tmpSetting : aPropertiesList) { + if (!PreferenceUtil.isValidName(tmpSetting.getName())) { + throw new UnsupportedOperationException("Setting " + tmpSetting.getName() + " has an invalid name."); + } + if (tmpSettingNames.contains(tmpSetting.getName())) { + throw new UnsupportedOperationException("Setting name " + tmpSetting.getName() + " is used multiple times."); + } else { + tmpSettingNames.add(tmpSetting.getName()); + } + switch (tmpSetting) { + case SimpleBooleanProperty simpleBooleanProperty -> { + //nothing to do here, booleans cannot have invalid values + } + case SimpleIntegerProperty simpleIntegerProperty -> { + if (!SingleIntegerPreference.isValidContent(Integer.toString(simpleIntegerProperty.get()))) { + throw new UnsupportedOperationException("Setting value " + simpleIntegerProperty.get() + + " of setting name " + tmpSetting.getName() + " is invalid."); + } + } + case SimpleDoubleProperty simpleDoubleProperty -> { + if (!SingleNumberPreference.isValidContent(simpleDoubleProperty.get())) { + throw new UnsupportedOperationException("Setting value " + simpleDoubleProperty.get() + + " of setting name " + tmpSetting.getName() + " is invalid."); + } + } + case SimpleIDisplayEnumConstantProperty simpleIDisplayEnumConstantProperty -> { + if (!SingleTermPreference.isValidContent(((Enum)simpleIDisplayEnumConstantProperty.get()).name())) { + throw new UnsupportedOperationException("Setting value " + simpleIDisplayEnumConstantProperty.get() + + " of setting name " + tmpSetting.getName() + " is invalid."); + } + } + case SimpleStringProperty simpleStringProperty -> { + //includes SimpleEnumConstantNameProperty + if (!SingleTermPreference.isValidContent(simpleStringProperty.get())) { + throw new UnsupportedOperationException("Setting value " + simpleStringProperty.get() + + " of setting name " + tmpSetting.getName() + " is invalid."); + } + } + default -> { + throw new UnsupportedOperationException("Setting " + tmpSetting.getName() + " is of an invalid type."); + } + } + } + } // } diff --git a/src/main/java/de/unijena/cheminf/mortar/preference/RGBColorPreference.java b/src/main/java/de/unijena/cheminf/mortar/preference/RGBColorPreference.java index aa4870ee..b62cee8b 100644 --- a/src/main/java/de/unijena/cheminf/mortar/preference/RGBColorPreference.java +++ b/src/main/java/de/unijena/cheminf/mortar/preference/RGBColorPreference.java @@ -25,11 +25,6 @@ package de.unijena.cheminf.mortar.preference; -/** - * TODO: - * - Implement clone() - */ - import de.unijena.cheminf.mortar.model.util.MiscUtil; import java.io.BufferedReader; @@ -59,7 +54,7 @@ public class RGBColorPreference extends BasePreference { /** * Character to separate the different color components when writing a representation of this object to file. - * If this is altered, older versions can not be read any more! So should you change this, hard-code the ':' + * If this is altered, older versions can not be read anymore! So should you change this, hard-code the ':' * character in reloadVersion1000() and introduce a new version! */ private static final String PERSISTENCE_VALUE_SEPARATOR = ":"; @@ -178,7 +173,7 @@ public RGBColorPreference(BufferedReader aReader) throws IOException { //... //break; default: - throw new Exception("Invalid version."); + throw new IOException("Invalid version."); } } catch (Exception anException) { RGBColorPreference.LOGGER.log(Level.SEVERE, anException.toString(), anException); @@ -188,6 +183,7 @@ public RGBColorPreference(BufferedReader aReader) throws IOException { // // // + @Override public String getContentRepresentative() { String tmpContentRepresentative = "RGB Color [red=" + RGBColorPreference.FORMAT_FOR_REPRESENTATIVE.format(this.red) @@ -290,19 +286,15 @@ public void setAlpha(int anAlphaValue) throws IllegalArgumentException { // // // - @Override - public IPreference clone() throws CloneNotSupportedException { - throw new UnsupportedOperationException("Not supported yet."); - } @Override public RGBColorPreference copy() { - RGBColorPreference tmpCopy = new RGBColorPreference(new String(this.name), - Double.valueOf(this.red), - Double.valueOf(this.green), - Double.valueOf(this.blue), - Double.valueOf(alpha)); - tmpCopy.guid = new String(this.guid); + RGBColorPreference tmpCopy = new RGBColorPreference(this.name, + this.red, + this.green, + this.blue, + alpha); + tmpCopy.guid = this.guid; return tmpCopy; } @@ -391,11 +383,10 @@ private void initialize(String aName, double aRedValue, double aGreenValue, doub /** * (Re-)instantiates a new RGBColorPreference object of version 1.0.0.0 from a line-based text file. */ - private void reloadVersion1000(BufferedReader aReader) throws Exception { + private void reloadVersion1000(BufferedReader aReader) throws IOException { this.name = aReader.readLine(); this.guid = aReader.readLine(); - //If RGBColorPreference.PERSISTENCE_VALUE_SEPARATOR is altered this will not work anymore, see above. - String[] tmpColorComponents = aReader.readLine().split(RGBColorPreference.PERSISTENCE_VALUE_SEPARATOR); + String[] tmpColorComponents = aReader.readLine().split(":"); this.red = Double.parseDouble(tmpColorComponents[0]); this.green = Double.parseDouble(tmpColorComponents[1]); this.blue = Double.parseDouble(tmpColorComponents[2]); diff --git a/src/main/java/de/unijena/cheminf/mortar/preference/SingleIntegerPreference.java b/src/main/java/de/unijena/cheminf/mortar/preference/SingleIntegerPreference.java index 7bfc685b..31b2915c 100644 --- a/src/main/java/de/unijena/cheminf/mortar/preference/SingleIntegerPreference.java +++ b/src/main/java/de/unijena/cheminf/mortar/preference/SingleIntegerPreference.java @@ -25,11 +25,6 @@ package de.unijena.cheminf.mortar.preference; -/** - * TODO: - * - Implement clone() - */ - import de.unijena.cheminf.mortar.model.util.MiscUtil; import java.io.BufferedReader; @@ -65,7 +60,7 @@ public class SingleIntegerPreference extends BasePreference { // // /** - * Single integer content of this preference + * Single integer content of this preference. */ private int content; // @@ -134,7 +129,7 @@ public SingleIntegerPreference(BufferedReader aReader) throws IOException { //... //break; default: - throw new Exception("Invalid version."); + throw new IOException("Invalid version."); } } catch (Exception anException) { SingleIntegerPreference.LOGGER.log(Level.SEVERE, anException.toString(), anException); @@ -144,6 +139,7 @@ public SingleIntegerPreference(BufferedReader aReader) throws IOException { // // // + @Override public String getContentRepresentative() { return Integer.toString(this.content); @@ -198,15 +194,11 @@ public void setContent(String anIntString) throws IllegalArgumentException { // // // - @Override - public IPreference clone() throws CloneNotSupportedException { - throw new UnsupportedOperationException("Not supported yet."); - } @Override public SingleIntegerPreference copy() { - SingleIntegerPreference tmpCopy = new SingleIntegerPreference(new String(this.name), this.content); - tmpCopy.guid = new String(this.guid); + SingleIntegerPreference tmpCopy = new SingleIntegerPreference(this.name, this.content); + tmpCopy.guid = this.guid; return tmpCopy; } @@ -247,7 +239,7 @@ public static boolean isValidContent(String anIntString) { /** * (Re-)instantiates a new SingleIntegerPreference object of version 1.0.0.0 from a line-based text file. */ - private void reloadVersion1000(BufferedReader aReader) throws Exception { + private void reloadVersion1000(BufferedReader aReader) throws IOException { this.name = aReader.readLine(); this.guid = aReader.readLine(); this.content = Integer.parseInt(aReader.readLine()); diff --git a/src/main/java/de/unijena/cheminf/mortar/preference/SingleNumberPreference.java b/src/main/java/de/unijena/cheminf/mortar/preference/SingleNumberPreference.java index a722d214..21664f83 100644 --- a/src/main/java/de/unijena/cheminf/mortar/preference/SingleNumberPreference.java +++ b/src/main/java/de/unijena/cheminf/mortar/preference/SingleNumberPreference.java @@ -25,11 +25,6 @@ package de.unijena.cheminf.mortar.preference; -/** - * TODO: - * - Implement clone() - */ - import de.unijena.cheminf.mortar.model.util.MiscUtil; import java.io.BufferedReader; @@ -65,7 +60,7 @@ public class SingleNumberPreference extends BasePreference { // // /** - * Single number content of this preference + * Single number content of this preference. */ private double content; // @@ -106,8 +101,8 @@ public SingleNumberPreference(String aName, String aDoubleString) throws Illegal if (!BasePreference.isValidName(aName)) { throw new IllegalArgumentException("Preference name " + aName + " does not match required pattern!"); } - Double tmpDouble = SingleNumberPreference.parseValidContent(aDoubleString); - if (tmpDouble.isNaN()) { + double tmpDouble = SingleNumberPreference.parseValidContent(aDoubleString); + if (Double.isNaN(tmpDouble)) { throw new IllegalArgumentException("The given number " + aDoubleString + " is no valid content!"); } // @@ -135,7 +130,7 @@ public SingleNumberPreference(BufferedReader aReader) throws IOException { //... //break; default: - throw new Exception("Invalid version."); + throw new IOException("Invalid version."); } } catch (Exception anException) { SingleNumberPreference.LOGGER.log(Level.SEVERE, anException.toString(), anException); @@ -145,6 +140,7 @@ public SingleNumberPreference(BufferedReader aReader) throws IOException { // // // + @Override public String getContentRepresentative() { return Double.toString(this.content); @@ -192,8 +188,8 @@ public void setContent(double aDouble) throws IllegalArgumentException { * double is infinite or 'NaN' */ public void setContent(String aDoubleString) throws IllegalArgumentException { - Double tmpDouble = SingleNumberPreference.parseValidContent(aDoubleString); - if (tmpDouble.isNaN()) { + double tmpDouble = SingleNumberPreference.parseValidContent(aDoubleString); + if (Double.isNaN(tmpDouble)) { throw new IllegalArgumentException("The given number " + aDoubleString + " is no valid content!"); } this.content = tmpDouble; @@ -201,15 +197,11 @@ public void setContent(String aDoubleString) throws IllegalArgumentException { // // // - @Override - public IPreference clone() throws CloneNotSupportedException { - throw new UnsupportedOperationException("Not supported yet."); - } @Override public SingleNumberPreference copy() { - SingleNumberPreference tmpCopy = new SingleNumberPreference(new String(this.name), Double.valueOf(this.content)); - tmpCopy.guid = new String(this.guid); + SingleNumberPreference tmpCopy = new SingleNumberPreference(this.name, this.content); + tmpCopy.guid = this.guid; return tmpCopy; } @@ -235,8 +227,8 @@ public String toString() { * @return true if aContent meets the requirements for contents of this preference */ public static boolean isValidContent(double aContent) { - Double tmpDouble = aContent; - return !(tmpDouble.isInfinite() || tmpDouble.isNaN()); + double tmpDouble = aContent; + return !(Double.isInfinite(tmpDouble) || Double.isNaN(tmpDouble)); } /** @@ -251,8 +243,8 @@ public static boolean isValidContent(String aDoubleString) { if (Objects.isNull(aDoubleString) || aDoubleString.isEmpty()) { return false; } - Double tmpResult = SingleNumberPreference.parseValidContent(aDoubleString); - return !tmpResult.isNaN(); + double tmpResult = SingleNumberPreference.parseValidContent(aDoubleString); + return !Double.isNaN(tmpResult); } // // @@ -260,7 +252,7 @@ public static boolean isValidContent(String aDoubleString) { /** * (Re-)instantiates a new SingleNumberPreference object of version 1.0.0.0 from a line-based text file. */ - private void reloadVersion1000(BufferedReader aReader) throws Exception { + private void reloadVersion1000(BufferedReader aReader) throws IOException { this.name = aReader.readLine(); this.guid = aReader.readLine(); this.content = Double.parseDouble(aReader.readLine()); diff --git a/src/main/java/de/unijena/cheminf/mortar/preference/SingleTermPreference.java b/src/main/java/de/unijena/cheminf/mortar/preference/SingleTermPreference.java index bf919514..0abde9a9 100644 --- a/src/main/java/de/unijena/cheminf/mortar/preference/SingleTermPreference.java +++ b/src/main/java/de/unijena/cheminf/mortar/preference/SingleTermPreference.java @@ -25,11 +25,6 @@ package de.unijena.cheminf.mortar.preference; -/** - * TODO: - * - Implement clone() - */ - import de.unijena.cheminf.mortar.model.util.MiscUtil; import java.io.BufferedReader; @@ -56,7 +51,7 @@ public class SingleTermPreference extends BasePreference { * -, _, [], (), {}, :, ;, /, \, ., ', space (ASCII 32, hexadecimal value: 20, see below) and ,. */ private static final Pattern INPUT_TEST_PATTERN = - Pattern.compile("\\A[0-9a-z\\xE4\\xFC\\xF6\\xDFA-Z\\xC4\\xDC\\xD6\\.\\,\\'\\-\\_\\[\\]\\(\\)\\{\\}\\:\\;\\/\\\\\\x20]++\\z"); + Pattern.compile("\\A[0-9a-z\\xE4\\xFC\\xF6\\xDFA-Z\\xC4\\xDC\\xD6.,'\\-_\\[\\](){}:;/\\\\\\x20]++\\z"); /** * The version of this class. @@ -76,7 +71,7 @@ public class SingleTermPreference extends BasePreference { // // /** - * String content of this preference + * String content of this preference. */ private String content; // @@ -122,7 +117,7 @@ public SingleTermPreference(BufferedReader aReader) throws IOException { //... //break; default: - throw new Exception("Invalid version."); + throw new IOException("Invalid version."); } } catch (Exception anException) { SingleTermPreference.LOGGER.log(Level.SEVERE, anException.toString(), anException); @@ -173,15 +168,11 @@ public void setContent(String aString) throws IllegalArgumentException { //
// // - @Override - public IPreference clone() throws CloneNotSupportedException { - throw new UnsupportedOperationException("Not supported yet."); - } @Override public SingleTermPreference copy() { - SingleTermPreference tmpCopy = new SingleTermPreference(new String(this.name), new String(this.content)); - tmpCopy.guid = new String(this.guid); + SingleTermPreference tmpCopy = new SingleTermPreference(this.name, this.content); + tmpCopy.guid = this.guid; return tmpCopy; } @@ -203,7 +194,7 @@ public String toString() { /** * (Re-)instantiates a new SingleTermPreference object of version 1.0.0.0 from a line-based text file. */ - private void reloadVersion1000(BufferedReader aReader) throws Exception { + private void reloadVersion1000(BufferedReader aReader) throws IOException { this.name = aReader.readLine(); this.guid = aReader.readLine(); this.content = aReader.readLine(); diff --git a/src/main/resources/de/unijena/cheminf/mortar/configuration/MORTAR_configuration.properties b/src/main/resources/de/unijena/cheminf/mortar/configuration/MORTAR_configuration.properties new file mode 100644 index 00000000..be213e43 --- /dev/null +++ b/src/main/resources/de/unijena/cheminf/mortar/configuration/MORTAR_configuration.properties @@ -0,0 +1,42 @@ +# +# * MORTAR - MOlecule fRagmenTAtion fRamework +# * Copyright (C) 2024 Felix Baensch, Jonas Schaub (felix.baensch@w-hs.de, jonas.schaub@uni-jena.de) +# * +# * Source code is available at +# * +# * Permission is hereby granted, free of charge, to any person obtaining a copy +# * of this software and associated documentation files (the "Software"), to deal +# * in the Software without restriction, including without limitation the rights +# * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# * copies of the Software, and to permit persons to whom the Software is +# * furnished to do so, subject to the following conditions: +# * +# * The above copyright notice and this permission notice shall be included in all +# * copies or substantial portions of the Software. +# * +# * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# * SOFTWARE. +# + +mortar.vendor.name = MORTAR +mortar.dataDirectory.name = MORTAR_Data +mortar.logDirectory.name = Logs +mortar.settingsDirectory.name = Settings +mortar.styleFolder = de/unijena/cheminf/mortar/style/ +mortar.imagesFolder = de/unijena/cheminf/mortar/images/ +mortar.descriptionsFolder = de/unijena/cheminf/mortar/descriptions/ +mortar.icon.copy.name = copy_icon_16x16.png +mortar.icon.gear.name = settings_gear_icon_16x16.png +mortar.logo.name = Mortar_Logo1.png +mortar.logo.withHalfAlpha.name = Mortar_Logo1_alpha50.png +mortar.logo.icon.name = Mortar_Logo_Icon1.png +mortar.stylesheet.name = StyleSheet.css +mortar.tools.description.name = tools_description.xml +mortar.github.repository.url = https://github.com/FelixBaensch/MORTAR +mortar.tutorial.url = https://felixbaensch.github.io/MORTAR/Tutorial/MORTAR_Tutorial.pdf +mortar.tutorial.relativeFilePath = ../tutorial/MORTAR_Tutorial.pdf \ No newline at end of file diff --git a/src/main/resources/de/unijena/cheminf/mortar/descriptions/tools_description.xml b/src/main/resources/de/unijena/cheminf/mortar/descriptions/tools_description.xml index 5a4f4c5e..f437c51a 100644 --- a/src/main/resources/de/unijena/cheminf/mortar/descriptions/tools_description.xml +++ b/src/main/resources/de/unijena/cheminf/mortar/descriptions/tools_description.xml @@ -28,21 +28,33 @@ CDK 2.9 - Steinbeck et al + Steinbeck et al. LGPL v2.1 (or later) ErtlFunctionalGroupsFinder 1.3.0.0 - Sebastian Fritsch et al + Fritsch et al. LGPL v2.1 (or later) Sugar Removal Utility (SRU) 1.4 - Jonas Schaub, Maria Sorokina + Schaub et al. MIT License + + CDK-Scaffold + 2.8 + Schaub, Zander, et al. + LGPL v2.1 (or later) + + + OpenJFX + 21.0.1 + OpenJDK + GPL v2 with Classpath exception + librepdf 1.3.26 @@ -51,9 +63,14 @@ spotless - 6.23.3 + 6.25.0 DiffPlug Apache License 2.0 - + + JUnit 5 + 5.9.3 + JUnit team + Eclipse Public License 2.0 + \ No newline at end of file diff --git a/src/main/resources/de/unijena/cheminf/mortar/message/Message_en_GB.properties b/src/main/resources/de/unijena/cheminf/mortar/message/Message_en_GB.properties index 2bb50bba..46397c35 100644 --- a/src/main/resources/de/unijena/cheminf/mortar/message/Message_en_GB.properties +++ b/src/main/resources/de/unijena/cheminf/mortar/message/Message_en_GB.properties @@ -54,7 +54,7 @@ MainView.menuBar.pipelineMenu.pipelineSettingsMenuItem.text = Create Pipeline MainView.menuBar.viewsMenu.text = Views MainView.menuBar.viewsMenu.HistogramMenuItem.text = Histogram MainView.menuBar.viewsMenu.overviewViewMenuItem.text = Overview -MainView.showHistogramViewButton.text = Histogram +MainView.showHistogramViewButton.text = Plot MainView.showHistogramViewButton.tooltip = Opens the histogram view MainView.showOverviewViewButton.text = Overview MainView.showOverviewViewButton.tooltip = Opens the overview view @@ -79,16 +79,16 @@ HistogramViewController.MenuItemStructure.text = Copy structure ##MainTabPane## #MoleculesTab# MainTabPane.moleculesTab.title = Molecules -MainTabPane.moleculesTab.fragmentButton.text = Start fragmentation with %s +MainTabPane.moleculesTab.fragmentButton.text = Start fragmentation with %s MainTabPane.moleculesTab.cancelFragmentationButton.text = Cancel MainTabPane.moleculesTab.cancelFragmentationButton.tooltip = Interrupts running fragmentation MainTabPane.moleculesTab.tableView.nameColumn.header = Name MainTabPane.moleculesTab.tableView.structureColumn.header = Structure #FragmentsTab# MainTabPane.fragmentsTab.title = Fragments -MainTabPane.fragments.buttonCSV.txt = Export CSV +MainTabPane.fragments.buttonCSV.txt = CSV MainTabPane.fragments.buttonCSV.tooltip = Export fragments as CSV file -MainTabPane.fragments.buttonPDF.txt = Export PDF +MainTabPane.fragments.buttonPDF.txt = PDF MainTabPane.fragments.buttonPDF.tooltip = Export fragments as PDF MainTabPane.fragments.buttonCancelExport.txt = Cancel MainTabPane.fragments.buttonCancelExport.tooltip = Cancel running export @@ -104,9 +104,9 @@ MainTabPane.fragmentsTab.tableView.parentMolNameColumn.header = Sample Parent MainTabPane.fragmentsTab.tableView.parentMolNameColumn.tooltip = First occurred molecule which contains fragment #ItemizationTab# MainTabPane.itemizationTab.title = Items -MainTabPane.itemizationTab.csvButton.txt = Export CSV +MainTabPane.itemizationTab.csvButton.txt = CSV MainTabPane.itemizationTab.csvButton.tooltip = Export items as CSV file -MainTabPane.itemizationTab.pdfButton.txt = Export PDF +MainTabPane.itemizationTab.pdfButton.txt = PDF MainTabPane.itemizationTab.pdfButton.tooltip = Export items as PDF MainTabPane.itemizationTab.tableView.nameColumn.header = Name MainTabPane.itemizationTab.tableView.moleculeStructureColumn.header = Structure @@ -140,28 +140,39 @@ Exporter.FragmentsTab.ExportNotPossible.label = fragments could not be exported Exporter.confirmationAlert.moleculesTabSelected.title = Notification Exporter.confirmationAlert.moleculesTabSelected.header = Molecules tab selected Exporter.confirmationAlert.moleculesTabSelected.text = Molecule tab is selected. To export the results of a fragmentation, a tab of the corresponding algorithm must be selected. -Exporter.fragmentationTab.pdfCellHeader.header = Export of the fragmentation tab -Exporter.fragmentationTab.pdfCellHeader.smiles = Smiles +Exporter.fragmentationTab.pdfCellHeader.header = MORTAR Fragmentation Tab Export +Exporter.fragmentationTab.pdfCellHeader.smiles = SMILES Exporter.fragmentationTab.pdfCellHeader.frequency = Frequency Exporter.fragmentationTab.pdfCellHeader.percentage = Percentage -Exporter.fragmentationTab.pdfCellHeader.moleculeFrequency = Molecule-frequency -Exporter.fragmentationTab.pdfCellHeader.moleculePercentage = Molecule-percentage +Exporter.fragmentationTab.pdfCellHeader.moleculeFrequency = Molecule Frequency +Exporter.fragmentationTab.pdfCellHeader.moleculePercentage = Molecule Percentage Exporter.fragmentationTab.pdfCellHeader.fragment = Fragment -Exporter.itemsTab.pdfCellHeader.header = Export of the Itemization tab +Exporter.itemsTab.pdfCellHeader.header = MORTAR Itemization Tab Export Exporter.itemsTab.pdfCellHeader.name = Name Exporter.itemsTab.pdfCellHeader.structure = Structure Exporter.itemsTab.pdfCellHeader.fragments = Fragments -Exporter.pdfHeader.algorithmUsed = Algorithm used +Exporter.pdfHeader.fragmentationName = Fragmentation name Exporter.pdfHeader.numberOfMolecules = Number of Molecules -Exporter.pdfHeader.numberOfFragments = Number of fragments -Exporter.fragmentationTab.csvHeader.smiles = Smiles +Exporter.pdfHeader.numberOfFragments = Number of Fragments +Exporter.pdfHeader.fileName = File Name +Exporter.pdfHeader.timeStamp = Date and Time +Exporter.fragmentationTab.csvHeader.smiles = SMILES Exporter.fragmentationTab.csvHeader.frequency = Frequency Exporter.fragmentationTab.csvHeader.percentage = Percentage Exporter.fragmentationTab.csvHeader.moleculeFrequency = MoleculeFrequency Exporter.fragmentationTab.csvHeader.moleculePercentage = MoleculePercentage -Exporter.itemsTab.csvHeader.moleculeName = Molecule Name -Exporter.itemsTab.csvHeader.smilesOfStructure = SmilesOfStructure -Exporter.itemsTab.csvHeader.smilesOfFragmentsAndFrequency = SmilesOfFragments and frequency +Exporter.itemsTab.csvHeader.moleculeName = Name +Exporter.itemsTab.csvHeader.smilesOfStructure = SMILES +Exporter.itemsTab.csvHeader.smilesOfFragment = SMILESOfFragment +Exporter.itemsTab.csvHeader.frequencyOfFragment = FrequencyOfFragment +Exporter.CSVSeparator.Comma.displayName = comma +Exporter.CSVSeparator.Comma.tooltip = use comma (',') as separator in CSV export files +Exporter.CSVSeparator.Semicolon.displayName = semicolon +Exporter.CSVSeparator.Semicolon.tooltip = use semicolon (';') as separator in CSV export files +Exporter.CSVSeparator.Tab.displayName = tab +Exporter.CSVSeparator.Tab.tooltip = use tab ('->|') as separator in CSV export files +Exporter.CSVSeparator.Space.displayName = space +Exporter.CSVSeparator.Space.tooltip = use space (' ') as separator in CSV export files ###General### ##Errors## Error.ExceptionAlert.Title = Exception Alert @@ -179,7 +190,9 @@ Error.UnexpectedError.Header = Unexpected error occurred. Error.UnexpectedError.Content = An unexpected error occurred somewhere in the application logic. Some operations might not function properly. Please, save your work and shut down the application. See details below. Error.SecondInstance.Title = Possible second instance Error.SecondInstance.Header = Warning -Error.SecondInstance.Content = Another MORTAR instance may be already running. Or an artifact from an earlier application crash might cause this warning. Click "OK" to start MORTAR anyway or "Cancel" to exit the application. The latter does not affect the possibly already running MORTAR instance. But running multiple instances at once creates problems with logging and setting persistence. +Error.SecondInstance.Content = Another MORTAR instance may be already running. Or an artifact from an earlier application crash might cause this warning. Click "OK" to start MORTAR anyway or "Cancel" to exit the application. The latter does not affect the possibly already running MORTAR instance. But running multiple instances at once creates problems with logging and setting persistence. +Error.NoConfigFile.Header = No configuration file found +Error.NoConfigFile.Content = No MORTAR configurations properties file was found in resources (designated path "%s"). The application cannot start. ##Status## Status.Ready = Ready Status.canceled = Canceled @@ -209,7 +222,7 @@ PipelineSettingsView.cancelButton.toolTip = Discards changes and closes window PipelineSettingsView.fragmentButton.text = Fragment PipelineSettingsView.fragmentButton.toolTip = Closes window and starts pipeline fragmentation PipelineSettingsView.defaultButton.text = Default -PipelineSettingsView.defaultButton.tooltip = Reset pipeline +PipelineSettingsView.defaultButton.tooltip = Reset pipeline PipelineSettingsView.applyButton.text = Apply PipelineSettingsView.applyButton.toolTip = Applies changes and closes window PipelineSettingsView.textField.promptText = Pipeline Name @@ -222,7 +235,7 @@ HistogramView.title = Histogram HistogramView.cancelButton.text = Close HistogramView.cancelButton.toolTip = Close window HistogramView.refreshButton.text = Apply -HistogramView.refreshButton.toolTip = Refreshes the histogram according to the values specified in the text fields +HistogramView.refreshButton.toolTip = Refreshes the histogram according to the values specified in the text fields HistogramView.maximumSMILESLengthSetting.name = Maximum SMILES length setting HistogramView.smilesLabel.text = SMILES length: HistogramView.smilesTooLong = SMILES too long @@ -236,10 +249,12 @@ HistogramView.checkBox.toolTip = Click to display the frequencies HistogramView.displayGridLinesSetting.name = Display grid lines setting HistogramView.checkBoxGridlines.text = Grid lines HistogramView.checkBoxGridlines.toolTip = Click to display the gridlines -HistogramView.barWidthSetting.name = Bar width setting -HistogramView.barWidths.small = Small -HistogramView.barWidths.medium = Medium -HistogramView.barWidths.large = Large +HistogramView.barWidths.small.displayName = small +HistogramView.barWidths.small.tooltip = use slim bars +HistogramView.barWidths.medium.displayName = medium +HistogramView.barWidths.medium.tooltip = use medium-width bars +HistogramView.barWidths.large.displayName = large +HistogramView.barWidths.large.tooltip = use broad bars HistogramView.comboBox.toolTip = 3 options for setting the bars widths. The gap between the bars is also adjusted. HistogramView.gapSettingLabel.text = Bar widths: HistogramView.checkBoxLogarithmicScale.text = Logarithmic scale @@ -250,9 +265,10 @@ HistogramView.checkBoxSmilesTickLabel.toolTip = Click here to display or hide th HistogramView.displayBarShadowsSetting.name = Bar style setting HistogramView.stylingCheckBox.text = Bar style HistogramView.stylingCheckBox.tooltip = Click to add the bar shadow -HistogramView.displayFrequencySetting.name = Displayed frequency setting -HistogramView.chooseDataComboBoxFragmentFrequency.text = Fragment frequency -HistogramView.chooseDataComboBoxMoleculeFrequency.text = Molecule frequency +HistogramView.frequencyOption.moleculeFrequency.displayName = molecule frequency +HistogramView.frequencyOption.moleculeFrequency.tooltip = display in how many molecules the individual fragment appears +HistogramView.frequencyOption.absoluteFrequency.displayName = absolute frequency +HistogramView.frequencyOption.absoluteFrequency.tooltip = display how often the individual fragment appears in total HistogramView.chooseDataComboBox.text = Selected frequency: HistogramView.chooseDataComboBox.toolTip = Click to select which frequency to use ##AboutView## @@ -279,6 +295,11 @@ AboutView.tutorialButton.tooltip = Opens the MORTAR tutorial in the standard PDF AboutView.tutorialButton.alert.title = Error AboutView.tutorialButton.alert.header = Could not find tutorial file.\nTutorial is available via GitHub: AboutView.tutorialButton.alert.hyperlink.text = MORTAR tutorial +##AboutViewController## +AboutViewController.Error.XMLParsing.Name = [External tools file +AboutViewController.Error.XMLParsing.Version = could not +AboutViewController.Error.XMLParsing.Author = be +AboutViewController.Error.XMLParsing.License = parsed] ##OverviewView## OverviewView.nameOfView = Overview OverviewView.titleOfView.molecule = molecule @@ -321,53 +342,184 @@ OverviewView.enlargedStructureView.issueWithStructureDepiction.text = For more i FragmentationService.defaultPipelineName = Pipeline FragmentationService.Error.settingsPersistence = Some fragmentation settings could not be persisted and will be set to their default values in the next session. FragmentationService.Error.settingsReload = Some fragmentation settings could not be restored from the previous session. They will be set to their default values. -FragmentationService.Error.invalidSettingFormat = The settings of one or more fragmentation algorithm are of illegal format and can therefore not be persisted and reloaded at the next session. +FragmentationService.Error.invalidSettingFormat = The settings of one or more fragmentation algorithm are of illegal format and can therefore not be persisted and reloaded at the next session. ##ViewToolsManager -ViewToolsManager.Error.invalidSettingFormat = The settings of one or more fragmentation algorithm are of illegal format and can therefore not be persisted and reloaded at the next session. +ViewToolsManager.Error.invalidSettingFormat = The settings of one or more fragmentation algorithm are of illegal format and can therefore not be persisted and reloaded at the next session. ViewToolsManager.Error.settingsPersistence = Some view tool settings could not be persisted and will be set to their default values in the next session. +##Fragmenters general## +Fragmenter.IllegalSettingValue.Header = Illegal Argument was set +Fragmenter.IllegalSettingValue.Title = Illegal Argument ##ErtlFunctionalGroupsFinderFragmenter## +ErtlFunctionalGroupsFinderFragmenter.displayName = Ertl algorithm ErtlFunctionalGroupsFinderFragmenter.fragmentSaturationSetting.tooltip = Defines how open valences resulting from bond breakages during fragmentation should be saturated +ErtlFunctionalGroupsFinderFragmenter.fragmentSaturationSetting.displayName = Fragment saturation setting ErtlFunctionalGroupsFinderFragmenter.environmentModeSetting.tooltip = Defines whether the functional group fragments should carry no environment, a generalized one (see Ertl 2017), or their full environment +ErtlFunctionalGroupsFinderFragmenter.environmentModeSetting.displayName = Environment mode setting ErtlFunctionalGroupsFinderFragmenter.returnedFragmentsSetting.tooltip = Defines which fragments should be returned, functional groups, alkane fragments, or both +ErtlFunctionalGroupsFinderFragmenter.returnedFragmentsSetting.displayName = Returned fragments setting ErtlFunctionalGroupsFinderFragmenter.cycleFinderSetting.tooltip = Defines which CDK cycle finder algorithm should be used for aromaticity detection +ErtlFunctionalGroupsFinderFragmenter.cycleFinderSetting.displayName = Cycle finder algorithm setting ErtlFunctionalGroupsFinderFragmenter.electronDonationModelSetting.tooltip = Defines which CDK electron donation model should be used for aromaticity detection +ErtlFunctionalGroupsFinderFragmenter.electronDonationModelSetting.displayName = Electron donation model setting ErtlFunctionalGroupsFinderFragmenter.filterSingleAtomsSetting.tooltip = Defines whether single-atom molecules should be filtered from inputs, i.e. if true, molecules consisting of only one atom are filtered from the input molecules prior to fragmentation and no functional groups are determined for them +ErtlFunctionalGroupsFinderFragmenter.filterSingleAtomsSetting.displayName = Filter single atoms setting ErtlFunctionalGroupsFinderFragmenter.applyInputRestrictionsSetting.tooltip = Defines whether strict input restrictions should be applied; if true, this fragmenter does not accept any molecules containing metal, metalloid, or pseudo atoms, formal charges, or multiple unconnected parts; these molecules are then filtered from the input molecules prior to fragmentation and no functional groups are determined for them +ErtlFunctionalGroupsFinderFragmenter.applyInputRestrictionsSetting.displayName = Apply input restrictions setting +ErtlFunctionalGroupsFinderFragmenter.FGEnvOption.Generalization.displayName = generalized environments +ErtlFunctionalGroupsFinderFragmenter.FGEnvOption.Generalization.tooltip = Generalize environments of functional groups according to Ertl (2017) +ErtlFunctionalGroupsFinderFragmenter.FGEnvOption.NoGeneralization.displayName = full environments +ErtlFunctionalGroupsFinderFragmenter.FGEnvOption.NoGeneralization.tooltip = extract functional groups with their full environments, no generalization +ErtlFunctionalGroupsFinderFragmenter.FGEnvOption.OnlyMarkedAtoms.displayName = no environments +ErtlFunctionalGroupsFinderFragmenter.FGEnvOption.OnlyMarkedAtoms.tooltip = extract only the functional group atoms marked according to the Ertl algorithm, without any environment +ErtlFunctionalGroupsFinderFragmenter.EFGFFragmenterReturnedFragmentsOption.OnlyFunctionalGroups.displayName = only functional groups +ErtlFunctionalGroupsFinderFragmenter.EFGFFragmenterReturnedFragmentsOption.OnlyFunctionalGroups.tooltip = return only the identified functional groups of a molecule after fragmentation +ErtlFunctionalGroupsFinderFragmenter.EFGFFragmenterReturnedFragmentsOption.OnlyAlkanes.displayName = only alkanes +ErtlFunctionalGroupsFinderFragmenter.EFGFFragmenterReturnedFragmentsOption.OnlyAlkanes.tooltip = return only the non-functional-group alkane fragments of a molecule after fragmentation +ErtlFunctionalGroupsFinderFragmenter.EFGFFragmenterReturnedFragmentsOption.All.displayName = functional groups and alkanes +ErtlFunctionalGroupsFinderFragmenter.EFGFFragmenterReturnedFragmentsOption.All.tooltip = return both, functional groups and alkane fragments, after fragmentation ##SugarRemovalUtilityFragmenter## +SugarRemovalUtilityFragmenter.displayName = Sugar Removal Utility SugarRemovalUtilityFragmenter.returnedFragmentsSetting.tooltip = Defines which fragments should be returned, sugar moieties, the aglycone, or both +SugarRemovalUtilityFragmenter.returnedFragmentsSetting.displayName = Returned fragments setting SugarRemovalUtilityFragmenter.fragmentSaturationSetting.tooltip = Defines how open valences resulting from bond breakages during fragmentation should be saturated +SugarRemovalUtilityFragmenter.fragmentSaturationSetting.displayName = Fragment saturation setting SugarRemovalUtilityFragmenter.sugarTypeToRemoveSetting.tooltip = Defines which type of sugar moieties should be detected, circular, linear, or both +SugarRemovalUtilityFragmenter.sugarTypeToRemoveSetting.displayName = Sugar type to remove setting SugarRemovalUtilityFragmenter.detectCircularSugarsOnlyWithGlycosidicBondSetting.tooltip = Defines whether circular sugars should be detected only if they have an O-glycosidic bond to another moiety or the core of the molecule +SugarRemovalUtilityFragmenter.detectCircularSugarsOnlyWithGlycosidicBondSetting.displayName = Detect circular sugars only with glycosidic bond setting SugarRemovalUtilityFragmenter.removeOnlyTerminalSugarsSetting.tooltip = Defines whether only terminal or also non-terminal moieties should be detected +SugarRemovalUtilityFragmenter.removeOnlyTerminalSugarsSetting.displayName = Remove only terminal sugars setting SugarRemovalUtilityFragmenter.preservationModeSetting.tooltip = Defines by which characteristic structures that get disconnected from the central core in the sugar removal process should be judged to decide whether to preserve or discard them; when the preservation mode is changed, the associated threshold setting (see below) is automatically set to the default value of the chosen option +SugarRemovalUtilityFragmenter.preservationModeSetting.displayName = Preservation mode setting SugarRemovalUtilityFragmenter.preservationModeThresholdSetting.tooltip = An integer number giving the threshold of the preservation mode, e.g. how many heavy atoms a disconnected structure needs to have at least to be not removed or how heavy (in terms of its molecular weight) it needs to be +SugarRemovalUtilityFragmenter.preservationModeThresholdSetting.displayName = Preservation mode threshold setting SugarRemovalUtilityFragmenter.detectCircularSugarsOnlyWithEnoughExocyclicOxygenAtomsSetting.tooltip = Defines whether circular sugars should be detected only if they have a sufficient number of attached exocyclic oxygen atoms +SugarRemovalUtilityFragmenter.detectCircularSugarsOnlyWithEnoughExocyclicOxygenAtomsSetting.displayName = Detect circular sugars only with enough exocyclic oxygen atoms setting SugarRemovalUtilityFragmenter.exocyclicOxygenAtomsToAtomsInRingRatioThresholdSetting.tooltip = A number giving the minimum attached exocyclic oxygen atoms to atom number in the ring ratio a circular sugar needs to have to be detected as such +SugarRemovalUtilityFragmenter.exocyclicOxygenAtomsToAtomsInRingRatioThresholdSetting.displayName = Exocyclic oxygen atoms to atoms in ring ratio threshold setting SugarRemovalUtilityFragmenter.detectLinearSugarsInRingsSetting.tooltip = Defines whether linear sugars that are substructures of rings should be detected +SugarRemovalUtilityFragmenter.detectLinearSugarsInRingsSetting.displayName = Detect linear sugars in rings setting SugarRemovalUtilityFragmenter.linearSugarCandidateMinimumSizeSetting.tooltip = An integer number indicating the minimum number of carbon atoms a linear sugar needs to have to be detected as such +SugarRemovalUtilityFragmenter.linearSugarCandidateMinimumSizeSetting.displayName = Linear sugar candidate minimum size setting SugarRemovalUtilityFragmenter.linearSugarCandidateMaximumSizeSetting.tooltip = An integer number indicating the maximum number of carbon atoms a linear sugar needs to have to be detected as such +SugarRemovalUtilityFragmenter.linearSugarCandidateMaximumSizeSetting.displayName = Linear sugar candidate maximum size setting SugarRemovalUtilityFragmenter.detectLinearAcidicSugarsSetting.tooltip = Defines whether linear acidic sugars should be included in the set of linear sugar patterns for the initial detection +SugarRemovalUtilityFragmenter.detectLinearAcidicSugarsSetting.displayName = Detect linear acidic sugars setting SugarRemovalUtilityFragmenter.detectSpiroRingsAsCircularSugarsSetting.tooltip = Defines whether spiro rings (rings that share one atom with another cycle) should be included in the circular sugar detection +SugarRemovalUtilityFragmenter.detectSpiroRingsAsCircularSugarsSetting.displayName = Detect spiro rings as circular sugars setting SugarRemovalUtilityFragmenter.detectCircularSugarsWithKetoGroupsSetting.tooltip = Defines whether circular sugar-like moieties with keto groups should be detected +SugarRemovalUtilityFragmenter.detectCircularSugarsWithKetoGroupsSetting.displayName = Detect circular sugars with keto groups setting +SugarRemovalUtilityFragmenter.SugarTypeToRemoveOption.Circular.displayName = circular +SugarRemovalUtilityFragmenter.SugarTypeToRemoveOption.Circular.tooltip = remove/detect only circular sugars +SugarRemovalUtilityFragmenter.SugarTypeToRemoveOption.Linear.displayName = linear +SugarRemovalUtilityFragmenter.SugarTypeToRemoveOption.Linear.tooltip = remove/detect only linear sugars +SugarRemovalUtilityFragmenter.SugarTypeToRemoveOption.Both.displayName = circular and linear +SugarRemovalUtilityFragmenter.SugarTypeToRemoveOption.Both.tooltip = remove/detect circular and linear sugars +SugarRemovalUtilityFragmenter.SRUFragmenterReturnedFragmentsOption.OnlySugars.displayName = only sugar moieties +SugarRemovalUtilityFragmenter.SRUFragmenterReturnedFragmentsOption.OnlySugars.tooltip = return only the identified sugar moieties of a molecule after fragmentation +SugarRemovalUtilityFragmenter.SRUFragmenterReturnedFragmentsOption.OnlyAglycone.displayName = only aglycone +SugarRemovalUtilityFragmenter.SRUFragmenterReturnedFragmentsOption.OnlyAglycone.tooltip = return only the identified aglycone of a molecule after fragmentation +SugarRemovalUtilityFragmenter.SRUFragmenterReturnedFragmentsOption.All.displayName = aglycone and sugars +SugarRemovalUtilityFragmenter.SRUFragmenterReturnedFragmentsOption.All.tooltip = return the identified aglycone and sugar moieties of a molecule together after fragmentation +SugarRemovalUtilityFragmenter.SRUFragmenterPreservationModeOption.All.displayName = preserve all fragments +SugarRemovalUtilityFragmenter.SRUFragmenterPreservationModeOption.All.tooltip = specifies that all structures should be preserved; note that if this option is combined with the removal of only terminal moieties, even the smallest attached structure will prevent the removal of a sugar; this includes hydroxy groups of sugar moieties because these are not considered as part of the sugar moiety +SugarRemovalUtilityFragmenter.SRUFragmenterPreservationModeOption.HAC.displayName = heavy atom count +SugarRemovalUtilityFragmenter.SRUFragmenterPreservationModeOption.HAC.tooltip = Specifies that whether a structure is worth preserving will be judged by its heavy atom count; the default threshold to preserve a structure is set to 5 heavy atoms (inclusive) +SugarRemovalUtilityFragmenter.SRUFragmenterPreservationModeOption.MW.displayName = molecular weight +SugarRemovalUtilityFragmenter.SRUFragmenterPreservationModeOption.MW.tooltip = Specifies that whether a structure is worth preserving will be judged by its molecular weight; the default threshold to preserve a structure is set to 60 Da (= 5 carbon atoms, inclusive) ##SettingsContainer## SettingsContainer.rowsPerPageSetting.tooltip = Defines how many rows (i.e. molecules or fragments) should be displayed per page +SettingsContainer.rowsPerPageSetting.displayName = Rows per page setting SettingsContainer.numberOfTasksForFragmentationSetting.tooltip = Defines how many parallel tasks should be used for the fragmentation; more tasks make the fragmentation faster in general but the number is limited by the given hardware; as a maximum, %s threads are available on your specific machine +SettingsContainer.numberOfTasksForFragmentationSetting.displayName = Nr of tasks for fragmentation setting SettingsContainer.addImplicitHydrogensAtImportSetting.tooltip = Defines whether open valences in the imported molecules should be filled with implicit hydrogen atoms +SettingsContainer.addImplicitHydrogensAtImportSetting.displayName = Add implicit hydrogens at import setting SettingsContainer.alwaysMDLV3000FormatAtExportSetting.tooltip = Defines whether MOL file exports should always be done in the version 3000 format that is otherwise only employed if the exported molecule is too big -SettingsContainer.csvExportSeparatorSetting.tooltip = Defines the separator character used in CSV file export, allowed characters are comma and semicolon +SettingsContainer.alwaysMDLV3000FormatAtExportSetting.displayName = Always MDL V3000 format at export setting +SettingsContainer.csvExportSeparatorSetting.tooltip = Defines the separator character used in CSV file export +SettingsContainer.csvExportSeparatorSetting.displayName = CSV export separator setting SettingsContainer.keepLastFragmentSetting.tooltip = Defines whether to keep last fragment during pipeline fragmentation if no new fragment is created +SettingsContainer.keepLastFragmentSetting.displayName = Keep last fragment in pipelining setting SettingsContainer.Error.settingsPersistence = An error occurred while saving the global settings for the next session. SettingsContainer.Error.invalidSettingFormat = One or more global settings are of illegal format and can therefore not be persisted and reloaded at the next session. SettingsContainer.Error.invalidSettingArgument.Title = Illegal Argument SettingsContainer.Error.invalidSettingArgument.Header = Illegal Argument was set. ##ScaffoldGeneratorFragmenter## +ScaffoldGeneratorFragmenter.displayName = Scaffold Generator +ScaffoldGeneratorFragmenter.SmilesGeneratorOption.UniqueWithoutStereo.displayName = unique without stereo +ScaffoldGeneratorFragmenter.SmilesGeneratorOption.UniqueWithoutStereo.tooltip = use CDK unique SMILES without stereo-chemical information +ScaffoldGeneratorFragmenter.SmilesGeneratorOption.UniqueWithStereo.displayName = unique with stereo +ScaffoldGeneratorFragmenter.SmilesGeneratorOption.UniqueWithStereo.tooltip = use CDK unique SMILES *with* stereo-chemical information ScaffoldGeneratorFragmenter.fragmentSaturationSetting.tooltip = Defines how open valences resulting from bond breakages during fragmentation should be saturated +ScaffoldGeneratorFragmenter.fragmentSaturationSetting.displayName = Fragment saturation setting ScaffoldGeneratorFragmenter.scaffoldModeSetting.tooltip = Defines which scaffold concept should be used for scaffold generation and dissection +ScaffoldGeneratorFragmenter.scaffoldModeSetting.displayName = Scaffold mode setting ScaffoldGeneratorFragmenter.determineAromaticitySetting.tooltip = defines whether aromaticity should be determined which can be important for some dissection steps and rules +ScaffoldGeneratorFragmenter.determineAromaticitySetting.displayName = Determine aromaticity setting ScaffoldGeneratorFragmenter.cycleFinderSetting.tooltip = Defines which CDK cycle finder algorithm should be used for aromaticity detection +ScaffoldGeneratorFragmenter.cycleFinderSetting.displayName = Cycle finder algorithm setting ScaffoldGeneratorFragmenter.electronDonationModelSetting.tooltip = Defines which CDK electron donation model should be used for aromaticity detection +ScaffoldGeneratorFragmenter.electronDonationModelSetting.displayName = Electron donation model setting ScaffoldGeneratorFragmenter.smilesGeneratorSetting.tooltip = Defines which SMILES generator configuration should be used to filter duplicates during the enumerative scaffold dissection +ScaffoldGeneratorFragmenter.smilesGeneratorSetting.displayName = SMILES generator setting ScaffoldGeneratorFragmenter.ruleSevenAppliedSetting.tooltip = Defines whether rule 7 of the Schuffenhauer Scaffold Tree dissection method should be applied +ScaffoldGeneratorFragmenter.ruleSevenAppliedSetting.displayName = Rule seven setting ScaffoldGeneratorFragmenter.retainOnlyHybridisationsAtAromaticBondsSetting.tooltip = Defines whether certain atom hybridisations should be preserved by the insertion of double bonds only at the removal of aromatic rings or always +ScaffoldGeneratorFragmenter.retainOnlyHybridisationsAtAromaticBondsSetting.displayName = Retain only hybridisations at aromatic bonds setting ScaffoldGeneratorFragmenter.fragmentationTypeSetting.tooltip = Defines whether only the direct scaffolds should be created or whether they should also be dissected into their parent scaffolds and which method to use for this dissection -ScaffoldGeneratorFragmenter.sideChainSetting.tooltip = Defines which fragments should be returned, scaffolds, side chains, or both \ No newline at end of file +ScaffoldGeneratorFragmenter.fragmentationTypeSetting.displayName = Fragmentation type setting +ScaffoldGeneratorFragmenter.sideChainSetting.tooltip = Defines which fragments should be returned, scaffolds, side chains, or both +ScaffoldGeneratorFragmenter.sideChainSetting.displayName = Side chain setting +ScaffoldGeneratorFragmenter.SideChainOption.OnlyScaffolds.displayName = only scaffolds +ScaffoldGeneratorFragmenter.SideChainOption.OnlyScaffolds.tooltip = return only the molecular scaffolds after fragmentation +ScaffoldGeneratorFragmenter.SideChainOption.OnlySideChains.displayName = only side chains +ScaffoldGeneratorFragmenter.SideChainOption.OnlySideChains.tooltip = return only the side chains without the scaffolds after fragmentation +ScaffoldGeneratorFragmenter.SideChainOption.ScaffoldsAndSideChains.displayName = scaffolds and side chains +ScaffoldGeneratorFragmenter.SideChainOption.ScaffoldsAndSideChains.tooltip = return molecular scaffolds and side chains after fragmentation +ScaffoldGeneratorFragmenter.FragmentationTypeOption.Enumerative.displayName = all sub-scaffolds +ScaffoldGeneratorFragmenter.FragmentationTypeOption.Enumerative.tooltip = generate all possible parent/sub scaffolds of the molecule that can be generated via the removal of one ring in a step-wise manner +ScaffoldGeneratorFragmenter.FragmentationTypeOption.Schuffenhauer.displayName = scaffold tree +ScaffoldGeneratorFragmenter.FragmentationTypeOption.Schuffenhauer.tooltip = generate parent/sub scaffolds according to the scaffold tree prioritization rules +ScaffoldGeneratorFragmenter.FragmentationTypeOption.Scaffold.displayName = main scaffold +ScaffoldGeneratorFragmenter.FragmentationTypeOption.Scaffold.tooltip = generate only the one main scaffold of each molecule +ScaffoldGeneratorFragmenter.FragmentationTypeOption.RingDissection.displayName = dissect into rings +ScaffoldGeneratorFragmenter.FragmentationTypeOption.RingDissection.tooltip = dissect the main scaffolds into its constituting rings +ScaffoldGeneratorFragmenter.SGFragmenterScaffoldModeOption.Scaffold.displayName = scaffold +ScaffoldGeneratorFragmenter.SGFragmenterScaffoldModeOption.Scaffold.tooltip = terminal side chains of the molecule are removed except for any atoms non-single bonded directly to linkers or rings +ScaffoldGeneratorFragmenter.SGFragmenterScaffoldModeOption.Murcko.displayName = Murcko framework +ScaffoldGeneratorFragmenter.SGFragmenterScaffoldModeOption.Murcko.tooltip = all terminal side chains are removed and only linkers and rings are retained +ScaffoldGeneratorFragmenter.SGFragmenterScaffoldModeOption.BasicWireFrame.displayName = basic wire frame +ScaffoldGeneratorFragmenter.SGFragmenterScaffoldModeOption.BasicWireFrame.tooltip = all side chains are removed, all bonds are converted into single bonds, and all atoms are converted into carbons +ScaffoldGeneratorFragmenter.SGFragmenterScaffoldModeOption.ElementalWireFrame.displayName = elemental wire frame +ScaffoldGeneratorFragmenter.SGFragmenterScaffoldModeOption.ElementalWireFrame.tooltip = all side chains are removed and multiple bonds are converted to single bonds, but the atomic elements remain +ScaffoldGeneratorFragmenter.SGFragmenterScaffoldModeOption.BasicFramework.displayName = basic framework +ScaffoldGeneratorFragmenter.SGFragmenterScaffoldModeOption.BasicFramework.tooltip = all side chains are removed and all atoms are converted into carbons; the order of the remaining bonds is not changed +##IMoleculeFragmenter## +IMoleculeFragmenter.FragmentSaturationOption.noSaturation.displayName = no saturation +IMoleculeFragmenter.FragmentSaturationOption.noSaturation.tooltip = do not saturate open valences left by the fragmentation +IMoleculeFragmenter.FragmentSaturationOption.hydrogenSaturation.displayName = hydrogen saturation +IMoleculeFragmenter.FragmentSaturationOption.hydrogenSaturation.tooltip = saturate open valences with hydrogen atoms +IMoleculeFragmenter.ElectronDonationModelOption.daylight.displayName = daylight +IMoleculeFragmenter.ElectronDonationModelOption.daylight.tooltip = Electron donation model closely mirroring the Daylight model for using when generating SMILES representations +IMoleculeFragmenter.ElectronDonationModelOption.cdk.displayName = CDK +IMoleculeFragmenter.ElectronDonationModelOption.cdk.tooltip = Use the preset CDK atom types to determine the electron contribution of atoms; exocyclic pi bonds are NOT allowed to contribute +IMoleculeFragmenter.ElectronDonationModelOption.cdkAllowingExocyclic.displayName = CDK allowing exocyclic +IMoleculeFragmenter.ElectronDonationModelOption.cdkAllowingExocyclic.tooltip = Use the preset CDK atom types to determine the electron contribution of atoms; exocyclic pi bonds ARE allowed to contribute +IMoleculeFragmenter.ElectronDonationModelOption.piBonds.displayName = pi bonds +IMoleculeFragmenter.ElectronDonationModelOption.piBonds.tooltip = A very simple aromaticity model which only allows atoms adjacent to cyclic pi bonds to contribute; lone pairs are not considered and as such molecules like furan and pyrrole are non-aromatic +IMoleculeFragmenter.CycleFinderOption.all.displayName = all cycles +IMoleculeFragmenter.CycleFinderOption.all.tooltip = All simple cycles in the graph, the number of cycles generated may be very large and may not be feasible for some molecules, such as fullerene +IMoleculeFragmenter.CycleFinderOption.cdkAromaticSet.displayName = CDK aromatic cycle set +IMoleculeFragmenter.CycleFinderOption.cdkAromaticSet.tooltip = Create a cycle finder which will compute a set of cycles traditionally used by the CDK to test for aromaticity +IMoleculeFragmenter.CycleFinderOption.edgeShort.displayName = edge short +IMoleculeFragmenter.CycleFinderOption.edgeShort.tooltip = The shortest cycles through each edge; linear independence is not checked; in practice the vertex/edge short cycles are similar to MCB +IMoleculeFragmenter.CycleFinderOption.essential.displayName = essential cycles +IMoleculeFragmenter.CycleFinderOption.essential.tooltip = Essential cycles of a graph; similar to the relevant cycles the set is unique for a graph; if a graph has a single MCB then the essential cycles and MCB are the same +IMoleculeFragmenter.CycleFinderOption.mcb.displayName = minimum cycle basis +IMoleculeFragmenter.CycleFinderOption.mcb.tooltip = Minimum cycle basis (MCB) of a graph, these cycles are linearly independent and can be used to generate all cycles in the graph. MCB is not unique; the smallest set of smallest rings (SSSR) is often used to refer to the MCB +IMoleculeFragmenter.CycleFinderOption.relevant.displayName = relevant cycles +IMoleculeFragmenter.CycleFinderOption.relevant.tooltip = Relevant cycles of a graph, the smallest set of uniquely defined short cycles; if a graph has a single MCB then the relevant cycles and MCB are the same; if the graph has multiple MCB then the relevant cycles is the union of all MCBs +IMoleculeFragmenter.CycleFinderOption.tripletShort.displayName = triplet short +IMoleculeFragmenter.CycleFinderOption.tripletShort.tooltip = The shortest cycle through each triple of vertices; allows to generate the envelope rings of some molecules (e.g. naphthalene) without generating all cycles +IMoleculeFragmenter.CycleFinderOption.vertexShort.displayName = vertex short +IMoleculeFragmenter.CycleFinderOption.vertexShort.tooltip = The shortest cycles through each vertex; linear independence is not checked; in practice similar to MCB \ No newline at end of file diff --git a/src/main/resources/de/unijena/cheminf/mortar/style/StyleSheet.css b/src/main/resources/de/unijena/cheminf/mortar/style/StyleSheet.css index b3cb85ab..db021e20 100644 --- a/src/main/resources/de/unijena/cheminf/mortar/style/StyleSheet.css +++ b/src/main/resources/de/unijena/cheminf/mortar/style/StyleSheet.css @@ -23,10 +23,13 @@ * SOFTWARE. */ +/*hides empty cells and rows in the MORTAR data tables*/ .table-row-cell:empty { -fx-background-color: white; } - .table-row-cell:empty .table-cell { -fx-border-width: 0px; +} +.root{ + -fx-font-size: 9pt; } \ No newline at end of file diff --git a/src/test/java/de/unijena/cheminf/mortar/model/fragmentation/algorithm/ErtlFunctionalGroupsFinderFragmenterTest.java b/src/test/java/de/unijena/cheminf/mortar/model/fragmentation/algorithm/ErtlFunctionalGroupsFinderFragmenterTest.java index 227c31a8..ffc4fd71 100644 --- a/src/test/java/de/unijena/cheminf/mortar/model/fragmentation/algorithm/ErtlFunctionalGroupsFinderFragmenterTest.java +++ b/src/test/java/de/unijena/cheminf/mortar/model/fragmentation/algorithm/ErtlFunctionalGroupsFinderFragmenterTest.java @@ -52,7 +52,7 @@ public class ErtlFunctionalGroupsFinderFragmenterTest { * fragmenter because the settings tooltips are imported from the message.properties file. */ public ErtlFunctionalGroupsFinderFragmenterTest() { - Locale.setDefault(new Locale("en", "GB")); + Locale.setDefault(Locale.of("en", "GB")); } // /** @@ -63,16 +63,17 @@ public ErtlFunctionalGroupsFinderFragmenterTest() { @Test public void basicTest() throws Exception { ErtlFunctionalGroupsFinderFragmenter tmpFragmenter = new ErtlFunctionalGroupsFinderFragmenter(); - System.out.println(tmpFragmenter.getFragmentationAlgorithmName()); - System.out.println(tmpFragmenter.getElectronDonationModelSetting()); - System.out.println(tmpFragmenter.getEnvironmentModeSetting()); - for (Property tmpSetting : tmpFragmenter.settingsProperties()) { - System.out.println(tmpSetting.getName()); + Assertions.assertDoesNotThrow(tmpFragmenter::getFragmentationAlgorithmName); + Assertions.assertDoesNotThrow(tmpFragmenter::getFragmentationAlgorithmDisplayName); + Assertions.assertDoesNotThrow(tmpFragmenter::getElectronDonationModelSetting); + Assertions.assertDoesNotThrow(tmpFragmenter::getEnvironmentModeSetting); + for (Property tmpSetting : tmpFragmenter.settingsProperties()) { + Assertions.assertDoesNotThrow(tmpSetting::getName); } } // /** - * Does a test fragmentation on the COCONUT natural product CNP0151033 and prints the results. + * Does a test fragmentation on the COCONUT natural product CNP0151033. * * @throws Exception if anything goes wrong */ @@ -85,7 +86,7 @@ public void fragmentationTest() throws Exception { ErtlFunctionalGroupsFinderFragmenter tmpFragmenter = new ErtlFunctionalGroupsFinderFragmenter(); tmpFragmenter.setEnvironmentModeSetting(ErtlFunctionalGroupsFinderFragmenter.FGEnvOption.NO_ENVIRONMENT); tmpFragmenter.setEnvironmentModeSetting(ErtlFunctionalGroupsFinderFragmenter.FGEnvOption.GENERALIZATION); - tmpFragmenter.environmentModeSettingProperty().set(ErtlFunctionalGroupsFinderFragmenter.FGEnvOption.FULL_ENVIRONMENT.name()); + tmpFragmenter.environmentModeSettingProperty().set(ErtlFunctionalGroupsFinderFragmenter.FGEnvOption.FULL_ENVIRONMENT); tmpFragmenter.setFragmentSaturationSetting(IMoleculeFragmenter.FragmentSaturationOption.HYDROGEN_SATURATION); tmpFragmenter.setElectronDonationModelSetting(ErtlFunctionalGroupsFinderFragmenter.ElectronDonationModelOption.CDK); tmpFragmenter.setReturnedFragmentsSetting(ErtlFunctionalGroupsFinderFragmenter.EFGFFragmenterReturnedFragmentsOption.ALL_FRAGMENTS); @@ -97,8 +98,8 @@ public void fragmentationTest() throws Exception { Assertions.assertTrue(tmpFragmenter.canBeFragmented(tmpOriginalMolecule)); tmpFragmentList = tmpFragmenter.fragmentMolecule(tmpOriginalMolecule); for (IAtomContainer tmpFragment : tmpFragmentList) { - System.out.println(tmpSmiGen.create(tmpFragment) + " " + tmpFragment.getProperty( - IMoleculeFragmenter.FRAGMENT_CATEGORY_PROPERTY_KEY)); + Assertions.assertDoesNotThrow(() -> tmpSmiGen.create(tmpFragment)); + Assertions.assertNotNull(tmpFragment.getProperty(IMoleculeFragmenter.FRAGMENT_CATEGORY_PROPERTY_KEY)); } } } diff --git a/src/test/java/de/unijena/cheminf/mortar/model/fragmentation/algorithm/SugarRemovalUtilityFragmenterTest.java b/src/test/java/de/unijena/cheminf/mortar/model/fragmentation/algorithm/SugarRemovalUtilityFragmenterTest.java index a664d88e..874f9dce 100644 --- a/src/test/java/de/unijena/cheminf/mortar/model/fragmentation/algorithm/SugarRemovalUtilityFragmenterTest.java +++ b/src/test/java/de/unijena/cheminf/mortar/model/fragmentation/algorithm/SugarRemovalUtilityFragmenterTest.java @@ -46,13 +46,12 @@ * @version 1.0.0.0 */ public class SugarRemovalUtilityFragmenterTest { - /** * Constructor that sets the default locale to british english, which is important for the correct functioning of the * fragmenter because the settings tooltips are imported from the message.properties file. */ public SugarRemovalUtilityFragmenterTest() { - Locale.setDefault(new Locale("en", "GB")); + Locale.setDefault(Locale.of("en", "GB")); } // /** @@ -63,10 +62,11 @@ public SugarRemovalUtilityFragmenterTest() { @Test public void basicTest() throws Exception { SugarRemovalUtilityFragmenter tmpFragmenter = new SugarRemovalUtilityFragmenter(); - System.out.println(tmpFragmenter.getFragmentationAlgorithmName()); - System.out.println(tmpFragmenter.getSugarTypeToRemoveSetting()); - for (Property tmpSetting : tmpFragmenter.settingsProperties()) { - System.out.println(tmpSetting.getName()); + Assertions.assertDoesNotThrow(tmpFragmenter::getFragmentationAlgorithmName); + Assertions.assertDoesNotThrow(tmpFragmenter::getFragmentationAlgorithmDisplayName); + Assertions.assertDoesNotThrow(tmpFragmenter::getSugarTypeToRemoveSetting); + for (Property tmpSetting : tmpFragmenter.settingsProperties()) { + Assertions.assertDoesNotThrow(tmpSetting::getName); } } // @@ -91,27 +91,24 @@ public void fragmentationTest() throws Exception { Assertions.assertFalse(tmpSRUFragmenter.shouldBePreprocessed(tmpOriginalMolecule)); Assertions.assertTrue(tmpSRUFragmenter.canBeFragmented(tmpOriginalMolecule)); tmpFragmentList = tmpSRUFragmenter.fragmentMolecule(tmpOriginalMolecule); - tmpSmilesCode = tmpSmiGen.create(tmpFragmentList.get(0)); - System.out.println(tmpSmilesCode + " " + tmpFragmentList.get(0).getProperty( - IMoleculeFragmenter.FRAGMENT_CATEGORY_PROPERTY_KEY)); + tmpSmilesCode = tmpSmiGen.create(tmpFragmentList.getFirst()); + Assertions.assertNotNull(tmpFragmentList.getFirst().getProperty(IMoleculeFragmenter.FRAGMENT_CATEGORY_PROPERTY_KEY)); //The sugar ring is not terminal and should not be removed, so the molecule remains unchanged Assertions.assertEquals("O=C(OC1C(OCC2=COC(OC(=O)CC(C)C)C3C2CC(O)C3(O)COC(=O)C)OC(CO)C(O)C1O)C=CC4=CC=C(O)C=C4", tmpSmilesCode); tmpSRUFragmenter.setRemoveOnlyTerminalSugarsSetting(false); tmpFragmentList = tmpSRUFragmenter.fragmentMolecule(tmpOriginalMolecule); - tmpSmilesCode = tmpSmiGen.create(tmpFragmentList.get(0)); - System.out.println(tmpSmilesCode + " " + tmpFragmentList.get(0).getProperty( - IMoleculeFragmenter.FRAGMENT_CATEGORY_PROPERTY_KEY)); + tmpSmilesCode = tmpSmiGen.create(tmpFragmentList.getFirst()); + Assertions.assertNotNull(tmpFragmentList.getFirst().getProperty(IMoleculeFragmenter.FRAGMENT_CATEGORY_PROPERTY_KEY)); //Now that all sugars are removed, the sugar ring is removed and an unconnected structure remains // the unconnected fragments are separated into different atom containers in the returned list Assertions.assertEquals("O=C(OCC1(O)C(O)CC2C(=COC(OC(=O)CC(C)C)C21)CO)C", tmpSmilesCode); Assertions.assertEquals("O=C(O)C=CC1=CC=C(O)C=C1", tmpSmiGen.create(tmpFragmentList.get(1))); - System.out.println(tmpSmiGen.create(tmpFragmentList.get(2)) + " " + tmpFragmentList.get(2).getProperty( - IMoleculeFragmenter.FRAGMENT_CATEGORY_PROPERTY_KEY)); + Assertions.assertNotNull(tmpFragmentList.get(2).getProperty(IMoleculeFragmenter.FRAGMENT_CATEGORY_PROPERTY_KEY)); tmpSRUFragmenter.setRemoveOnlyTerminalSugarsSetting(true); - Assertions.assertFalse(tmpSRUFragmenter.shouldBeFiltered(tmpFragmentList.get(0))); - Assertions.assertFalse(tmpSRUFragmenter.shouldBePreprocessed(tmpFragmentList.get(0))); - Assertions.assertTrue(tmpSRUFragmenter.canBeFragmented(tmpFragmentList.get(0))); - IAtomContainer tmpAfterPreprocessing = tmpSRUFragmenter.applyPreprocessing(tmpFragmentList.get(0)); + Assertions.assertFalse(tmpSRUFragmenter.shouldBeFiltered(tmpFragmentList.getFirst())); + Assertions.assertFalse(tmpSRUFragmenter.shouldBePreprocessed(tmpFragmentList.getFirst())); + Assertions.assertTrue(tmpSRUFragmenter.canBeFragmented(tmpFragmentList.getFirst())); + IAtomContainer tmpAfterPreprocessing = tmpSRUFragmenter.applyPreprocessing(tmpFragmentList.getFirst()); Assertions.assertTrue(tmpSRUFragmenter.canBeFragmented(tmpAfterPreprocessing)); } } diff --git a/src/test/java/de/unijena/cheminf/mortar/model/settings/SettingsContainerTest.java b/src/test/java/de/unijena/cheminf/mortar/model/settings/SettingsContainerTest.java index 8a28715f..11911a30 100644 --- a/src/test/java/de/unijena/cheminf/mortar/model/settings/SettingsContainerTest.java +++ b/src/test/java/de/unijena/cheminf/mortar/model/settings/SettingsContainerTest.java @@ -25,6 +25,9 @@ package de.unijena.cheminf.mortar.model.settings; +import de.unijena.cheminf.mortar.configuration.Configuration; +import de.unijena.cheminf.mortar.model.io.Exporter; + import javafx.beans.property.Property; import org.junit.jupiter.api.Assertions; @@ -40,6 +43,13 @@ * @version 1.0.0.0 */ public class SettingsContainerTest { + /** + * Constructor to initialize locale and configuration. + */ + public SettingsContainerTest() throws Exception { + Locale.setDefault(Locale.of("en", "GB")); + Configuration.getInstance(); + } /** * Tests the basic functionalities of SettingsContainer. These are instantiation, restoring default settings, * getting the settings, changing the settings, persisting the settings, and reloading them. @@ -48,26 +58,23 @@ public class SettingsContainerTest { */ @Test public void testSettingsContainerBasics() throws Exception { - Locale.setDefault(new Locale("en", "GB")); - String tmpCsvExportSeparatorTest = ";"; + Exporter.CSVSeparator tmpCsvExportSeparatorTest = Exporter.CSVSeparator.COMMA; //if there is a persisted settings container file already on the machine, it is loaded into the new SettingsContainer object SettingsContainer tmpSettingsContainer = new SettingsContainer(); //restoring to default because a previous settings file with altered settings may have been imported (see below) tmpSettingsContainer.restoreDefaultSettings(); - List tmpPropertiesList = tmpSettingsContainer.settingsProperties(); - System.out.println(); - for (Property tmpProp : tmpPropertiesList) { - //recent directory path setting is not included because it is an internal setting - System.out.println(tmpProp.getName() + ": " + tmpProp.getValue()); + List> tmpPropertiesList = tmpSettingsContainer.settingsProperties(); + for (Property tmpProp : tmpPropertiesList) { + Assertions.assertNotNull(tmpProp.getName()); + Assertions.assertNotNull(tmpProp.getValue()); } - System.out.println(tmpSettingsContainer.recentDirectoryPathSettingProperty().getName() + ": " - + tmpSettingsContainer.recentDirectoryPathSettingProperty().getValue()); - System.out.println(); + Assertions.assertNotNull(tmpSettingsContainer.recentDirectoryPathSettingProperty().getName()); + Assertions.assertNotNull(tmpSettingsContainer.recentDirectoryPathSettingProperty().getValue()); Assertions.assertEquals(SettingsContainer.ROWS_PER_PAGE_SETTING_DEFAULT, tmpSettingsContainer.getRowsPerPageSetting()); Assertions.assertEquals(SettingsContainer.ADD_IMPLICIT_HYDROGENS_AT_IMPORT_SETTING_DEFAULT, tmpSettingsContainer.getAddImplicitHydrogensAtImportSetting()); Assertions.assertEquals(tmpSettingsContainer.getNumberOfTasksForFragmentationSettingDefault(), tmpSettingsContainer.getNumberOfTasksForFragmentationSetting()); Assertions.assertEquals(SettingsContainer.RECENT_DIRECTORY_PATH_SETTING_DEFAULT, tmpSettingsContainer.getRecentDirectoryPathSetting()); - Assertions.assertEquals(SettingsContainer.KEEP_ATOM_CONTAINER_IN_DATA_MODEL_SETTING_DEFAULT, tmpSettingsContainer.getKeepAtomContainerInDataModelSetting()); + //Assertions.assertEquals(SettingsContainer.KEEP_ATOM_CONTAINER_IN_DATA_MODEL_SETTING_DEFAULT, tmpSettingsContainer.getKeepAtomContainerInDataModelSetting()); Assertions.assertEquals(SettingsContainer.ALWAYS_MDLV3000_FORMAT_AT_EXPORT_SETTING_DEFAULT, tmpSettingsContainer.getAlwaysMDLV3000FormatAtExportSetting()); Assertions.assertEquals(SettingsContainer.CSV_EXPORT_SEPARATOR_SETTING_DEFAULT, tmpSettingsContainer.getCsvExportSeparatorSetting()); tmpSettingsContainer.setRowsPerPageSetting(SettingsContainer.ROWS_PER_PAGE_SETTING_DEFAULT + 5); diff --git a/src/test/java/de/unijena/cheminf/mortar/model/util/CollectionUtilTest.java b/src/test/java/de/unijena/cheminf/mortar/model/util/CollectionUtilTest.java index 34988b27..2a652755 100644 --- a/src/test/java/de/unijena/cheminf/mortar/model/util/CollectionUtilTest.java +++ b/src/test/java/de/unijena/cheminf/mortar/model/util/CollectionUtilTest.java @@ -44,11 +44,6 @@ public void calculateInitialHashMapSizeTest() { for (int i = 0; i < tmpNumberOfElements.length; i++) { int tmpCalculatedInitialHashMapCapacity = CollectionUtil.calculateInitialHashCollectionCapacity(tmpNumberOfElements[i], tmpLoadFactor[i]); float tmpRehashThreshold = tmpCalculatedInitialHashMapCapacity * tmpLoadFactor[i]; - System.out.println("\nNumber of elements: " + tmpNumberOfElements[i]); - System.out.println("Load factor: " + tmpLoadFactor[i]); - System.out.println("Expected initial capacity ((int) n_elements * (1/lf) + 2): " + tmpExpectedInitialCapacity[i]); - System.out.println("Calculated initial capacity: " + tmpCalculatedInitialHashMapCapacity); - System.out.println("Rehash threshold (lf * initCap, must be higher than number of elements): " + tmpRehashThreshold); Assertions.assertTrue(tmpNumberOfElements[i] < tmpRehashThreshold); Assertions.assertEquals(tmpExpectedInitialCapacity[i], tmpCalculatedInitialHashMapCapacity); } diff --git a/src/test/java/de/unijena/cheminf/mortar/model/util/SimpleEnumConstantNamePropertyTest.java b/src/test/java/de/unijena/cheminf/mortar/model/util/SimpleEnumConstantNamePropertyTest.java index 481b4794..33047fd2 100644 --- a/src/test/java/de/unijena/cheminf/mortar/model/util/SimpleEnumConstantNamePropertyTest.java +++ b/src/test/java/de/unijena/cheminf/mortar/model/util/SimpleEnumConstantNamePropertyTest.java @@ -27,6 +27,7 @@ import de.unijena.cheminf.mortar.model.fragmentation.algorithm.IMoleculeFragmenter; +import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; /** @@ -37,10 +38,8 @@ * @version 1.0.0.0 */ public class SimpleEnumConstantNamePropertyTest { - /** - * Basic test for retrieval of associated enum, currently set option, and available options. All are printed to - * console. + * Basic test for retrieval of associated enum, currently set option, and available options. * * @throws Exception if anything goes wrong */ @@ -49,16 +48,14 @@ public void test() throws Exception { SimpleEnumConstantNameProperty tmpEnumProperty = new SimpleEnumConstantNameProperty(this, "testProp", IMoleculeFragmenter.FragmentSaturationOption.HYDROGEN_SATURATION.name(), IMoleculeFragmenter.FragmentSaturationOption.class); - System.out.println("Associated enum: " + tmpEnumProperty.getAssociatedEnum()); + Assertions.assertEquals(IMoleculeFragmenter.FragmentSaturationOption.class, tmpEnumProperty.getAssociatedEnum()); + Assertions.assertEquals("HYDROGEN_SATURATION", tmpEnumProperty.get()); + Assertions.assertEquals(IMoleculeFragmenter.FragmentSaturationOption.HYDROGEN_SATURATION, tmpEnumProperty.getEnumValue()); Enum[] tmpAvailableOptions = tmpEnumProperty.getAssociatedEnumConstants(); - System.out.println("Currently set option: " + tmpEnumProperty.get()); - tmpEnumProperty.getEnumValue(); - System.out.println("Available options: "); for (Enum tmpOption : tmpAvailableOptions) { - System.out.println("\t" + tmpOption.name()); - tmpEnumProperty.setEnumValue(tmpOption); - tmpEnumProperty.set(tmpOption.name()); + Assertions.assertDoesNotThrow(() -> {IMoleculeFragmenter.FragmentSaturationOption.valueOf(tmpOption.name());}); + Assertions.assertDoesNotThrow(() -> tmpEnumProperty.setEnumValue(tmpOption)); + Assertions.assertDoesNotThrow(() -> tmpEnumProperty.set(tmpOption.name())); } } - } diff --git a/src/test/java/de/unijena/cheminf/mortar/model/util/SimpleIDisplayEnumConstantPropertyTest.java b/src/test/java/de/unijena/cheminf/mortar/model/util/SimpleIDisplayEnumConstantPropertyTest.java new file mode 100644 index 00000000..3aebdf81 --- /dev/null +++ b/src/test/java/de/unijena/cheminf/mortar/model/util/SimpleIDisplayEnumConstantPropertyTest.java @@ -0,0 +1,70 @@ +/* + * MORTAR - MOlecule fRagmenTAtion fRamework + * Copyright (C) 2024 Felix Baensch, Jonas Schaub (felix.baensch@w-hs.de, jonas.schaub@uni-jena.de) + * + * Source code is available at + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package de.unijena.cheminf.mortar.model.util; + +import de.unijena.cheminf.mortar.message.Message; +import de.unijena.cheminf.mortar.model.fragmentation.algorithm.IMoleculeFragmenter; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.util.Locale; + +/** + * Test class for the custom-made {@link SimpleIDisplayEnumConstantProperty} JavaFx + * property wrapping an enum constant display name. + * + * @author Jonas Schaub + * @version 1.0.0.0 + */ +public class SimpleIDisplayEnumConstantPropertyTest { + /** + * Constructor setting the default locale. + * + * @throws Exception if anything goes wrong + */ + public SimpleIDisplayEnumConstantPropertyTest() throws Exception { + Locale.setDefault(Locale.of("en", "GB")); + } + /** + * Basic test for retrieval of associated enum, currently set option, and available options. + * + * @throws Exception if anything goes wrong + */ + @Test + public void test() throws Exception { + SimpleIDisplayEnumConstantProperty tmpEnumProperty = new SimpleIDisplayEnumConstantProperty(this, "testProp", + IMoleculeFragmenter.FragmentSaturationOption.HYDROGEN_SATURATION, + IMoleculeFragmenter.FragmentSaturationOption.class); + Assertions.assertEquals(IMoleculeFragmenter.FragmentSaturationOption.class, tmpEnumProperty.getAssociatedEnum()); + Assertions.assertEquals(Message.get("IMoleculeFragmenter.FragmentSaturationOption.hydrogenSaturation.displayName"), tmpEnumProperty.get().getDisplayName()); + Assertions.assertEquals(IMoleculeFragmenter.FragmentSaturationOption.HYDROGEN_SATURATION, tmpEnumProperty.get()); + IDisplayEnum[] tmpAvailableOptions = (IDisplayEnum[]) tmpEnumProperty.getAssociatedEnumConstants(); + for (IDisplayEnum tmpOption : tmpAvailableOptions) { + Assertions.assertDoesNotThrow(() -> tmpEnumProperty.set(tmpOption)); + } + } +} diff --git a/src/test/java/de/unijena/cheminf/mortar/preference/PreferenceContainerTest.java b/src/test/java/de/unijena/cheminf/mortar/preference/PreferenceContainerTest.java index 7057c602..017b57cd 100644 --- a/src/test/java/de/unijena/cheminf/mortar/preference/PreferenceContainerTest.java +++ b/src/test/java/de/unijena/cheminf/mortar/preference/PreferenceContainerTest.java @@ -25,6 +25,7 @@ package de.unijena.cheminf.mortar.preference; +import de.unijena.cheminf.mortar.configuration.Configuration; import de.unijena.cheminf.mortar.model.fragmentation.algorithm.ErtlFunctionalGroupsFinderFragmenter; import de.unijena.cheminf.mortar.model.fragmentation.algorithm.SugarRemovalUtilityFragmenter; import de.unijena.cheminf.mortar.model.settings.SettingsContainer; @@ -45,19 +46,19 @@ * @version 1.0.0.0 */ public class PreferenceContainerTest { - /** - * Constructor (empty). + * Constructor to initialize locale and configuration. */ - public PreferenceContainerTest() { - + public PreferenceContainerTest() throws Exception { + Locale.setDefault(Locale.of("en", "GB")); + Configuration.getInstance(); } // /** * Tests basic functionalities of PreferenceContainer class/objects, like preference management, management of * public properties and persistence. * - * @throws Exception + * @throws Exception if anything goes wrong */ @Test public void testPreferenceContainerBasics() throws Exception { @@ -94,13 +95,14 @@ public void testPreferenceContainerBasics() throws Exception { Assertions.assertEquals(tmpPreference3, tmpSortedNameAscending[3]); Assertions.assertEquals(tmpPreference5, tmpSortedNameAscending[4]); - System.out.println(tmpContainer.getGUID()); - System.out.println(tmpContainer.getTimeStamp()); - System.out.println(tmpContainer.getVersion()); - System.out.println(tmpContainer.toString()); + Assertions.assertDoesNotThrow(tmpContainer::getGUID); + Assertions.assertDoesNotThrow(tmpContainer::getTimeStamp); + Assertions.assertDoesNotThrow(tmpContainer::getVersion); + Assertions.assertDoesNotThrow(tmpContainer::toString); IPreference[] tmpPreferences = tmpContainer.getPreferences(); for (IPreference tmpPreference : tmpPreferences) { - System.out.println(tmpPreference.getName() + " : " + tmpPreference.getContentRepresentative()); + Assertions.assertNotNull(tmpPreference.getName()); + Assertions.assertNotNull(tmpPreference.getContentRepresentative()); } tmpContainer.writeRepresentation(); @@ -112,7 +114,6 @@ public void testPreferenceContainerBasics() throws Exception { Assertions.assertEquals(tmpContainer.getTimeStamp(), tmpReloadedContainer.getTimeStamp()); Assertions.assertEquals(tmpContainer.toString(), tmpReloadedContainer.toString()); Assertions.assertEquals(tmpContainer, tmpReloadedContainer); - System.out.println(); } // /** @@ -124,7 +125,6 @@ public void testPreferenceContainerBasics() throws Exception { */ @Test public void testPropertyToPreferenceConversion() throws Exception { - Locale.setDefault(new Locale("en", "GB")); SugarRemovalUtilityFragmenter tmpSRUFragmenter = new SugarRemovalUtilityFragmenter(); String tmpDir = FileUtil.getAppDirPath() + File.separatorChar @@ -132,11 +132,11 @@ public void testPropertyToPreferenceConversion() throws Exception { + File.separatorChar; (new File(tmpDir)).mkdirs(); String tmpFilePathname = tmpDir + "SRUFragmenterSettings.txt"; - PreferenceContainer tmpContainer = PreferenceUtil.translateJavaFxPropertiesToPreferences(tmpSRUFragmenter.settingsProperties(), tmpFilePathname); - tmpContainer.writeRepresentation(); - tmpContainer = PreferenceUtil.translateJavaFxPropertiesToPreferences(new ErtlFunctionalGroupsFinderFragmenter().settingsProperties(), tmpDir + "EFGFFragmenterSettings.txt"); - tmpContainer.writeRepresentation(); - tmpContainer = PreferenceUtil.translateJavaFxPropertiesToPreferences(new SettingsContainer().settingsProperties(), tmpDir + "SettingContainer.txt"); - tmpContainer.writeRepresentation(); + final PreferenceContainer tmpContainer = PreferenceUtil.translateJavaFxPropertiesToPreferences(tmpSRUFragmenter.settingsProperties(), tmpFilePathname); + Assertions.assertDoesNotThrow(tmpContainer::writeRepresentation); + final PreferenceContainer tmpNewContainer = PreferenceUtil.translateJavaFxPropertiesToPreferences(new ErtlFunctionalGroupsFinderFragmenter().settingsProperties(), tmpDir + "EFGFFragmenterSettings.txt"); + Assertions.assertDoesNotThrow(tmpNewContainer::writeRepresentation); + final PreferenceContainer tmpNewNewContainer = PreferenceUtil.translateJavaFxPropertiesToPreferences(new SettingsContainer().settingsProperties(), tmpDir + "SettingContainer.txt"); + Assertions.assertDoesNotThrow(tmpNewNewContainer::writeRepresentation); } } diff --git a/src/test/java/de/unijena/cheminf/mortar/preference/PreferenceTest.java b/src/test/java/de/unijena/cheminf/mortar/preference/PreferenceTest.java index 7d089a74..c7c11457 100644 --- a/src/test/java/de/unijena/cheminf/mortar/preference/PreferenceTest.java +++ b/src/test/java/de/unijena/cheminf/mortar/preference/PreferenceTest.java @@ -25,12 +25,7 @@ package de.unijena.cheminf.mortar.preference; -/** - * TODO: - * - implement comparing test (?) - * - implement preference name test (?) - */ - +import de.unijena.cheminf.mortar.configuration.Configuration; import de.unijena.cheminf.mortar.model.util.FileUtil; import org.junit.jupiter.api.Assertions; @@ -41,6 +36,7 @@ import java.io.File; import java.io.FileReader; import java.io.PrintWriter; +import java.util.Locale; /** * Test class for preferences. @@ -49,17 +45,18 @@ * @version 1.0.0.0 */ public class PreferenceTest { - /** - * Constructor (empty) + * Constructor to initialize locale and configuration. */ - public PreferenceTest() { + public PreferenceTest() throws Exception { + Locale.setDefault(Locale.of("en", "GB")); + Configuration.getInstance(); } // /** * Tests basic functionalities of class BooleanPreference. * - * @throws Exception + * @throws Exception if anything goes wrong */ @Test public void testBooleanPreference() throws Exception { @@ -72,7 +69,7 @@ public void testBooleanPreference() throws Exception { /** * Tests basic functionalities of class RGBColorPreference. * - * @throws Exception + * @throws Exception if anything goes wrong */ @Test public void testColorPreference() throws Exception { @@ -94,7 +91,7 @@ public void testColorPreference() throws Exception { /** * Tests basic functionalities of class SingleIntegerPreference. * - * @throws Exception + * @throws Exception if anything goes wrong */ @Test public void testSingleIntegerPreference() throws Exception { @@ -105,7 +102,7 @@ public void testSingleIntegerPreference() throws Exception { /** * Tests basic functionalities of class SingleNumberPreference. * - * @throws Exception + * @throws Exception if anything goes wrong */ @Test public void testSingleNumberPreference() throws Exception { @@ -116,7 +113,7 @@ public void testSingleNumberPreference() throws Exception { /** * Tests basic functionalities of class SingleTermPreference. * - * @throws Exception + * @throws Exception if anything goes wrong */ @Test public void testSingleTermPreference() throws Exception { @@ -128,12 +125,11 @@ public void testSingleTermPreference() throws Exception { * Tests basic functionalities of given preference object, like management of public properties and persistence. */ private void testPreferenceBasics(IPreference aPreference) throws Exception { - System.out.println(); - System.out.println(aPreference.getType()); - System.out.println(aPreference.getContentRepresentative()); - System.out.println(aPreference.getGUID()); - System.out.println(aPreference.getName()); - System.out.println(aPreference.toString()); + Assertions.assertDoesNotThrow(aPreference::getType); + Assertions.assertDoesNotThrow(aPreference::getContentRepresentative); + Assertions.assertDoesNotThrow(aPreference::getGUID); + Assertions.assertDoesNotThrow(aPreference::getName); + Assertions.assertDoesNotThrow(aPreference::toString); String tmpDir = FileUtil.getAppDirPath() + File.separatorChar + "Test"; (new File(tmpDir)).mkdirs(); @@ -146,7 +142,7 @@ private void testPreferenceBasics(IPreference aPreference) throws Exception { IPreference tmpPreference = PreferenceFactory.reinitializePreference(tmpReader.readLine(), tmpReader); tmpWriter.close(); tmpReader.close(); - Assertions.assertTrue(aPreference.getContentRepresentative().equals(tmpPreference.getContentRepresentative())); + Assertions.assertEquals(aPreference.getContentRepresentative(), tmpPreference.getContentRepresentative()); Assertions.assertEquals(aPreference.getName(), tmpPreference.getName()); Assertions.assertEquals(aPreference.getGUID(), tmpPreference.getGUID()); Assertions.assertEquals(aPreference.toString(), tmpPreference.toString());