1
0
mirror of https://github.com/DanilaFe/abacus synced 2024-12-23 16:00:09 -08:00

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 28b9e15fc8
commit c66f36c77a
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.config.ConfigurationObject;
import org.nwapw.abacus.function.Operator; import org.nwapw.abacus.function.Operator;
import org.nwapw.abacus.number.NaiveNumber;
import org.nwapw.abacus.number.NumberInterface; import org.nwapw.abacus.number.NumberInterface;
import org.nwapw.abacus.plugin.ClassFinder; import org.nwapw.abacus.plugin.ClassFinder;
import org.nwapw.abacus.plugin.PluginListener; import org.nwapw.abacus.plugin.PluginListener;
@ -15,6 +16,7 @@ import org.nwapw.abacus.window.Window;
import javax.swing.*; import javax.swing.*;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
/** /**
* The main calculator class. This is responsible * The main calculator class. This is responsible
@ -23,10 +25,14 @@ import java.io.IOException;
*/ */
public class Abacus implements PluginListener { 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. * 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. * The main Abacus UI.
@ -137,9 +143,22 @@ public class Abacus implements PluginListener {
return tree.reduce(numberReducer); 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 @Override
public void onLoad(PluginManager manager) { public void onLoad(PluginManager manager) {
treeBuilder = new TreeBuilder(); treeBuilder = new TreeBuilder(this);
for(String function : manager.getAllFunctions()){ for(String function : manager.getAllFunctions()){
treeBuilder.registerFunction(function); treeBuilder.registerFunction(function);
} }

View File

@ -2,13 +2,9 @@ package org.nwapw.abacus.config;
import com.moandjiezana.toml.Toml; import com.moandjiezana.toml.Toml;
import com.moandjiezana.toml.TomlWriter; import com.moandjiezana.toml.TomlWriter;
import org.nwapw.abacus.number.NaiveNumber;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.Map;
/** /**
* A configuration object, which essentially * A configuration object, which essentially
@ -18,10 +14,6 @@ import java.util.Map;
*/ */
public class ConfigurationObject { 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. * The writer used to store the configuration.
*/ */
@ -30,11 +22,6 @@ public class ConfigurationObject {
* The configuration instance being modeled. * The configuration instance being modeled.
*/ */
private Configuration configuration; 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. * Sets up the ConfigurationObject.
@ -44,7 +31,6 @@ public class ConfigurationObject {
*/ */
private void setup(Configuration configuration){ private void setup(Configuration configuration){
this.configuration = configuration; this.configuration = configuration;
numberImplementations = new HashMap<>();
} }
/** /**
@ -58,33 +44,6 @@ public class ConfigurationObject {
return configuration; 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. * Returns the configured, user-requested precision.
* @return the precision. * @return the precision.
@ -93,6 +52,15 @@ public class ConfigurationObject {
return configuration.decimalPrecision; 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. * Saves the ConfigurationObject to the given file.
* @param toFile the file to save ot. * @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.Function;
import org.nwapw.abacus.function.Operator; import org.nwapw.abacus.function.Operator;
import org.nwapw.abacus.number.NumberInterface;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
@ -24,6 +25,10 @@ public abstract class Plugin {
* A hash map of operators mapped to their string names. * A hash map of operators mapped to their string names.
*/ */
private Map<String, Operator> operators; 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 * The plugin manager in which to search for functions
* not inside this package, * not inside this package,
@ -44,6 +49,7 @@ public abstract class Plugin {
this.manager = manager; this.manager = manager;
functions = new HashMap<>(); functions = new HashMap<>();
operators = new HashMap<>(); operators = new HashMap<>();
numbers = new HashMap<>();
enabled = false; enabled = false;
} }
@ -63,6 +69,14 @@ public abstract class Plugin {
return operators.keySet(); 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. * Gets a function under the given function name.
* @param functionName the name of the function to get * @param functionName the name of the function to get
@ -81,6 +95,15 @@ public abstract class Plugin {
return operators.get(operatorName); 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 * Enables the function, loading the necessary instances
* of functions. * of functions.
@ -124,6 +147,18 @@ public abstract class Plugin {
operators.put(name, operator); 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. * Searches the PluginManager for the given function name.
* This can be used by the plugins internally in order to call functions * 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.Abacus;
import org.nwapw.abacus.function.Function; import org.nwapw.abacus.function.Function;
import org.nwapw.abacus.function.Operator; import org.nwapw.abacus.function.Operator;
import org.nwapw.abacus.number.NumberInterface;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
import java.util.*; import java.util.*;
@ -27,10 +28,15 @@ public class PluginManager {
*/ */
private Map<String, Function> cachedFunctions; 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. * that is, found in a plugin and returned.
*/ */
private Map<String, Operator> cachedOperators; 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. * List of all functions loaded by the plugins.
*/ */
@ -39,6 +45,10 @@ public class PluginManager {
* List of all operators loaded by the plugins. * List of all operators loaded by the plugins.
*/ */
private Set<String> allOperators; 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. * The list of plugin listeners attached to this instance.
*/ */
@ -58,8 +68,10 @@ public class PluginManager {
plugins = new HashSet<>(); plugins = new HashSet<>();
cachedFunctions = new HashMap<>(); cachedFunctions = new HashMap<>();
cachedOperators = new HashMap<>(); cachedOperators = new HashMap<>();
cachedNumbers = new HashMap<>();
allFunctions = new HashSet<>(); allFunctions = new HashSet<>();
allOperators = new HashSet<>(); allOperators = new HashSet<>();
allNumbers = new HashSet<>();
listeners = new HashSet<>(); listeners = new HashSet<>();
} }
@ -111,6 +123,15 @@ public class PluginManager {
return searchCached(plugins, cachedOperators, Plugin::providedOperators, Plugin::getOperator, name); 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. * Adds an instance of Plugin that already has been instantiated.
* @param plugin the plugin to add. * @param plugin the plugin to add.
@ -143,6 +164,7 @@ public class PluginManager {
for(Plugin plugin : plugins){ for(Plugin plugin : plugins){
allFunctions.addAll(plugin.providedFunctions()); allFunctions.addAll(plugin.providedFunctions());
allOperators.addAll(plugin.providedOperators()); allOperators.addAll(plugin.providedOperators());
allNumbers.addAll(plugin.providedNumbers());
} }
listeners.forEach(e -> e.onLoad(this)); listeners.forEach(e -> e.onLoad(this));
} }
@ -154,6 +176,7 @@ public class PluginManager {
for(Plugin plugin : plugins) plugin.disable(); for(Plugin plugin : plugins) plugin.disable();
allFunctions.clear(); allFunctions.clear();
allOperators.clear(); allOperators.clear();
allNumbers.clear();
listeners.forEach(e -> e.onUnload(this)); listeners.forEach(e -> e.onUnload(this));
} }
@ -181,6 +204,14 @@ public class PluginManager {
return allOperators; 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. * Adds a plugin change listener to this plugin manager.
* @param listener the listener to add. * @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.function.OperatorType;
import org.nwapw.abacus.number.NaiveNumber; import org.nwapw.abacus.number.NaiveNumber;
import org.nwapw.abacus.number.NumberInterface; import org.nwapw.abacus.number.NumberInterface;
import org.nwapw.abacus.number.PreciseNumber;
import java.util.function.BiFunction; import java.util.function.BiFunction;
@ -21,6 +22,9 @@ public class StandardPlugin extends Plugin {
@Override @Override
public void onEnable() { public void onEnable() {
registerNumber("naive", NaiveNumber.class);
registerNumber("precise", PreciseNumber.class);
registerOperator("+", new Operator(OperatorAssociativity.LEFT, OperatorType.BINARY_INFIX, 0, new Function() { registerOperator("+", new Operator(OperatorAssociativity.LEFT, OperatorType.BINARY_INFIX, 0, new Function() {
@Override @Override
protected boolean matchesParams(NumberInterface[] params) { protected boolean matchesParams(NumberInterface[] params) {

View File

@ -1,12 +1,11 @@
package org.nwapw.abacus.tree; package org.nwapw.abacus.tree;
import org.nwapw.abacus.Abacus;
import org.nwapw.abacus.function.OperatorAssociativity; import org.nwapw.abacus.function.OperatorAssociativity;
import org.nwapw.abacus.function.OperatorType; import org.nwapw.abacus.function.OperatorType;
import org.nwapw.abacus.lexing.Lexer; import org.nwapw.abacus.lexing.Lexer;
import org.nwapw.abacus.lexing.pattern.Match; import org.nwapw.abacus.lexing.pattern.Match;
import org.nwapw.abacus.lexing.pattern.Pattern; import org.nwapw.abacus.lexing.pattern.Pattern;
import org.nwapw.abacus.number.NaiveNumber;
import org.nwapw.abacus.number.PreciseNumber;
import java.util.*; import java.util.*;
@ -31,6 +30,11 @@ public class TreeBuilder {
* The map of operator types. * The map of operator types.
*/ */
private Map<String, OperatorType> typeMap; 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. * Comparator used to sort token types.
@ -40,7 +44,8 @@ public class TreeBuilder {
/** /**
* Creates a new TreeBuilder. * Creates a new TreeBuilder.
*/ */
public TreeBuilder(){ public TreeBuilder(Abacus abacus){
this.abacus = abacus;
lexer = new Lexer<TokenType>(){{ lexer = new Lexer<TokenType>(){{
register(" ", TokenType.WHITESPACE); register(" ", TokenType.WHITESPACE);
register(",", TokenType.COMMA); register(",", TokenType.COMMA);
@ -172,7 +177,7 @@ public class TreeBuilder {
else return new UnaryPrefixNode(operator, applyTo); else return new UnaryPrefixNode(operator, applyTo);
} }
} else if(matchType == TokenType.NUM){ } 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){ } else if(matchType == TokenType.FUNCTION){
String functionName = source.substring(match.getFrom(), match.getTo()); String functionName = source.substring(match.getFrom(), match.getTo());
FunctionNode node = new FunctionNode(functionName); FunctionNode node = new FunctionNode(functionName);