1 package com.github.celldynamics.quimp; 2 3 import java.lang.reflect.Type; 4 import java.util.ArrayList; 5 import java.util.Collections; 6 import java.util.List; 7 8 import org.slf4j.Logger; 9 import org.slf4j.LoggerFactory; 10 11 import com.github.celldynamics.quimp.filesystem.IQuimpSerialize; 12 import com.github.celldynamics.quimp.plugin.IQuimpCorePlugin; 13 import com.github.celldynamics.quimp.plugin.IQuimpPluginSynchro; 14 import com.github.celldynamics.quimp.plugin.ParamList; 15 import com.github.celldynamics.quimp.plugin.QuimpPluginException; 16 import com.github.celldynamics.quimp.plugin.engine.PluginFactory; 17 import com.google.gson.Gson; 18 import com.google.gson.InstanceCreator; 19 20 /* 21 * //!> 22 * @startuml doc-files/SnakePluginList_1_UML.png 23 * left to right direction 24 * :User: as user 25 * user--(//Initialize//) 26 * user--(setInstance) 27 * user--(Serialize) 28 * user--(delete plugin) 29 * user--(set active) 30 * (Serialize)<|--(Handle\nconfiguration):<<extend>> 31 * @enduml 32 * 33 * @startuml doc-files/SnakePluginList_2_UML.png 34 * actor User 35 * participant SnakePluginList as slist 36 * participant Plugin as plugin 37 * participant PluginFactory as pfact 38 * participant IQuimpPlugin as iPlugin 39 * note over plugin : Internal representation\nof plugin instance 40 * note over iPlugin : external instance\nof plugin 41 * User->slist : <<create>>\nPluginFactory\nData\nViewUpdater 42 * activate slist 43 * loop all slots 44 * slist->plugin : <<create>> 45 * activate plugin 46 * note left 47 * Create empty instances of plugins 48 * end note 49 * end 50 * @enduml 51 * 52 * @startuml doc-files/SnakePluginList_3_UML.png 53 * actor User 54 * participant SnakePluginList as slist 55 * participant Plugin as plugin 56 * participant PluginFactory as pfact 57 * participant IQuimpPlugin as iPlugin 58 * note over plugin : Internal representation\nof plugin instance 59 * note over iPlugin : external instance\nof plugin 60 * activate slist 61 * activate plugin 62 * activate pfact 63 * User->slist : ""setInstance""\ni\nName\nActivity 64 * slist-->plugin : <<destroy>> 65 * destroy plugin 66 * slist -> plugin : <<create>>\nName\nActivity\nPluginFactory 67 * activate plugin 68 * plugin -> pfact : ""getInstance(Name)"" 69 * pfact -> iPlugin : <<create>> 70 * activate iPlugin 71 * pfact --> plugin : instance 72 * note left: instance is stored in\nPlugin 73 * plugin --> slist 74 * slist->plugin : ""getInstance(i)"" 75 * plugin-->slist : instance 76 * slist -> iPlugin : ""attachContext(ViewUpdater)"" 77 * @enduml 78 * 79 * @startuml doc-files/SnakePluginList_4_UML.png 80 * actor User 81 * participant SnakePluginList as slist 82 * participant Plugin as plugin 83 * participant PluginFactory as pfact 84 * participant IQuimpPlugin as iPlugin 85 * note over plugin : Internal representation\nof plugin instance 86 * note over iPlugin : external instance\nof plugin 87 * activate slist 88 * activate plugin 89 * activate pfact 90 * activate iPlugin 91 * == before Serialization == 92 * User --/ slist : save loop all slots 93 * slist->plugin : ""downloadPluginConfig()"" 94 * plugin -> iPlugin : ""getPluginConfig()"" 95 * iPlugin --> plugin: config 96 * plugin -> iPlugin : ""getVersion()"" 97 * iPlugin --> plugin : version 98 * == after serialization == 99 * User --/ slist : load 100 * note left 101 * On load fields of SnakePluginList 102 * and Plugin are restored except plugin 103 * instances 104 * end note 105 * loop all slots 106 * slist->plugin : get version 107 * plugin --> slist: version 108 * slist->plugin : get Name 109 * plugin --> slist: name 110 * note left: Those restored from load 111 * slist->slist : ""setInstance(i,Name,Activity,Config)"" 112 * activate slist 113 * destroy plugin 114 * destroy iPlugin 115 * slist->slist : ""setInstance(i,Name,Activity)"" 116 * activate slist 117 * activate plugin 118 * activate iPlugin 119 * activate slist 120 * note left: See setInstance UseCase ... 121 * slist->plugin : ""uploadPluginConfig(config)"" 122 * plugin -> iPlugin : ""setPluginConfig(config)"" 123 * end 124 * @enduml 125 * 126 * @startuml doc-files/SnakePluginList_5_UML.png 127 * actor User 128 * participant SnakePluginList as slist 129 * participant Plugin as plugin 130 * participant PluginFactory as pfact 131 * participant IQuimpPlugin as iPlugin 132 * note over plugin: Internal representation\nof plugin instance 133 * note over iPlugin : external instance\nof plugin 134 * activate slist 135 * activate plugin 136 * activate pfact 137 * activate iPlugin 138 * User --/ slist : Delete plugin 139 * destroy plugin 140 * destroy iPlugin 141 * slist->plugin : <<create>> 142 * activate plugin 143 * @enduml 144 * 145 * @startuml doc-files/SnakePluginList_6_UML.png 146 * actor User 147 * participant SnakePluginList as slist 148 * participant Plugin as plugin 149 * participant PluginFactory as pfact 150 * participant IQuimpPlugin as iPlugin 151 * note over plugin : Internal representation\nof plugin instance 152 * note over iPlugin :external instance\nof plugin 153 * activate slist 154 * activate plugin 155 * activate pfact 156 * activate iPlugin 157 * User --/ slist : setActive(i,Activity) 158 * slist->plugin : set isActive 159 * @enduml 160 * 161 * 162 * //!< 163 */ 164 165 /** 166 * Ordered list of plugins related to snake processing for one frame. 167 * 168 * <p>This class keeps configuration for all plugin slots but for one frame. If you look for global 169 * Configuration check {@link BOAState#snakePluginListSnapshots}. Note 170 * also that each plugin is instanced only once by {@link PluginFactory} so their configuration must 171 * be updated for each frame separately. 172 * 173 * <p>Related to GUI, first plugin is at index 0, etc. Keeps also UI settings activating or 174 * deactivating plugins. Produces plugins from their names using provided 175 * {@link com.github.celldynamics.quimp.plugin.engine.PluginFactory} The <tt>sPluginList</tt> is 176 * serialized 177 * (saved as JSON object). Because serialization does not touch plugins (understood as jars) 178 * directly and moreover they reference the same object among frames, their 179 * configuration and state must be stored locally in Plugin object. This task is 180 * must be 181 * accomplished on any change in plugin configuration, serialisation and deserialisation. Check 182 * {@link #afterSerialize()}, {@link #beforeSerialize()} 183 * {@link BOAState#restore(int)} and {@link BOAState#store(int)}. From plugin side it is enough to 184 * use {@link com.github.celldynamics.quimp.plugin.IQuimpPluginSynchro} interface to store its 185 * current configuration locally. 186 * For the whole stack of images, plugin stack is kept for each frame 187 * ({@link BOAState#snakePluginListSnapshots}) together with updated configuration inside 188 * {@link SnakePluginList.Plugin}. Browsing through frames upload configuration to plugin for each 189 * frame. 190 * 191 * <p>This class is serializable and it is part of QuimP config. 192 * 193 * <p>The most important use cases are:<br> 194 * <img src="doc-files/SnakePluginList_1_UML.png"/><br> 195 * 196 * <p>During initialization basic structures are created. Note that plugins are stored in 197 * intermediate 198 * class {@link SnakePluginList.Plugin} that holds current state of plugin: 199 * <ol> 200 * <li>reference to jar (obtained from PluginFactory) 201 * <li>name of plugin (name uniquely defines the plugin) 202 * <li>version of plugin (read from jar) 203 * <li>status of plugin (active or inactive, related to QuimP UI) 204 * <li>configuration - updated for each frame or on each action in plugin, always actual 205 * </ol> 206 * <br> 207 * <img src="doc-files/SnakePluginList_2_UML.png"/><br> 208 * 209 * <p>During setInstance the instance of plugin is created and assign to Plugin object <br> 210 * <img src="doc-files/SnakePluginList_3_UML.png"/><br> 211 * 212 * <p>During Serialize plugins are prepared for serialization what means saving current state of 213 * plugins like: 214 * <ol> 215 * <li>Loaded plugins (those kept in SnakePluginList only, selected by user in UI) 216 * <li>Their configuration 217 * </ol> 218 * <br> 219 * <img src="doc-files/SnakePluginList_4_UML.png"/><br> 220 * 221 * <p>During Deletion of plugin the new empty plugin is created in place of old one <br> 222 * <img src="doc-files/SnakePluginList_5_UML.png"/><br> 223 * 224 * <p>During Set Active state, the internal state of plugin is set to active. This is important for 225 * method {@link com.github.celldynamics.quimp.SnakePluginList.Plugin#isExecutable()}. <br> 226 * <img src="doc-files/SnakePluginList_6_UML.png"/><br> 227 * 228 * @see com.github.celldynamics.quimp.BOA_#run(String) 229 * @author p.baniukiewicz 230 */ 231 public class SnakePluginList implements IQuimpSerialize { 232 233 /** 234 * The Constant LOGGER. 235 */ 236 static final Logger LOGGER = LoggerFactory.getLogger(SnakePluginList.class.getName()); 237 // all other data that are necessary for plugins 238 private transient PluginFactory pluginFactory; 239 private transient ViewUpdater viewUpdater; 240 241 /** 242 * Keeps all Plugin related information and produces plugin instance using PluginFactory. 243 * 244 * <p>Fields like config and ver are used during saving so they are initialized in this time only 245 * and they are not valid during object lifetime. 246 * 247 * @author p.baniukiewicz 248 * 249 */ 250 class Plugin { 251 /** 252 * Reference to plugin instance. 253 */ 254 private transient IQuimpCorePlugin ref; 255 /** 256 * Is activate in GUI?. 257 */ 258 private boolean isActive; 259 /** 260 * Name of plugin delivered from PluginFactory. 261 */ 262 private String name; 263 /** 264 * This is local snapshot of current plugin configuration. 265 * 266 * <p>Updated on each call 267 * {@link com.github.celldynamics.quimp.ViewUpdater#updateView()} made by plugin (indirectly 268 * {@link BOA_#recalculatePlugins()} and {@link BOAState#store(int)}. Configuration is pushed to 269 * plugin on each change of displayed frame {@link BOAState#restore(int)} because all plugins 270 * with the same name are references of one instance. This field should not be null. 271 * 272 * @see #uploadPluginConfig(ParamList) 273 * @see #uploadPluginConfig() 274 * @see #downloadPluginConfig() 275 * @see SnakePluginList#downloadPluginsConfig() 276 * @see SnakePluginList#uploadPluginsConfig() 277 */ 278 private ParamList config; 279 /** 280 * Version read from plugin on save operation. 281 */ 282 private String ver; 283 284 /** 285 * Initializes empty default plugin. 286 */ 287 public Plugin() { 288 ref = null; 289 isActive = true; // Default value 290 name = ""; 291 config = new ParamList(); // no config or not supported by plugin 292 ver = ""; // no version or not supported 293 } 294 295 /** 296 * Main constructor. Creates instance of plugin name if name is known to provided 297 * PluginFactory. 298 * 299 * <p>If name is not found in registered names of plugins in provided PluginFactory pf, the 300 * reference ref will be null 301 * 302 * @param name Name of plugin to be instanced 303 * @param isActive is plugin active? 304 * @param pf PluginFactory that provides plugin objects 305 * @throws QuimpPluginException if plugin can not be instanced 306 */ 307 public Plugin(final String name, boolean isActive, final PluginFactory pf) 308 throws QuimpPluginException { 309 this.isActive = isActive; 310 ref = pf.getInstance(name); // create instance of plugin 311 if (ref == null) { 312 throw new QuimpPluginException( 313 "Plugin initialization failed. Plugin " + name + " can not be loaded or instanced"); 314 } 315 ver = ref.getVersion(); 316 this.name = name; 317 } 318 319 /** 320 * Instantiates a new plugin. 321 * 322 * @param name the name 323 * @param isActive the is active 324 * @param pf the pf 325 * @param config the config 326 * @throws QuimpPluginException the quimp plugin exception 327 */ 328 @Deprecated 329 public Plugin(final String name, boolean isActive, final PluginFactory pf, 330 final ParamList config) throws QuimpPluginException { 331 this(name, isActive, pf); 332 ref.setPluginConfig(config); 333 } 334 335 /** 336 * Copy method. 337 * 338 * <p>Returns copy of current object with some limitations. It does not copy loaded plugin 339 * (ref). Should be called after {@link #downloadPluginConfig()} to make sure that config, 340 * ver are filled correctly 341 * 342 * @return Copy of current object 343 */ 344 private Plugin getShallowCopy() { 345 Plugin ret = new Plugin(); 346 ret.isActive = this.isActive; 347 ret.name = this.name; 348 ret.config = new ParamList(this.config); // copy config 349 ret.ver = this.ver; 350 return ret; 351 } 352 353 /** 354 * Copy method. 355 * 356 * <p>Returns copy of current object. It does copy loaded plugin (ref). Should be called after 357 * {@link #downloadPluginConfig()} to make sure that config, ver are filled correctly 358 * 359 * @return Copy of current object 360 */ 361 private Plugin getDeepCopy() { 362 Plugin ret = getShallowCopy(); 363 ret.ref = this.ref; 364 return ret; 365 } 366 367 /** 368 * Check if all execution conditions are met. 369 * 370 * <p>These conditions are: 1) Plugin exist 2) Plugin is activated in UI 371 * 372 * @return true if plugin can be executed 373 */ 374 public boolean isExecutable() { 375 if (ref == null) { 376 return false; 377 } else { 378 return isActive; 379 } 380 } 381 382 /** 383 * Copies plugin configuration to local object. 384 * 385 * <p>Local copy of configuration is necessary for saving/loading. Should be called before 386 * saving to make sure that latest settings are stored. 387 */ 388 public void downloadPluginConfig() { 389 if (ref != null) { 390 ParamList tmpconfig = ref.getPluginConfig(); 391 // if plugin returns null, create empty container 392 config = tmpconfig != null ? tmpconfig : new ParamList(); 393 String tmpVer = ref.getVersion(); 394 ver = tmpVer != null ? tmpVer : ""; 395 } else { 396 LOGGER.debug("WARN: Plugin not instanced. Config not downloaded"); 397 } 398 } 399 400 /** 401 * Upload provided configuration to plugin and set it as local. 402 * 403 * @param config Configuration to upload 404 * @throws QuimpPluginException when config can not be uploaded to plugin 405 */ 406 public void uploadPluginConfig(final ParamList config) throws QuimpPluginException { 407 if (ref != null) { 408 ref.setPluginConfig(config); 409 } 410 this.config = config; 411 } 412 413 /** 414 * Upload local configuration to plugin. 415 * 416 * @throws QuimpPluginException when config can not be uploaded to plugin 417 */ 418 public void uploadPluginConfig() throws QuimpPluginException { 419 if (ref != null) { 420 ref.setPluginConfig(config); 421 } 422 } 423 424 /** 425 * Return reference to plugin loaded from jar. 426 * 427 * @return reference to jar 428 */ 429 public IQuimpCorePlugin getRef() { 430 return ref; 431 } 432 433 } 434 435 /** 436 * Holds list of plugins up to max slots allowed. 437 * 438 * <p>This list always contains valid Plugin objects but they can point to null reference ( 439 * Plugin.ref) when there is no plugin on i-th slot. Keep also plugin configuration for 440 * serialisation purposes. Configuration can be exchanged with jar plugin. 441 * 442 */ 443 private ArrayList<Plugin> sPluginList; 444 445 /** 446 * Default constructor. 447 * 448 * <p>Create empty Plugin object that refers to nothing 449 */ 450 public SnakePluginList() { 451 sPluginList = new ArrayList<Plugin>(); 452 updateRefs(null, null); 453 } 454 455 /** 456 * Main constructor. Collect all external data necessary to use plugins. 457 * 458 * @param s Number of supported plugins 459 * @param pf Deliverer of plugins 460 * @param vu ViewUpdater to be connected to plugin 461 */ 462 public SnakePluginList(int s, final PluginFactory pf, final ViewUpdater vu) { 463 this(); // initialize structures 464 for (int i = 0; i < s; i++) { 465 sPluginList.add(new Plugin()); // fill list with empty Plugins 466 } 467 // store plugin deliverer and external data that may be important for plugins 468 updateRefs(pf, vu); 469 } 470 471 /** 472 * Copy method. 473 * 474 * <p>Returns copy of current object with some limitations. It does not copy loaded plugin (ref). 475 * Should be called after {@link SnakePluginList.Plugin#downloadPluginConfig()} to make sure 476 * that config, ver are filled correctly 477 * 478 * @return Copy of current object 479 */ 480 public SnakePluginList getShallowCopy() { 481 beforeSerialize(); // get plugin config from Plugins (jars->Plugin) to fill Plugin subclass 482 SnakePluginListePluginList.html#SnakePluginList">SnakePluginList ret = new SnakePluginList(); 483 ret.updateRefs(pluginFactory, viewUpdater); // assign current external data 484 // make deep copy of the list 485 for (Plugin p : this.sPluginList) { 486 ret.sPluginList.add(p.getShallowCopy()); 487 } 488 return ret; 489 } 490 491 /** 492 * Copy method. 493 * 494 * <p>Returns copy of current object.It does copy loaded plugin (ref). It requires that plugins 495 * must be correctly instanced. This code copies plugin stack between frames. Note 496 * double afterSerialise(): 497 * 498 * <pre> 499 * <code> 500 * qState.snakePluginList.clear(); 501 * // snakePluginListSnapshots keeps only params and name, whereas deepCopy requires real 502 * // instance to download config from it. 503 * SnakePluginList previous = qState.snakePluginListSnapshots.get(frameCopyFrom - 1); 504 * // So create instances on copy first (config will be uploaded here) 505 * previous.afterSerialize(); 506 * // and make deep copy downloading config back 507 * qState.snakePluginList = previous.getDeepCopy(); 508 * qState.snakePluginList.afterSerialize(); // instance all plugins 509 * qState.store(qState.boap.frame); // copy to current snapshot 510 * 511 * recalculatePlugins(); // update screen 512 * </code> 513 * </pre> 514 * 515 * <p>All copied plugin will refer to the same jar thus they will share e.g. configuration 516 * parameters. To have independent copies {@link #afterSerialize()} should be called on the list. 517 * Refer to this code making full independent copies of plugins from current frame over all 518 * frames: 519 * 520 * <pre> 521 * <code> 522 * SnakePluginList tmp = qState.snakePluginList.getDeepCopy(); 523 * for (int i = 0; i < qState.snakePluginListSnapshots.size(); i++) { 524 * // make a deep copy 525 * qState.snakePluginListSnapshots.set(i, tmp.getDeepCopy()); 526 * // instance separate copy of jar for this plugin 527 * qState.snakePluginListSnapshots.get(i).afterSerialize(); 528 * } 529 * int cf = qState.boap.frame; 530 * for (boap.frame = 1; boap.frame <= boap.getFrames(); qState.boap.frame++) { 531 * imageGroup.updateToFrame(boap.frame); 532 * recalculatePlugins(); 533 * } 534 * qState.boap.frame = cf; 535 * imageGroup.updateToFrame(qState.boap.frame); 536 * </code> 537 * </pre> 538 * 539 * @return Copy of current object 540 */ 541 public SnakePluginList getDeepCopy() { 542 // Should be called after downloadPluginConfig()} to make sure that config, ver are filled 543 // correctly 544 downloadPluginsConfig(); // get plugin config from Plugins (jars->Plugin) to fill Plugin 545 SnakePluginListePluginList.html#SnakePluginList">SnakePluginList ret = new SnakePluginList(); 546 ret.updateRefs(pluginFactory, viewUpdater); // assign current external data 547 // make deep copy of the list 548 for (Plugin p : this.sPluginList) { 549 ret.sPluginList.add(p.getDeepCopy()); 550 } 551 return ret; 552 } 553 554 /** 555 * Updates references of external object connected in constructor. 556 * 557 * <p>External references are not copied by {@link #getShallowCopy()} thus they should be 558 * reinitialized after that operation 559 * 560 * @param pf new PluginFactory 561 * @param vu new ViewUpdater 562 */ 563 public void updateRefs(PluginFactory pf, ViewUpdater vu) { 564 this.pluginFactory = pf; 565 this.viewUpdater = vu; 566 } 567 568 /** 569 * Returns unmodifiable list of plugins. 570 * 571 * <p>Particular fields in Plugin may not be valid unless beforeSerialize() is called. 572 * 573 * @return unmodifiable list of plugins 574 */ 575 public List<Plugin> getList() { 576 return Collections.unmodifiableList(sPluginList); 577 } 578 579 /** 580 * Return i-th instance of plugin. 581 * 582 * @param i Number of plugin to return 583 * @return Instance of plugin 584 */ 585 public IQuimpCorePlugin getInstance(int i) { 586 return sPluginList.get(i).ref; 587 } 588 589 /** 590 * Return i-th plugin name. 591 * 592 * @param i Number of plugin to return 593 * @return Name of plugin 594 */ 595 public String getName(int i) { 596 return sPluginList.get(i).name; 597 } 598 599 /** 600 * Return i-th plugin version. 601 * 602 * @param i Number of plugin to return 603 * @return Version of plugin 604 */ 605 public String getVer(int i) { 606 return sPluginList.get(i).ver; 607 } 608 609 /** 610 * Return i-th plugin configuration. 611 * 612 * @param i Number of plugin to return 613 * @return Copy of configuration of plugin 614 */ 615 public ParamList getConfig(int i) { 616 if (sPluginList.get(i).config != null) { 617 return new ParamList(sPluginList.get(i).config); // TODO makes copy? of plugin configuration 618 } else { 619 return new ParamList(); // return empty list 620 } 621 } 622 623 /** 624 * Check if plugin is active. 625 * 626 * @param i Number of plugin to check 627 * @return bool if i-th plugin is active or not 628 */ 629 public boolean isActive(int i) { 630 return sPluginList.get(i).isActive; 631 } 632 633 /** 634 * Sets instance of plugin on slot i. 635 * 636 * <p>If there is other plugin there, it replaces instance keeping its selection state. Connects 637 * also ViewUpdater and data to plugin if necessary. 638 * 639 * @param i Slot to be set 640 * @param name Name of plugin - must be registered in PluginFactory or ref will be null 641 * @param act true for active plugin, false for inactive 642 * @throws QuimpPluginException When instance can not be created 643 */ 644 public void setInstance(int i, final String name, boolean act) throws QuimpPluginException { 645 646 if (name.isEmpty()) { 647 sPluginList.set(i, new Plugin()); // just create new empty plugin with no instance 648 return; 649 } 650 sPluginList.set(i, new Plugin(name, act, pluginFactory)); // create new Plugin 651 652 IQuimpCorePlugin ref = getInstance(i); 653 // connects all goods to created plugin 654 if (ref != null) { 655 if (ref instanceof IQuimpPluginSynchro) { // if it supports backward synchronization 656 ((IQuimpPluginSynchro) ref).attachContext(viewUpdater); // attach BOA context 657 } 658 } 659 } 660 661 /** 662 * Sets instance of plugin on slot i. 663 * 664 * <p>If there is other plugin there, it replaces instance keeping its selection. Connects also 665 * ViewUpdater and data to plugin if necessary. 666 * 667 * @param i Slot to be set 668 * @param name Name of plugin - must be registered in PluginFactory or ref will be null 669 * @param act true for active plugin, false for inactive 670 * @param config Configuration to connect to plugin 671 * @throws QuimpPluginException When config is not compatible or instance can not be created 672 */ 673 private void setInstance(int i, final String name, boolean act, final ParamList config) 674 throws QuimpPluginException { 675 setInstance(i, name, act); 676 try { 677 sPluginList.get(i).uploadPluginConfig(config); // and set given config as local as well 678 } catch (QuimpPluginException e) { // catch here if e.g. lack of backward comp. 679 LOGGER.warn("Plugin " + sPluginList.get(i).name + " refused provided configuration"); 680 } 681 682 } 683 684 /** 685 * Activate or deactivate plugin. 686 * 687 * @param i Slot to be set 688 * @param act true for active plugin, false for inactive 689 */ 690 public void setActive(int i, boolean act) { 691 sPluginList.get(i).isActive = act; 692 } 693 694 /** 695 * Deletes plugin from memory. 696 * 697 * @param i Number of slot to delete 698 */ 699 public void deletePlugin(int i) { 700 sPluginList.set(i, new Plugin()); 701 } 702 703 /** 704 * Deletes all plugins from list and closes theirs windows. 705 */ 706 public void clear() { 707 closeAllWindows(); 708 for (int i = 0; i < sPluginList.size(); i++) { 709 sPluginList.set(i, new Plugin()); 710 } 711 } 712 713 /** 714 * Check if list of references contains all null elements. 715 * 716 * @return true if list does not contain any valid plugin, false otherwise 717 */ 718 public boolean isRefListEmpty() { 719 for (Plugin i : sPluginList) { 720 if (i.ref != null) { 721 return false; 722 } 723 } 724 return true; 725 } 726 727 /** 728 * Download configuration from all opened plugins into internal structure. Performs transfer 729 * jar->local object. 730 * 731 * <p>Configuration is kept locally for serialisation purposes. 732 * 733 * @see #beforeSerialize() 734 */ 735 public void downloadPluginsConfig() { 736 for (Plugin i : sPluginList) { 737 i.downloadPluginConfig(); 738 } 739 } 740 741 /** 742 * Transfer locally stored config to plugins instance for current list of plugin. 743 * 744 * @see #downloadPluginsConfig() 745 */ 746 public void uploadPluginsConfig() { 747 for (Plugin i : sPluginList) { 748 try { 749 i.uploadPluginConfig(); 750 } catch (QuimpPluginException e) { 751 LOGGER.warn("Plugin " + i.name 752 + " refused provided configuration. Default values will be used on next run"); 753 } 754 } 755 } 756 757 /** 758 * Fills fields in Plugin class related to configuration and version. These fields are 759 * serialized then. 760 * 761 * <p>This method should be called directly before saving to have most recent options. 762 */ 763 @Override 764 public void beforeSerialize() { 765 downloadPluginsConfig(); 766 } 767 768 /* 769 * Restore plugins instances after deserialization. 770 * 771 * <p>On load all fields of {@link Plugin} object are restored from JSON automaticly except 772 * plugin instance. In this step the instance is created using fields populated from JSON. 773 * 774 * <p>This method masks all QuimpPluginException exceptions that allows to skip defective plugin 775 * and load all next plugins. 776 */ 777 @Override 778 public void afterSerialize() { 779 // go through list and create new Plugin using old values that were restored after loading 780 for (int i = 0; i < sPluginList.size(); i++) { 781 String savedVer = sPluginList.get(i).ver; 782 String name = getName(i); // only for exception handling to know name before setInstance 783 // sets new instance of plugin using old configuration loaded 784 // skip plugin that cannot be loaded or with wrong configuration 785 try { 786 // try load jar according to data saved in JSON 787 setInstance(i, getName(i), isActive(i), sPluginList.get(i).config); 788 } catch (QuimpPluginException e) { 789 deletePlugin(i); // delete plugin on any error 790 LOGGER.warn("Plugin name: " + name + " " + e.getMessage()); 791 } 792 // check version compatibility - only inform user 793 if (getInstance(i) != null) { 794 if (!savedVer.equals(sPluginList.get(i).ver)) { 795 LOGGER.warn("Loaded plugin (" + sPluginList.get(i).name 796 + ") is in different version than saved (" + sPluginList.get(i).ver + " vs. " 797 + savedVer + ")"); 798 } 799 } 800 } 801 } 802 803 /** 804 * Close all opened plugins windows. 805 */ 806 public void closeAllWindows() { 807 for (int i = 0; i < sPluginList.size(); i++) { 808 if (getInstance(i) != null) { 809 getInstance(i).showUi(false); 810 } 811 } 812 } 813 814 /** 815 * Return names of plugins of given type registered in PluginFactory associated with this 816 * object. 817 * 818 * @param type requested plugin type {@link PluginFactory} 819 * @return list of plugin names 820 * 821 * @see com.github.celldynamics.quimp.plugin.engine.PluginFactory#getPluginNames(int) 822 */ 823 public ArrayList<String> getPluginNames(int type) { 824 return pluginFactory.getPluginNames(type); 825 } 826 } 827 828 /** 829 * Object builder for GSon and SnakePluginList class. 830 * 831 * <p>This class is used on load JSon representation of SnakePluginList class. 832 * 833 * @author p.baniukiewicz 834 * @see Gson documentation 835 */ 836 class SnakePluginListInstanceCreator implements InstanceCreator<SnakePluginList> { 837 838 private int size; 839 private PluginFactory pf; 840 private ViewUpdater vu; 841 842 public SnakePluginListInstanceCreator(int size, final PluginFactory pf, final ViewUpdater vu) { 843 this.size = size; 844 this.pf = pf; 845 this.vu = vu; 846 } 847 848 @Override 849 public SnakePluginList createInstance(Type arg0) { 850 return new SnakePluginList(size, pf, vu); 851 } 852 853 }