001/* 002 * Copyright 2010-2015 Institut Pasteur. 003 * 004 * This file is part of Icy. 005 * 006 * Icy is free software: you can redistribute it and/or modify 007 * it under the terms of the GNU General Public License as published by 008 * the Free Software Foundation, either version 3 of the License, or 009 * (at your option) any later version. 010 * 011 * Icy is distributed in the hope that it will be useful, 012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 014 * GNU General Public License for more details. 015 * 016 * You should have received a copy of the GNU General Public License 017 * along with Icy. If not, see <http://www.gnu.org/licenses/>. 018 */ 019package icy.plugin; 020 021import icy.common.Version; 022import icy.file.FileUtil; 023import icy.file.xml.XMLPersistent; 024import icy.file.xml.XMLPersistentHelper; 025import icy.image.ImageUtil; 026import icy.network.NetworkUtil; 027import icy.network.URLUtil; 028import icy.plugin.abstract_.Plugin; 029import icy.plugin.interface_.PluginBundled; 030import icy.plugin.interface_.PluginImageAnalysis; 031import icy.preferences.RepositoryPreferences.RepositoryInfo; 032import icy.resource.ResourceUtil; 033import icy.util.ClassUtil; 034import icy.util.JarUtil; 035import icy.util.StringUtil; 036import icy.util.XMLUtil; 037 038import java.awt.Image; 039import java.net.URL; 040import java.util.ArrayList; 041import java.util.Comparator; 042import java.util.List; 043import java.util.Set; 044 045import javax.swing.ImageIcon; 046 047import org.w3c.dom.Document; 048import org.w3c.dom.Element; 049import org.w3c.dom.Node; 050 051/** 052 * <br> 053 * The plugin descriptor contains all the data needed to launch a plugin. <br> 054 * 055 * @see PluginLauncher 056 * @author Fabrice de Chaumont & Stephane 057 */ 058public class PluginDescriptor implements XMLPersistent 059{ 060 public static final int ICON_SIZE = 64; 061 public static final int IMAGE_SIZE = 256; 062 063 public static final ImageIcon DEFAULT_ICON = ResourceUtil.getImageIcon(ResourceUtil.IMAGE_PLUGIN_SMALL); 064 public static final Image DEFAULT_IMAGE = ResourceUtil.IMAGE_PLUGIN; 065 066 /** 067 * @deprecated Use {@link PluginIdent#ID_CLASSNAME} instead 068 */ 069 @Deprecated 070 public static final String ID_CLASSNAME = PluginIdent.ID_CLASSNAME; 071 /** 072 * @deprecated Use {@link PluginIdent#ID_REQUIRED_KERNEL_VERSION} instead 073 */ 074 @Deprecated 075 public static final String ID_REQUIRED_KERNEL_VERSION = PluginIdent.ID_REQUIRED_KERNEL_VERSION; 076 077 public static final String ID_URL = "url"; 078 public static final String ID_NAME = "name"; 079 080 public static final String ID_JAR_URL = "jar_url"; 081 public static final String ID_IMAGE_URL = "image_url"; 082 public static final String ID_ICON_URL = "icon_url"; 083 public static final String ID_AUTHOR = "author"; 084 public static final String ID_CHANGELOG = "changelog"; 085 public static final String ID_WEB = "web"; 086 public static final String ID_EMAIL = "email"; 087 public static final String ID_DESCRIPTION = "description"; 088 public static final String ID_DEPENDENCIES = "dependencies"; 089 public static final String ID_DEPENDENCY = "dependency"; 090 091 protected Class<? extends Plugin> pluginClass; 092 093 protected ImageIcon icon; 094 protected Image image; 095 096 protected String name; 097 protected PluginIdent ident; 098 protected String localXmlUrl; 099 protected String xmlUrl; 100 protected String jarUrl; 101 protected String imageUrl; 102 protected String iconUrl; 103 protected String author; 104 protected String web; 105 protected String email; 106 protected String desc; 107 protected String changeLog; 108 109 protected boolean enabled; 110 protected boolean descriptorLoaded; 111 protected boolean iconLoaded; 112 protected boolean imageLoaded; 113 protected boolean changeLogLoaded; 114 // boolean checkingForUpdate; 115 // boolean updateChecked; 116 // PluginDescriptor onlineDescriptor; 117 118 // private final List<String> publicClasseNames; 119 protected final List<PluginIdent> required; 120 121 // only for online descriptor 122 protected RepositoryInfo repository; 123 124 // /** 125 // * Get online plugin of specified PluginIdent<br> 126 // * Take care of "allow beta" global flag<br> 127 // * This method can take sometime !<br> 128 // */ 129 // public static PluginDescriptor getOnlinePlugin(PluginIdent ident, boolean loadImage) 130 // { 131 // PluginDescriptor betaDescriptor = null; 132 // PluginDescriptor stableDescriptor = null; 133 // 134 // try 135 // { 136 // // get beta online plugin descriptor if allowed 137 // if (PluginPreferences.getAllowBeta()) 138 // betaDescriptor = new PluginDescriptor(ident.getUrlBeta(), loadImage); 139 // } 140 // catch (Exception e) 141 // { 142 // betaDescriptor = null; 143 // } 144 // 145 // try 146 // { 147 // // get stable online plugin descriptor 148 // stableDescriptor = new PluginDescriptor(ident.getUrlStable(), loadImage); 149 // } 150 // catch (Exception e) 151 // { 152 // stableDescriptor = null; 153 // } 154 // 155 // if ((betaDescriptor != null) && ((stableDescriptor == null) || 156 // betaDescriptor.isNewerOrEqual(stableDescriptor))) 157 // return betaDescriptor; 158 // 159 // return stableDescriptor; 160 // } 161 162 /** 163 * Returns the index for the specified plugin in the specified list.<br> 164 * Returns -1 if not found. 165 */ 166 public static int getIndex(List<PluginDescriptor> list, PluginDescriptor plugin) 167 { 168 return getIndex(list, plugin.getIdent()); 169 } 170 171 /** 172 * Returns the index for the specified plugin in the specified list.<br> 173 * Returns -1 if not found. 174 */ 175 public static int getIndex(List<PluginDescriptor> list, PluginIdent ident) 176 { 177 final int size = list.size(); 178 179 for (int i = 0; i < size; i++) 180 if (list.get(i).getIdent().equals(ident)) 181 return i; 182 183 return -1; 184 } 185 186 /** 187 * Returns the index for the specified plugin in the specified list.<br> 188 * Returns -1 if not found. 189 */ 190 public static int getIndex(List<PluginDescriptor> list, String className) 191 { 192 final int size = list.size(); 193 194 for (int i = 0; i < size; i++) 195 if (list.get(i).getClassName().equals(className)) 196 return i; 197 198 return -1; 199 } 200 201 /** 202 * Returns true if the specified plugin is present in the specified list. 203 */ 204 public static boolean existInList(List<PluginDescriptor> list, PluginDescriptor plugin) 205 { 206 return existInList(list, plugin.getIdent()); 207 } 208 209 /** 210 * Returns true if the specified plugin is present in the specified list. 211 */ 212 public static boolean existInList(List<PluginDescriptor> list, PluginIdent ident) 213 { 214 return getIndex(list, ident) != -1; 215 } 216 217 /** 218 * Returns true if the specified plugin is present in the specified list. 219 */ 220 public static boolean existInList(List<PluginDescriptor> list, String className) 221 { 222 return getIndex(list, className) != -1; 223 } 224 225 public static boolean existInList(Set<PluginDescriptor> plugins, PluginIdent ident) 226 { 227 for (PluginDescriptor plugin : plugins) 228 if (plugin.getIdent().equals(ident)) 229 return true; 230 231 return false; 232 } 233 234 public static void addToList(List<PluginDescriptor> list, PluginDescriptor plugin, int position) 235 { 236 if ((plugin != null) && !existInList(list, plugin)) 237 list.add(position, plugin); 238 } 239 240 public static void addToList(List<PluginDescriptor> list, PluginDescriptor plugin) 241 { 242 if ((plugin != null) && !existInList(list, plugin)) 243 list.add(plugin); 244 } 245 246 public static boolean removeFromList(List<PluginDescriptor> list, String className) 247 { 248 for (int i = list.size() - 1; i >= 0; i--) 249 { 250 final PluginDescriptor p = list.get(i); 251 252 if (p.getClassName().equals(className)) 253 { 254 list.remove(i); 255 return true; 256 } 257 } 258 259 return false; 260 } 261 262 // public static String getPluginTypeString(int type) 263 // { 264 // if ((type >= 0) && (type < pluginTypeString.length)) 265 // return pluginTypeString[type]; 266 // 267 // return ""; 268 // } 269 270 public static ArrayList<PluginDescriptor> getPlugins(List<PluginDescriptor> list, String className) 271 { 272 final ArrayList<PluginDescriptor> result = new ArrayList<PluginDescriptor>(); 273 274 for (PluginDescriptor plugin : list) 275 if (plugin.getClassName().equals(className)) 276 result.add(plugin); 277 278 return result; 279 } 280 281 public static PluginDescriptor getPlugin(List<PluginDescriptor> list, String className) 282 { 283 for (PluginDescriptor plugin : list) 284 if (plugin.getClassName().equals(className)) 285 return plugin; 286 287 return null; 288 } 289 290 public static PluginDescriptor getPlugin(List<PluginDescriptor> list, PluginIdent ident, boolean acceptNewer) 291 { 292 if (acceptNewer) 293 { 294 for (PluginDescriptor plugin : list) 295 if (plugin.getIdent().isNewerOrEqual(ident)) 296 return plugin; 297 } 298 else 299 { 300 for (PluginDescriptor plugin : list) 301 if (plugin.getIdent().equals(ident)) 302 return plugin; 303 } 304 305 return null; 306 } 307 308 public PluginDescriptor() 309 { 310 super(); 311 312 pluginClass = null; 313 314 icon = DEFAULT_ICON; 315 image = DEFAULT_IMAGE; 316 317 localXmlUrl = ""; 318 xmlUrl = ""; 319 name = ""; 320 ident = new PluginIdent(); 321 jarUrl = ""; 322 imageUrl = ""; 323 iconUrl = ""; 324 author = ""; 325 web = ""; 326 email = ""; 327 desc = ""; 328 changeLog = ""; 329 330 required = new ArrayList<PluginIdent>(); 331 repository = null; 332 333 // default 334 enabled = true; 335 descriptorLoaded = true; 336 changeLogLoaded = true; 337 iconLoaded = true; 338 imageLoaded = true; 339 } 340 341 /** 342 * Create from class, used for local plugin. 343 */ 344 public PluginDescriptor(Class<? extends Plugin> clazz) 345 { 346 this(); 347 348 this.pluginClass = clazz; 349 350 final String baseResourceName; 351 final String baseLocalName; 352 final boolean bundled = isBundled(); 353 354 // bundled plugin ? 355 if (bundled) 356 { 357 // find original JAR file 358 final String jarPath = getPluginJarPath(); 359 360 // get base resource and local name from it 361 baseResourceName = FileUtil.getFileName(jarPath, false); 362 baseLocalName = FileUtil.setExtension(jarPath, ""); 363 } 364 else 365 { 366 baseResourceName = clazz.getSimpleName(); 367 baseLocalName = ClassUtil.getPathFromQualifiedName(clazz.getName()); 368 } 369 370 // load icon 371 URL iconUrl = clazz.getResource(baseResourceName + getIconExtension()); 372 if (iconUrl == null) 373 iconUrl = URLUtil.getURL(baseLocalName + getIconExtension()); 374 // loadIcon(url); 375 376 // load image 377 URL imageUrl = clazz.getResource(baseResourceName + getImageExtension()); 378 if (imageUrl == null) 379 imageUrl = URLUtil.getURL(baseLocalName + getImageExtension()); 380 // loadImage(url); 381 382 // load xml 383 URL xmlUrl = clazz.getResource(baseResourceName + getXMLExtension()); 384 if (xmlUrl == null) 385 xmlUrl = URLUtil.getURL(baseLocalName + getXMLExtension()); 386 387 // can't load details from XML file or bundled plugin 388 if (!loadFromXML(xmlUrl) || bundled) 389 { 390 // set default informations 391 name = pluginClass.getSimpleName(); 392 393 if (bundled) 394 desc = name + " plugin (Bundled)" + (!StringUtil.isEmpty(desc) ? "\n" + desc : ""); 395 else 396 desc = name + " plugin"; 397 } 398 399 // always overwrite class name from class object (more as bundled plugin may have incorrect one from XML file 400 ident.setClassName(pluginClass.getName()); 401 402 // overwrite image, icon url with their local equivalent 403 this.iconUrl = iconUrl.toString(); 404 this.imageUrl = imageUrl.toString(); 405 // store local XML URL 406 this.localXmlUrl = xmlUrl.toString(); 407 408 // only descriptor is loaded here 409 descriptorLoaded = true; 410 changeLogLoaded = false; 411 iconLoaded = false; 412 imageLoaded = false; 413 } 414 415 /** 416 * Create from plugin online identifier, used for online plugin only. 417 * 418 * @throws IllegalArgumentException 419 */ 420 public PluginDescriptor(PluginOnlineIdent ident, RepositoryInfo repos) throws IllegalArgumentException 421 { 422 this(); 423 424 this.ident.setClassName(ident.getClassName()); 425 this.ident.setVersion(ident.getVersion()); 426 this.ident.setRequiredKernelVersion(ident.getRequiredKernelVersion()); 427 this.xmlUrl = ident.getUrl(); 428 this.name = ident.getName(); 429 this.repository = repos; 430 431 // mark descriptor and images as not yet loaded 432 descriptorLoaded = false; 433 changeLogLoaded = false; 434 iconLoaded = false; 435 imageLoaded = false; 436 } 437 438 /** 439 * @deprecated Use {@link #loadDescriptor()} or {@link #loadAll()} instead 440 */ 441 @Deprecated 442 public boolean load(boolean loadImages) 443 { 444 if (loadDescriptor()) 445 { 446 loadChangeLog(); 447 if (loadImages) 448 return loadImages(); 449 } 450 451 return false; 452 } 453 454 /** 455 * Load descriptor informations (xmlUrl field should be correctly filled) 456 */ 457 public boolean loadDescriptor() 458 { 459 return loadDescriptor(false); 460 } 461 462 /** 463 * Load descriptor informations (xmlUrl field should be correctly filled).<br> 464 * Returns <code>false</code> if the operation failed. 465 */ 466 public boolean loadDescriptor(boolean reload) 467 { 468 // already loaded ? 469 if (descriptorLoaded && !reload) 470 return true; 471 472 // just to avoid retry indefinitely if it fails 473 descriptorLoaded = true; 474 475 // retrieve document 476 final Document document = XMLUtil.loadDocument(xmlUrl, 477 (repository != null) ? repository.getAuthenticationInfo() : null, true); 478 479 if (document != null) 480 { 481 // load xml 482 if (!loadFromXML(document.getDocumentElement())) 483 { 484 System.err.println("Can't find valid XML file from '" + xmlUrl + "' for plugin class '" 485 + ident.getClassName() + "'"); 486 return false; 487 } 488 489 return true; 490 } 491 492 // display error only for first load 493 if (!reload) 494 System.err.println( 495 "Can't load XML file from '" + xmlUrl + "' for plugin class '" + ident.getClassName() + "'"); 496 497 return false; 498 } 499 500 /** 501 * Load change log field (xmlUrl field should be correctly filled) 502 */ 503 public boolean loadChangeLog() 504 { 505 // already loaded ? 506 if (changeLogLoaded) 507 return true; 508 509 // just to avoid retry indefinitely if it fails 510 changeLogLoaded = true; 511 512 // retrieve document 513 final Document document = XMLUtil.loadDocument(xmlUrl, 514 (repository != null) ? repository.getAuthenticationInfo() : null, true); 515 516 if (document != null) 517 { 518 final Element node = document.getDocumentElement(); 519 520 if (node != null) 521 { 522 setChangeLog(XMLUtil.getElementValue(node, ID_CHANGELOG, "")); 523 return true; 524 } 525 526 System.err.println( 527 "Can't find valid XML file from '" + xmlUrl + "' for plugin class '" + ident.getClassName() + "'"); 528 } 529 530 System.err.println("Can't load XML file from '" + xmlUrl + "' for plugin class '" + ident.getClassName() + "'"); 531 532 return false; 533 } 534 535 /** 536 * Load 64x64 icon (icon url field should be correctly filled) 537 */ 538 public boolean loadIcon() 539 { 540 // already loaded ? 541 if (iconLoaded) 542 return true; 543 544 // need descriptor to be loaded first 545 loadDescriptor(); 546 // just to avoid retry indefinitely if it fails 547 iconLoaded = true; 548 549 // load icon 550 return loadIcon(URLUtil.getURL(iconUrl)); 551 } 552 553 /** 554 * Load 256x256 image (image url field should be correctly filled) 555 */ 556 public boolean loadImage() 557 { 558 // already loaded ? 559 if (imageLoaded) 560 return true; 561 562 // need descriptor to be loaded first 563 loadDescriptor(); 564 // just to avoid retry indefinitely if it fails 565 imageLoaded = true; 566 567 // load image 568 return loadImage(URLUtil.getURL(imageUrl)); 569 } 570 571 /** 572 * Load icon and image (both icon and image url fields should be correctly filled) 573 */ 574 public boolean loadImages() 575 { 576 return loadIcon() & loadImage(); 577 } 578 579 /** 580 * Load descriptor and images if not already done 581 */ 582 public boolean loadAll() 583 { 584 return loadDescriptor() & loadChangeLog() & loadImages(); 585 } 586 587 /** 588 * Check if the plugin class is an instance of (or subclass of) the specified class. 589 */ 590 public boolean isInstanceOf(Class<?> baseClazz) 591 { 592 return ClassUtil.isSubClass(pluginClass, baseClazz); 593 } 594 595 /** 596 * Return true if the plugin class is abstract 597 */ 598 public boolean isAbstract() 599 { 600 return ClassUtil.isAbstract(pluginClass); 601 } 602 603 /** 604 * Return true if the plugin class is private 605 */ 606 public boolean isPrivate() 607 { 608 return ClassUtil.isPrivate(pluginClass); 609 } 610 611 /** 612 * Return true if the plugin class is an interface 613 */ 614 public boolean isInterface() 615 { 616 return pluginClass.isInterface(); 617 } 618 619 /** 620 * return true if the plugin has an action which can be started from menu 621 */ 622 public boolean isActionable() 623 { 624 return isClassLoaded() && !isPrivate() && !isAbstract() && !isInterface() 625 && isInstanceOf(PluginImageAnalysis.class); 626 } 627 628 /** 629 * Return true if the plugin is bundled inside another plugin (mean it does not have a proper 630 * descriptor) 631 */ 632 public boolean isBundled() 633 { 634 return isClassLoaded() && isInstanceOf(PluginBundled.class); 635 } 636 637 /** 638 * Return true if the plugin is in beta state 639 */ 640 public boolean isBeta() 641 { 642 return getVersion().isBeta(); 643 } 644 645 /** 646 * Return true if this plugin is a system application plugin (declared in plugins.kernel 647 * package). 648 */ 649 public boolean isKernelPlugin() 650 { 651 return getClassName().startsWith(PluginLoader.PLUGIN_KERNEL_PACKAGE + "."); 652 } 653 654 boolean loadIcon(URL url) 655 { 656 // load icon 657 if (url != null) 658 icon = ResourceUtil.getImageIcon(ImageUtil.load(NetworkUtil.getInputStream(url, 659 (repository != null) ? repository.getAuthenticationInfo() : null, true, false), false), ICON_SIZE); 660 661 // get default icon 662 if (icon == null) 663 { 664 icon = DEFAULT_ICON; 665 return false; 666 } 667 668 return true; 669 } 670 671 boolean loadImage(URL url) 672 { 673 // load image 674 if (url != null) 675 image = ImageUtil 676 .scale(ImageUtil.load( 677 NetworkUtil.getInputStream(url, 678 (repository != null) ? repository.getAuthenticationInfo() : null, true, false), 679 false), IMAGE_SIZE, IMAGE_SIZE); 680 681 // get default image 682 if (image == null) 683 { 684 image = DEFAULT_IMAGE; 685 return false; 686 } 687 688 return true; 689 } 690 691 // public void save() 692 // { 693 // // save icon 694 // if (icon != null) 695 // ImageUtil.saveImage(ImageUtil.toRenderedImage(icon.getImage()), "png", getIconFilename()); 696 // // save image 697 // if (image != null) 698 // ImageUtil.saveImage(ImageUtil.toRenderedImage(image), "png", getImageFilename()); 699 // // save xml 700 // saveToXML(); 701 // } 702 703 public boolean loadFromXML(String path) 704 { 705 return XMLPersistentHelper.loadFromXML(this, path); 706 } 707 708 public boolean loadFromXML(URL xmlUrl) 709 { 710 return XMLPersistentHelper.loadFromXML(this, xmlUrl); 711 } 712 713 @Override 714 public boolean loadFromXML(Node node) 715 { 716 return loadFromXML(node, false); 717 } 718 719 public boolean loadFromXML(Node node, boolean loadChangeLog) 720 { 721 if (node == null) 722 return false; 723 724 // get the plugin ident 725 ident.loadFromXML(node); 726 727 setName(XMLUtil.getElementValue(node, ID_NAME, "")); 728 setXmlUrl(XMLUtil.getElementValue(node, ID_URL, "")); 729 setJarUrl(XMLUtil.getElementValue(node, ID_JAR_URL, "")); 730 setImageUrl(XMLUtil.getElementValue(node, ID_IMAGE_URL, "")); 731 setIconUrl(XMLUtil.getElementValue(node, ID_ICON_URL, "")); 732 setAuthor(XMLUtil.getElementValue(node, ID_AUTHOR, "")); 733 setWeb(XMLUtil.getElementValue(node, ID_WEB, "")); 734 setEmail(XMLUtil.getElementValue(node, ID_EMAIL, "")); 735 setDescription(XMLUtil.getElementValue(node, ID_DESCRIPTION, "")); 736 if (loadChangeLog) 737 setChangeLog(XMLUtil.getElementValue(node, ID_CHANGELOG, "")); 738 else 739 setChangeLog(""); 740 741 final Node nodeDependances = XMLUtil.getElement(node, ID_DEPENDENCIES); 742 if (nodeDependances != null) 743 { 744 final ArrayList<Node> nodesDependances = XMLUtil.getChildren(nodeDependances, ID_DEPENDENCY); 745 746 for (Node n : nodesDependances) 747 { 748 final PluginIdent ident = new PluginIdent(); 749 // required don't need URL information as we now search from classname 750 ident.loadFromXML(n); 751 if (!ident.isEmpty()) 752 required.add(ident); 753 } 754 } 755 756 return true; 757 } 758 759 public boolean saveToXML() 760 { 761 return XMLPersistentHelper.saveToXML(this, getXMLFilename()); 762 } 763 764 @Override 765 public boolean saveToXML(Node node) 766 { 767 if (node == null) 768 return false; 769 770 ident.saveToXML(node); 771 772 XMLUtil.setElementValue(node, ID_NAME, getName()); 773 XMLUtil.setElementValue(node, ID_URL, getXmlUrl()); 774 XMLUtil.setElementValue(node, ID_JAR_URL, getJarUrl()); 775 XMLUtil.setElementValue(node, ID_IMAGE_URL, getImageUrl()); 776 XMLUtil.setElementValue(node, ID_ICON_URL, getIconUrl()); 777 XMLUtil.setElementValue(node, ID_AUTHOR, getAuthor()); 778 XMLUtil.setElementValue(node, ID_WEB, getWeb()); 779 XMLUtil.setElementValue(node, ID_EMAIL, getEmail()); 780 XMLUtil.setElementValue(node, ID_DESCRIPTION, getDescription()); 781 loadChangeLog(); 782 XMLUtil.setElementValue(node, ID_CHANGELOG, getChangeLog()); 783 784 // synchronized (dateFormatter) 785 // { 786 // XMLUtil.addChildElement(root, ID_INSTALL_DATE, dateFormatter.format(installed)); 787 // XMLUtil.addChildElement(root, ID_LASTUSE_DATE, dateFormatter.format(lastUse)); 788 // } 789 790 // final Element publicClasses = XMLUtil.setElement(node, ID_PUBLIC_CLASSES); 791 // if (publicClasses != null) 792 // { 793 // XMLUtil.removeAllChilds(publicClasses); 794 // for (String className : publicClasseNames) 795 // XMLUtil.addValue(XMLUtil.addElement(publicClasses, ID_CLASSNAME), className); 796 // } 797 798 final Element dependances = XMLUtil.setElement(node, ID_DEPENDENCIES); 799 if (dependances != null) 800 { 801 XMLUtil.removeAllChildren(dependances); 802 for (PluginIdent dep : required) 803 dep.saveToXML(XMLUtil.addElement(dependances, ID_DEPENDENCY)); 804 } 805 806 return true; 807 } 808 809 public boolean isClassLoaded() 810 { 811 return pluginClass != null; 812 } 813 814 /** 815 * Returns the plugin class name.<br> 816 * Ex: "plugins.tutorial.Example1" 817 */ 818 public String getClassName() 819 { 820 return ident.getClassName(); 821 } 822 823 public String getSimpleClassName() 824 { 825 return ident.getSimpleClassName(); 826 } 827 828 /** 829 * Returns the package name of the plugin class. 830 */ 831 public String getPackageName() 832 { 833 return ident.getPackageName(); 834 } 835 836 /** 837 * Returns the minimum package name (remove "icy" or/and "plugin" header)<br> 838 */ 839 public String getSimplePackageName() 840 { 841 return ident.getSimplePackageName(); 842 } 843 844 /** 845 * Returns the author package name (first part of simple package name) 846 */ 847 public String getAuthorPackageName() 848 { 849 return ident.getAuthorPackageName(); 850 } 851 852 /** 853 * @deprecated useless method 854 */ 855 @Deprecated 856 public String getClassAsString() 857 { 858 if (pluginClass != null) 859 return pluginClass.toString(); 860 861 return ""; 862 } 863 864 /** 865 * @return the pluginClass 866 */ 867 public Class<? extends Plugin> getPluginClass() 868 { 869 return pluginClass; 870 } 871 872 /** 873 * @return the JAR file hosting this plugin (returns <code>null</code> if the plugin is not installed).<br> 874 */ 875 public String getPluginJarPath() 876 { 877 if (pluginClass != null) 878 return ClassUtil.getJarPath(pluginClass); 879 880 return null; 881 } 882 883 /** 884 * return associated filename 885 */ 886 public String getFilename() 887 { 888 return ClassUtil.getPathFromQualifiedName(getClassName()); 889 } 890 891 /** 892 * Returns the XML file extension. 893 */ 894 public String getXMLExtension() 895 { 896 return XMLUtil.FILE_DOT_EXTENSION; 897 } 898 899 /** 900 * return xml filename (local XML file) 901 */ 902 public String getXMLFilename() 903 { 904 if (!StringUtil.isEmpty(localXmlUrl)) 905 return localXmlUrl; 906 907 return getFilename() + getXMLExtension(); 908 } 909 910 /** 911 * return icon extension 912 */ 913 public String getIconExtension() 914 { 915 return "_icon.png"; 916 } 917 918 /** 919 * return icon filename 920 */ 921 public String getIconFilename() 922 { 923 return getFilename() + getIconExtension(); 924 } 925 926 /** 927 * return image extension 928 */ 929 public String getImageExtension() 930 { 931 return ".png"; 932 } 933 934 /** 935 * return image filename 936 */ 937 public String getImageFilename() 938 { 939 return getFilename() + getImageExtension(); 940 } 941 942 /** 943 * Returns the JAR file extension. 944 */ 945 public String getJarExtension() 946 { 947 return JarUtil.FILE_DOT_EXTENSION; 948 } 949 950 /** 951 * return jar filename 952 */ 953 public String getJarFilename() 954 { 955 return getFilename() + getJarExtension(); 956 } 957 958 /** 959 * @return the icon 960 */ 961 public ImageIcon getIcon() 962 { 963 loadIcon(); 964 return icon; 965 } 966 967 /** 968 * @return the icon as image 969 */ 970 public Image getIconAsImage() 971 { 972 final ImageIcon i = getIcon(); 973 974 if (i != null) 975 return i.getImage(); 976 977 return null; 978 } 979 980 /** 981 * @return the image 982 */ 983 public Image getImage() 984 { 985 loadImage(); 986 return image; 987 } 988 989 // /** 990 // * @return the lastUse 991 // */ 992 // public Date getLastUse() 993 // { 994 // return lastUse; 995 // } 996 // 997 // /** 998 // * @param lastUse 999 // * the lastUse to set 1000 // */ 1001 // public void setLastUse(Date lastUse) 1002 // { 1003 // this.lastUse = lastUse; 1004 // } 1005 1006 /** 1007 * @return the ident 1008 */ 1009 public PluginIdent getIdent() 1010 { 1011 return ident; 1012 } 1013 1014 /** 1015 * @return the name 1016 */ 1017 public String getName() 1018 { 1019 return name; 1020 } 1021 1022 /** 1023 * @return the version 1024 */ 1025 public Version getVersion() 1026 { 1027 if (ident != null) 1028 return ident.getVersion(); 1029 1030 return new Version(); 1031 } 1032 1033 // /** 1034 // * @return the url for current version 1035 // */ 1036 // public String getUrlCurrent() 1037 // { 1038 // if (ident != null) 1039 // { 1040 // final Version ver = ident.getVersion(); 1041 // 1042 // if (ver.isBeta()) 1043 // return ident.getUrlBeta(); 1044 // 1045 // return ident.getUrlStable(); 1046 // } 1047 // 1048 // return ""; 1049 // } 1050 1051 /** 1052 * @return the url 1053 */ 1054 public String getUrl() 1055 { 1056 // url is default XML url 1057 return getXmlUrl(); 1058 } 1059 1060 /** 1061 * @return the url for xml file 1062 */ 1063 public String getXmlUrl() 1064 { 1065 return xmlUrl; 1066 } 1067 1068 /** 1069 * @return the desc 1070 * @deprecated use {@link #getDescription()} instead 1071 */ 1072 @Deprecated 1073 public String getDesc() 1074 { 1075 return getDescription(); 1076 } 1077 1078 /** 1079 * @return the description 1080 */ 1081 public String getDescription() 1082 { 1083 return desc; 1084 } 1085 1086 /** 1087 * @param xmlUrl 1088 * the xmlUrl to set 1089 */ 1090 public void setXmlUrl(String xmlUrl) 1091 { 1092 this.xmlUrl = xmlUrl; 1093 } 1094 1095 /** 1096 * @param repository 1097 * the repository to set 1098 */ 1099 public void setRepository(RepositoryInfo repository) 1100 { 1101 this.repository = repository; 1102 } 1103 1104 /** 1105 * @return the jarUrl 1106 */ 1107 public String getJarUrl() 1108 { 1109 return jarUrl; 1110 } 1111 1112 /** 1113 * @param jarUrl 1114 * the jarUrl to set 1115 */ 1116 public void setJarUrl(String jarUrl) 1117 { 1118 this.jarUrl = jarUrl; 1119 } 1120 1121 /** 1122 * @return the imageUrl 1123 */ 1124 public String getImageUrl() 1125 { 1126 return imageUrl; 1127 } 1128 1129 /** 1130 * @param imageUrl 1131 * the imageUrl to set 1132 */ 1133 public void setImageUrl(String imageUrl) 1134 { 1135 this.imageUrl = imageUrl; 1136 } 1137 1138 /** 1139 * @return the iconUrl 1140 */ 1141 public String getIconUrl() 1142 { 1143 return iconUrl; 1144 } 1145 1146 /** 1147 * @param iconUrl 1148 * the iconUrl to set 1149 */ 1150 public void setIconUrl(String iconUrl) 1151 { 1152 this.iconUrl = iconUrl; 1153 } 1154 1155 /** 1156 * Returns the author's plugin name. 1157 */ 1158 public String getAuthor() 1159 { 1160 return author; 1161 } 1162 1163 /** 1164 * Returns the website url of this plugin. 1165 */ 1166 public String getWeb() 1167 { 1168 return web; 1169 } 1170 1171 /** 1172 * @return the email 1173 */ 1174 public String getEmail() 1175 { 1176 return email; 1177 } 1178 1179 /** 1180 * @return the changeLog 1181 */ 1182 public String getChangeLog() 1183 { 1184 return changeLog; 1185 } 1186 1187 /** 1188 * @deprecated Use {@link #getChangeLog()} instead 1189 */ 1190 @Deprecated 1191 public String getChangesLog() 1192 { 1193 return getChangeLog(); 1194 } 1195 1196 /** 1197 * @return the requiredKernelVersion 1198 */ 1199 public Version getRequiredKernelVersion() 1200 { 1201 return ident.getRequiredKernelVersion(); 1202 } 1203 1204 /** 1205 * Returns true if descriptor is loaded. 1206 */ 1207 public boolean isDescriptorLoaded() 1208 { 1209 return descriptorLoaded; 1210 } 1211 1212 /** 1213 * @deprecated Use {@link #isDescriptorLoaded()} instead 1214 */ 1215 @Deprecated 1216 public boolean isLoaded() 1217 { 1218 return descriptorLoaded; 1219 } 1220 1221 /** 1222 * Returns true if change log is loaded. 1223 */ 1224 public boolean isChangeLogLoaded() 1225 { 1226 return changeLogLoaded; 1227 } 1228 1229 /** 1230 * Returns true if icon is loaded. 1231 */ 1232 public boolean isIconLoaded() 1233 { 1234 return iconLoaded; 1235 } 1236 1237 /** 1238 * Returns true if image is loaded. 1239 */ 1240 public boolean isImageLoaded() 1241 { 1242 return imageLoaded; 1243 } 1244 1245 /** 1246 * Returns true if image and icon are loaded. 1247 */ 1248 public boolean isImagesLoaded() 1249 { 1250 return iconLoaded && imageLoaded; 1251 } 1252 1253 /** 1254 * Returns true if both descriptor and images are loaded. 1255 */ 1256 public boolean isAllLoaded() 1257 { 1258 return descriptorLoaded && changeLogLoaded && iconLoaded && imageLoaded; 1259 } 1260 1261 /** 1262 * @return the required 1263 */ 1264 public List<PluginIdent> getRequired() 1265 { 1266 return new ArrayList<PluginIdent>(required); 1267 } 1268 1269 public RepositoryInfo getRepository() 1270 { 1271 return repository; 1272 } 1273 1274 /** 1275 * @return the enabled 1276 */ 1277 public boolean isEnabled() 1278 { 1279 return enabled; 1280 } 1281 1282 /** 1283 * @param enabled 1284 * the enabled to set 1285 */ 1286 public void setEnabled(boolean enabled) 1287 { 1288 this.enabled = enabled; 1289 } 1290 1291 /** 1292 * Return true if plugin is installed (corresponding JAR file exist) 1293 */ 1294 public boolean isInstalled() 1295 { 1296 return FileUtil.exists(getJarFilename()); 1297 } 1298 1299 // /** 1300 // * @return the hasUpdate 1301 // */ 1302 // public boolean getHasUpdate() 1303 // { 1304 // // true if online version > local version 1305 // return (onlineDescriptor != null) && onlineDescriptor.getVersion().isGreater(getVersion()); 1306 // } 1307 // 1308 // /** 1309 // * @return the checkingForUpdate 1310 // */ 1311 // public boolean isCheckingForUpdate() 1312 // { 1313 // return checkingForUpdate; 1314 // } 1315 // 1316 // /** 1317 // * @return the onlineDescriptor 1318 // */ 1319 // public PluginDescriptor getOnlineDescriptor() 1320 // { 1321 // return onlineDescriptor; 1322 // } 1323 1324 // /** 1325 // * @return the updateChecked 1326 // */ 1327 // public boolean isUpdateChecked() 1328 // { 1329 // return updateChecked; 1330 // } 1331 // 1332 // /** 1333 // * check for update (asynchronous as it can take sometime) 1334 // */ 1335 // public void checkForUpdate() 1336 // { 1337 // if (updateChecked) 1338 // return; 1339 // 1340 // checkingForUpdate = true; 1341 // 1342 // ThreadUtil.bgRunWait(new Runnable() 1343 // { 1344 // @Override 1345 // public void run() 1346 // { 1347 // try 1348 // { 1349 // onlineDescriptor = getOnlinePlugin(getIdent(), false); 1350 // } 1351 // catch (Exception E) 1352 // { 1353 // onlineDescriptor = null; 1354 // } 1355 // finally 1356 // { 1357 // checkingForUpdate = false; 1358 // updateChecked = true; 1359 // } 1360 // } 1361 // }); 1362 // } 1363 1364 /** 1365 * @param name 1366 * the name to set 1367 */ 1368 public void setName(String name) 1369 { 1370 this.name = name; 1371 } 1372 1373 /** 1374 * @param author 1375 * the author to set 1376 */ 1377 public void setAuthor(String author) 1378 { 1379 this.author = author; 1380 } 1381 1382 /** 1383 * @param web 1384 * the web to set 1385 */ 1386 public void setWeb(String web) 1387 { 1388 this.web = web; 1389 } 1390 1391 /** 1392 * @param email 1393 * the email to set 1394 */ 1395 public void setEmail(String email) 1396 { 1397 this.email = email; 1398 } 1399 1400 /** 1401 * @param desc 1402 * the description to set 1403 */ 1404 public void setDescription(String desc) 1405 { 1406 this.desc = desc; 1407 } 1408 1409 /** 1410 * @param value 1411 * the changeLog to set 1412 */ 1413 public void setChangeLog(String value) 1414 { 1415 this.changeLog = value; 1416 } 1417 1418 /** 1419 * @deprecated use {@link #setChangeLog(String)} 1420 */ 1421 @Deprecated 1422 public void setChangesLog(String value) 1423 { 1424 setChangeLog(value); 1425 } 1426 1427 /** 1428 * Return true if specified plugin is required by current plugin 1429 */ 1430 public boolean requires(PluginDescriptor plugin) 1431 { 1432 final PluginIdent curIdent = plugin.getIdent(); 1433 1434 for (PluginIdent ident : required) 1435 if (ident.isOlderOrEqual(curIdent)) 1436 return true; 1437 1438 return false; 1439 } 1440 1441 public boolean isOlderOrEqual(PluginDescriptor plugin) 1442 { 1443 return ident.isOlderOrEqual(plugin.getIdent()); 1444 } 1445 1446 public boolean isOlder(PluginDescriptor plugin) 1447 { 1448 return ident.isOlder(plugin.getIdent()); 1449 } 1450 1451 public boolean isNewerOrEqual(PluginDescriptor plugin) 1452 { 1453 return ident.isNewerOrEqual(plugin.getIdent()); 1454 } 1455 1456 public boolean isNewer(PluginDescriptor plugin) 1457 { 1458 return ident.isNewer(plugin.getIdent()); 1459 } 1460 1461 @Override 1462 public String toString() 1463 { 1464 return getName() + " " + getVersion().toString(); 1465 } 1466 1467 @Override 1468 public boolean equals(Object obj) 1469 { 1470 if (obj instanceof PluginDescriptor) 1471 { 1472 final PluginDescriptor plug = (PluginDescriptor) obj; 1473 1474 return getClassName().equals(plug.getClassName()) && getVersion().equals(plug.getVersion()); 1475 } 1476 1477 return super.equals(obj); 1478 } 1479 1480 @Override 1481 public int hashCode() 1482 { 1483 return getClassName().hashCode() ^ getVersion().hashCode(); 1484 } 1485 1486 public static class PluginIdent implements XMLPersistent 1487 { 1488 /** 1489 * Returns the index for the specified plugin ident in the specified list.<br> 1490 * Returns -1 if not found. 1491 */ 1492 public static int getIndex(List<PluginIdent> list, PluginIdent ident) 1493 { 1494 final int size = list.size(); 1495 1496 for (int i = 0; i < size; i++) 1497 if (list.get(i).equals(ident)) 1498 return i; 1499 1500 return -1; 1501 } 1502 1503 /** 1504 * Returns the index for the specified plugin in the specified list.<br> 1505 * Returns -1 if not found. 1506 */ 1507 public static int getIndex(List<? extends PluginIdent> list, String className) 1508 { 1509 final int size = list.size(); 1510 1511 for (int i = 0; i < size; i++) 1512 if (list.get(i).getClassName().equals(className)) 1513 return i; 1514 1515 return -1; 1516 } 1517 1518 public static final String ID_CLASSNAME = "classname"; 1519 public static final String ID_VERSION = "version"; 1520 public static final String ID_REQUIRED_KERNEL_VERSION = "required_kernel_version"; 1521 1522 protected String className; 1523 protected Version version; 1524 protected Version requiredKernelVersion; 1525 1526 /** 1527 * 1528 */ 1529 public PluginIdent() 1530 { 1531 super(); 1532 1533 // default 1534 className = ""; 1535 version = new Version(); 1536 requiredKernelVersion = new Version(); 1537 } 1538 1539 public boolean loadFromXMLShort(Node node) 1540 { 1541 if (node == null) 1542 return false; 1543 1544 setClassName(XMLUtil.getElementValue(node, ID_CLASSNAME, "")); 1545 setVersion(new Version(XMLUtil.getElementValue(node, ID_VERSION, ""))); 1546 1547 return true; 1548 } 1549 1550 @Override 1551 public boolean loadFromXML(Node node) 1552 { 1553 if (!loadFromXMLShort(node)) 1554 return false; 1555 1556 setRequiredKernelVersion(new Version(XMLUtil.getElementValue(node, ID_REQUIRED_KERNEL_VERSION, ""))); 1557 1558 return true; 1559 } 1560 1561 public boolean saveToXMLShort(Node node) 1562 { 1563 if (node == null) 1564 return false; 1565 1566 XMLUtil.setElementValue(node, ID_CLASSNAME, getClassName()); 1567 XMLUtil.setElementValue(node, ID_VERSION, getVersion().toString()); 1568 1569 return true; 1570 } 1571 1572 @Override 1573 public boolean saveToXML(Node node) 1574 { 1575 if (!saveToXMLShort(node)) 1576 return false; 1577 1578 XMLUtil.setElementValue(node, ID_REQUIRED_KERNEL_VERSION, getRequiredKernelVersion().toString()); 1579 1580 return true; 1581 } 1582 1583 public boolean isEmpty() 1584 { 1585 return StringUtil.isEmpty(className) && version.isEmpty() && requiredKernelVersion.isEmpty(); 1586 } 1587 1588 /** 1589 * @return the className 1590 */ 1591 public String getClassName() 1592 { 1593 return className; 1594 } 1595 1596 /** 1597 * @param className 1598 * the className to set 1599 */ 1600 public void setClassName(String className) 1601 { 1602 this.className = className; 1603 } 1604 1605 /** 1606 * return the simple className 1607 */ 1608 public String getSimpleClassName() 1609 { 1610 return ClassUtil.getSimpleClassName(className); 1611 } 1612 1613 /** 1614 * return the package name 1615 */ 1616 public String getPackageName() 1617 { 1618 return ClassUtil.getPackageName(className); 1619 } 1620 1621 /** 1622 * return the minimum package name (remove "icy" or/and "plugin" header)<br> 1623 */ 1624 public String getSimplePackageName() 1625 { 1626 String result = getPackageName(); 1627 1628 if (result.startsWith("icy.")) 1629 result = result.substring(4); 1630 if (result.startsWith(PluginLoader.PLUGIN_PACKAGE)) 1631 result = result.substring(PluginLoader.PLUGIN_PACKAGE.length() + 1); 1632 1633 return result; 1634 } 1635 1636 /** 1637 * return the author package name (first part of simple package name) 1638 */ 1639 public String getAuthorPackageName() 1640 { 1641 final String result = getSimplePackageName(); 1642 final int index = result.indexOf('.'); 1643 1644 if (index != -1) 1645 return result.substring(0, index); 1646 1647 return result; 1648 } 1649 1650 /** 1651 * @param version 1652 * the version to set 1653 */ 1654 public void setVersion(Version version) 1655 { 1656 this.version = version; 1657 } 1658 1659 /** 1660 * @return the version 1661 */ 1662 public Version getVersion() 1663 { 1664 return version; 1665 } 1666 1667 /** 1668 * @return the requiredKernelVersion 1669 */ 1670 public Version getRequiredKernelVersion() 1671 { 1672 return requiredKernelVersion; 1673 } 1674 1675 /** 1676 * @param requiredKernelVersion 1677 * the requiredKernelVersion to set 1678 */ 1679 public void setRequiredKernelVersion(Version requiredKernelVersion) 1680 { 1681 this.requiredKernelVersion = requiredKernelVersion; 1682 } 1683 1684 public boolean isOlderOrEqual(PluginIdent ident) 1685 { 1686 return className.equals(ident.getClassName()) && version.isOlderOrEqual(ident.getVersion()); 1687 } 1688 1689 public boolean isOlder(PluginIdent ident) 1690 { 1691 return className.equals(ident.getClassName()) && version.isOlder(ident.getVersion()); 1692 } 1693 1694 public boolean isNewerOrEqual(PluginIdent ident) 1695 { 1696 return className.equals(ident.getClassName()) && version.isNewerOrEqual(ident.getVersion()); 1697 } 1698 1699 public boolean isNewer(PluginIdent ident) 1700 { 1701 return className.equals(ident.getClassName()) && version.isNewer(ident.getVersion()); 1702 } 1703 1704 @Override 1705 public boolean equals(Object obj) 1706 { 1707 if (obj instanceof PluginIdent) 1708 { 1709 final PluginIdent ident = (PluginIdent) obj; 1710 return ident.getClassName().equals(className) && ident.getVersion().equals(getVersion()); 1711 } 1712 1713 return super.equals(obj); 1714 } 1715 1716 @Override 1717 public int hashCode() 1718 { 1719 return className.hashCode() ^ version.hashCode(); 1720 } 1721 1722 @Override 1723 public String toString() 1724 { 1725 return className + " " + version.toString(); 1726 } 1727 } 1728 1729 public static class PluginOnlineIdent extends PluginIdent 1730 { 1731 protected String name; 1732 protected String url; 1733 1734 public PluginOnlineIdent() 1735 { 1736 super(); 1737 1738 name = ""; 1739 url = ""; 1740 } 1741 1742 /** 1743 * @return the name 1744 */ 1745 public String getName() 1746 { 1747 return name; 1748 } 1749 1750 public void setName(String name) 1751 { 1752 this.name = name; 1753 } 1754 1755 /** 1756 * @return the url 1757 */ 1758 public String getUrl() 1759 { 1760 return url; 1761 } 1762 1763 public void setUrl(String url) 1764 { 1765 this.url = url; 1766 } 1767 1768 @Override 1769 public boolean loadFromXML(Node node) 1770 { 1771 if (super.loadFromXML(node)) 1772 { 1773 setName(XMLUtil.getElementValue(node, PluginDescriptor.ID_NAME, "")); 1774 setUrl(XMLUtil.getElementValue(node, PluginDescriptor.ID_URL, "")); 1775 return true; 1776 } 1777 1778 return false; 1779 } 1780 1781 @Override 1782 public boolean saveToXML(Node node) 1783 { 1784 if (super.saveToXML(node)) 1785 { 1786 XMLUtil.setElementValue(node, PluginDescriptor.ID_NAME, getName()); 1787 XMLUtil.setElementValue(node, PluginDescriptor.ID_URL, getUrl()); 1788 return true; 1789 } 1790 1791 return false; 1792 } 1793 } 1794 1795 /** 1796 * Sort plugins on name with kernel plugins appearing first. 1797 */ 1798 public static class PluginKernelNameSorter implements Comparator<PluginDescriptor> 1799 { 1800 // static class 1801 public static PluginKernelNameSorter instance = new PluginKernelNameSorter(); 1802 1803 // static class 1804 private PluginKernelNameSorter() 1805 { 1806 super(); 1807 } 1808 1809 @Override 1810 public int compare(PluginDescriptor o1, PluginDescriptor o2) 1811 { 1812 final String packageName1 = o1.getPackageName(); 1813 final String packageName2 = o2.getPackageName(); 1814 1815 if (packageName1.startsWith(PluginLoader.PLUGIN_KERNEL_PACKAGE)) 1816 { 1817 if (!packageName2.startsWith(PluginLoader.PLUGIN_KERNEL_PACKAGE)) 1818 return -1; 1819 } 1820 else if (packageName2.startsWith(PluginLoader.PLUGIN_KERNEL_PACKAGE)) 1821 return 1; 1822 1823 return o1.toString().compareToIgnoreCase(o2.toString()); 1824 } 1825 } 1826 1827 /** 1828 * Sort plugins on name. 1829 */ 1830 public static class PluginNameSorter implements Comparator<PluginDescriptor> 1831 { 1832 // static class 1833 public static PluginNameSorter instance = new PluginNameSorter(); 1834 1835 // static class 1836 private PluginNameSorter() 1837 { 1838 super(); 1839 } 1840 1841 @Override 1842 public int compare(PluginDescriptor o1, PluginDescriptor o2) 1843 { 1844 return o1.toString().compareToIgnoreCase(o2.toString()); 1845 } 1846 } 1847 1848 /** 1849 * Sort plugins on class name. 1850 */ 1851 public static class PluginClassNameSorter implements Comparator<PluginDescriptor> 1852 { 1853 // static class 1854 public static PluginClassNameSorter instance = new PluginClassNameSorter(); 1855 1856 // static class 1857 private PluginClassNameSorter() 1858 { 1859 super(); 1860 } 1861 1862 @Override 1863 public int compare(PluginDescriptor o1, PluginDescriptor o2) 1864 { 1865 return o1.getClassName().compareToIgnoreCase(o2.getClassName()); 1866 } 1867 } 1868 1869}