package lipstone.joshua.parser.plugin;
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.util.ArrayList;
import java.util.HashMap;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import lipstone.joshua.parser.Parser;
import lipstone.joshua.parser.Tokenizer;
import lipstone.joshua.parser.exceptions.PluginConflictException;
import lipstone.joshua.parser.exceptions.SyntaxException;
import lipstone.joshua.parser.exceptions.UnbalancedParenthesesException;
import lipstone.joshua.parser.plugin.helpdata.Command;
import lipstone.joshua.parser.plugin.helpdata.Keyword;
import lipstone.joshua.parser.plugin.helpdata.Operation;
import lipstone.joshua.parser.plugin.settings.ParserSettingsItem;
import lipstone.joshua.parser.plugin.types.CommandPlugin;
import lipstone.joshua.parser.plugin.types.InputFilterPlugin;
import lipstone.joshua.parser.plugin.types.KeywordPlugin;
import lipstone.joshua.parser.plugin.types.OperationPlugin;
import lipstone.joshua.parser.plugin.types.OutputFilterPlugin;
import lipstone.joshua.parser.plugin.types.SettingsPlugin;
import lipstone.joshua.parser.util.ConsCell;
import lipstone.joshua.simpleXML.Converter;
import lipstone.joshua.simpleXML.SimpleXML;
import pluginLibrary.Plugin;
public class ParserPlugin extends Plugin implements Cloneable {
protected Parser parser;
/**
* An open-access {@link javax.swing.JPanel JPanel} that uses a {@link java.awt.GridBagLayout GridBagLayout}.
*/
protected final JPanel settingsPanel = new JPanel();
public static final String DATA_NODE = "data";
/**
* Contains the data that is in the plugin's data XML file
*/
private SimpleXML<ConsCell> data;
/**
* Contains the keywords in this plugin
*/
private final HashMap<String, Keyword> keywords = new HashMap<String, Keyword>();
/**
* Contains the commands in this plugin
*/
private final HashMap<String, Command> commands = new HashMap<String, Command>();
/**
* Contains the operations in this plugin, mapped by their respective IDs
*/
private final HashMap<String, Operation> operations = new HashMap<String, Operation>();
/**
* Contains the configuration items in this plugin mapped to their respective IDs
*/
private final HashMap<String, ParserSettingsItem<?>> configuration = new HashMap<String, ParserSettingsItem<?>>();
/**
* Contains the XML data behind this plugin's configuration
*/
private SimpleXML<ConsCell> configurationData;
/**
* Gets the ID of this plugin
*
* @return The package and class name of this plugin
*/
@Override
public final String getID() {
return this.getClass().getName();
}
/**
* initializes all of the data for this plugin
*
* @param parser
* the <tt>Parser</tt> that precipitated this method call
* @throws PluginConflictException
*/
public final void loadPlugin(final Parser parser) throws PluginConflictException {
this.parser = parser;
data = new SimpleXML<ConsCell>(parser.getBaseLocation() + "data/" + getID() + ".xml", new Converter<ConsCell>() {
@Override
public ConsCell fromString(String data) {
try {
return Tokenizer.tokenizeString(data);
}
catch (SyntaxException | UnbalancedParenthesesException e) {
parser.getLog().logError("Unable to load the saved data for " + getID());
return new ConsCell();
}
}
@Override
public String toString(ConsCell data) {
return data.toString();
}
});
if (this instanceof SettingsPlugin && (System.getProperty("java.awt.headless") == null || (System.getProperty("java.awt.headless") != null && !System.getProperty("java.awt.headless").equals("true")))) {
settingsPanel.setName(getClass().getSimpleName());
settingsPanel.setLayout(new GridBagLayout());
settingsPanel.setOpaque(false);
configurationData = new SimpleXML<ConsCell>(parser.getBaseLocation() + "configuration/" + getID() + ".settings", new Converter<ConsCell>() {
@Override
public ConsCell fromString(String data) {
try {
return Tokenizer.tokenizeString(data);
}
catch (SyntaxException | UnbalancedParenthesesException e) {
parser.getLog().logError("Unable to load the saved configuration in " + getID());
return new ConsCell();
}
}
@Override
public String toString(ConsCell data) {
return data.toString();
}
});
}
if (this instanceof OperationPlugin)
((OperationPlugin) this).loadOperations();
if (this instanceof OutputFilterPlugin)
((OutputFilterPlugin) this).loadOutputFilter();
if (this instanceof InputFilterPlugin)
((InputFilterPlugin) this).loadInputFilter();
if (this instanceof KeywordPlugin)
((KeywordPlugin) this).loadKeywords();
if (this instanceof CommandPlugin)
((CommandPlugin) this).loadCommands();
if (this instanceof SettingsPlugin && (System.getProperty("java.awt.headless") == null || (System.getProperty("java.awt.headless") != null && !System.getProperty("java.awt.headless").equals("true")))) {
((SettingsPlugin) this).loadSettings();
ArrayList<String> remove = new ArrayList<String>();
for (String key : configurationData) {
if (!configuration.containsKey(key))
remove.add(key);
else
configuration.get(key).setValue(configurationData.get(key, DATA_NODE));
}
//Keeps the plugin configuration map clean.
for (String key : remove)
configurationData.remove(key);
}
}
/**
* Core method for operations that need to be performed when this plugin is unloaded are here. Any additional unloading
* operations should be added to the appropriate helper methods.
*
* @param parser
* the <tt>Parser</tt> that precipitated this method call
* @throws PluginConflictException
*/
public final void unloadPlugin(Parser parser) throws PluginConflictException {
if (this instanceof OperationPlugin)
((OperationPlugin) this).unloadOperations();
if (this instanceof OutputFilterPlugin)
((OutputFilterPlugin) this).unloadOutputFilter();
if (this instanceof InputFilterPlugin)
((InputFilterPlugin) this).unloadInputFilter();
if (this instanceof KeywordPlugin)
((KeywordPlugin) this).unloadKeywords();
if (this instanceof CommandPlugin)
((CommandPlugin) this).unloadCommands();
for (String operation : getOperations())
removeOperation(operation);
for (String keyword : getKeywords())
removeKeyword(keyword);
for (String command : getCommands())
removeCommand(command);
data = null;
configurationData = null;
settingsPanel.removeAll();
}
/**
* Saves data in an XML file found in the parser classpath /data/<String>ID</String>.xml
*
* @param key
* the key to store this data in, the data will be retrieved by this key.
* @param value
* the data to be stored
* @return the value being saved
*/
public final ConsCell saveData(String key, ConsCell value) {
data.put(key, DATA_NODE, value);
return value;
}
/**
* Remove the data specified by key from the data HashMap
*
* @param key
* the key pointing to the data to be removed
* @return the value mapped to the key being removed
*/
public final ConsCell removeData(String key) {
ConsCell output = data.remove(key, DATA_NODE);
data.remove(key);
return output;
}
/**
* Get data from the data HashMap
*
* @param key
* the key that points to the data
* @return the data or null if it does not exist
*/
public final ConsCell getData(String key) {
return data.get(key, DATA_NODE);
}
public final ArrayList<String> getDataKeyset() {
ArrayList<String> keys = new ArrayList<String>();
for (String key : data)
keys.add(key);
return keys;
}
public final SimpleXML<ConsCell> getDataMap() {
return data;
}
/**
* Puts data in the data HashMap
*
* @param key
* to store the data under
* @param value
* the value to map to this key
*/
public final void setData(String key, ConsCell value) {
data.put(key, DATA_NODE, value);
}
public final void saveSetting(String item, ConsCell value) {
configurationData.put(item, DATA_NODE, value);
}
public final ConsCell getSetting(String item) {
return configurationData.get(item, DATA_NODE);
}
/**
* Add a keyword with the given name and description to this plugin
*
* @param keyword
* the keyword to be added
* @throws PluginConflictException
*/
public final void addKeyword(Keyword keyword) throws PluginConflictException {
keywords.put(keyword.getName(), keyword);
parser.addKeyword(keyword, this);
}
/**
* Remove the keyword with the given name from this plugin
*
* @param name
* the name of the keyword to remove
* @throws PluginConflictException
*/
public final void removeKeyword(String name) throws PluginConflictException {
keywords.remove(name);
parser.removeKeyword(name, this);
}
/**
* Add a command with the given name and description to this plugin
*
* @param command
* the command to be added
* @throws PluginConflictException
*/
public final void addCommand(Command command) throws PluginConflictException {
commands.put(command.getName(), command);
parser.addCommand(command, this);
}
/**
* Remove the command with the given name from this plugin
*
* @param name
* the name of the command to remove
* @throws PluginConflictException
*/
public final void removeCommand(String name) throws PluginConflictException {
commands.remove(name);
parser.removeCommand(name, this);
}
/**
* Add the given operation to this plugin
*
* @param operation
* the operation to add
* @throws PluginConflictException
*/
public final void addOperation(Operation operation) throws PluginConflictException {
operations.put(operation.getName(), operation);
parser.addOperation(operation, this);
}
/**
* Remove the keyword with the given name from this plugin
*
* @param name
* the name of the keyword to remove
* @throws PluginConflictException
*/
public final void removeOperation(String name) throws PluginConflictException {
operations.remove(name);
parser.removeOperation(name, this);
}
/**
* Adds the given {@link lipstone.joshua.parser.plugin.settings.ParserSettingsItem ParserSettingsItem} to the
* {@link #settingsPanel settingsPanel}.
*
* @param settingsItem
* the item to add to the panel
*/
public final void addSettingsItem(ParserSettingsItem<?> settingsItem) {
configuration.put(settingsItem.getName(), settingsItem);
settingsPanel.add(settingsItem.getGUIItem());
}
/**
* Adds the given {@link lipstone.joshua.parser.plugin.settings.ParserSettingsItem ParserSettingsItem} to the
* {@link #settingsPanel settingsPanel} with the given {@link java.awt.GridBagConstraints GridBagConstraints}.
*
* @param settingsItem
* the item to add to the panel
* @param c
* the {@link java.awt.GridBagConstraints GridBagConstraints} to use in adding this item.
*/
public final void addSettingsItem(ParserSettingsItem<?> settingsItem, GridBagConstraints c) {
configuration.put(settingsItem.getName(), settingsItem);
settingsPanel.add(settingsItem.getGUIItem(), c);
}
/**
* Adds the given {@link lipstone.joshua.parser.plugin.settings.ParserSettingsItem ParserSettingsItem} to the
* {@link #settingsPanel settingsPanel} with the given {@link java.awt.GridBagConstraints GridBagConstraints}.
*
* @param settingsItem
* the item to add to the panel
* @param c
* the {@link java.awt.GridBagConstraints GridBagConstraints} to use in adding this item.
* @param scrollPaneSize
* the size of the scroll pane to place the given
* {@link lipstone.joshua.parser.plugin.settings.ParserSettingsItem ParserSettingsItem} in.
*/
public final void addSettingsItem(ParserSettingsItem<?> settingsItem, GridBagConstraints c, Dimension scrollPaneSize) {
configuration.put(settingsItem.getName(), settingsItem);
JScrollPane scrollPane = new JScrollPane(settingsItem.getGUIItem());
scrollPane.setPreferredSize(scrollPaneSize);
settingsPanel.add(scrollPane, c);
}
/**
* @return an ArrayList<String> containing the names of all of the operations mapped to this plugin
*/
protected final ArrayList<String> getOperations() {
return new ArrayList<String>(operations.keySet());
}
/**
* @return an ArrayList<String> containing the names of all of the keywords mapped to this plugin
*/
protected final ArrayList<String> getKeywords() {
return new ArrayList<String>(keywords.keySet());
}
/**
* @return an ArrayList<String> containing the names of all of the commands mapped to this plugin
*/
protected final ArrayList<String> getCommands() {
return new ArrayList<String>(commands.keySet());
}
/**
* @return the parser that created this plugin
*/
public final Parser getParser() {
return parser;
}
/**
* @return the JPanel that the <tt>Parser</tt> will use to display this plugin's settings.
*/
public final JPanel getSettingsPanel() {
return settingsPanel;
}
public ParserPlugin clone(Parser parser) throws PluginConflictException {
ParserPlugin clone = new ParserPlugin();
clone.loadPlugin(parser);
return clone;
}
}