From a0bc85a8992782d59d4e0556efd025db048b9fc2 Mon Sep 17 00:00:00 2001 From: Danila Fedorin Date: Fri, 28 Jul 2017 20:03:50 -0700 Subject: [PATCH 1/6] Create a ConfigurationObject class. --- build.gradle | 1 + .../nwapw/abacus/config/Configuration.java | 18 +++ .../abacus/config/ConfigurationObject.java | 146 ++++++++++++++++++ 3 files changed, 165 insertions(+) create mode 100644 src/org/nwapw/abacus/config/Configuration.java create mode 100644 src/org/nwapw/abacus/config/ConfigurationObject.java diff --git a/build.gradle b/build.gradle index 7733484..36fd331 100644 --- a/build.gradle +++ b/build.gradle @@ -14,6 +14,7 @@ repositories { } dependencies { + compile 'com.moandjiezana.toml:toml4j:0.7.1' testCompile 'junit:junit:4.12' } diff --git a/src/org/nwapw/abacus/config/Configuration.java b/src/org/nwapw/abacus/config/Configuration.java new file mode 100644 index 0000000..d22d50d --- /dev/null +++ b/src/org/nwapw/abacus/config/Configuration.java @@ -0,0 +1,18 @@ +package org.nwapw.abacus.config; + +/** + * Serializable class that will be used to load TOML + * configurations. + */ +public class Configuration { + + /** + * The precision to which the calculator should operator. + */ + public int decimalPrecision; + /** + * The type of number this calculator should use. + */ + public String numberType; + +} diff --git a/src/org/nwapw/abacus/config/ConfigurationObject.java b/src/org/nwapw/abacus/config/ConfigurationObject.java new file mode 100644 index 0000000..c8147a4 --- /dev/null +++ b/src/org/nwapw/abacus/config/ConfigurationObject.java @@ -0,0 +1,146 @@ +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 + * manages saving, loading, and getting values + * from the configuration. While Configuration is + * the data model, this is the interface with it. + */ +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. + */ + private static final TomlWriter TOML_WRITER = new TomlWriter(); + /** + * 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. + * different constructors do different things, + * but they all lead here. + * @param configuration the configuration to set up with. + */ + private void setup(Configuration configuration){ + this.configuration = configuration; + numberImplementations = new HashMap<>(); + } + + /** + * Creates a default configuration. + * @return the newly created default configuration. + */ + private Configuration getDefaultConfig(){ + configuration = new Configuration(); + configuration.decimalPrecision = -1; + configuration.numberType = "naive"; + 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. + */ + public int getPrecision(){ + return configuration.decimalPrecision; + } + + /** + * Saves the ConfigurationObject to the given file. + * @param toFile the file to save ot. + * @return true if the save succeed, false if otherwise. + */ + public boolean save(File toFile){ + if(toFile.getParentFile() != null) toFile.getParentFile().mkdirs(); + try { + TOML_WRITER.write(configuration, toFile); + return true; + } catch (IOException e) { + e.printStackTrace(); + } + return false; + } + + /** + * Creates a new configuration object with the given config. + * @param config the config to use. + */ + public ConfigurationObject(Configuration config){ + setup(config); + } + + /** + * Create a configuration object by attempting to + * load a config from the given path, using the + * default configuration otherwise. + * @param path the path to attempt to load. + */ + public ConfigurationObject(File path){ + Configuration config; + if(!path.exists()) { + config = getDefaultConfig(); + } else { + Toml parse = new Toml(); + parse.read(path); + config = parse.to(Configuration.class); + } + setup(config); + } + + /** + * Creates a new configuration object with the + * default configuration. + */ + public ConfigurationObject(){ + setup(getDefaultConfig()); + } + +} From 90f04a20add93e6b69640130a2b5e93175dd072e Mon Sep 17 00:00:00 2001 From: Danila Fedorin Date: Fri, 28 Jul 2017 20:04:13 -0700 Subject: [PATCH 2/6] Change precision to getMaxPrecision, as precision can be configured. --- src/org/nwapw/abacus/number/NaiveNumber.java | 9 ++++++++- src/org/nwapw/abacus/number/NumberInterface.java | 4 ++-- src/org/nwapw/abacus/number/PreciseNumber.java | 8 +++----- src/org/nwapw/abacus/plugin/StandardPlugin.java | 2 +- 4 files changed, 14 insertions(+), 9 deletions(-) diff --git a/src/org/nwapw/abacus/number/NaiveNumber.java b/src/org/nwapw/abacus/number/NaiveNumber.java index 1a80cd1..96e42dd 100755 --- a/src/org/nwapw/abacus/number/NaiveNumber.java +++ b/src/org/nwapw/abacus/number/NaiveNumber.java @@ -10,6 +10,13 @@ public class NaiveNumber implements NumberInterface { */ private double value; + /** + * Creates a new NaiveNumber with the given string. + * @param value the value, which will be parsed as a double. + */ + public NaiveNumber(String value) { + this(Double.parseDouble(value)); + } /** * Creates a new NaiveNumber with the given value. * @param value the value to use. @@ -28,7 +35,7 @@ public class NaiveNumber implements NumberInterface { public static final NaiveNumber ONE = new NaiveNumber(1); @Override - public int precision() { + public int getMaxPrecision() { return 18; } diff --git a/src/org/nwapw/abacus/number/NumberInterface.java b/src/org/nwapw/abacus/number/NumberInterface.java index f425a30..1f87d11 100755 --- a/src/org/nwapw/abacus/number/NumberInterface.java +++ b/src/org/nwapw/abacus/number/NumberInterface.java @@ -6,10 +6,10 @@ package org.nwapw.abacus.number; public interface NumberInterface { /** - * The precision to which this number operates. + * The maximum precision to which this number operates. * @return the precision. */ - int precision(); + int getMaxPrecision(); /** * Multiplies this number by another, returning diff --git a/src/org/nwapw/abacus/number/PreciseNumber.java b/src/org/nwapw/abacus/number/PreciseNumber.java index 1641cff..8b01622 100755 --- a/src/org/nwapw/abacus/number/PreciseNumber.java +++ b/src/org/nwapw/abacus/number/PreciseNumber.java @@ -1,8 +1,6 @@ package org.nwapw.abacus.number; import java.math.BigDecimal; -import java.math.BigInteger; -import java.math.MathContext; import java.math.RoundingMode; public class PreciseNumber implements NumberInterface{ @@ -43,7 +41,7 @@ public class PreciseNumber implements NumberInterface{ } @Override - public int precision() { + public int getMaxPrecision() { return 54; } @@ -54,7 +52,7 @@ public class PreciseNumber implements NumberInterface{ @Override public NumberInterface divide(NumberInterface divisor) { - return new PreciseNumber(value.divide(((PreciseNumber) divisor).value, this.precision(), RoundingMode.HALF_UP)); + return new PreciseNumber(value.divide(((PreciseNumber) divisor).value, this.getMaxPrecision(), RoundingMode.HALF_UP)); } @Override @@ -109,7 +107,7 @@ public class PreciseNumber implements NumberInterface{ @Override public String toString() { - BigDecimal rounded = value.setScale(precision() - 4, RoundingMode.HALF_UP); + BigDecimal rounded = value.setScale(getMaxPrecision() - 4, RoundingMode.HALF_UP); return rounded.stripTrailingZeros().toPlainString(); } } diff --git a/src/org/nwapw/abacus/plugin/StandardPlugin.java b/src/org/nwapw/abacus/plugin/StandardPlugin.java index 1ae0969..088c877 100755 --- a/src/org/nwapw/abacus/plugin/StandardPlugin.java +++ b/src/org/nwapw/abacus/plugin/StandardPlugin.java @@ -292,7 +292,7 @@ public class StandardPlugin extends Plugin { * @return the maximum error. */ private NumberInterface getMaxError(NumberInterface number){ - return (new NaiveNumber(10)).promoteTo(number.getClass()).intPow(-number.precision()); + return (new NaiveNumber(10)).promoteTo(number.getClass()).intPow(-number.getMaxPrecision()); } } From a16e85dfcda1e3080b7c2688b3cf4e516adf8ad3 Mon Sep 17 00:00:00 2001 From: Danila Fedorin Date: Fri, 28 Jul 2017 21:25:02 -0700 Subject: [PATCH 3/6] Rewrite Abacus to be the central class of the application. --- src/org/nwapw/abacus/Abacus.java | 150 +++++++++++++++--- .../nwapw/abacus/plugin/PluginManager.java | 9 +- src/org/nwapw/abacus/tree/NumberReducer.java | 18 +-- src/org/nwapw/abacus/window/Window.java | 58 ++----- 4 files changed, 155 insertions(+), 80 deletions(-) diff --git a/src/org/nwapw/abacus/Abacus.java b/src/org/nwapw/abacus/Abacus.java index 3343d71..9b6a6ea 100644 --- a/src/org/nwapw/abacus/Abacus.java +++ b/src/org/nwapw/abacus/Abacus.java @@ -1,46 +1,148 @@ package org.nwapw.abacus; -import org.nwapw.abacus.plugin.PluginManager; -//import org.nwapw.abacus.plugin.StandardPlugin; -import org.nwapw.abacus.plugin.StandardPlugin; -import org.nwapw.abacus.window.Window; +import org.nwapw.abacus.function.Operator; +import org.nwapw.abacus.number.NumberInterface; import org.nwapw.abacus.plugin.ClassFinder; +import org.nwapw.abacus.plugin.PluginListener; +import org.nwapw.abacus.plugin.PluginManager; +import org.nwapw.abacus.plugin.StandardPlugin; +import org.nwapw.abacus.tree.NumberReducer; +import org.nwapw.abacus.tree.TreeBuilder; +import org.nwapw.abacus.tree.TreeNode; +import org.nwapw.abacus.window.Window; import javax.swing.*; import java.io.IOException; -import java.util.ArrayList; -public class Abacus { +/** + * The main calculator class. This is responsible + * for piecing together all of the components, allowing + * their interaction with each other. + */ +public class Abacus implements PluginListener { + /** + * The main Abacus UI. + */ private Window mainUi; - private PluginManager manager; + /** + * The plugin manager responsible for + * loading and unloading plugins, + * and getting functions from them. + */ + private PluginManager pluginManager; + /** + * Tree builder built from plugin manager, + * used to construct parse trees. + */ + private TreeBuilder treeBuilder; + /** + * The reducer used to evaluate the tree. + */ + private NumberReducer numberReducer; + /** + * Creates a new instance of the Abacus calculator. + */ public Abacus(){ - init(); + pluginManager = new PluginManager(this); + mainUi = new Window(this); + numberReducer = new NumberReducer(this); + + pluginManager.addListener(this); + pluginManager.addInstantiated(new StandardPlugin(pluginManager)); + try { + ClassFinder.loadJars("plugins") + .forEach(plugin -> pluginManager.addClass(plugin)); + } catch (IOException | ClassNotFoundException e) { + e.printStackTrace(); + } + pluginManager.load(); + + mainUi.setVisible(true); } - private void init() { + /** + * Gets the current tree builder. + * @return the main tree builder in this abacus instance. + */ + public TreeBuilder getTreeBuilder() { + return treeBuilder; + } + + /** + * Gets the current plugin manager, + * @return the plugin manager in this abacus instance. + */ + public PluginManager getPluginManager() { + return pluginManager; + } + + /** + * Gets the current UI. + * @return the UI window in this abacus instance. + */ + public Window getMainUi() { + return mainUi; + } + + /** + * Get the reducer that is responsible for transforming + * an expression into a number. + * @return the number reducer in this abacus instance. + */ + public NumberReducer getNumberReducer() { + return numberReducer; + } + + /** + * Parses a string into a tree structure using the main + * tree builder. + * @param input the input string to parse + * @return the resulting tree, null if the tree builder or the produced tree are null. + */ + public TreeNode parseString(String input){ + if(treeBuilder == null) return null; + return treeBuilder.fromString(input); + } + + /** + * Evaluates the given tree using the main + * number reducer. + * @param tree the tree to reduce, must not be null. + * @return the resulting number, or null of the reduction failed. + */ + public NumberInterface evaluateTree(TreeNode tree){ + return tree.reduce(numberReducer); + } + + @Override + public void onLoad(PluginManager manager) { + treeBuilder = new TreeBuilder(); + for(String function : manager.getAllFunctions()){ + treeBuilder.registerFunction(function); + } + for(String operator : manager.getAllOperators()){ + Operator operatorObject = manager.operatorFor(operator); + treeBuilder.registerOperator(operator, + operatorObject.getAssociativity(), + operatorObject.getType(), + operatorObject.getPrecedence()); + } + } + + @Override + public void onUnload(PluginManager manager) { + treeBuilder = null; + } + + public static void main(String[] args){ try { UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); } catch (ClassNotFoundException | InstantiationException | UnsupportedLookAndFeelException | IllegalAccessException e) { e.printStackTrace(); } - manager = new PluginManager(); - manager.addInstantiated(new StandardPlugin(manager)); - try { - ClassFinder.loadJars("plugins") - .forEach(plugin -> manager.addClass(plugin)); - } catch (IOException | ClassNotFoundException e) { - e.printStackTrace(); - } - mainUi = new Window(manager); - mainUi.setVisible(true); - manager.load(); - } - - public static void main(String[] args){ new Abacus(); } - } diff --git a/src/org/nwapw/abacus/plugin/PluginManager.java b/src/org/nwapw/abacus/plugin/PluginManager.java index 8bb3879..25e3aac 100644 --- a/src/org/nwapw/abacus/plugin/PluginManager.java +++ b/src/org/nwapw/abacus/plugin/PluginManager.java @@ -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; @@ -42,11 +43,17 @@ public class PluginManager { * The list of plugin listeners attached to this instance. */ private Set listeners; + /** + * The instance of Abacus that is used to interact with its other + * components. + */ + 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<>(); diff --git a/src/org/nwapw/abacus/tree/NumberReducer.java b/src/org/nwapw/abacus/tree/NumberReducer.java index bb10a5e..6fcff47 100644 --- a/src/org/nwapw/abacus/tree/NumberReducer.java +++ b/src/org/nwapw/abacus/tree/NumberReducer.java @@ -1,8 +1,8 @@ package org.nwapw.abacus.tree; +import org.nwapw.abacus.Abacus; import org.nwapw.abacus.function.Function; import org.nwapw.abacus.number.NumberInterface; -import org.nwapw.abacus.plugin.PluginManager; /** * A reducer implementation that turns a tree into a single number. @@ -13,14 +13,14 @@ public class NumberReducer implements Reducer { /** * The plugin manager from which to draw the functions. */ - private PluginManager manager; + private Abacus abacus; /** - * Creates a new number reducer with the given plugin manager. - * @param manager the plugin manager. + * Creates a new number reducer. + * @param abacus the calculator instance. */ - public NumberReducer(PluginManager manager){ - this.manager = manager; + public NumberReducer(Abacus abacus){ + this.abacus = abacus; } @Override @@ -30,12 +30,12 @@ public class NumberReducer implements Reducer { } else if(node instanceof BinaryInfixNode){ NumberInterface left = (NumberInterface) children[0]; NumberInterface right = (NumberInterface) children[1]; - Function function = manager.operatorFor(((BinaryInfixNode) node).getOperation()).getFunction(); + Function function = abacus.getPluginManager().operatorFor(((BinaryInfixNode) node).getOperation()).getFunction(); if(function == null) return null; return function.apply(left, right); } else if(node instanceof UnaryPrefixNode) { NumberInterface child = (NumberInterface) children[0]; - Function functionn = manager.operatorFor(((UnaryPrefixNode) node).getOperation()).getFunction(); + Function functionn = abacus.getPluginManager().operatorFor(((UnaryPrefixNode) node).getOperation()).getFunction(); if(functionn == null) return null; return functionn.apply(child); } else if(node instanceof FunctionNode){ @@ -43,7 +43,7 @@ public class NumberReducer implements Reducer { for(int i = 0; i < convertedChildren.length; i++){ convertedChildren[i] = (NumberInterface) children[i]; } - Function function = manager.functionFor(((FunctionNode) node).getFunction()); + Function function = abacus.getPluginManager().functionFor(((FunctionNode) node).getFunction()); if(function == null) return null; return function.apply(convertedChildren); } diff --git a/src/org/nwapw/abacus/window/Window.java b/src/org/nwapw/abacus/window/Window.java index 0e7a95e..0484cf2 100644 --- a/src/org/nwapw/abacus/window/Window.java +++ b/src/org/nwapw/abacus/window/Window.java @@ -1,11 +1,7 @@ package org.nwapw.abacus.window; -import org.nwapw.abacus.function.Operator; +import org.nwapw.abacus.Abacus; import org.nwapw.abacus.number.NumberInterface; -import org.nwapw.abacus.plugin.PluginListener; -import org.nwapw.abacus.plugin.PluginManager; -import org.nwapw.abacus.tree.NumberReducer; -import org.nwapw.abacus.tree.TreeBuilder; import org.nwapw.abacus.tree.TreeNode; import javax.swing.*; @@ -18,7 +14,7 @@ import java.awt.event.MouseEvent; /** * The main UI window for the calculator. */ -public class Window extends JFrame implements PluginListener { +public class Window extends JFrame { private static final String CALC_STRING = "Calculate"; private static final String SYNTAX_ERR_STRING = "Syntax Error"; @@ -47,18 +43,10 @@ public class Window extends JFrame implements PluginListener { }; /** - * The plugin manager used to retrieve functions. + * The instance of the Abacus class, used + * for interaction with plugins and configuration. */ - private PluginManager manager; - /** - * The builder used to construct the parse trees. - */ - private TreeBuilder builder; - /** - * The reducer used to evaluate the tree. - */ - private NumberReducer reducer; - + private Abacus abacus; /** * The last output by the calculator. */ @@ -130,15 +118,14 @@ public class Window extends JFrame implements PluginListener { * Action listener that causes the input to be evaluated. */ private ActionListener evaluateListener = (event) -> { - if(builder == null) return; - TreeNode parsedExpression = builder.fromString(inputField.getText()); + TreeNode parsedExpression = abacus.parseString(inputField.getText()); if(parsedExpression == null){ lastOutputArea.setText(SYNTAX_ERR_STRING); return; } - NumberInterface numberInterface = parsedExpression.reduce(reducer); - if(numberInterface == null){ - lastOutputArea.setText(EVAL_ERR_STRING);; + NumberInterface numberInterface = abacus.evaluateTree(parsedExpression); + if(numberInterface == null) { + lastOutputArea.setText(EVAL_ERR_STRING); return; } lastOutput = numberInterface.toString(); @@ -159,13 +146,11 @@ public class Window extends JFrame implements PluginListener { /** * Creates a new window with the given manager. - * @param manager the manager to use. + * @param abacus the calculator instance to interact with other components. */ - public Window(PluginManager manager){ + public Window(Abacus abacus){ this(); - this.manager = manager; - manager.addListener(this); - reducer = new NumberReducer(manager); + this.abacus = abacus; } /** @@ -261,23 +246,4 @@ public class Window extends JFrame implements PluginListener { }); } - @Override - public void onLoad(PluginManager manager) { - builder = new TreeBuilder(); - for(String function : manager.getAllFunctions()){ - builder.registerFunction(function); - } - for(String operator : manager.getAllOperators()){ - Operator operatorObject = manager.operatorFor(operator); - builder.registerOperator(operator, - operatorObject.getAssociativity(), - operatorObject.getType(), - operatorObject.getPrecedence()); - } - } - - @Override - public void onUnload(PluginManager manager) { - builder = null; - } } From 28b9e15fc8d2dd8c3b709c3e161807bf9853952b Mon Sep 17 00:00:00 2001 From: Danila Fedorin Date: Fri, 28 Jul 2017 21:37:47 -0700 Subject: [PATCH 4/6] Add configuration object to Abacus. --- src/org/nwapw/abacus/Abacus.java | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/org/nwapw/abacus/Abacus.java b/src/org/nwapw/abacus/Abacus.java index 9b6a6ea..be3b378 100644 --- a/src/org/nwapw/abacus/Abacus.java +++ b/src/org/nwapw/abacus/Abacus.java @@ -1,5 +1,6 @@ package org.nwapw.abacus; +import org.nwapw.abacus.config.ConfigurationObject; import org.nwapw.abacus.function.Operator; import org.nwapw.abacus.number.NumberInterface; import org.nwapw.abacus.plugin.ClassFinder; @@ -12,6 +13,7 @@ import org.nwapw.abacus.tree.TreeNode; import org.nwapw.abacus.window.Window; import javax.swing.*; +import java.io.File; import java.io.IOException; /** @@ -21,6 +23,11 @@ import java.io.IOException; */ public class Abacus implements PluginListener { + /** + * The file used for saving and loading configuration. + */ + public static final File CONFIG_FILE = new File("config.yml"); + /** * The main Abacus UI. */ @@ -40,6 +47,10 @@ public class Abacus implements PluginListener { * The reducer used to evaluate the tree. */ private NumberReducer numberReducer; + /** + * The configuration loaded from a file. + */ + private ConfigurationObject configuration; /** * Creates a new instance of the Abacus calculator. @@ -48,6 +59,8 @@ public class Abacus implements PluginListener { pluginManager = new PluginManager(this); mainUi = new Window(this); numberReducer = new NumberReducer(this); + configuration = new ConfigurationObject(CONFIG_FILE); + configuration.save(CONFIG_FILE); pluginManager.addListener(this); pluginManager.addInstantiated(new StandardPlugin(pluginManager)); @@ -95,6 +108,14 @@ public class Abacus implements PluginListener { return numberReducer; } + /** + * Gets the configuration object associated with this instance. + * @return the configuration object. + */ + public ConfigurationObject getConfiguration() { + return configuration; + } + /** * Parses a string into a tree structure using the main * tree builder. From c66f36c77a3f28aa129195668b00e099628461f2 Mon Sep 17 00:00:00 2001 From: Danila Fedorin Date: Fri, 28 Jul 2017 22:17:22 -0700 Subject: [PATCH 5/6] 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); From 768b9b0e8fbc15401076779612905b1e1a5cff41 Mon Sep 17 00:00:00 2001 From: Danila Fedorin Date: Fri, 28 Jul 2017 22:51:59 -0700 Subject: [PATCH 6/6] Remove precision specification as it seems detrimental. --- src/org/nwapw/abacus/config/Configuration.java | 4 ---- src/org/nwapw/abacus/config/ConfigurationObject.java | 9 --------- 2 files changed, 13 deletions(-) diff --git a/src/org/nwapw/abacus/config/Configuration.java b/src/org/nwapw/abacus/config/Configuration.java index d22d50d..2039d60 100644 --- a/src/org/nwapw/abacus/config/Configuration.java +++ b/src/org/nwapw/abacus/config/Configuration.java @@ -6,10 +6,6 @@ package org.nwapw.abacus.config; */ public class Configuration { - /** - * The precision to which the calculator should operator. - */ - public int decimalPrecision; /** * The type of number this calculator should use. */ diff --git a/src/org/nwapw/abacus/config/ConfigurationObject.java b/src/org/nwapw/abacus/config/ConfigurationObject.java index 89f1e2e..1f0c066 100644 --- a/src/org/nwapw/abacus/config/ConfigurationObject.java +++ b/src/org/nwapw/abacus/config/ConfigurationObject.java @@ -39,19 +39,10 @@ public class ConfigurationObject { */ private Configuration getDefaultConfig(){ configuration = new Configuration(); - configuration.decimalPrecision = -1; configuration.numberType = "naive"; return configuration; } - /** - * Returns the configured, user-requested precision. - * @return the precision. - */ - public int getPrecision(){ - return configuration.decimalPrecision; - } - /** * Returns the implementation the user has requested to * represent their numbers.