source: josm/trunk/src/org/openstreetmap/josm/gui/GettingStarted.java @ 1480

Last change on this file since 1480 was 1480, checked in by stoecker, 10 years ago

added user agent

  • Property svn:eol-style set to native
File size: 12.8 KB
Line 
1// License: GPL. See LICENSE file for details.
2
3package org.openstreetmap.josm.gui;
4
5import static org.openstreetmap.josm.tools.I18n.tr;
6
7import java.io.IOException;
8import java.util.ArrayList;
9import java.util.concurrent.Executors;
10import java.util.concurrent.ExecutorService;
11import java.util.concurrent.Callable;
12import java.util.concurrent.Future;
13import java.util.regex.Matcher;
14import java.util.regex.Pattern;
15
16import java.awt.BorderLayout;
17import java.awt.EventQueue;
18
19import javax.swing.JScrollPane;
20import javax.swing.JEditorPane;
21import javax.swing.JPanel;
22import javax.swing.event.HyperlinkEvent;
23import javax.swing.event.HyperlinkListener;
24import javax.swing.border.EmptyBorder;
25
26import org.openstreetmap.josm.Main;
27import org.openstreetmap.josm.io.CacheCustomContent;
28import org.openstreetmap.josm.tools.OpenBrowser;
29import org.openstreetmap.josm.tools.WikiReader;
30import org.openstreetmap.josm.actions.AboutAction;
31
32public class GettingStarted extends JPanel {
33    private String content = "";
34    static private String styles = "<style type=\"text/css\">\n"+
35            "body { font-family: sans-serif; font-weight: bold; }\n"+
36            "h1 {text-align: center;}\n"+
37            "</style>\n";
38
39    public class LinkGeneral extends JEditorPane implements HyperlinkListener {
40        public LinkGeneral(String text) {
41            setContentType("text/html");
42            setText(text);
43            setEditable(false);
44            setOpaque(false);
45            addHyperlinkListener(this);
46        }
47        public void hyperlinkUpdate(HyperlinkEvent e) {
48            if (e.getEventType() == HyperlinkEvent.EventType.ACTIVATED) {
49                OpenBrowser.displayUrl(e.getDescription());
50            }
51        }
52    }
53
54    /**
55     * This class encapsulates the "reading URL" task and can be executed in background and in
56     * parallel. Since the MOTD is many separate pages this speeds things up quite a lot. If no
57     * localized version is available, it automatically falls back to the international one.
58     */
59    private class readMOTD implements Callable<String> {
60        private boolean isLocalized;
61        private boolean isHelp;
62        private String urlLoc;
63        private String urlIntl;
64        private String urlBase;
65
66        /**
67         * Read a MOTD page
68         * @param isLocalized If true, tries to get localized version as defined in urlLoc
69         * @param urlBase Base URL (i.e. http://www.openstreetmap.de/wiki/)
70         * @param urlLoc Part to append to base URL to receive localized version
71         * @param urlIntl Part to append to base URL to receive international version
72         * @param makeList If true, the URL's contents will be wrapped in a list (<ul><li>)
73         */
74        readMOTD(boolean isLocalized, String urlBase, String urlLoc, String urlIntl, boolean makeList) {
75          this.isLocalized = isLocalized;
76          this.urlBase = urlBase;
77          this.urlLoc = urlLoc;
78          this.urlIntl = urlIntl;
79          this.isHelp = makeList;
80        }
81
82        /*
83         * Does the actual work
84         * @see java.util.concurrent.Callable#call()
85         */
86        public String call() {
87            WikiReader wr = new WikiReader(urlBase);
88            String content = "";
89            try {
90                // If we hit a non-localized link here, we already know there's no translated
91                // version available
92                String message = isLocalized ? wr.read(urlLoc) : "";
93                // Look for non-localized version
94                if (message.equals(""))
95                    message = wr.read(urlIntl);
96
97                if (!message.equals(""))
98                    if(isHelp)
99                        content += message;
100                    else
101                        content += "<ul><li>"+ message.substring(8)
102                                            .replaceAll("\n *\\* +","</li><li>")+"</li></ul>";
103            } catch (IOException ioe) {
104                try {
105                    if(isHelp)
106                        content += wr.read(urlIntl);
107                    else
108                        content += "<ul><li>"+wr.read(urlIntl).substring(8)
109                                            .replaceAll("\n *\\* +","</li><li>")+"</li></ul>";
110                } catch (IOException ioe2) {
111                }
112            }
113
114            return content;
115        }
116    }
117
118    /**
119     * Grabs current MOTD from cache or webpage and parses it.
120     */
121    private class assignContent extends CacheCustomContent {
122        public assignContent() {
123            super("motd.html", CacheCustomContent.INTERVAL_DAILY);
124        }
125
126        final private int myVersion = AboutAction.getVersionNumber();
127
128        /**
129         * This function gets executed whenever the cached files need updating
130         * @see org.openstreetmap.josm.io.CacheCustomContent#updateData()
131         */
132        protected byte[] updateData() {
133            String motd = "";
134            String baseurl = Main.pref.get("help.baseurl", "http://josm.openstreetmap.de");
135            WikiReader wr = new WikiReader(baseurl);
136            String motdcontent = "";
137            try {
138                motdcontent = wr.read(baseurl + "/wiki/MessageOfTheDay?format=txt");
139            } catch (IOException ioe) {
140                motdcontent = "<html>" + styles + "<body><h1>" +
141                    "JOSM - " + tr("Java OpenStreetMap Editor") +
142                    "</h1>\n<h2 align=\"center\">(" +
143                    tr ("Message of the day not available") +
144                    ")</h2>";
145            }
146
147            String languageCode = Main.getLanguageCodeU();
148
149            // Finds wiki links like (underscores inserted for readability):
150            // [wiki:LANGCODE:messageoftheday_CONDITON_REVISION LANGCODE]
151            // Langcode usually consists of two letters describing the language and may be omitted
152            // Condition may be one of the following: >  <  <=  =>
153            // Revision is the JOSM version
154            Pattern versionPattern = Pattern.compile(
155                    "\\[wiki:(?:[A-Z]+:)?MessageOfTheDay(\\>\\=|\\<\\=|\\<|\\>)([0-9]+)\\s*([A-Z]*)\\]",
156                    Pattern.CASE_INSENSITIVE);
157            // 1=condition, 2=targetVersion, 3=lang
158            Matcher matcher = versionPattern.matcher(motdcontent);
159            matcher.reset();
160
161            ArrayList<String[]> links = new ArrayList<String[]>();
162            String linksList="";
163            while (matcher.find()) {
164                // Discards all but the selected locale and non-localized links
165                if(!(matcher.group(3)+":").equals(languageCode) && !matcher.group(3).equals(""))
166                    continue;
167
168                links.add(new String[] {matcher.group(1), matcher.group(2), matcher.group(3)});
169                linksList += matcher.group(1)+matcher.group(2)+matcher.group(3)+": ";
170            }
171
172            // We cannot use Main.worker here because it's single-threaded and
173            // setting it to multi-threading will cause problems elsewhere
174            ExecutorService slave = Executors.newCachedThreadPool();
175
176            ArrayList<Future<String>> linkContents = new ArrayList<Future<String>>();
177            for(int i=0; i < links.size(); i++) {
178                String[] obj = links.get(i);
179                int targetVersion = Integer.parseInt(obj[1]);
180                String condition = obj[0];
181                Boolean isLocalized = !obj[2].equals("");
182
183                // Prefer localized over non-localized links, if they're otherwise the same
184                if(!isLocalized && linksList.indexOf(condition + obj[1] + languageCode + " ") >= 0)
185                    continue;
186
187                boolean included = false;
188
189                if(myVersion == 0)
190                  included = true;
191                else if(condition.equals(">="))
192                  included=myVersion >= targetVersion;
193                else if(condition.equals(">"))
194                  included = myVersion > targetVersion;
195                else if(condition.equals("<"))
196                  included=myVersion < targetVersion;
197                else
198                  included = myVersion <= targetVersion;
199
200                if(!included) continue;
201
202                boolean isHelp = targetVersion == 1;
203                String urlStart = baseurl + "/wiki/";
204                String urlEnd = "MessageOfTheDay" + condition + targetVersion
205                                    + (isHelp ? "" : "?format=txt");
206                String urlLoc = urlStart + languageCode + urlEnd;
207                String urlIntl = urlStart + urlEnd;
208
209                // This adds all links to the worker which will download them concurrently
210                linkContents.add(slave.submit(new readMOTD(isLocalized, baseurl, urlLoc, urlIntl, isHelp)));
211            }
212            // Gets newest version numbers
213            linkContents.add(slave.submit(new readMOTD(false, baseurl, "",
214                    baseurl + "/version?format=txt", true)));
215
216            for(int i=0; i < linkContents.size()-1; i++) {
217                try {
218                    motd += linkContents.get(i).get();
219                } catch (Exception e) {}
220            }
221
222            motd = "<html>"
223                + styles
224                + "<h1>JOSM - "
225                + tr("Java OpenStreetMap Editor")
226                + "</h1>"
227                + motd.replace("</html>", "")
228                + getVersionNumber(linkContents.get(linkContents.size()-1))
229                + "</html>";
230
231            linkContents.clear();
232            try {
233                slave.shutdown();
234            } catch(SecurityException x) {}
235
236            // Save this to prefs in case JOSM is updated so MOTD can be refreshed
237            Main.pref.putInteger("cache.motd.html.version", myVersion);
238
239            return motd.getBytes();
240        }
241
242        /**
243         * Additionally check if JOSM has been updated and refresh MOTD
244         */
245        @Override
246        protected boolean isCacheValid() {
247            // We assume a default of myVersion because it only kicks in in two cases:
248            // 1. Not yet written - but so isn't the interval variable, so it gets updated anyway
249            // 2. Cannot be written (e.g. while developing). Obviously we don't want to update
250            //    everytime because of something we can't read.
251            return Main.pref.getInteger("cache.motd.html.version", myVersion) == myVersion;
252        }
253
254        /**
255         * Tries to read the version number from a given Future<String>
256         * @param Future<String> that contains the version page
257         * @return String with HTML Code
258         */
259        private String getVersionNumber(Future<String> linkContent) {
260            try {
261                String str = linkContent.get();
262                Matcher m = Pattern.compile(".*josm-tested\\.jar: *(\\d+).*", Pattern.DOTALL).matcher(str);
263                m.matches();
264                int curVersion = Integer.parseInt(m.group(1));
265                m = Pattern.compile(".*josm-latest\\.jar: *(\\d+).*", Pattern.DOTALL).matcher(str);
266                m.matches();
267                int latest = Integer.parseInt(m.group(1));
268                return "<div style=\"text-align:right;font-size:small;font-weight:normal;\">"
269                + "<b>"
270                + (curVersion > myVersion ? tr("Update available") + " &#151; ": "")
271                + tr("Version Details:") + "</b> "
272                + tr("Yours: {2}; Current: {0}; <font style=\"font-size:x-small\">"
273                + "(latest untested: {1} &#150; not recommended)</font>",
274                curVersion, latest, myVersion)
275                + "</div>";
276            } catch(Exception e) {
277              // e.printStackTrace();
278            }
279
280            return "";
281        }
282    }
283
284    /**
285     * Initializes getting the MOTD as well as enabling the FileDrop Listener.
286     * Displays a message while the MOTD is downloading.
287     */
288    public GettingStarted() {
289        super(new BorderLayout());
290        final LinkGeneral lg = new LinkGeneral(
291            "<html>" +
292            styles +
293            "<h1>" +
294            "JOSM - " +
295            tr("Java OpenStreetMap Editor") +
296            "</h1><h2 align=\"center\">" +
297            tr("Downloading \"Message of the day\"") +
298            "</h2>");
299        JScrollPane scroller = new JScrollPane(lg);
300        scroller.setViewportBorder(new EmptyBorder(10,100,10,100));
301        add(scroller, BorderLayout.CENTER);
302
303        // Asynchronously get MOTD to speed-up JOSM startup
304        Thread t = new Thread(new Runnable() {
305            public void run() {
306                if (content.length() == 0 && Main.pref.getBoolean("help.displaymotd", true))
307                    content = new assignContent().updateIfRequiredString();
308
309                EventQueue.invokeLater(new Runnable() {
310                    public void run() {
311                       lg.setText(content);
312                       //lg.moveCaretPosition(0);
313                    }
314                });
315            }
316        }, "MOTD-Loader");
317        t.setDaemon(true);
318        t.start();
319
320        new FileDrop(scroller);
321    }
322}
Note: See TracBrowser for help on using the repository browser.