[6380] | 1 | // License: GPL. For details, see LICENSE file.
|
---|
[153] | 2 | package org.openstreetmap.josm.plugins;
|
---|
| 3 |
|
---|
[293] | 4 | import static org.openstreetmap.josm.tools.I18n.tr;
|
---|
| 5 |
|
---|
[153] | 6 | import java.io.File;
|
---|
| 7 | import java.io.FileInputStream;
|
---|
| 8 | import java.io.IOException;
|
---|
[243] | 9 | import java.io.InputStream;
|
---|
[2830] | 10 | import java.lang.reflect.Constructor;
|
---|
[153] | 11 | import java.net.URL;
|
---|
[6571] | 12 | import java.text.MessageFormat;
|
---|
[167] | 13 | import java.util.ArrayList;
|
---|
[243] | 14 | import java.util.Collection;
|
---|
[293] | 15 | import java.util.LinkedList;
|
---|
[167] | 16 | import java.util.List;
|
---|
[8404] | 17 | import java.util.Locale;
|
---|
[159] | 18 | import java.util.Map;
|
---|
[11553] | 19 | import java.util.Optional;
|
---|
[159] | 20 | import java.util.TreeMap;
|
---|
| 21 | import java.util.jar.Attributes;
|
---|
[153] | 22 | import java.util.jar.JarInputStream;
|
---|
| 23 | import java.util.jar.Manifest;
|
---|
[6248] | 24 |
|
---|
[3330] | 25 | import javax.swing.ImageIcon;
|
---|
[153] | 26 |
|
---|
[243] | 27 | import org.openstreetmap.josm.Main;
|
---|
[2358] | 28 | import org.openstreetmap.josm.data.Version;
|
---|
[3330] | 29 | import org.openstreetmap.josm.tools.ImageProvider;
|
---|
[1755] | 30 | import org.openstreetmap.josm.tools.LanguageInfo;
|
---|
[5874] | 31 | import org.openstreetmap.josm.tools.Utils;
|
---|
[243] | 32 |
|
---|
[153] | 33 | /**
|
---|
| 34 | * Encapsulate general information about a plugin. This information is available
|
---|
| 35 | * without the need of loading any class from the plugin jar file.
|
---|
| 36 | *
|
---|
| 37 | * @author imi
|
---|
[7588] | 38 | * @since 153
|
---|
[153] | 39 | */
|
---|
| 40 | public class PluginInformation {
|
---|
[6843] | 41 |
|
---|
| 42 | /** The plugin jar file. */
|
---|
[8840] | 43 | public File file;
|
---|
[6843] | 44 | /** The plugin name. */
|
---|
[8840] | 45 | public String name;
|
---|
[6843] | 46 | /** The lowest JOSM version required by this plugin (from plugin list). **/
|
---|
[8840] | 47 | public int mainversion;
|
---|
[6843] | 48 | /** The lowest JOSM version required by this plugin (from locally available jar). **/
|
---|
[8840] | 49 | public int localmainversion;
|
---|
[6843] | 50 | /** The plugin class name. */
|
---|
[8840] | 51 | public String className;
|
---|
[7588] | 52 | /** Determines if the plugin is an old version loaded for incompatibility with latest JOSM (from plugin list) */
|
---|
[8840] | 53 | public boolean oldmode;
|
---|
[6843] | 54 | /** The list of required plugins, separated by ';' (from plugin list). */
|
---|
[8840] | 55 | public String requires;
|
---|
[6843] | 56 | /** The list of required plugins, separated by ';' (from locally available jar). */
|
---|
[8840] | 57 | public String localrequires;
|
---|
[6843] | 58 | /** The plugin link (for documentation). */
|
---|
[8840] | 59 | public String link;
|
---|
[6843] | 60 | /** The plugin description. */
|
---|
[8840] | 61 | public String description;
|
---|
[6843] | 62 | /** Determines if the plugin must be loaded early or not. */
|
---|
[8840] | 63 | public boolean early;
|
---|
[6843] | 64 | /** The plugin author. */
|
---|
[8840] | 65 | public String author;
|
---|
[6843] | 66 | /** The plugin stage, determining the loading sequence order of plugins. */
|
---|
[1623] | 67 | public int stage = 50;
|
---|
[6843] | 68 | /** The plugin version (from plugin list). **/
|
---|
[8840] | 69 | public String version;
|
---|
[6843] | 70 | /** The plugin version (from locally available jar). **/
|
---|
[8840] | 71 | public String localversion;
|
---|
[6843] | 72 | /** The plugin download link. */
|
---|
[8840] | 73 | public String downloadlink;
|
---|
[7588] | 74 | /** The plugin icon path inside jar. */
|
---|
[3330] | 75 | public String iconPath;
|
---|
[6843] | 76 | /** The plugin icon. */
|
---|
[10358] | 77 | private ImageProvider icon;
|
---|
[8017] | 78 | /** Plugin can be loaded at any time and not just at start. */
|
---|
[8840] | 79 | public boolean canloadatruntime;
|
---|
[7588] | 80 | /** The libraries referenced in Class-Path manifest attribute. */
|
---|
[7005] | 81 | public List<URL> libraries = new LinkedList<>();
|
---|
[7588] | 82 | /** All manifest attributes. */
|
---|
[7005] | 83 | public final Map<String, String> attr = new TreeMap<>();
|
---|
[10358] | 84 | /** Empty icon for these plugins which have none */
|
---|
| 85 | private static final ImageIcon emptyIcon = ImageProvider.getEmpty(ImageProvider.ImageSizes.LARGEICON);
|
---|
[6070] | 86 |
|
---|
[1169] | 87 | /**
|
---|
[3090] | 88 | * Creates a plugin information object by reading the plugin information from
|
---|
| 89 | * the manifest in the plugin jar.
|
---|
[3530] | 90 | *
|
---|
[3090] | 91 | * The plugin name is derived from the file name.
|
---|
[3530] | 92 | *
|
---|
[3090] | 93 | * @param file the plugin jar file
|
---|
| 94 | * @throws PluginException if reading the manifest fails
|
---|
[1169] | 95 | */
|
---|
[8510] | 96 | public PluginInformation(File file) throws PluginException {
|
---|
[1623] | 97 | this(file, file.getName().substring(0, file.getName().length()-4));
|
---|
[1169] | 98 | }
|
---|
[293] | 99 |
|
---|
[3090] | 100 | /**
|
---|
| 101 | * Creates a plugin information object for the plugin with name {@code name}.
|
---|
[5723] | 102 | * Information about the plugin is extracted from the manifest file in the plugin jar
|
---|
[3090] | 103 | * {@code file}.
|
---|
| 104 | * @param file the plugin jar
|
---|
| 105 | * @param name the plugin name
|
---|
[8291] | 106 | * @throws PluginException if reading the manifest file fails
|
---|
[3090] | 107 | */
|
---|
[5723] | 108 | public PluginInformation(File file, String name) throws PluginException {
|
---|
| 109 | if (!PluginHandler.isValidJar(file)) {
|
---|
[9983] | 110 | throw new PluginException(tr("Invalid jar file ''{0}''", file));
|
---|
[5723] | 111 | }
|
---|
[1169] | 112 | this.name = name;
|
---|
| 113 | this.file = file;
|
---|
[7033] | 114 | try (
|
---|
| 115 | FileInputStream fis = new FileInputStream(file);
|
---|
| 116 | JarInputStream jar = new JarInputStream(fis)
|
---|
| 117 | ) {
|
---|
[1623] | 118 | Manifest manifest = jar.getManifest();
|
---|
| 119 | if (manifest == null)
|
---|
[9983] | 120 | throw new PluginException(tr("The plugin file ''{0}'' does not include a Manifest.", file.toString()));
|
---|
[1767] | 121 | scanManifest(manifest, false);
|
---|
[6615] | 122 | libraries.add(0, Utils.fileToURL(file));
|
---|
[1623] | 123 | } catch (IOException e) {
|
---|
[2817] | 124 | throw new PluginException(name, e);
|
---|
[1623] | 125 | }
|
---|
| 126 | }
|
---|
[504] | 127 |
|
---|
[3090] | 128 | /**
|
---|
| 129 | * Creates a plugin information object by reading plugin information in Manifest format
|
---|
| 130 | * from the input stream {@code manifestStream}.
|
---|
[3530] | 131 | *
|
---|
[3090] | 132 | * @param manifestStream the stream to read the manifest from
|
---|
| 133 | * @param name the plugin name
|
---|
| 134 | * @param url the download URL for the plugin
|
---|
[8291] | 135 | * @throws PluginException if the plugin information can't be read from the input stream
|
---|
[3090] | 136 | */
|
---|
[2817] | 137 | public PluginInformation(InputStream manifestStream, String name, String url) throws PluginException {
|
---|
[1623] | 138 | this.name = name;
|
---|
| 139 | try {
|
---|
| 140 | Manifest manifest = new Manifest();
|
---|
| 141 | manifest.read(manifestStream);
|
---|
[8510] | 142 | if (url != null) {
|
---|
[1767] | 143 | downloadlink = url;
|
---|
[1860] | 144 | }
|
---|
[1767] | 145 | scanManifest(manifest, url != null);
|
---|
[1169] | 146 | } catch (IOException e) {
|
---|
[2817] | 147 | throw new PluginException(name, e);
|
---|
[1169] | 148 | }
|
---|
| 149 | }
|
---|
[159] | 150 |
|
---|
[3090] | 151 | /**
|
---|
| 152 | * Updates the plugin information of this plugin information object with the
|
---|
| 153 | * plugin information in a plugin information object retrieved from a plugin
|
---|
| 154 | * update site.
|
---|
[3530] | 155 | *
|
---|
[6843] | 156 | * @param other the plugin information object retrieved from the update site
|
---|
[3090] | 157 | */
|
---|
| 158 | public void updateFromPluginSite(PluginInformation other) {
|
---|
| 159 | this.mainversion = other.mainversion;
|
---|
| 160 | this.className = other.className;
|
---|
| 161 | this.requires = other.requires;
|
---|
| 162 | this.link = other.link;
|
---|
| 163 | this.description = other.description;
|
---|
| 164 | this.early = other.early;
|
---|
| 165 | this.author = other.author;
|
---|
| 166 | this.stage = other.stage;
|
---|
| 167 | this.version = other.version;
|
---|
| 168 | this.downloadlink = other.downloadlink;
|
---|
[3330] | 169 | this.icon = other.icon;
|
---|
| 170 | this.iconPath = other.iconPath;
|
---|
[8017] | 171 | this.canloadatruntime = other.canloadatruntime;
|
---|
[3090] | 172 | this.libraries = other.libraries;
|
---|
| 173 | this.attr.clear();
|
---|
| 174 | this.attr.putAll(other.attr);
|
---|
| 175 | }
|
---|
[6070] | 176 |
|
---|
[5601] | 177 | /**
|
---|
| 178 | * Updates the plugin information of this plugin information object with the
|
---|
[8943] | 179 | * plugin information in a plugin information object retrieved from a plugin jar.
|
---|
[5601] | 180 | *
|
---|
| 181 | * @param other the plugin information object retrieved from the jar file
|
---|
| 182 | * @since 5601
|
---|
| 183 | */
|
---|
| 184 | public void updateFromJar(PluginInformation other) {
|
---|
| 185 | updateLocalInfo(other);
|
---|
| 186 | if (other.icon != null) {
|
---|
| 187 | this.icon = other.icon;
|
---|
| 188 | }
|
---|
| 189 | this.early = other.early;
|
---|
| 190 | this.className = other.className;
|
---|
[8017] | 191 | this.canloadatruntime = other.canloadatruntime;
|
---|
[5601] | 192 | this.libraries = other.libraries;
|
---|
| 193 | this.stage = other.stage;
|
---|
[8943] | 194 | this.file = other.file;
|
---|
[5601] | 195 | }
|
---|
[6070] | 196 |
|
---|
[8512] | 197 | private void scanManifest(Manifest manifest, boolean oldcheck) {
|
---|
[1755] | 198 | String lang = LanguageInfo.getLanguageCodeManifest();
|
---|
[1623] | 199 | Attributes attr = manifest.getMainAttributes();
|
---|
| 200 | className = attr.getValue("Plugin-Class");
|
---|
[11553] | 201 | String s = Optional.ofNullable(attr.getValue(lang+"Plugin-Link")).orElseGet(() -> attr.getValue("Plugin-Link"));
|
---|
[10294] | 202 | if (s != null && !Utils.isValidUrl(s)) {
|
---|
| 203 | Main.info(tr("Invalid URL ''{0}'' in plugin {1}", s, name));
|
---|
| 204 | s = null;
|
---|
[3532] | 205 | }
|
---|
[1623] | 206 | link = s;
|
---|
| 207 | requires = attr.getValue("Plugin-Requires");
|
---|
| 208 | s = attr.getValue(lang+"Plugin-Description");
|
---|
[6843] | 209 | if (s == null) {
|
---|
[1623] | 210 | s = attr.getValue("Plugin-Description");
|
---|
[6843] | 211 | if (s != null) {
|
---|
[5583] | 212 | try {
|
---|
| 213 | s = tr(s);
|
---|
| 214 | } catch (IllegalArgumentException e) {
|
---|
[10627] | 215 | Main.debug(e);
|
---|
[6248] | 216 | Main.info(tr("Invalid plugin description ''{0}'' in plugin {1}", s, name));
|
---|
[5583] | 217 | }
|
---|
[1860] | 218 | }
|
---|
[6571] | 219 | } else {
|
---|
[6615] | 220 | s = MessageFormat.format(s, (Object[]) null);
|
---|
[1623] | 221 | }
|
---|
| 222 | description = s;
|
---|
| 223 | early = Boolean.parseBoolean(attr.getValue("Plugin-Early"));
|
---|
| 224 | String stageStr = attr.getValue("Plugin-Stage");
|
---|
| 225 | stage = stageStr == null ? 50 : Integer.parseInt(stageStr);
|
---|
| 226 | version = attr.getValue("Plugin-Version");
|
---|
[6843] | 227 | s = attr.getValue("Plugin-Mainversion");
|
---|
| 228 | if (s != null) {
|
---|
| 229 | try {
|
---|
| 230 | mainversion = Integer.parseInt(s);
|
---|
[8510] | 231 | } catch (NumberFormatException e) {
|
---|
[6843] | 232 | Main.warn(tr("Invalid plugin main version ''{0}'' in plugin {1}", s, name));
|
---|
| 233 | }
|
---|
| 234 | } else {
|
---|
| 235 | Main.warn(tr("Missing plugin main version in plugin {0}", name));
|
---|
[6310] | 236 | }
|
---|
[1623] | 237 | author = attr.getValue("Author");
|
---|
[3330] | 238 | iconPath = attr.getValue("Plugin-Icon");
|
---|
[8395] | 239 | if (iconPath != null) {
|
---|
[7655] | 240 | if (file != null) {
|
---|
| 241 | // extract icon from the plugin jar file
|
---|
[10358] | 242 | icon = new ImageProvider(iconPath).setArchive(file).setMaxSize(ImageProvider.ImageSizes.LARGEICON).setOptional(true);
|
---|
[7655] | 243 | } else if (iconPath.startsWith("data:")) {
|
---|
[10358] | 244 | icon = new ImageProvider(iconPath).setMaxSize(ImageProvider.ImageSizes.LARGEICON).setOptional(true);
|
---|
[7655] | 245 | }
|
---|
[3330] | 246 | }
|
---|
[8017] | 247 | canloadatruntime = Boolean.parseBoolean(attr.getValue("Plugin-Canloadatruntime"));
|
---|
[6843] | 248 | if (oldcheck && mainversion > Version.getInstance().getVersion()) {
|
---|
[2358] | 249 | int myv = Version.getInstance().getVersion();
|
---|
[6843] | 250 | for (Map.Entry<Object, Object> entry : attr.entrySet()) {
|
---|
[1767] | 251 | try {
|
---|
[8510] | 252 | String key = ((Attributes.Name) entry.getKey()).toString();
|
---|
[6843] | 253 | if (key.endsWith("_Plugin-Url")) {
|
---|
[8510] | 254 | int mv = Integer.parseInt(key.substring(0, key.length()-11));
|
---|
[6843] | 255 | if (mv <= myv && (mv > mainversion || mainversion > myv)) {
|
---|
[8510] | 256 | String v = (String) entry.getValue();
|
---|
[6083] | 257 | int i = v.indexOf(';');
|
---|
[6843] | 258 | if (i > 0) {
|
---|
[1767] | 259 | downloadlink = v.substring(i+1);
|
---|
| 260 | mainversion = mv;
|
---|
[8510] | 261 | version = v.substring(0, i);
|
---|
[1767] | 262 | oldmode = true;
|
---|
| 263 | }
|
---|
| 264 | }
|
---|
| 265 | }
|
---|
[10212] | 266 | } catch (NumberFormatException e) {
|
---|
[6843] | 267 | Main.error(e);
|
---|
| 268 | }
|
---|
[1767] | 269 | }
|
---|
| 270 | }
|
---|
[1623] | 271 |
|
---|
| 272 | String classPath = attr.getValue(Attributes.Name.CLASS_PATH);
|
---|
| 273 | if (classPath != null) {
|
---|
| 274 | for (String entry : classPath.split(" ")) {
|
---|
| 275 | File entryFile;
|
---|
[3553] | 276 | if (new File(entry).isAbsolute() || file == null) {
|
---|
[1623] | 277 | entryFile = new File(entry);
|
---|
[1860] | 278 | } else {
|
---|
[1623] | 279 | entryFile = new File(file.getParent(), entry);
|
---|
[1860] | 280 | }
|
---|
[1623] | 281 |
|
---|
[6615] | 282 | libraries.add(Utils.fileToURL(entryFile));
|
---|
[1623] | 283 | }
|
---|
| 284 | }
|
---|
[1860] | 285 | for (Object o : attr.keySet()) {
|
---|
[1623] | 286 | this.attr.put(o.toString(), attr.getValue(o.toString()));
|
---|
[1860] | 287 | }
|
---|
[1623] | 288 | }
|
---|
| 289 |
|
---|
[2817] | 290 | /**
|
---|
| 291 | * Replies the description as HTML document, including a link to a web page with
|
---|
| 292 | * more information, provided such a link is available.
|
---|
[3530] | 293 | *
|
---|
[2817] | 294 | * @return the description as HTML document
|
---|
| 295 | */
|
---|
| 296 | public String getDescriptionAsHtml() {
|
---|
[8390] | 297 | StringBuilder sb = new StringBuilder(128);
|
---|
[8379] | 298 | sb.append("<html><body>")
|
---|
[11848] | 299 | .append(description == null ? tr("no description available") : Utils.escapeReservedCharactersHTML(description));
|
---|
[2817] | 300 | if (link != null) {
|
---|
| 301 | sb.append(" <a href=\"").append(link).append("\">").append(tr("More info...")).append("</a>");
|
---|
[1860] | 302 | }
|
---|
[7582] | 303 | if (downloadlink != null
|
---|
| 304 | && !downloadlink.startsWith("http://svn.openstreetmap.org/applications/editors/josm/dist/")
|
---|
[8101] | 305 | && !downloadlink.startsWith("https://svn.openstreetmap.org/applications/editors/josm/dist/")
|
---|
[7582] | 306 | && !downloadlink.startsWith("http://trac.openstreetmap.org/browser/applications/editors/josm/dist/")
|
---|
| 307 | && !downloadlink.startsWith("https://github.com/JOSM/")) {
|
---|
[8390] | 308 | sb.append("<p> </p><p>").append(tr("<b>Plugin provided by an external source:</b> {0}", downloadlink)).append("</p>");
|
---|
[3936] | 309 | }
|
---|
[2817] | 310 | sb.append("</body></html>");
|
---|
| 311 | return sb.toString();
|
---|
[1588] | 312 | }
|
---|
| 313 |
|
---|
[1169] | 314 | /**
|
---|
[6843] | 315 | * Loads and instantiates the plugin.
|
---|
[3530] | 316 | *
|
---|
[5836] | 317 | * @param klass the plugin class
|
---|
[12322] | 318 | * @param classLoader the class loader for the plugin
|
---|
[2830] | 319 | * @return the instantiated and initialized plugin
|
---|
[6843] | 320 | * @throws PluginException if the plugin cannot be loaded or instanciated
|
---|
[12322] | 321 | * @since 12322
|
---|
[1169] | 322 | */
|
---|
[12322] | 323 | public PluginProxy load(Class<?> klass, PluginClassLoader classLoader) throws PluginException {
|
---|
[1169] | 324 | try {
|
---|
[3070] | 325 | Constructor<?> c = klass.getConstructor(PluginInformation.class);
|
---|
| 326 | Object plugin = c.newInstance(this);
|
---|
[12322] | 327 | return new PluginProxy(plugin, this, classLoader);
|
---|
[12218] | 328 | } catch (ReflectiveOperationException e) {
|
---|
[2830] | 329 | throw new PluginException(name, e);
|
---|
[1169] | 330 | }
|
---|
| 331 | }
|
---|
[157] | 332 |
|
---|
[1169] | 333 | /**
|
---|
[6843] | 334 | * Loads the class of the plugin.
|
---|
[3530] | 335 | *
|
---|
[2830] | 336 | * @param classLoader the class loader to use
|
---|
| 337 | * @return the loaded class
|
---|
[6843] | 338 | * @throws PluginException if the class cannot be loaded
|
---|
[1169] | 339 | */
|
---|
[2817] | 340 | public Class<?> loadClass(ClassLoader classLoader) throws PluginException {
|
---|
[1169] | 341 | if (className == null)
|
---|
| 342 | return null;
|
---|
[6792] | 343 | try {
|
---|
| 344 | return Class.forName(className, true, classLoader);
|
---|
[7294] | 345 | } catch (NoClassDefFoundError | ClassNotFoundException | ClassCastException e) {
|
---|
[2817] | 346 | throw new PluginException(name, e);
|
---|
[1169] | 347 | }
|
---|
| 348 | }
|
---|
[250] | 349 |
|
---|
[1169] | 350 | /**
|
---|
| 351 | * Try to find a plugin after some criterias. Extract the plugin-information
|
---|
| 352 | * from the plugin and return it. The plugin is searched in the following way:
|
---|
[6830] | 353 | *<ol>
|
---|
| 354 | *<li>first look after an MANIFEST.MF in the package org.openstreetmap.josm.plugins.<plugin name>
|
---|
[1169] | 355 | * (After removing all fancy characters from the plugin name).
|
---|
[6830] | 356 | * If found, the plugin is loaded using the bootstrap classloader.</li>
|
---|
[1169] | 357 | *<li>If not found, look for a jar file in the user specific plugin directory
|
---|
[6830] | 358 | * (~/.josm/plugins/<plugin name>.jar)</li>
|
---|
| 359 | *<li>If not found and the environment variable JOSM_RESOURCES + "/plugins/" exist, look there.</li>
|
---|
| 360 | *<li>Try for the java property josm.resources + "/plugins/" (set via java -Djosm.plugins.path=...)</li>
|
---|
[1169] | 361 | *<li>If the environment variable ALLUSERSPROFILE and APPDATA exist, look in
|
---|
[6830] | 362 | * ALLUSERSPROFILE/<the last stuff from APPDATA>/JOSM/plugins.
|
---|
[1169] | 363 | * (*sic* There is no easy way under Windows to get the All User's application
|
---|
[6830] | 364 | * directory)</li>
|
---|
[1169] | 365 | *<li>Finally, look in some typical unix paths:<ul>
|
---|
[6830] | 366 | * <li>/usr/local/share/josm/plugins/</li>
|
---|
| 367 | * <li>/usr/local/lib/josm/plugins/</li>
|
---|
| 368 | * <li>/usr/share/josm/plugins/</li>
|
---|
| 369 | * <li>/usr/lib/josm/plugins/</li></ul></li>
|
---|
| 370 | *</ol>
|
---|
[1169] | 371 | * If a plugin class or jar file is found earlier in the list but seem not to
|
---|
| 372 | * be working, an PluginException is thrown rather than continuing the search.
|
---|
| 373 | * This is so JOSM can detect broken user-provided plugins and do not go silently
|
---|
| 374 | * ignore them.
|
---|
| 375 | *
|
---|
| 376 | * The plugin is not initialized. If the plugin is a .jar file, it is not loaded
|
---|
| 377 | * (only the manifest is extracted). In the classloader-case, the class is
|
---|
| 378 | * bootstraped (e.g. static {} - declarations will run. However, nothing else is done.
|
---|
| 379 | *
|
---|
| 380 | * @param pluginName The name of the plugin (in all lowercase). E.g. "lang-de"
|
---|
| 381 | * @return Information about the plugin or <code>null</code>, if the plugin
|
---|
| 382 | * was nowhere to be found.
|
---|
| 383 | * @throws PluginException In case of broken plugins.
|
---|
| 384 | */
|
---|
| 385 | public static PluginInformation findPlugin(String pluginName) throws PluginException {
|
---|
| 386 | String name = pluginName;
|
---|
| 387 | name = name.replaceAll("[-. ]", "");
|
---|
[7033] | 388 | try (InputStream manifestStream = PluginInformation.class.getResourceAsStream("/org/openstreetmap/josm/plugins/"+name+"/MANIFEST.MF")) {
|
---|
| 389 | if (manifestStream != null) {
|
---|
| 390 | return new PluginInformation(manifestStream, pluginName, null);
|
---|
| 391 | }
|
---|
| 392 | } catch (IOException e) {
|
---|
| 393 | Main.warn(e);
|
---|
| 394 | }
|
---|
[1169] | 395 |
|
---|
[243] | 396 | Collection<String> locations = getPluginLocations();
|
---|
| 397 |
|
---|
[1169] | 398 | for (String s : locations) {
|
---|
| 399 | File pluginFile = new File(s, pluginName + ".jar");
|
---|
| 400 | if (pluginFile.exists()) {
|
---|
[6792] | 401 | return new PluginInformation(pluginFile);
|
---|
[1169] | 402 | }
|
---|
| 403 | }
|
---|
| 404 | return null;
|
---|
| 405 | }
|
---|
[243] | 406 |
|
---|
[6615] | 407 | /**
|
---|
| 408 | * Returns all possible plugin locations.
|
---|
| 409 | * @return all possible plugin locations.
|
---|
| 410 | */
|
---|
[1169] | 411 | public static Collection<String> getPluginLocations() {
|
---|
| 412 | Collection<String> locations = Main.pref.getAllPossiblePreferenceDirs();
|
---|
[7005] | 413 | Collection<String> all = new ArrayList<>(locations.size());
|
---|
[1860] | 414 | for (String s : locations) {
|
---|
[1169] | 415 | all.add(s+"plugins");
|
---|
[1860] | 416 | }
|
---|
[1169] | 417 | return all;
|
---|
[243] | 418 | }
|
---|
[2817] | 419 |
|
---|
| 420 | /**
|
---|
| 421 | * Replies true if the plugin with the given information is most likely outdated with
|
---|
| 422 | * respect to the referenceVersion.
|
---|
[3530] | 423 | *
|
---|
[2817] | 424 | * @param referenceVersion the reference version. Can be null if we don't know a
|
---|
| 425 | * reference version
|
---|
[3530] | 426 | *
|
---|
[2817] | 427 | * @return true, if the plugin needs to be updated; false, otherweise
|
---|
| 428 | */
|
---|
| 429 | public boolean isUpdateRequired(String referenceVersion) {
|
---|
[3287] | 430 | if (this.downloadlink == null) return false;
|
---|
[8510] | 431 | if (this.version == null && referenceVersion != null)
|
---|
[2817] | 432 | return true;
|
---|
[11893] | 433 | return this.version != null && !this.version.equals(referenceVersion);
|
---|
[2817] | 434 | }
|
---|
| 435 |
|
---|
| 436 | /**
|
---|
| 437 | * Replies true if this this plugin should be updated/downloaded because either
|
---|
| 438 | * it is not available locally (its local version is null) or its local version is
|
---|
| 439 | * older than the available version on the server.
|
---|
[3530] | 440 | *
|
---|
[2817] | 441 | * @return true if the plugin should be updated
|
---|
| 442 | */
|
---|
| 443 | public boolean isUpdateRequired() {
|
---|
[3287] | 444 | if (this.downloadlink == null) return false;
|
---|
[2934] | 445 | if (this.localversion == null) return true;
|
---|
[2817] | 446 | return isUpdateRequired(this.localversion);
|
---|
| 447 | }
|
---|
| 448 |
|
---|
| 449 | protected boolean matches(String filter, String value) {
|
---|
| 450 | if (filter == null) return true;
|
---|
| 451 | if (value == null) return false;
|
---|
[8404] | 452 | return value.toLowerCase(Locale.ENGLISH).contains(filter.toLowerCase(Locale.ENGLISH));
|
---|
[2817] | 453 | }
|
---|
| 454 |
|
---|
| 455 | /**
|
---|
| 456 | * Replies true if either the name, the description, or the version match (case insensitive)
|
---|
| 457 | * one of the words in filter. Replies true if filter is null.
|
---|
[3530] | 458 | *
|
---|
[2817] | 459 | * @param filter the filter expression
|
---|
| 460 | * @return true if this plugin info matches with the filter
|
---|
| 461 | */
|
---|
| 462 | public boolean matches(String filter) {
|
---|
| 463 | if (filter == null) return true;
|
---|
[6085] | 464 | String[] words = filter.split("\\s+");
|
---|
[2817] | 465 | for (String word: words) {
|
---|
| 466 | if (matches(word, name)
|
---|
| 467 | || matches(word, description)
|
---|
| 468 | || matches(word, version)
|
---|
| 469 | || matches(word, localversion))
|
---|
| 470 | return true;
|
---|
| 471 | }
|
---|
| 472 | return false;
|
---|
| 473 | }
|
---|
| 474 |
|
---|
| 475 | /**
|
---|
[6615] | 476 | * Replies the name of the plugin.
|
---|
| 477 | * @return The plugin name
|
---|
[2817] | 478 | */
|
---|
| 479 | public String getName() {
|
---|
| 480 | return name;
|
---|
| 481 | }
|
---|
| 482 |
|
---|
| 483 | /**
|
---|
| 484 | * Sets the name
|
---|
[8470] | 485 | * @param name Plugin name
|
---|
[2817] | 486 | */
|
---|
| 487 | public void setName(String name) {
|
---|
| 488 | this.name = name;
|
---|
| 489 | }
|
---|
| 490 |
|
---|
[5601] | 491 | /**
|
---|
[10358] | 492 | * Replies the plugin icon, scaled to LARGE_ICON size.
|
---|
| 493 | * @return the plugin icon, scaled to LARGE_ICON size.
|
---|
[5601] | 494 | */
|
---|
[3330] | 495 | public ImageIcon getScaledIcon() {
|
---|
[10358] | 496 | ImageIcon img = (icon != null) ? icon.get() : null;
|
---|
| 497 | if (img == null)
|
---|
[5121] | 498 | return emptyIcon;
|
---|
[10358] | 499 | return img;
|
---|
[3330] | 500 | }
|
---|
[5588] | 501 |
|
---|
| 502 | @Override
|
---|
[6890] | 503 | public final String toString() {
|
---|
[5588] | 504 | return getName();
|
---|
| 505 | }
|
---|
[5601] | 506 |
|
---|
| 507 | private static List<String> getRequiredPlugins(String pluginList) {
|
---|
[7005] | 508 | List<String> requiredPlugins = new ArrayList<>();
|
---|
[5601] | 509 | if (pluginList != null) {
|
---|
| 510 | for (String s : pluginList.split(";")) {
|
---|
| 511 | String plugin = s.trim();
|
---|
| 512 | if (!plugin.isEmpty()) {
|
---|
| 513 | requiredPlugins.add(plugin);
|
---|
| 514 | }
|
---|
| 515 | }
|
---|
| 516 | }
|
---|
| 517 | return requiredPlugins;
|
---|
| 518 | }
|
---|
[6070] | 519 |
|
---|
[5601] | 520 | /**
|
---|
[6070] | 521 | * Replies the list of plugins required by the up-to-date version of this plugin.
|
---|
[5601] | 522 | * @return List of plugins required. Empty if no plugin is required.
|
---|
| 523 | * @since 5601
|
---|
| 524 | */
|
---|
| 525 | public List<String> getRequiredPlugins() {
|
---|
| 526 | return getRequiredPlugins(requires);
|
---|
| 527 | }
|
---|
[6070] | 528 |
|
---|
[5601] | 529 | /**
|
---|
[6070] | 530 | * Replies the list of plugins required by the local instance of this plugin.
|
---|
[5601] | 531 | * @return List of plugins required. Empty if no plugin is required.
|
---|
| 532 | * @since 5601
|
---|
| 533 | */
|
---|
| 534 | public List<String> getLocalRequiredPlugins() {
|
---|
| 535 | return getRequiredPlugins(localrequires);
|
---|
| 536 | }
|
---|
[6070] | 537 |
|
---|
[5601] | 538 | /**
|
---|
[6070] | 539 | * Updates the local fields ({@link #localversion}, {@link #localmainversion}, {@link #localrequires})
|
---|
| 540 | * to values contained in the up-to-date fields ({@link #version}, {@link #mainversion}, {@link #requires})
|
---|
[5601] | 541 | * of the given PluginInformation.
|
---|
| 542 | * @param info The plugin information to get the data from.
|
---|
| 543 | * @since 5601
|
---|
| 544 | */
|
---|
| 545 | public void updateLocalInfo(PluginInformation info) {
|
---|
| 546 | if (info != null) {
|
---|
| 547 | this.localversion = info.version;
|
---|
| 548 | this.localmainversion = info.mainversion;
|
---|
| 549 | this.localrequires = info.requires;
|
---|
| 550 | }
|
---|
| 551 | }
|
---|
[243] | 552 | }
|
---|