SnakePluginList.java
package com.github.celldynamics.quimp;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.github.celldynamics.quimp.filesystem.IQuimpSerialize;
import com.github.celldynamics.quimp.plugin.IQuimpCorePlugin;
import com.github.celldynamics.quimp.plugin.IQuimpPluginSynchro;
import com.github.celldynamics.quimp.plugin.ParamList;
import com.github.celldynamics.quimp.plugin.QuimpPluginException;
import com.github.celldynamics.quimp.plugin.engine.PluginFactory;
import com.google.gson.Gson;
import com.google.gson.InstanceCreator;
/*
* //!>
* @startuml doc-files/SnakePluginList_1_UML.png
* left to right direction
* :User: as user
* user--(//Initialize//)
* user--(setInstance)
* user--(Serialize)
* user--(delete plugin)
* user--(set active)
* (Serialize)<|--(Handle\nconfiguration):<<extend>>
* @enduml
*
* @startuml doc-files/SnakePluginList_2_UML.png
* actor User
* participant SnakePluginList as slist
* participant Plugin as plugin
* participant PluginFactory as pfact
* participant IQuimpPlugin as iPlugin
* note over plugin : Internal representation\nof plugin instance
* note over iPlugin : external instance\nof plugin
* User->slist : <<create>>\nPluginFactory\nData\nViewUpdater
* activate slist
* loop all slots
* slist->plugin : <<create>>
* activate plugin
* note left
* Create empty instances of plugins
* end note
* end
* @enduml
*
* @startuml doc-files/SnakePluginList_3_UML.png
* actor User
* participant SnakePluginList as slist
* participant Plugin as plugin
* participant PluginFactory as pfact
* participant IQuimpPlugin as iPlugin
* note over plugin : Internal representation\nof plugin instance
* note over iPlugin : external instance\nof plugin
* activate slist
* activate plugin
* activate pfact
* User->slist : ""setInstance""\ni\nName\nActivity
* slist-->plugin : <<destroy>>
* destroy plugin
* slist -> plugin : <<create>>\nName\nActivity\nPluginFactory
* activate plugin
* plugin -> pfact : ""getInstance(Name)""
* pfact -> iPlugin : <<create>>
* activate iPlugin
* pfact --> plugin : instance
* note left: instance is stored in\nPlugin
* plugin --> slist
* slist->plugin : ""getInstance(i)""
* plugin-->slist : instance
* slist -> iPlugin : ""attachContext(ViewUpdater)""
* @enduml
*
* @startuml doc-files/SnakePluginList_4_UML.png
* actor User
* participant SnakePluginList as slist
* participant Plugin as plugin
* participant PluginFactory as pfact
* participant IQuimpPlugin as iPlugin
* note over plugin : Internal representation\nof plugin instance
* note over iPlugin : external instance\nof plugin
* activate slist
* activate plugin
* activate pfact
* activate iPlugin
* == before Serialization ==
* User --/ slist : save loop all slots
* slist->plugin : ""downloadPluginConfig()""
* plugin -> iPlugin : ""getPluginConfig()""
* iPlugin --> plugin: config
* plugin -> iPlugin : ""getVersion()""
* iPlugin --> plugin : version
* == after serialization ==
* User --/ slist : load
* note left
* On load fields of SnakePluginList
* and Plugin are restored except plugin
* instances
* end note
* loop all slots
* slist->plugin : get version
* plugin --> slist: version
* slist->plugin : get Name
* plugin --> slist: name
* note left: Those restored from load
* slist->slist : ""setInstance(i,Name,Activity,Config)""
* activate slist
* destroy plugin
* destroy iPlugin
* slist->slist : ""setInstance(i,Name,Activity)""
* activate slist
* activate plugin
* activate iPlugin
* activate slist
* note left: See setInstance UseCase ...
* slist->plugin : ""uploadPluginConfig(config)""
* plugin -> iPlugin : ""setPluginConfig(config)""
* end
* @enduml
*
* @startuml doc-files/SnakePluginList_5_UML.png
* actor User
* participant SnakePluginList as slist
* participant Plugin as plugin
* participant PluginFactory as pfact
* participant IQuimpPlugin as iPlugin
* note over plugin: Internal representation\nof plugin instance
* note over iPlugin : external instance\nof plugin
* activate slist
* activate plugin
* activate pfact
* activate iPlugin
* User --/ slist : Delete plugin
* destroy plugin
* destroy iPlugin
* slist->plugin : <<create>>
* activate plugin
* @enduml
*
* @startuml doc-files/SnakePluginList_6_UML.png
* actor User
* participant SnakePluginList as slist
* participant Plugin as plugin
* participant PluginFactory as pfact
* participant IQuimpPlugin as iPlugin
* note over plugin : Internal representation\nof plugin instance
* note over iPlugin :external instance\nof plugin
* activate slist
* activate plugin
* activate pfact
* activate iPlugin
* User --/ slist : setActive(i,Activity)
* slist->plugin : set isActive
* @enduml
*
*
* //!<
*/
/**
* Ordered list of plugins related to snake processing for one frame.
*
* <p>This class keeps configuration for all plugin slots but for one frame. If you look for global
* Configuration check {@link BOAState#snakePluginListSnapshots}. Note
* also that each plugin is instanced only once by {@link PluginFactory} so their configuration must
* be updated for each frame separately.
*
* <p>Related to GUI, first plugin is at index 0, etc. Keeps also UI settings activating or
* deactivating plugins. Produces plugins from their names using provided
* {@link com.github.celldynamics.quimp.plugin.engine.PluginFactory} The <tt>sPluginList</tt> is
* serialized
* (saved as JSON object). Because serialization does not touch plugins (understood as jars)
* directly and moreover they reference the same object among frames, their
* configuration and state must be stored locally in Plugin object. This task is
* must be
* accomplished on any change in plugin configuration, serialisation and deserialisation. Check
* {@link #afterSerialize()}, {@link #beforeSerialize()}
* {@link BOAState#restore(int)} and {@link BOAState#store(int)}. From plugin side it is enough to
* use {@link com.github.celldynamics.quimp.plugin.IQuimpPluginSynchro} interface to store its
* current configuration locally.
* For the whole stack of images, plugin stack is kept for each frame
* ({@link BOAState#snakePluginListSnapshots}) together with updated configuration inside
* {@link SnakePluginList.Plugin}. Browsing through frames upload configuration to plugin for each
* frame.
*
* <p>This class is serializable and it is part of QuimP config.
*
* <p>The most important use cases are:<br>
* <img src="doc-files/SnakePluginList_1_UML.png"/><br>
*
* <p>During initialization basic structures are created. Note that plugins are stored in
* intermediate
* class {@link SnakePluginList.Plugin} that holds current state of plugin:
* <ol>
* <li>reference to jar (obtained from PluginFactory)
* <li>name of plugin (name uniquely defines the plugin)
* <li>version of plugin (read from jar)
* <li>status of plugin (active or inactive, related to QuimP UI)
* <li>configuration - updated for each frame or on each action in plugin, always actual
* </ol>
* <br>
* <img src="doc-files/SnakePluginList_2_UML.png"/><br>
*
* <p>During setInstance the instance of plugin is created and assign to Plugin object <br>
* <img src="doc-files/SnakePluginList_3_UML.png"/><br>
*
* <p>During Serialize plugins are prepared for serialization what means saving current state of
* plugins like:
* <ol>
* <li>Loaded plugins (those kept in SnakePluginList only, selected by user in UI)
* <li>Their configuration
* </ol>
* <br>
* <img src="doc-files/SnakePluginList_4_UML.png"/><br>
*
* <p>During Deletion of plugin the new empty plugin is created in place of old one <br>
* <img src="doc-files/SnakePluginList_5_UML.png"/><br>
*
* <p>During Set Active state, the internal state of plugin is set to active. This is important for
* method {@link com.github.celldynamics.quimp.SnakePluginList.Plugin#isExecutable()}. <br>
* <img src="doc-files/SnakePluginList_6_UML.png"/><br>
*
* @see com.github.celldynamics.quimp.BOA_#run(String)
* @author p.baniukiewicz
*/
public class SnakePluginList implements IQuimpSerialize {
/**
* The Constant LOGGER.
*/
static final Logger LOGGER = LoggerFactory.getLogger(SnakePluginList.class.getName());
// all other data that are necessary for plugins
private transient PluginFactory pluginFactory;
private transient ViewUpdater viewUpdater;
/**
* Keeps all Plugin related information and produces plugin instance using PluginFactory.
*
* <p>Fields like config and ver are used during saving so they are initialized in this time only
* and they are not valid during object lifetime.
*
* @author p.baniukiewicz
*
*/
class Plugin {
/**
* Reference to plugin instance.
*/
private transient IQuimpCorePlugin ref;
/**
* Is activate in GUI?.
*/
private boolean isActive;
/**
* Name of plugin delivered from PluginFactory.
*/
private String name;
/**
* This is local snapshot of current plugin configuration.
*
* <p>Updated on each call
* {@link com.github.celldynamics.quimp.ViewUpdater#updateView()} made by plugin (indirectly
* {@link BOA_#recalculatePlugins()} and {@link BOAState#store(int)}. Configuration is pushed to
* plugin on each change of displayed frame {@link BOAState#restore(int)} because all plugins
* with the same name are references of one instance. This field should not be null.
*
* @see #uploadPluginConfig(ParamList)
* @see #uploadPluginConfig()
* @see #downloadPluginConfig()
* @see SnakePluginList#downloadPluginsConfig()
* @see SnakePluginList#uploadPluginsConfig()
*/
private ParamList config;
/**
* Version read from plugin on save operation.
*/
private String ver;
/**
* Initializes empty default plugin.
*/
public Plugin() {
ref = null;
isActive = true; // Default value
name = "";
config = new ParamList(); // no config or not supported by plugin
ver = ""; // no version or not supported
}
/**
* Main constructor. Creates instance of plugin name if name is known to provided
* PluginFactory.
*
* <p>If name is not found in registered names of plugins in provided PluginFactory pf, the
* reference ref will be null
*
* @param name Name of plugin to be instanced
* @param isActive is plugin active?
* @param pf PluginFactory that provides plugin objects
* @throws QuimpPluginException if plugin can not be instanced
*/
public Plugin(final String name, boolean isActive, final PluginFactory pf)
throws QuimpPluginException {
this.isActive = isActive;
ref = pf.getInstance(name); // create instance of plugin
if (ref == null) {
throw new QuimpPluginException(
"Plugin initialization failed. Plugin " + name + " can not be loaded or instanced");
}
ver = ref.getVersion();
this.name = name;
}
/**
* Instantiates a new plugin.
*
* @param name the name
* @param isActive the is active
* @param pf the pf
* @param config the config
* @throws QuimpPluginException the quimp plugin exception
*/
@Deprecated
public Plugin(final String name, boolean isActive, final PluginFactory pf,
final ParamList config) throws QuimpPluginException {
this(name, isActive, pf);
ref.setPluginConfig(config);
}
/**
* Copy method.
*
* <p>Returns copy of current object with some limitations. It does not copy loaded plugin
* (ref). Should be called after {@link #downloadPluginConfig()} to make sure that config,
* ver are filled correctly
*
* @return Copy of current object
*/
private Plugin getShallowCopy() {
Plugin ret = new Plugin();
ret.isActive = this.isActive;
ret.name = this.name;
ret.config = new ParamList(this.config); // copy config
ret.ver = this.ver;
return ret;
}
/**
* Copy method.
*
* <p>Returns copy of current object. It does copy loaded plugin (ref). Should be called after
* {@link #downloadPluginConfig()} to make sure that config, ver are filled correctly
*
* @return Copy of current object
*/
private Plugin getDeepCopy() {
Plugin ret = getShallowCopy();
ret.ref = this.ref;
return ret;
}
/**
* Check if all execution conditions are met.
*
* <p>These conditions are: 1) Plugin exist 2) Plugin is activated in UI
*
* @return true if plugin can be executed
*/
public boolean isExecutable() {
if (ref == null) {
return false;
} else {
return isActive;
}
}
/**
* Copies plugin configuration to local object.
*
* <p>Local copy of configuration is necessary for saving/loading. Should be called before
* saving to make sure that latest settings are stored.
*/
public void downloadPluginConfig() {
if (ref != null) {
ParamList tmpconfig = ref.getPluginConfig();
// if plugin returns null, create empty container
config = tmpconfig != null ? tmpconfig : new ParamList();
String tmpVer = ref.getVersion();
ver = tmpVer != null ? tmpVer : "";
} else {
LOGGER.debug("WARN: Plugin not instanced. Config not downloaded");
}
}
/**
* Upload provided configuration to plugin and set it as local.
*
* @param config Configuration to upload
* @throws QuimpPluginException when config can not be uploaded to plugin
*/
public void uploadPluginConfig(final ParamList config) throws QuimpPluginException {
if (ref != null) {
ref.setPluginConfig(config);
}
this.config = config;
}
/**
* Upload local configuration to plugin.
*
* @throws QuimpPluginException when config can not be uploaded to plugin
*/
public void uploadPluginConfig() throws QuimpPluginException {
if (ref != null) {
ref.setPluginConfig(config);
}
}
/**
* Return reference to plugin loaded from jar.
*
* @return reference to jar
*/
public IQuimpCorePlugin getRef() {
return ref;
}
}
/**
* Holds list of plugins up to max slots allowed.
*
* <p>This list always contains valid Plugin objects but they can point to null reference (
* Plugin.ref) when there is no plugin on i-th slot. Keep also plugin configuration for
* serialisation purposes. Configuration can be exchanged with jar plugin.
*
*/
private ArrayList<Plugin> sPluginList;
/**
* Default constructor.
*
* <p>Create empty Plugin object that refers to nothing
*/
public SnakePluginList() {
sPluginList = new ArrayList<Plugin>();
updateRefs(null, null);
}
/**
* Main constructor. Collect all external data necessary to use plugins.
*
* @param s Number of supported plugins
* @param pf Deliverer of plugins
* @param vu ViewUpdater to be connected to plugin
*/
public SnakePluginList(int s, final PluginFactory pf, final ViewUpdater vu) {
this(); // initialize structures
for (int i = 0; i < s; i++) {
sPluginList.add(new Plugin()); // fill list with empty Plugins
}
// store plugin deliverer and external data that may be important for plugins
updateRefs(pf, vu);
}
/**
* Copy method.
*
* <p>Returns copy of current object with some limitations. It does not copy loaded plugin (ref).
* Should be called after {@link SnakePluginList.Plugin#downloadPluginConfig()} to make sure
* that config, ver are filled correctly
*
* @return Copy of current object
*/
public SnakePluginList getShallowCopy() {
beforeSerialize(); // get plugin config from Plugins (jars->Plugin) to fill Plugin subclass
SnakePluginList ret = new SnakePluginList();
ret.updateRefs(pluginFactory, viewUpdater); // assign current external data
// make deep copy of the list
for (Plugin p : this.sPluginList) {
ret.sPluginList.add(p.getShallowCopy());
}
return ret;
}
/**
* Copy method.
*
* <p>Returns copy of current object.It does copy loaded plugin (ref). It requires that plugins
* must be correctly instanced. This code copies plugin stack between frames. Note
* double afterSerialise():
*
* <pre>
* <code>
* qState.snakePluginList.clear();
* // snakePluginListSnapshots keeps only params and name, whereas deepCopy requires real
* // instance to download config from it.
* SnakePluginList previous = qState.snakePluginListSnapshots.get(frameCopyFrom - 1);
* // So create instances on copy first (config will be uploaded here)
* previous.afterSerialize();
* // and make deep copy downloading config back
* qState.snakePluginList = previous.getDeepCopy();
* qState.snakePluginList.afterSerialize(); // instance all plugins
* qState.store(qState.boap.frame); // copy to current snapshot
*
* recalculatePlugins(); // update screen
* </code>
* </pre>
*
* <p>All copied plugin will refer to the same jar thus they will share e.g. configuration
* parameters. To have independent copies {@link #afterSerialize()} should be called on the list.
* Refer to this code making full independent copies of plugins from current frame over all
* frames:
*
* <pre>
* <code>
* SnakePluginList tmp = qState.snakePluginList.getDeepCopy();
* for (int i = 0; i < qState.snakePluginListSnapshots.size(); i++) {
* // make a deep copy
* qState.snakePluginListSnapshots.set(i, tmp.getDeepCopy());
* // instance separate copy of jar for this plugin
* qState.snakePluginListSnapshots.get(i).afterSerialize();
* }
* int cf = qState.boap.frame;
* for (boap.frame = 1; boap.frame <= boap.getFrames(); qState.boap.frame++) {
* imageGroup.updateToFrame(boap.frame);
* recalculatePlugins();
* }
* qState.boap.frame = cf;
* imageGroup.updateToFrame(qState.boap.frame);
* </code>
* </pre>
*
* @return Copy of current object
*/
public SnakePluginList getDeepCopy() {
// Should be called after downloadPluginConfig()} to make sure that config, ver are filled
// correctly
downloadPluginsConfig(); // get plugin config from Plugins (jars->Plugin) to fill Plugin
SnakePluginList ret = new SnakePluginList();
ret.updateRefs(pluginFactory, viewUpdater); // assign current external data
// make deep copy of the list
for (Plugin p : this.sPluginList) {
ret.sPluginList.add(p.getDeepCopy());
}
return ret;
}
/**
* Updates references of external object connected in constructor.
*
* <p>External references are not copied by {@link #getShallowCopy()} thus they should be
* reinitialized after that operation
*
* @param pf new PluginFactory
* @param vu new ViewUpdater
*/
public void updateRefs(PluginFactory pf, ViewUpdater vu) {
this.pluginFactory = pf;
this.viewUpdater = vu;
}
/**
* Returns unmodifiable list of plugins.
*
* <p>Particular fields in Plugin may not be valid unless beforeSerialize() is called.
*
* @return unmodifiable list of plugins
*/
public List<Plugin> getList() {
return Collections.unmodifiableList(sPluginList);
}
/**
* Return i-th instance of plugin.
*
* @param i Number of plugin to return
* @return Instance of plugin
*/
public IQuimpCorePlugin getInstance(int i) {
return sPluginList.get(i).ref;
}
/**
* Return i-th plugin name.
*
* @param i Number of plugin to return
* @return Name of plugin
*/
public String getName(int i) {
return sPluginList.get(i).name;
}
/**
* Return i-th plugin version.
*
* @param i Number of plugin to return
* @return Version of plugin
*/
public String getVer(int i) {
return sPluginList.get(i).ver;
}
/**
* Return i-th plugin configuration.
*
* @param i Number of plugin to return
* @return Copy of configuration of plugin
*/
public ParamList getConfig(int i) {
if (sPluginList.get(i).config != null) {
return new ParamList(sPluginList.get(i).config); // TODO makes copy? of plugin configuration
} else {
return new ParamList(); // return empty list
}
}
/**
* Check if plugin is active.
*
* @param i Number of plugin to check
* @return bool if i-th plugin is active or not
*/
public boolean isActive(int i) {
return sPluginList.get(i).isActive;
}
/**
* Sets instance of plugin on slot i.
*
* <p>If there is other plugin there, it replaces instance keeping its selection state. Connects
* also ViewUpdater and data to plugin if necessary.
*
* @param i Slot to be set
* @param name Name of plugin - must be registered in PluginFactory or ref will be null
* @param act true for active plugin, false for inactive
* @throws QuimpPluginException When instance can not be created
*/
public void setInstance(int i, final String name, boolean act) throws QuimpPluginException {
if (name.isEmpty()) {
sPluginList.set(i, new Plugin()); // just create new empty plugin with no instance
return;
}
sPluginList.set(i, new Plugin(name, act, pluginFactory)); // create new Plugin
IQuimpCorePlugin ref = getInstance(i);
// connects all goods to created plugin
if (ref != null) {
if (ref instanceof IQuimpPluginSynchro) { // if it supports backward synchronization
((IQuimpPluginSynchro) ref).attachContext(viewUpdater); // attach BOA context
}
}
}
/**
* Sets instance of plugin on slot i.
*
* <p>If there is other plugin there, it replaces instance keeping its selection. Connects also
* ViewUpdater and data to plugin if necessary.
*
* @param i Slot to be set
* @param name Name of plugin - must be registered in PluginFactory or ref will be null
* @param act true for active plugin, false for inactive
* @param config Configuration to connect to plugin
* @throws QuimpPluginException When config is not compatible or instance can not be created
*/
private void setInstance(int i, final String name, boolean act, final ParamList config)
throws QuimpPluginException {
setInstance(i, name, act);
try {
sPluginList.get(i).uploadPluginConfig(config); // and set given config as local as well
} catch (QuimpPluginException e) { // catch here if e.g. lack of backward comp.
LOGGER.warn("Plugin " + sPluginList.get(i).name + " refused provided configuration");
}
}
/**
* Activate or deactivate plugin.
*
* @param i Slot to be set
* @param act true for active plugin, false for inactive
*/
public void setActive(int i, boolean act) {
sPluginList.get(i).isActive = act;
}
/**
* Deletes plugin from memory.
*
* @param i Number of slot to delete
*/
public void deletePlugin(int i) {
sPluginList.set(i, new Plugin());
}
/**
* Deletes all plugins from list and closes theirs windows.
*/
public void clear() {
closeAllWindows();
for (int i = 0; i < sPluginList.size(); i++) {
sPluginList.set(i, new Plugin());
}
}
/**
* Check if list of references contains all null elements.
*
* @return true if list does not contain any valid plugin, false otherwise
*/
public boolean isRefListEmpty() {
for (Plugin i : sPluginList) {
if (i.ref != null) {
return false;
}
}
return true;
}
/**
* Download configuration from all opened plugins into internal structure. Performs transfer
* jar->local object.
*
* <p>Configuration is kept locally for serialisation purposes.
*
* @see #beforeSerialize()
*/
public void downloadPluginsConfig() {
for (Plugin i : sPluginList) {
i.downloadPluginConfig();
}
}
/**
* Transfer locally stored config to plugins instance for current list of plugin.
*
* @see #downloadPluginsConfig()
*/
public void uploadPluginsConfig() {
for (Plugin i : sPluginList) {
try {
i.uploadPluginConfig();
} catch (QuimpPluginException e) {
LOGGER.warn("Plugin " + i.name
+ " refused provided configuration. Default values will be used on next run");
}
}
}
/**
* Fills fields in Plugin class related to configuration and version. These fields are
* serialized then.
*
* <p>This method should be called directly before saving to have most recent options.
*/
@Override
public void beforeSerialize() {
downloadPluginsConfig();
}
/*
* Restore plugins instances after deserialization.
*
* <p>On load all fields of {@link Plugin} object are restored from JSON automaticly except
* plugin instance. In this step the instance is created using fields populated from JSON.
*
* <p>This method masks all QuimpPluginException exceptions that allows to skip defective plugin
* and load all next plugins.
*/
@Override
public void afterSerialize() {
// go through list and create new Plugin using old values that were restored after loading
for (int i = 0; i < sPluginList.size(); i++) {
String savedVer = sPluginList.get(i).ver;
String name = getName(i); // only for exception handling to know name before setInstance
// sets new instance of plugin using old configuration loaded
// skip plugin that cannot be loaded or with wrong configuration
try {
// try load jar according to data saved in JSON
setInstance(i, getName(i), isActive(i), sPluginList.get(i).config);
} catch (QuimpPluginException e) {
deletePlugin(i); // delete plugin on any error
LOGGER.warn("Plugin name: " + name + " " + e.getMessage());
}
// check version compatibility - only inform user
if (getInstance(i) != null) {
if (!savedVer.equals(sPluginList.get(i).ver)) {
LOGGER.warn("Loaded plugin (" + sPluginList.get(i).name
+ ") is in different version than saved (" + sPluginList.get(i).ver + " vs. "
+ savedVer + ")");
}
}
}
}
/**
* Close all opened plugins windows.
*/
public void closeAllWindows() {
for (int i = 0; i < sPluginList.size(); i++) {
if (getInstance(i) != null) {
getInstance(i).showUi(false);
}
}
}
/**
* Return names of plugins of given type registered in PluginFactory associated with this
* object.
*
* @param type requested plugin type {@link PluginFactory}
* @return list of plugin names
*
* @see com.github.celldynamics.quimp.plugin.engine.PluginFactory#getPluginNames(int)
*/
public ArrayList<String> getPluginNames(int type) {
return pluginFactory.getPluginNames(type);
}
}
/**
* Object builder for GSon and SnakePluginList class.
*
* <p>This class is used on load JSon representation of SnakePluginList class.
*
* @author p.baniukiewicz
* @see Gson documentation
*/
class SnakePluginListInstanceCreator implements InstanceCreator<SnakePluginList> {
private int size;
private PluginFactory pf;
private ViewUpdater vu;
public SnakePluginListInstanceCreator(int size, final PluginFactory pf, final ViewUpdater vu) {
this.size = size;
this.pf = pf;
this.vu = vu;
}
@Override
public SnakePluginList createInstance(Type arg0) {
return new SnakePluginList(size, pf, vu);
}
}