mirror of
				https://github.com/DanilaFe/abacus
				synced 2025-11-03 10:23:41 -08:00 
			
		
		
		
	Merge branch 'plugin-list'
This commit is contained in:
		
						commit
						20f6e0b0b2
					
				@ -57,7 +57,7 @@ public class Abacus {
 | 
			
		||||
     * Creates a new instance of the Abacus calculator.
 | 
			
		||||
     */
 | 
			
		||||
    public Abacus() {
 | 
			
		||||
        pluginManager = new PluginManager();
 | 
			
		||||
        pluginManager = new PluginManager(this);
 | 
			
		||||
        numberReducer = new NumberReducer(this);
 | 
			
		||||
        configuration = new Configuration(CONFIG_FILE);
 | 
			
		||||
        configuration.saveTo(CONFIG_FILE);
 | 
			
		||||
 | 
			
		||||
@ -5,6 +5,9 @@ import com.moandjiezana.toml.TomlWriter;
 | 
			
		||||
 | 
			
		||||
import java.io.File;
 | 
			
		||||
import java.io.IOException;
 | 
			
		||||
import java.util.Arrays;
 | 
			
		||||
import java.util.HashSet;
 | 
			
		||||
import java.util.Set;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * The configuration object that stores
 | 
			
		||||
@ -12,26 +15,38 @@ import java.io.IOException;
 | 
			
		||||
 */
 | 
			
		||||
public class Configuration {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * The defaults TOML string.
 | 
			
		||||
     */
 | 
			
		||||
    private static final String DEFAULT_CONFIG =
 | 
			
		||||
            "numberImplementation = \"naive\"\n" +
 | 
			
		||||
            "disabledPlugins = []";
 | 
			
		||||
    /**
 | 
			
		||||
     * The defaults TOML object, parsed from the string.
 | 
			
		||||
     */
 | 
			
		||||
    private static final Toml DEFAULT_TOML = new Toml().read(DEFAULT_CONFIG);
 | 
			
		||||
    /**
 | 
			
		||||
     * The TOML writer used to write this configuration to a file.
 | 
			
		||||
     */
 | 
			
		||||
    private static final TomlWriter TOML_WRITER = new TomlWriter();
 | 
			
		||||
    /**
 | 
			
		||||
     * The TOML reader used to load this config from a file.
 | 
			
		||||
     */
 | 
			
		||||
    private static final Toml TOML_READER = new Toml();
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * The implementation of the number that should be used.
 | 
			
		||||
     */
 | 
			
		||||
    private String numberImplementation = "naive";
 | 
			
		||||
    private String numberImplementation = "<default>";
 | 
			
		||||
    /**
 | 
			
		||||
     * The list of disabled plugins in this Configuration.
 | 
			
		||||
     */
 | 
			
		||||
    private Set<String> disabledPlugins = new HashSet<>();
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Creates a new configuration with the given values.
 | 
			
		||||
     * @param numberImplementation the number implementation, like "naive" or "precise"
 | 
			
		||||
     * @param disabledPlugins the list of disabled plugins.
 | 
			
		||||
     */
 | 
			
		||||
    public Configuration(String numberImplementation){
 | 
			
		||||
    public Configuration(String numberImplementation, String[] disabledPlugins){
 | 
			
		||||
        this.numberImplementation = numberImplementation;
 | 
			
		||||
        this.disabledPlugins.addAll(Arrays.asList(disabledPlugins));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@ -40,7 +55,7 @@ public class Configuration {
 | 
			
		||||
     */
 | 
			
		||||
    public Configuration(File fromFile){
 | 
			
		||||
        if(!fromFile.exists()) return;
 | 
			
		||||
        copyFrom(TOML_READER.read(fromFile).to(Configuration.class));
 | 
			
		||||
        copyFrom(new Toml(DEFAULT_TOML).read(fromFile).to(Configuration.class));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@ -49,6 +64,7 @@ public class Configuration {
 | 
			
		||||
     */
 | 
			
		||||
    public void copyFrom(Configuration otherConfiguration){
 | 
			
		||||
        this.numberImplementation = otherConfiguration.numberImplementation;
 | 
			
		||||
        this.disabledPlugins.addAll(otherConfiguration.disabledPlugins);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@ -80,4 +96,13 @@ public class Configuration {
 | 
			
		||||
    public void setNumberImplementation(String numberImplementation) {
 | 
			
		||||
        this.numberImplementation = numberImplementation;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Gets the list of disabled plugins.
 | 
			
		||||
     * @return the list of disabled plugins.
 | 
			
		||||
     */
 | 
			
		||||
    public Set<String> getDisabledPlugins() {
 | 
			
		||||
        return disabledPlugins;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -4,18 +4,25 @@ import javafx.collections.FXCollections;
 | 
			
		||||
import javafx.collections.ObservableList;
 | 
			
		||||
import javafx.fxml.FXML;
 | 
			
		||||
import javafx.scene.control.*;
 | 
			
		||||
import javafx.scene.control.cell.CheckBoxListCell;
 | 
			
		||||
import javafx.scene.text.Text;
 | 
			
		||||
import javafx.util.Callback;
 | 
			
		||||
import javafx.util.StringConverter;
 | 
			
		||||
import org.nwapw.abacus.Abacus;
 | 
			
		||||
import org.nwapw.abacus.config.Configuration;
 | 
			
		||||
import org.nwapw.abacus.number.NumberInterface;
 | 
			
		||||
import org.nwapw.abacus.plugin.PluginListener;
 | 
			
		||||
import org.nwapw.abacus.plugin.PluginManager;
 | 
			
		||||
import org.nwapw.abacus.tree.TreeNode;
 | 
			
		||||
 | 
			
		||||
import java.util.Set;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * The controller for the abacus FX UI, responsible
 | 
			
		||||
 * for all the user interaction.
 | 
			
		||||
 */
 | 
			
		||||
public class AbacusController {
 | 
			
		||||
public class AbacusController implements PluginListener {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Constant string that is displayed if the text could not be lexed or parsed.
 | 
			
		||||
@ -42,6 +49,8 @@ public class AbacusController {
 | 
			
		||||
    private Button inputButton;
 | 
			
		||||
    @FXML
 | 
			
		||||
    private ComboBox<String> numberImplementationBox;
 | 
			
		||||
    @FXML
 | 
			
		||||
    private ListView<ToggleablePlugin> enabledPluginView;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * The list of history entries, created by the users.
 | 
			
		||||
@ -54,23 +63,39 @@ public class AbacusController {
 | 
			
		||||
     */
 | 
			
		||||
    private ObservableList<String> numberImplementationOptions;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * The list of plugin objects that can be toggled on and off,
 | 
			
		||||
     * and, when reloaded, get added to the plugin manager's black list.
 | 
			
		||||
     */
 | 
			
		||||
    private ObservableList<ToggleablePlugin> enabledPlugins;
 | 
			
		||||
 | 
			
		||||
    private Abacus abacus;
 | 
			
		||||
 | 
			
		||||
    @FXML
 | 
			
		||||
    public void initialize(){
 | 
			
		||||
        Callback<TableColumn<HistoryModel, String>, TableCell<HistoryModel, String>> cellFactory =
 | 
			
		||||
                param -> new CopyableCell<>();
 | 
			
		||||
        Callback<ListView<ToggleablePlugin>, ListCell<ToggleablePlugin>> pluginCellFactory =
 | 
			
		||||
                param -> new CheckBoxListCell<>(ToggleablePlugin::enabledProperty, new StringConverter<ToggleablePlugin>() {
 | 
			
		||||
                    @Override
 | 
			
		||||
                    public String toString(ToggleablePlugin object) {
 | 
			
		||||
                        return object.getClassName().substring(object.getClassName().lastIndexOf('.') + 1);
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    @Override
 | 
			
		||||
                    public ToggleablePlugin fromString(String string) {
 | 
			
		||||
                        return new ToggleablePlugin(true, string);
 | 
			
		||||
                    }
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
        historyData = FXCollections.observableArrayList();
 | 
			
		||||
        historyTable.setItems(historyData);
 | 
			
		||||
        numberImplementationOptions = FXCollections.observableArrayList();
 | 
			
		||||
        numberImplementationBox.setItems(numberImplementationOptions);
 | 
			
		||||
        numberImplementationBox.valueProperty().addListener((observable, oldValue, newValue)
 | 
			
		||||
                -> {
 | 
			
		||||
            abacus.getConfiguration().setNumberImplementation(newValue);
 | 
			
		||||
            abacus.getConfiguration().saveTo(Abacus.CONFIG_FILE);
 | 
			
		||||
        });
 | 
			
		||||
        historyTable.getSelectionModel().setCellSelectionEnabled(true);
 | 
			
		||||
        enabledPlugins = FXCollections.observableArrayList();
 | 
			
		||||
        enabledPluginView.setItems(enabledPlugins);
 | 
			
		||||
        enabledPluginView.setCellFactory(pluginCellFactory);
 | 
			
		||||
        inputColumn.setCellFactory(cellFactory);
 | 
			
		||||
        inputColumn.setCellValueFactory(cell -> cell.getValue().inputProperty());
 | 
			
		||||
        parsedColumn.setCellFactory(cellFactory);
 | 
			
		||||
@ -79,10 +104,8 @@ public class AbacusController {
 | 
			
		||||
        outputColumn.setCellValueFactory(cell -> cell.getValue().outputProperty());
 | 
			
		||||
 | 
			
		||||
        abacus = new Abacus();
 | 
			
		||||
        numberImplementationOptions.addAll(abacus.getPluginManager().getAllNumbers());
 | 
			
		||||
        String actualImplementation = abacus.getConfiguration().getNumberImplementation();
 | 
			
		||||
        String toSelect = (numberImplementationOptions.contains(actualImplementation)) ? actualImplementation : "naive";
 | 
			
		||||
        numberImplementationBox.getSelectionModel().select(toSelect);
 | 
			
		||||
        abacus.getPluginManager().addListener(this);
 | 
			
		||||
        abacus.getPluginManager().reload();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @FXML
 | 
			
		||||
@ -107,4 +130,41 @@ public class AbacusController {
 | 
			
		||||
        inputField.setText("");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @FXML
 | 
			
		||||
    private void performReload(){
 | 
			
		||||
        Configuration configuration = abacus.getConfiguration();
 | 
			
		||||
        Set<String> disabledPlugins = configuration.getDisabledPlugins();
 | 
			
		||||
        disabledPlugins.clear();
 | 
			
		||||
        for(ToggleablePlugin pluginEntry : enabledPlugins){
 | 
			
		||||
            if(!pluginEntry.isEnabled()) disabledPlugins.add(pluginEntry.getClassName());
 | 
			
		||||
        }
 | 
			
		||||
        abacus.getPluginManager().reload();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @FXML
 | 
			
		||||
    private void performSave(){
 | 
			
		||||
        Configuration configuration = abacus.getConfiguration();
 | 
			
		||||
        configuration.setNumberImplementation(numberImplementationBox.getSelectionModel().getSelectedItem());
 | 
			
		||||
        configuration.saveTo(Abacus.CONFIG_FILE);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void onLoad(PluginManager manager) {
 | 
			
		||||
        Configuration configuration = abacus.getConfiguration();
 | 
			
		||||
        Set<String> disabledPlugins = configuration.getDisabledPlugins();
 | 
			
		||||
        numberImplementationOptions.addAll(abacus.getPluginManager().getAllNumbers());
 | 
			
		||||
        String actualImplementation = configuration.getNumberImplementation();
 | 
			
		||||
        String toSelect = (numberImplementationOptions.contains(actualImplementation)) ? actualImplementation : "<default>";
 | 
			
		||||
        numberImplementationBox.getSelectionModel().select(toSelect);
 | 
			
		||||
        for(Class<?> pluginClass : abacus.getPluginManager().getLoadedPluginClasses()){
 | 
			
		||||
            String fullName = pluginClass.getName();
 | 
			
		||||
            enabledPlugins.add(new ToggleablePlugin(!disabledPlugins.contains(fullName), fullName));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void onUnload(PluginManager manager) {
 | 
			
		||||
        enabledPlugins.clear();
 | 
			
		||||
        numberImplementationOptions.clear();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										29
									
								
								src/main/java/org/nwapw/abacus/fx/ToggleablePlugin.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								src/main/java/org/nwapw/abacus/fx/ToggleablePlugin.java
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,29 @@
 | 
			
		||||
package org.nwapw.abacus.fx;
 | 
			
		||||
 | 
			
		||||
import javafx.beans.property.BooleanProperty;
 | 
			
		||||
import javafx.beans.property.SimpleBooleanProperty;
 | 
			
		||||
 | 
			
		||||
public class ToggleablePlugin {
 | 
			
		||||
 | 
			
		||||
    private final BooleanProperty enabled;
 | 
			
		||||
    private final String className;
 | 
			
		||||
 | 
			
		||||
    public ToggleablePlugin(boolean enabled, String className){
 | 
			
		||||
        this.enabled = new SimpleBooleanProperty();
 | 
			
		||||
        this.enabled.setValue(enabled);
 | 
			
		||||
        this.className = className;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public BooleanProperty enabledProperty() {
 | 
			
		||||
        return enabled;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public boolean isEnabled() {
 | 
			
		||||
        return enabled.get();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public String getClassName() {
 | 
			
		||||
        return className;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -1,5 +1,6 @@
 | 
			
		||||
package org.nwapw.abacus.plugin;
 | 
			
		||||
 | 
			
		||||
import org.nwapw.abacus.Abacus;
 | 
			
		||||
import org.nwapw.abacus.function.Function;
 | 
			
		||||
import org.nwapw.abacus.function.Operator;
 | 
			
		||||
import org.nwapw.abacus.number.NumberInterface;
 | 
			
		||||
@ -52,11 +53,17 @@ public class PluginManager {
 | 
			
		||||
     * The list of plugin listeners attached to this instance.
 | 
			
		||||
     */
 | 
			
		||||
    private Set<PluginListener> listeners;
 | 
			
		||||
    /**
 | 
			
		||||
     * The abacus instance used to access other
 | 
			
		||||
     * components of the application.
 | 
			
		||||
     */
 | 
			
		||||
    private Abacus abacus;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Creates a new plugin manager.
 | 
			
		||||
     */
 | 
			
		||||
    public PluginManager() {
 | 
			
		||||
    public PluginManager(Abacus abacus) {
 | 
			
		||||
        this.abacus = abacus;
 | 
			
		||||
        loadedPluginClasses = new HashSet<>();
 | 
			
		||||
        plugins = new HashSet<>();
 | 
			
		||||
        cachedFunctions = new HashMap<>();
 | 
			
		||||
@ -160,8 +167,13 @@ public class PluginManager {
 | 
			
		||||
     * Loads all the plugins in the PluginManager.
 | 
			
		||||
     */
 | 
			
		||||
    public void load() {
 | 
			
		||||
        for (Plugin plugin : plugins) plugin.enable();
 | 
			
		||||
        Set<String> disabledPlugins = abacus.getConfiguration().getDisabledPlugins();
 | 
			
		||||
        for (Plugin plugin : plugins) {
 | 
			
		||||
            if(disabledPlugins.contains(plugin.getClass().getName())) continue;
 | 
			
		||||
            plugin.enable();
 | 
			
		||||
        }
 | 
			
		||||
        for (Plugin plugin : plugins) {
 | 
			
		||||
            if(disabledPlugins.contains(plugin.getClass().getName())) continue;
 | 
			
		||||
            allFunctions.addAll(plugin.providedFunctions());
 | 
			
		||||
            allOperators.addAll(plugin.providedOperators());
 | 
			
		||||
            allNumbers.addAll(plugin.providedNumbers());
 | 
			
		||||
@ -173,11 +185,18 @@ public class PluginManager {
 | 
			
		||||
     * Unloads all the plugins in the PluginManager.
 | 
			
		||||
     */
 | 
			
		||||
    public void unload() {
 | 
			
		||||
        for (Plugin plugin : plugins) plugin.disable();
 | 
			
		||||
        listeners.forEach(e -> e.onUnload(this));
 | 
			
		||||
        Set<String> disabledPlugins = abacus.getConfiguration().getDisabledPlugins();
 | 
			
		||||
        for (Plugin plugin : plugins) {
 | 
			
		||||
            if(disabledPlugins.contains(plugin.getClass().getName())) continue;
 | 
			
		||||
            plugin.disable();
 | 
			
		||||
        }
 | 
			
		||||
        cachedFunctions.clear();
 | 
			
		||||
        cachedOperators.clear();
 | 
			
		||||
        cachedNumbers.clear();
 | 
			
		||||
        allFunctions.clear();
 | 
			
		||||
        allOperators.clear();
 | 
			
		||||
        allNumbers.clear();
 | 
			
		||||
        listeners.forEach(e -> e.onUnload(this));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@ -185,7 +204,7 @@ public class PluginManager {
 | 
			
		||||
     */
 | 
			
		||||
    public void reload() {
 | 
			
		||||
        unload();
 | 
			
		||||
        reload();
 | 
			
		||||
        load();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@ -233,4 +252,12 @@ public class PluginManager {
 | 
			
		||||
        listeners.remove(listener);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Gets a list of all the plugin class files that have been
 | 
			
		||||
     * added to the plugin manager.
 | 
			
		||||
     * @return the list of all the added plugin classes.
 | 
			
		||||
     */
 | 
			
		||||
    public Set<Class<?>> getLoadedPluginClasses() {
 | 
			
		||||
        return loadedPluginClasses;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -46,6 +46,13 @@
 | 
			
		||||
                    <padding><Insets left="10" right="10" top="10" bottom="10"/></padding>
 | 
			
		||||
                    <Label text="Number Implementation" GridPane.columnIndex="0" GridPane.rowIndex="0"/>
 | 
			
		||||
                    <ComboBox fx:id="numberImplementationBox" GridPane.columnIndex="1" GridPane.rowIndex="0"/>
 | 
			
		||||
                    <ListView fx:id="enabledPluginView"
 | 
			
		||||
                              GridPane.rowIndex="1" GridPane.columnIndex="0"
 | 
			
		||||
                              GridPane.columnSpan="2" maxHeight="100"/>
 | 
			
		||||
                    <HBox GridPane.columnIndex="0" GridPane.columnSpan="2" GridPane.rowIndex="2" spacing="10">
 | 
			
		||||
                        <Button text="Apply" onAction="#performSave"/>
 | 
			
		||||
                        <Button text="Reload" onAction="#performReload"/>
 | 
			
		||||
                    </HBox>
 | 
			
		||||
                </GridPane>
 | 
			
		||||
            </Tab>
 | 
			
		||||
        </TabPane>
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user