From c66f36c77a3f28aa129195668b00e099628461f2 Mon Sep 17 00:00:00 2001 From: Danila Fedorin Date: Fri, 28 Jul 2017 22:17:22 -0700 Subject: [PATCH] Allow plugins to register number implementations and use user's choice. --- src/org/nwapw/abacus/Abacus.java | 23 ++++++++- .../abacus/config/ConfigurationObject.java | 50 ++++--------------- src/org/nwapw/abacus/plugin/Plugin.java | 35 +++++++++++++ .../nwapw/abacus/plugin/PluginManager.java | 33 +++++++++++- .../nwapw/abacus/plugin/StandardPlugin.java | 4 ++ src/org/nwapw/abacus/tree/TreeBuilder.java | 13 +++-- 6 files changed, 110 insertions(+), 48 deletions(-) diff --git a/src/org/nwapw/abacus/Abacus.java b/src/org/nwapw/abacus/Abacus.java index be3b378..eacc0d8 100644 --- a/src/org/nwapw/abacus/Abacus.java +++ b/src/org/nwapw/abacus/Abacus.java @@ -2,6 +2,7 @@ package org.nwapw.abacus; import org.nwapw.abacus.config.ConfigurationObject; import org.nwapw.abacus.function.Operator; +import org.nwapw.abacus.number.NaiveNumber; import org.nwapw.abacus.number.NumberInterface; import org.nwapw.abacus.plugin.ClassFinder; import org.nwapw.abacus.plugin.PluginListener; @@ -15,6 +16,7 @@ import org.nwapw.abacus.window.Window; import javax.swing.*; import java.io.File; import java.io.IOException; +import java.lang.reflect.InvocationTargetException; /** * The main calculator class. This is responsible @@ -23,10 +25,14 @@ import java.io.IOException; */ public class Abacus implements PluginListener { + /** + * The default implementation to use for the number representation. + */ + public static final Class DEFAULT_NUMBER = NaiveNumber.class; /** * The file used for saving and loading configuration. */ - public static final File CONFIG_FILE = new File("config.yml"); + public static final File CONFIG_FILE = new File("config.toml"); /** * The main Abacus UI. @@ -137,9 +143,22 @@ public class Abacus implements PluginListener { return tree.reduce(numberReducer); } + public NumberInterface numberFromString(String numberString){ + Class toInstantiate = + pluginManager.numberFor(configuration.getNumberImplementation()); + if(toInstantiate == null) toInstantiate = DEFAULT_NUMBER; + + try { + return toInstantiate.getConstructor(String.class).newInstance(numberString); + } catch (InstantiationException | IllegalAccessException | NoSuchMethodException | InvocationTargetException e) { + e.printStackTrace(); + } + return null; + } + @Override public void onLoad(PluginManager manager) { - treeBuilder = new TreeBuilder(); + treeBuilder = new TreeBuilder(this); for(String function : manager.getAllFunctions()){ treeBuilder.registerFunction(function); } diff --git a/src/org/nwapw/abacus/config/ConfigurationObject.java b/src/org/nwapw/abacus/config/ConfigurationObject.java index c8147a4..89f1e2e 100644 --- a/src/org/nwapw/abacus/config/ConfigurationObject.java +++ b/src/org/nwapw/abacus/config/ConfigurationObject.java @@ -2,13 +2,9 @@ package org.nwapw.abacus.config; import com.moandjiezana.toml.Toml; import com.moandjiezana.toml.TomlWriter; -import org.nwapw.abacus.number.NaiveNumber; import java.io.File; import java.io.IOException; -import java.lang.reflect.InvocationTargetException; -import java.util.HashMap; -import java.util.Map; /** * A configuration object, which essentially @@ -18,10 +14,6 @@ import java.util.Map; */ public class ConfigurationObject { - /** - * The default implementation to use for instantiating numbers. - */ - private static final Class DEFAULT_IMPLEMENTATION = NaiveNumber.class; /** * The writer used to store the configuration. */ @@ -30,11 +22,6 @@ public class ConfigurationObject { * The configuration instance being modeled. */ private Configuration configuration; - /** - * A map of number names to their implementations, which - * will be provided by plugins. - */ - private Map> numberImplementations; /** * Sets up the ConfigurationObject. @@ -44,7 +31,6 @@ public class ConfigurationObject { */ private void setup(Configuration configuration){ this.configuration = configuration; - numberImplementations = new HashMap<>(); } /** @@ -58,33 +44,6 @@ public class ConfigurationObject { return configuration; } - /** - * Register a number implementation. - * @param name the name of the number implementation to register the class as. - * @param newClass the class that will be used to instantiate the new number. - * It is required that this class provides a Number(String) constructor. - */ - public void registerImplementation(String name, Class newClass){ - numberImplementations.put(name, newClass); - } - - /** - * Creates a new number with the configured type, passing - * it the given string. - * @param string the string from which the number should be parsed. - * @return the resulting number, or null if an error occurred. - */ - public NaiveNumber numberFromString(String string) { - Class toLoad = - numberImplementations.getOrDefault(configuration.numberType, DEFAULT_IMPLEMENTATION); - try { - return toLoad.getConstructor(String.class).newInstance(string); - } catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) { - e.printStackTrace(); - } - return null; - } - /** * Returns the configured, user-requested precision. * @return the precision. @@ -93,6 +52,15 @@ public class ConfigurationObject { return configuration.decimalPrecision; } + /** + * Returns the implementation the user has requested to + * represent their numbers. + * @return the implementation name. + */ + public String getNumberImplementation() { + return configuration.numberType; + } + /** * Saves the ConfigurationObject to the given file. * @param toFile the file to save ot. diff --git a/src/org/nwapw/abacus/plugin/Plugin.java b/src/org/nwapw/abacus/plugin/Plugin.java index 1f57b1d..2bf4a2d 100644 --- a/src/org/nwapw/abacus/plugin/Plugin.java +++ b/src/org/nwapw/abacus/plugin/Plugin.java @@ -2,6 +2,7 @@ package org.nwapw.abacus.plugin; import org.nwapw.abacus.function.Function; import org.nwapw.abacus.function.Operator; +import org.nwapw.abacus.number.NumberInterface; import java.util.HashMap; import java.util.Map; @@ -24,6 +25,10 @@ public abstract class Plugin { * A hash map of operators mapped to their string names. */ private Map operators; + /** + * A hash map of operators mapped to their string names. + */ + private Map> numbers; /** * The plugin manager in which to search for functions * not inside this package, @@ -44,6 +49,7 @@ public abstract class Plugin { this.manager = manager; functions = new HashMap<>(); operators = new HashMap<>(); + numbers = new HashMap<>(); enabled = false; } @@ -63,6 +69,14 @@ public abstract class Plugin { return operators.keySet(); } + /** + * Gets the list of all numbers provided by this plugin. + * @return the list of registered numbers. + */ + public final Set providedNumbers(){ + return numbers.keySet(); + } + /** * Gets a function under the given function name. * @param functionName the name of the function to get @@ -81,6 +95,15 @@ public abstract class Plugin { return operators.get(operatorName); } + /** + * Gets the class under the given name. + * @param numberName the name of the class. + * @return the class, or null if the plugin doesn't provide it. + */ + public final Class getNumber(String numberName){ + return numbers.get(numberName); + } + /** * Enables the function, loading the necessary instances * of functions. @@ -124,6 +147,18 @@ public abstract class Plugin { operators.put(name, operator); } + /** + * To be used in load(). Registers a number class + * with the plugin internally, which makes it possible + * for the user to select it as an "implementation" for the + * number that they would like to use. + * @param name the name to register it under. + * @param toRegister the class to register. + */ + protected final void registerNumber(String name, Class toRegister){ + numbers.put(name, toRegister); + } + /** * Searches the PluginManager for the given function name. * This can be used by the plugins internally in order to call functions diff --git a/src/org/nwapw/abacus/plugin/PluginManager.java b/src/org/nwapw/abacus/plugin/PluginManager.java index 25e3aac..33af14a 100644 --- a/src/org/nwapw/abacus/plugin/PluginManager.java +++ b/src/org/nwapw/abacus/plugin/PluginManager.java @@ -3,6 +3,7 @@ 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; import java.lang.reflect.InvocationTargetException; import java.util.*; @@ -27,10 +28,15 @@ public class PluginManager { */ private Map cachedFunctions; /** - * List of operators tha have been cached, + * List of operators that have been cached, * that is, found in a plugin and returned. */ private Map cachedOperators; + /** + * List of registered number implementations that have + * been cached, that is, found in a plugin and returned. + */ + private Map> cachedNumbers; /** * List of all functions loaded by the plugins. */ @@ -39,6 +45,10 @@ public class PluginManager { * List of all operators loaded by the plugins. */ private Set allOperators; + /** + * List of all numbers loaded by the plugins. + */ + private Set allNumbers; /** * The list of plugin listeners attached to this instance. */ @@ -58,8 +68,10 @@ public class PluginManager { plugins = new HashSet<>(); cachedFunctions = new HashMap<>(); cachedOperators = new HashMap<>(); + cachedNumbers = new HashMap<>(); allFunctions = new HashSet<>(); allOperators = new HashSet<>(); + allNumbers = new HashSet<>(); listeners = new HashSet<>(); } @@ -111,6 +123,15 @@ public class PluginManager { return searchCached(plugins, cachedOperators, Plugin::providedOperators, Plugin::getOperator, name); } + /** + * Gets a numer implementation under the given name. + * @param name the name of the implementation. + * @return the implementation class + */ + public Class numberFor(String name){ + return searchCached(plugins, cachedNumbers, Plugin::providedNumbers, Plugin::getNumber, name); + } + /** * Adds an instance of Plugin that already has been instantiated. * @param plugin the plugin to add. @@ -143,6 +164,7 @@ public class PluginManager { for(Plugin plugin : plugins){ allFunctions.addAll(plugin.providedFunctions()); allOperators.addAll(plugin.providedOperators()); + allNumbers.addAll(plugin.providedNumbers()); } listeners.forEach(e -> e.onLoad(this)); } @@ -154,6 +176,7 @@ public class PluginManager { for(Plugin plugin : plugins) plugin.disable(); allFunctions.clear(); allOperators.clear(); + allNumbers.clear(); listeners.forEach(e -> e.onUnload(this)); } @@ -181,6 +204,14 @@ public class PluginManager { return allOperators; } + /** + * Gets all the number implementations loaded by the Plugin Manager + * @return the set of all implementations that were loaded + */ + public Set getAllNumbers() { + return allNumbers; + } + /** * Adds a plugin change listener to this plugin manager. * @param listener the listener to add. diff --git a/src/org/nwapw/abacus/plugin/StandardPlugin.java b/src/org/nwapw/abacus/plugin/StandardPlugin.java index 088c877..a0f234c 100755 --- a/src/org/nwapw/abacus/plugin/StandardPlugin.java +++ b/src/org/nwapw/abacus/plugin/StandardPlugin.java @@ -6,6 +6,7 @@ import org.nwapw.abacus.function.OperatorAssociativity; import org.nwapw.abacus.function.OperatorType; import org.nwapw.abacus.number.NaiveNumber; import org.nwapw.abacus.number.NumberInterface; +import org.nwapw.abacus.number.PreciseNumber; import java.util.function.BiFunction; @@ -21,6 +22,9 @@ public class StandardPlugin extends Plugin { @Override public void onEnable() { + registerNumber("naive", NaiveNumber.class); + registerNumber("precise", PreciseNumber.class); + registerOperator("+", new Operator(OperatorAssociativity.LEFT, OperatorType.BINARY_INFIX, 0, new Function() { @Override protected boolean matchesParams(NumberInterface[] params) { diff --git a/src/org/nwapw/abacus/tree/TreeBuilder.java b/src/org/nwapw/abacus/tree/TreeBuilder.java index 83f2ebd..97800e4 100644 --- a/src/org/nwapw/abacus/tree/TreeBuilder.java +++ b/src/org/nwapw/abacus/tree/TreeBuilder.java @@ -1,12 +1,11 @@ package org.nwapw.abacus.tree; +import org.nwapw.abacus.Abacus; import org.nwapw.abacus.function.OperatorAssociativity; import org.nwapw.abacus.function.OperatorType; import org.nwapw.abacus.lexing.Lexer; import org.nwapw.abacus.lexing.pattern.Match; import org.nwapw.abacus.lexing.pattern.Pattern; -import org.nwapw.abacus.number.NaiveNumber; -import org.nwapw.abacus.number.PreciseNumber; import java.util.*; @@ -31,6 +30,11 @@ public class TreeBuilder { * The map of operator types. */ private Map typeMap; + /** + * The abacus instance required to interact with + * other components of the calculator. + */ + private Abacus abacus; /** * Comparator used to sort token types. @@ -40,7 +44,8 @@ public class TreeBuilder { /** * Creates a new TreeBuilder. */ - public TreeBuilder(){ + public TreeBuilder(Abacus abacus){ + this.abacus = abacus; lexer = new Lexer(){{ register(" ", TokenType.WHITESPACE); register(",", TokenType.COMMA); @@ -172,7 +177,7 @@ public class TreeBuilder { else return new UnaryPrefixNode(operator, applyTo); } } else if(matchType == TokenType.NUM){ - return new NumberNode(new NaiveNumber(Double.parseDouble(source.substring(match.getFrom(), match.getTo())))); + return new NumberNode(abacus.numberFromString(source.substring(match.getFrom(), match.getTo()))); } else if(matchType == TokenType.FUNCTION){ String functionName = source.substring(match.getFrom(), match.getTo()); FunctionNode node = new FunctionNode(functionName);