From ed708e836b9c129a552ca03c623fd0b713890dc8 Mon Sep 17 00:00:00 2001 From: frosch95 Date: Sun, 29 Sep 2013 12:39:01 +0200 Subject: [PATCH] created a simple plugin interface --- plugins/BasicListStatisticsPlugin.groovy | 103 ++++++++++++++++++ .../application/ServiceManager.java | 10 ++ .../fx/geofrogger/GeofroggerController.java | 26 +++-- .../geofrogger/fx/geofrogger/geofrogger.fxml | 1 + .../geofrogger/fx/geofrogger_de.properties | 1 + .../geofrogger/fx/geofrogger_en.properties | 1 + .../frosch95/geofrogger/plugins/Plugin.java | 30 +++++ .../geofrogger/plugins/PluginService.java | 18 +++ .../geofrogger/plugins/PluginServiceImpl.java | 61 +++++++++++ 9 files changed, 241 insertions(+), 10 deletions(-) create mode 100644 plugins/BasicListStatisticsPlugin.groovy create mode 100644 src/de/frosch95/geofrogger/plugins/Plugin.java create mode 100644 src/de/frosch95/geofrogger/plugins/PluginService.java create mode 100644 src/de/frosch95/geofrogger/plugins/PluginServiceImpl.java diff --git a/plugins/BasicListStatisticsPlugin.groovy b/plugins/BasicListStatisticsPlugin.groovy new file mode 100644 index 0000000..0fcc5e6 --- /dev/null +++ b/plugins/BasicListStatisticsPlugin.groovy @@ -0,0 +1,103 @@ +import de.frosch95.geofrogger.plugins.Plugin +import javafx.collections.FXCollections +import javafx.scene.chart.PieChart +import javafx.scene.control.Label +import javafx.scene.control.ScrollPane +import javafx.scene.layout.Pane +import javafx.scene.layout.VBox +import org.controlsfx.dialog.Dialog + +import static javafx.scene.chart.PieChart.Data + +class BasicListStatisticsPlugin implements Plugin { + + private difficultyTerrainValues = ['1', '1.5', '2', '2.5', '3', '3.5', '4', '4.5', '5'] + + final String name = "Basic List Statistic" + final String version = "0.0.1" + + @Override + void run(final Map context) { + showDialog(createHeader(context.sessionContext), createContent(context.sessionContext)) + } + + private javafx.scene.Node createHeader(sessionContext) { + def pane = new Pane() + def Label label = new Label() + label.text = "This example plugin shows some stats based on the current list.\nThese statistics are global statistics over all caches in list\nand not personalized statistics over own founds." + pane.children.add(label) + pane + } + + /** + * creates the statistic charts to show + * @param sessionContext context with the cache list in it + * @return + */ + private javafx.scene.Node createContent(sessionContext) { + + // get the cache list out of the context + def cacheList = sessionContext.getData("cache-list") + + // create a vbox as layout container + VBox contenPane = new VBox() + + // groovy maps for selecting the statistic numbers + def typeStats = [:] + def difficultyStats = [:] + def terrainStats = [:] + + // iterate over all the caches and count the data + for (def cache in cacheList) { + incrementStats(typeStats, cache.type) + incrementStats(difficultyStats, cache.difficulty) + incrementStats(terrainStats, cache.terrain) + } + + // create javafx chart + def typeData = FXCollections.observableArrayList() + typeStats.each() { key, value -> typeData.add(new Data(key as String, value as double)) } + def typeChart = new PieChart(typeData); + typeChart.setTitle("Number of cache types in list."); + + // create javafx chart + def difficultyData = FXCollections.observableArrayList() + difficultyTerrainValues.each { + def value = difficultyStats[it] + if (value) difficultyData.add(new Data(it, value)) + } + def difficultyChart = new PieChart(difficultyData); + difficultyChart.setTitle("Number of difficulties in list."); + + // create javafx chart + def terrainData = FXCollections.observableArrayList() + difficultyTerrainValues.each { + def value = terrainStats[it] + if (value) terrainData.add(new Data(it, value)) + } + def terrainChart = new PieChart(terrainData); + terrainChart.setTitle("Number of terrain in list."); + + // add charts to layout container + contenPane.children.addAll(typeChart, difficultyChart, terrainChart) + + // return the layout container + def scrollPane = new ScrollPane(contenPane) + scrollPane.minWidth = 600 + scrollPane.prefWidth = 600 + scrollPane.maxWidth = 600 + scrollPane.minHeight = 450 + scrollPane + } + + private void incrementStats(map, key) { + map[key] = map[key] ? map[key] + 1 : 1 + } + + private void showDialog(header, content) { + Dialog dialog = new Dialog(null, name+" ("+version+")") + dialog.setMasthead(header) + dialog.setContent(content) + dialog.show() + } +} \ No newline at end of file diff --git a/src/de/frosch95/geofrogger/application/ServiceManager.java b/src/de/frosch95/geofrogger/application/ServiceManager.java index 6926831..aab7e52 100644 --- a/src/de/frosch95/geofrogger/application/ServiceManager.java +++ b/src/de/frosch95/geofrogger/application/ServiceManager.java @@ -27,6 +27,8 @@ package de.frosch95.geofrogger.application; import de.frosch95.geofrogger.gpx.GPXReader; import de.frosch95.geofrogger.gpx.GroundspeakGPXReader; +import de.frosch95.geofrogger.plugins.PluginService; +import de.frosch95.geofrogger.plugins.PluginServiceImpl; import de.frosch95.geofrogger.service.CacheService; import de.frosch95.geofrogger.service.CacheServiceImpl; import de.frosch95.geofrogger.sql.DatabaseService; @@ -42,6 +44,7 @@ public class ServiceManager { private GPXReader gpxReader; private DatabaseService databaseService; private CacheService cacheService; + private PluginService pluginService; private ServiceManager() { // private for singleton pattern @@ -72,4 +75,11 @@ public class ServiceManager { return cacheService; } + public synchronized PluginService getPluginService() { + if (pluginService == null) { + pluginService = new PluginServiceImpl(); + } + return pluginService; + } + } diff --git a/src/de/frosch95/geofrogger/fx/geofrogger/GeofroggerController.java b/src/de/frosch95/geofrogger/fx/geofrogger/GeofroggerController.java index d3d42cc..187b18f 100644 --- a/src/de/frosch95/geofrogger/fx/geofrogger/GeofroggerController.java +++ b/src/de/frosch95/geofrogger/fx/geofrogger/GeofroggerController.java @@ -30,6 +30,8 @@ import de.frosch95.geofrogger.application.ServiceManager; import de.frosch95.geofrogger.application.SessionContext; import de.frosch95.geofrogger.gpx.GPXReader; import de.frosch95.geofrogger.model.Cache; +import de.frosch95.geofrogger.plugins.Plugin; +import de.frosch95.geofrogger.plugins.PluginService; import de.frosch95.geofrogger.service.CacheService; import de.frosch95.geofrogger.service.CacheSortField; import de.frosch95.geofrogger.service.SortDirection; @@ -39,12 +41,10 @@ import javafx.beans.property.SimpleObjectProperty; import javafx.concurrent.Service; import javafx.concurrent.Task; import javafx.event.ActionEvent; +import javafx.event.EventHandler; import javafx.fxml.FXML; import javafx.fxml.Initializable; -import javafx.scene.control.Label; -import javafx.scene.control.ProgressBar; -import javafx.scene.control.ProgressIndicator; -import javafx.scene.control.TextArea; +import javafx.scene.control.*; import javafx.stage.FileChooser; import org.controlsfx.dialog.Dialog; @@ -106,6 +106,7 @@ public class GeofroggerController implements Initializable { private final GPXReader gpxReader = ServiceManager.getInstance().getGPXReader(); private final CacheService cacheService = ServiceManager.getInstance().getCacheService(); + private final PluginService pluginService = ServiceManager.getInstance().getPluginService(); @FXML private Label leftStatus; @@ -113,6 +114,9 @@ public class GeofroggerController implements Initializable { @FXML private ProgressBar progress; + @FXML + private Menu pluginsMenu; + /** * Initializes the controller class. * @@ -121,14 +125,16 @@ public class GeofroggerController implements Initializable { */ @Override public void initialize(URL url, ResourceBundle rb) { - gpxReader.addListener((ProgressEvent event) -> { - updateStatus(event.getMessage(), event.getProgress()); - }); - cacheService.addListener((ProgressEvent event) -> { - updateStatus(event.getMessage(), event.getProgress()); - }); + List plugins = pluginService.getAllPlugins(); + for (Plugin plugin: plugins) { + MenuItem menuItem = new MenuItem(plugin.getName()+" ("+plugin.getVersion()+")"); + menuItem.setOnAction(actionEvent -> pluginService.executePlugin(plugin)); + pluginsMenu.getItems().add(menuItem); + } + gpxReader.addListener((ProgressEvent event) -> updateStatus(event.getMessage(), event.getProgress())); + cacheService.addListener((ProgressEvent event) -> updateStatus(event.getMessage(), event.getProgress())); loadFromDBService.start(); } diff --git a/src/de/frosch95/geofrogger/fx/geofrogger/geofrogger.fxml b/src/de/frosch95/geofrogger/fx/geofrogger/geofrogger.fxml index 512465c..d803399 100644 --- a/src/de/frosch95/geofrogger/fx/geofrogger/geofrogger.fxml +++ b/src/de/frosch95/geofrogger/fx/geofrogger/geofrogger.fxml @@ -18,6 +18,7 @@ + diff --git a/src/de/frosch95/geofrogger/fx/geofrogger_de.properties b/src/de/frosch95/geofrogger/fx/geofrogger_de.properties index 54ef470..6e431ce 100644 --- a/src/de/frosch95/geofrogger/fx/geofrogger_de.properties +++ b/src/de/frosch95/geofrogger/fx/geofrogger_de.properties @@ -3,6 +3,7 @@ menu.title.file = Datei menu.title.quit = Beenden menu.title.help = Hilfe menu.title.about = \u00dcber GeoFroggerFX +menu.title.plugins = Plugins menu.title.sort = Sortieren menu.title.filter = Filtern diff --git a/src/de/frosch95/geofrogger/fx/geofrogger_en.properties b/src/de/frosch95/geofrogger/fx/geofrogger_en.properties index ff0968b..21cb68a 100644 --- a/src/de/frosch95/geofrogger/fx/geofrogger_en.properties +++ b/src/de/frosch95/geofrogger/fx/geofrogger_en.properties @@ -3,6 +3,7 @@ menu.title.file = File menu.title.quit = Quit menu.title.help = Help menu.title.about = About GeoFroggerFX +menu.title.plugins = Plugins menu.title.sort = Sort menu.title.filter = Filter diff --git a/src/de/frosch95/geofrogger/plugins/Plugin.java b/src/de/frosch95/geofrogger/plugins/Plugin.java new file mode 100644 index 0000000..0e00f54 --- /dev/null +++ b/src/de/frosch95/geofrogger/plugins/Plugin.java @@ -0,0 +1,30 @@ +package de.frosch95.geofrogger.plugins; + +import java.util.Map; + +/** + * This interface defines the method a plugin has to provide to be called correctly + * + * @author abi + */ +public interface Plugin { + + /** + * @return name + */ + String getName(); + + /** + * @return version + */ + String getVersion(); + + /** + * Run the main method of the plugin. All the logic is done in this method. + * Every run method will get a context map, with all the services inside, + * to use them. + * @param context services and data + */ + void run(Map context); + +} diff --git a/src/de/frosch95/geofrogger/plugins/PluginService.java b/src/de/frosch95/geofrogger/plugins/PluginService.java new file mode 100644 index 0000000..3a58b9c --- /dev/null +++ b/src/de/frosch95/geofrogger/plugins/PluginService.java @@ -0,0 +1,18 @@ +package de.frosch95.geofrogger.plugins; + +import java.util.List; + +/** + * TODO: class description + * + * @author abi + */ +public interface PluginService { + + List getAllPlugins(); + + void executePlugin(Plugin plugin); + + + +} diff --git a/src/de/frosch95/geofrogger/plugins/PluginServiceImpl.java b/src/de/frosch95/geofrogger/plugins/PluginServiceImpl.java new file mode 100644 index 0000000..4aab363 --- /dev/null +++ b/src/de/frosch95/geofrogger/plugins/PluginServiceImpl.java @@ -0,0 +1,61 @@ +package de.frosch95.geofrogger.plugins; + +import de.frosch95.geofrogger.application.ServiceManager; +import de.frosch95.geofrogger.application.SessionContext; +import groovy.lang.GroovyClassLoader; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * TODO: class description + * + * @author abi + */ +public class PluginServiceImpl implements PluginService { + + private final GroovyClassLoader gcl = new GroovyClassLoader(); + private final ServiceManager serviceManager = ServiceManager.getInstance(); + + + @Override + public List getAllPlugins() { + + List plugins = new ArrayList<>(); + + try { + File file = new File("./plugins"); + if (!file.exists()) { + throw new IllegalArgumentException("plugins folder does not exit"); + } + + File[] pluginFiles = file.listFiles((dir, name) -> name.endsWith("Plugin.groovy")); + for (File pluginFile: pluginFiles) { + Class clazz = gcl.parseClass(pluginFile); + for (Class interf: clazz.getInterfaces()) { + if (interf.equals(Plugin.class)) { + plugins.add((Plugin)clazz.newInstance()); + break; + } + } + } + + } catch (IOException | InstantiationException | IllegalAccessException e) { + e.printStackTrace(); + } + + return plugins; + } + + @Override + public void executePlugin(final Plugin plugin) { + Map context = new HashMap<>(); + context.put("sessionContext", SessionContext.getInstance()); + context.put("cacheService", serviceManager.getCacheService()); + plugin.run(context); + } +}