Ticket #1848: Cache Getting Started with Extra Class.2.patch

File Cache Getting Started with Extra Class.2.patch, 21.7 KB (added by xeen, 16 years ago)

Daily Interval was wrong

  • src/org/openstreetmap/josm/gui/GettingStarted.java

     
    2424import javax.swing.border.EmptyBorder;
    2525
    2626import org.openstreetmap.josm.Main;
     27import org.openstreetmap.josm.io.CacheCustomContent;
    2728import org.openstreetmap.josm.tools.OpenBrowser;
    2829import org.openstreetmap.josm.tools.WikiReader;
    2930import org.openstreetmap.josm.actions.AboutAction;
    3031
    3132public class GettingStarted extends JPanel {
    32 
    33     static private String content = "";
     33    private String content = "";
    3434    static private String styles = "<style type=\"text/css\">\n"+
    3535            "body { font-family: sans-serif; font-weight: bold; }\n"+
    3636            "h1 {text-align: center;}\n"+
     
    5050            }
    5151        }
    5252    }
    53 
    54     public class readMOTD implements Callable<String> {
     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> {
    5560        private boolean isLocalized;
    5661        private boolean isHelp;
    5762        private String urlLoc;
    5863        private String urlIntl;
    5964        private String urlBase;
    6065
    61         readMOTD(boolean isLocalized, String urlBase, String urlLoc, String urlIntl, boolean isHelp) {
     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) {
    6275          this.isLocalized = isLocalized;
    6376          this.urlBase = urlBase;
    6477          this.urlLoc = urlLoc;
    6578          this.urlIntl = urlIntl;
    66           this.isHelp = isHelp;
     79          this.isHelp = makeList;
    6780        }
    6881
     82        /*
     83         * Does the actual work
     84         * @see java.util.concurrent.Callable#call()
     85         */
    6986        public String call() {
    7087            WikiReader wr = new WikiReader(urlBase);
    7188            String content = "";
    7289            try {
    73                 // If we hit a non-localized link here, we already know there's no translated version available
     90                // If we hit a non-localized link here, we already know there's no translated
     91                // version available
    7492                String message = isLocalized ? wr.read(urlLoc) : "";
    7593                // Look for non-localized version
    7694                if (message.equals(""))
     
    8098                    if(isHelp)
    8199                        content += message;
    82100                    else
    83                         content += "<ul><li>"+ message.substring(8).replaceAll("\n *\\* +","</li><li>")+"</li></ul>";
     101                        content += "<ul><li>"+ message.substring(8)
     102                                            .replaceAll("\n *\\* +","</li><li>")+"</li></ul>";
    84103            } catch (IOException ioe) {
    85104                try {
    86105                    if(isHelp)
    87106                        content += wr.read(urlIntl);
    88107                    else
    89                         content += "<ul><li>"+wr.read(urlIntl).substring(8).replaceAll("\n *\\* +","</li><li>")+"</li></ul>";
     108                        content += "<ul><li>"+wr.read(urlIntl).substring(8)
     109                                            .replaceAll("\n *\\* +","</li><li>")+"</li></ul>";
    90110                } catch (IOException ioe2) {
    91111                }
    92112            }
     
    95115        }
    96116    }
    97117
    98     private void assignContent() {
    99         if (content.length() > 0 && Main.pref.getBoolean("help.displaymotd", true)) return;
    100 
    101         String baseurl = Main.pref.get("help.baseurl", "http://josm.openstreetmap.de");
    102         WikiReader wr = new WikiReader(baseurl);
    103         String motdcontent = "";
    104         try {
    105             motdcontent = wr.read(baseurl + "/wiki/MessageOfTheDay?format=txt");
    106         } catch (IOException ioe) {
    107             motdcontent = "<html>" + styles + "<body><h1>" +
    108                 "JOSM - " + tr("Java OpenStreetMap Editor") +
    109                 "</h1>\n<h2 align=\"center\">(" +
    110                 tr ("Message of the day not available") +
    111                 ")</h2>";
     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);
    112124        }
     125       
     126        final private int myVersion = AboutAction.getVersionNumber();
    113127
    114         int myVersion = AboutAction.getVersionNumber();
    115         String languageCode = Main.getLanguageCodeU();
    116 
    117         // Finds wiki links like (underscores inserted for readability): [wiki:LANGCODE:messageoftheday_CONDITON_REVISION LANGCODE]
    118         // Langcode usually consists of two letters describing the language and may be omitted
    119         // Condition may be one of the following: >  <  <=  =>
    120         // Revision is the JOSM version
    121         Pattern versionPattern = Pattern.compile("\\[wiki:(?:[A-Z]+:)?MessageOfTheDay(\\>\\=|\\<\\=|\\<|\\>)([0-9]+)\\s*([A-Z]*)\\]", Pattern.CASE_INSENSITIVE);
    122         // 1=condition, 2=targetVersion, 3=lang
    123         Matcher matcher = versionPattern.matcher(motdcontent);
    124         matcher.reset();
    125 
    126         ArrayList<String[]> links = new ArrayList<String[]>();
    127         String linksList="";
    128         while (matcher.find()) {
    129             // Discards all but the selected locale and non-localized links
    130             if(!(matcher.group(3)+":").equals(languageCode) && !matcher.group(3).equals(""))
    131                 continue;
    132 
    133             links.add(new String[] {matcher.group(1), matcher.group(2), matcher.group(3)});
    134             linksList += matcher.group(1)+matcher.group(2)+matcher.group(3)+": ";
    135         }
    136 
    137         // We cannot use Main.worker here because it's single-threaded and
    138         // setting it to multi-threading will cause problems elsewhere
    139         ExecutorService slave = Executors.newCachedThreadPool();
    140 
    141         ArrayList<Future<String>> linkContent = new ArrayList<Future<String>>();
    142         for(int i=0; i < links.size(); i++) {
    143             String[] obj = links.get(i);
    144             int targetVersion = Integer.parseInt(obj[1]);
    145             String condition = obj[0];
    146             Boolean isLocalized = !obj[2].equals("");
    147 
    148             // Prefer localized over non-localized links, if they're otherwise the same
    149             if(!isLocalized && linksList.indexOf(condition + obj[1] + languageCode + " ") >= 0)
    150                 continue;
    151 
    152             boolean included = false;
    153 
    154             if(myVersion == 0)
    155               included = true;
    156             else if(condition.equals(">="))
    157               included=myVersion >= targetVersion;
    158             else if(condition.equals(">"))
    159               included = myVersion > targetVersion;
    160             else if(condition.equals("<"))
    161               included=myVersion < targetVersion;
    162             else
    163               included = myVersion <= targetVersion;
    164 
    165             if(!included) continue;
    166 
    167             boolean isHelp = targetVersion == 1;
    168             String urlStart = baseurl + "/wiki/";
    169             String urlEnd = "MessageOfTheDay" + condition + targetVersion + (isHelp ? "" : "?format=txt");
    170             String urlLoc = urlStart + languageCode + urlEnd;
    171             String urlIntl = urlStart + urlEnd;
    172 
    173             // This adds all links to the worker which will download them concurrently
    174             linkContent.add(slave.submit(new readMOTD(isLocalized, baseurl, urlLoc, urlIntl, isHelp)));
    175         }
    176 
    177         for(int i=0; i < linkContent.size(); i++) {
     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 = "";
    178137            try {
    179                 content += linkContent.get(i).get();
    180             } catch (Exception e) {}
     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();
    181240        }
    182241       
    183         linkContent.clear();
    184         try {
    185             slave.shutdown();
    186         } catch(SecurityException x) {}
     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        }
    187253       
    188 
    189         content = "<html>\n"+
    190             styles +
    191             "<h1>JOSM - " + tr("Java OpenStreetMap Editor") + "</h1>\n"+
    192             content+"\n"+
    193             "</html>";
     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            String version = "";
     261            int curVersion = 0;
     262            try {
     263                String v[] = linkContent.get().split("\n");
     264                // tested (6: <html>, 17: josm-tested.jar)
     265                curVersion = Integer.parseInt(v[0].substring(6+17));
     266                version = tr("Yours: {2}; Current: {0}; <font style=\"font-size:x-small\">"
     267                                + "(latest untested: {1} &#150; not recommended)</font>",
     268                            curVersion,
     269                            new Integer(v[1].substring(17).trim()), // latest
     270                            myVersion);
     271            } catch(Exception e) {}
     272           
     273            if(version.equals(""))
     274                return "";
     275           
     276            return "<div style=\"text-align:right;font-size:small;font-weight:normal;\">"
     277                        + "<b>"
     278                        + (curVersion > myVersion ? tr("Update available") + " &#151; ": "")
     279                        + tr("Version Details:") + "</b> "
     280                        + version
     281                        + "</div>";
     282        }
    194283    }
    195 
     284   
     285    /**
     286     * Initializes getting the MOTD as well as enabling the FileDrop Listener.
     287     * Displays a message while the MOTD is downloading.
     288     */
    196289    public GettingStarted() {
    197290        super(new BorderLayout());
    198291        final LinkGeneral lg = new LinkGeneral(
     
    211304        // Asynchronously get MOTD to speed-up JOSM startup
    212305        Thread t = new Thread(new Runnable() {
    213306            public void run() {
    214                 assignContent();
     307                if (content.length() == 0 && Main.pref.getBoolean("help.displaymotd", true))
     308                    content = new assignContent().updateIfRequiredString();
     309                     
    215310                EventQueue.invokeLater(new Runnable() {
    216311                    public void run() {
    217312                       lg.setText(content);
  • src/org/openstreetmap/josm/io/CacheCustomContent.java

     
     1// License: GPL. For details, see LICENSE file.
     2package org.openstreetmap.josm.io;
     3
     4import java.io.BufferedInputStream;
     5import java.io.BufferedOutputStream;
     6import java.io.File;
     7import java.io.FileInputStream;
     8import java.io.FileOutputStream;
     9import java.util.Date;
     10
     11import org.openstreetmap.josm.Main;
     12
     13public abstract class CacheCustomContent {
     14    /**
     15     * Common intervals
     16     */
     17    final static public int INTERVAL_ALWAYS = -1;
     18    final static public int INTERVAL_HOURLY = 60*60;
     19    final static public int INTERVAL_DAILY = INTERVAL_HOURLY * 24;
     20    final static public int INTERVAL_WEEKLY = INTERVAL_DAILY * 7;
     21    final static public int INTERVAL_MONTHLY = INTERVAL_WEEKLY * 4;
     22    final static public int INTERVAL_NEVER = Integer.MAX_VALUE;
     23   
     24    /**
     25     * Where the data will be stored   
     26     */
     27    private byte[] data = null;
     28   
     29    /**
     30     * The ident that identifies the stored file. Includes file-ending.
     31     */
     32    final private String ident;
     33   
     34    /**
     35     * The (file-)path where the data will be stored
     36     */
     37    final private File path;
     38   
     39    /**
     40     * How often to update the cached version
     41     */
     42    final private int updateInterval;
     43   
     44    /**
     45     * This function will be executed when an update is required. It has to be implemented by the
     46     * inheriting class and should use a worker if it has a long wall time as the function is
     47     * executed in the current thread.
     48     * @return the data to cache
     49     */
     50    protected abstract byte[] updateData();
     51   
     52    /**
     53     * This function serves as a comfort hook to perform additional checks if the cache is valid
     54     * @return True if the cached copy is still valid
     55     */
     56    protected boolean isCacheValid() {
     57        return true;
     58    }   
     59   
     60    /**
     61     * Initializes the class. Note that all read data will be stored in memory until it is flushed
     62     * by flushData().
     63     * @param ident
     64     * @param updateInterval
     65     */
     66    public CacheCustomContent(String ident, int updateInterval) {
     67        this.ident = ident;
     68        this.updateInterval = updateInterval;
     69        this.path = new File(Main.pref.getPreferencesDir(), ident);
     70    }
     71   
     72    /**
     73     * Updates data if required
     74     * @return Returns the data
     75     */
     76    public byte[] updateIfRequired() {
     77        if(Main.pref.getInteger("cache." + ident, 0) + updateInterval < new Date().getTime()/1000
     78                || !isCacheValid())
     79            return updateForce();
     80        return getData();
     81    }
     82   
     83    /**
     84     * Updates data if required
     85     * @return Returns the data as string
     86     */
     87    public String updateIfRequiredString() {
     88        if(Main.pref.getInteger("cache." + ident, 0) + updateInterval < new Date().getTime()/1000
     89                || !isCacheValid())
     90            return updateForceString();
     91        return getDataString();
     92    }
     93   
     94    /**
     95     * Executes an update regardless of updateInterval
     96     * @return Returns the data
     97     */
     98    public byte[] updateForce() {
     99        this.data = updateData();
     100        saveToDisk();
     101        Main.pref.putInteger("cache." + ident, (int)(new Date().getTime()/1000));
     102        return data;
     103    }
     104   
     105    /**
     106     * Executes an update regardless of updateInterval
     107     * @return Returns the data as String
     108     */
     109    public String updateForceString() {
     110        updateForce();
     111        return new String(data);
     112    }
     113
     114    /**
     115     * Returns the data without performing any updates
     116     * @return the data
     117     */
     118    public byte[] getData() {
     119        if(data == null)
     120            loadFromDisk();
     121        return data;
     122    }
     123   
     124    /**
     125     * Returns the data without performing any updates
     126     * @return the data as String
     127     */
     128    public String getDataString() {
     129        return new String(getData());
     130    }
     131
     132    /**
     133     * Tries to load the data using the given ident from disk. If this fails, data will be updated
     134     */
     135    private void loadFromDisk() {
     136        try {
     137            BufferedInputStream input = new BufferedInputStream(new FileInputStream(path));
     138            this.data = new byte[input.available()];
     139            input.read(this.data);
     140            input.close();
     141        } catch(Exception e) {
     142            this.data = updateForce();
     143        }
     144    }
     145   
     146    /**
     147     * Stores the data to disk
     148     */
     149    private void saveToDisk() {
     150        try {
     151            BufferedOutputStream output = new BufferedOutputStream(new FileOutputStream(path));
     152            output.write(this.data);
     153            output.flush();
     154            output.close();
     155        } catch(Exception e) {}
     156    }       
     157   
     158    /**
     159     * Flushes the data from memory. Class automatically reloads it from disk or updateData() if
     160     * required
     161     */
     162    public void flushData() {
     163        data = null;
     164    }
     165}