Allow plugins to register number implementations and use user's choice.

This commit is contained in:
Danila Fedorin 2017-07-28 22:17:22 -07:00
parent 1ce9fc6b1c
commit 1160768ee5
6 changed files with 110 additions and 48 deletions

View File

@ -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<? extends NumberInterface> 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<? extends NumberInterface> 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);
}

View File

@ -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<? extends NaiveNumber> 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<String, Class<? extends NaiveNumber>> 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<? extends NaiveNumber> 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<? extends NaiveNumber> 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.

View File

@ -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<String, Operator> operators;
/**
* A hash map of operators mapped to their string names.
*/
private Map<String, Class<? extends NumberInterface>> 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<String> 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<? extends NumberInterface> 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<? extends NumberInterface> 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

View File

@ -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<String, Function> 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<String, Operator> cachedOperators;
/**
* List of registered number implementations that have
* been cached, that is, found in a plugin and returned.
*/
private Map<String, Class<? extends NumberInterface>> 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<String> allOperators;
/**
* List of all numbers loaded by the plugins.
*/
private Set<String> 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<? extends NumberInterface> 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<String> getAllNumbers() {
return allNumbers;
}
/**
* Adds a plugin change listener to this plugin manager.
* @param listener the listener to add.

View File

@ -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) {

View File

@ -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<String, OperatorType> 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<TokenType>(){{
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);