001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.plugins;
003
004import static org.openstreetmap.josm.tools.I18n.tr;
005
006import java.io.BufferedReader;
007import java.io.ByteArrayInputStream;
008import java.io.IOException;
009import java.io.InputStream;
010import java.io.InputStreamReader;
011import java.nio.charset.StandardCharsets;
012import java.util.LinkedList;
013import java.util.List;
014
015import org.openstreetmap.josm.Main;
016
017/**
018 * A parser for the plugin list provided by a JOSM Plugin Download Site.
019 *
020 * See <a href="https://josm.openstreetmap.de/plugin">https://josm.openstreetmap.de/plugin</a>
021 * for a sample of the document. The format is a custom format, kind of mix of CSV and RFC822 style
022 * name/value-pairs.
023 *
024 */
025public class PluginListParser {
026
027    /**
028     * Creates the plugin information object
029     *
030     * @param name the plugin name
031     * @param url the plugin download url
032     * @param manifest the plugin manifest
033     * @return a plugin information object
034     * @throws PluginListParseException if plugin manifest cannot be parsed
035     */
036    public static PluginInformation createInfo(String name, String url, String manifest) throws PluginListParseException {
037        try {
038            return new PluginInformation(
039                    new ByteArrayInputStream(manifest.getBytes(StandardCharsets.UTF_8)),
040                    name.substring(0, name.length() - 4),
041                    url
042                    );
043        } catch (PluginException e) {
044            throw new PluginListParseException(tr("Failed to create plugin information from manifest for plugin ''{0}''", name), e);
045        }
046    }
047
048    /**
049     * Parses a plugin information document and replies a list of plugin information objects.
050     *
051     * See <a href="https://josm.openstreetmap.de/plugin">https://josm.openstreetmap.de/plugin</a>
052     * for a sample of the document. The format is a custom format, kind of mix of CSV and RFC822 style
053     * name/value-pairs.
054     *
055     * @param in the input stream from which to parse
056     * @return the list of plugin information objects
057     * @throws PluginListParseException if something goes wrong while parsing
058     */
059    public List<PluginInformation> parse(InputStream in) throws PluginListParseException {
060        List<PluginInformation> ret = new LinkedList<>();
061        BufferedReader r = null;
062        try {
063            r = new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8));
064            String name = null;
065            String url = null;
066            StringBuilder manifest = new StringBuilder();
067            for (String line = r.readLine(); line != null; line = r.readLine()) {
068                if (line.startsWith("\t")) {
069                    line = line.substring(1);
070                    while (line.length() > 70) {
071                        manifest.append(line.substring(0, 70)).append('\n');
072                        line = ' ' + line.substring(70);
073                    }
074                    manifest.append(line).append('\n');
075                    continue;
076                }
077                addPluginInformation(ret, name, url, manifest.toString());
078                String[] x = line.split(";");
079                if (x.length != 2)
080                    throw new IOException(tr("Illegal entry in plugin list."));
081                name = x[0];
082                url = x[1];
083                manifest = new StringBuilder();
084
085            }
086            addPluginInformation(ret, name, url, manifest.toString());
087            return ret;
088        } catch (IOException e) {
089            throw new PluginListParseException(e);
090        }
091    }
092
093    private static void addPluginInformation(List<PluginInformation> ret, String name, String url, String manifest) {
094        try {
095            if (name != null) {
096                PluginInformation info = createInfo(name, url, manifest);
097                if (info != null) {
098                    for (PluginProxy plugin : PluginHandler.pluginList) {
099                        if (plugin.getPluginInformation().name.equals(info.getName())) {
100                            info.localversion = plugin.getPluginInformation().localversion;
101                        }
102                    }
103                    ret.add(info);
104                }
105            }
106        } catch (PluginListParseException ex) {
107            Main.error(ex);
108        }
109    }
110
111}