Ticket #1848: Cache Getting Started with Extra Class.2.patch
File Cache Getting Started with Extra Class.2.patch, 21.7 KB (added by , 16 years ago) |
---|
-
src/org/openstreetmap/josm/gui/GettingStarted.java
24 24 import javax.swing.border.EmptyBorder; 25 25 26 26 import org.openstreetmap.josm.Main; 27 import org.openstreetmap.josm.io.CacheCustomContent; 27 28 import org.openstreetmap.josm.tools.OpenBrowser; 28 29 import org.openstreetmap.josm.tools.WikiReader; 29 30 import org.openstreetmap.josm.actions.AboutAction; 30 31 31 32 public class GettingStarted extends JPanel { 32 33 static private String content = ""; 33 private String content = ""; 34 34 static private String styles = "<style type=\"text/css\">\n"+ 35 35 "body { font-family: sans-serif; font-weight: bold; }\n"+ 36 36 "h1 {text-align: center;}\n"+ … … 50 50 } 51 51 } 52 52 } 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> { 55 60 private boolean isLocalized; 56 61 private boolean isHelp; 57 62 private String urlLoc; 58 63 private String urlIntl; 59 64 private String urlBase; 60 65 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) { 62 75 this.isLocalized = isLocalized; 63 76 this.urlBase = urlBase; 64 77 this.urlLoc = urlLoc; 65 78 this.urlIntl = urlIntl; 66 this.isHelp = isHelp;79 this.isHelp = makeList; 67 80 } 68 81 82 /* 83 * Does the actual work 84 * @see java.util.concurrent.Callable#call() 85 */ 69 86 public String call() { 70 87 WikiReader wr = new WikiReader(urlBase); 71 88 String content = ""; 72 89 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 74 92 String message = isLocalized ? wr.read(urlLoc) : ""; 75 93 // Look for non-localized version 76 94 if (message.equals("")) … … 80 98 if(isHelp) 81 99 content += message; 82 100 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>"; 84 103 } catch (IOException ioe) { 85 104 try { 86 105 if(isHelp) 87 106 content += wr.read(urlIntl); 88 107 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>"; 90 110 } catch (IOException ioe2) { 91 111 } 92 112 } … … 95 115 } 96 116 } 97 117 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); 112 124 } 125 126 final private int myVersion = AboutAction.getVersionNumber(); 113 127 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 = ""; 178 137 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(); 181 240 } 182 241 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 } 187 253 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} – 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") + " — ": "") 279 + tr("Version Details:") + "</b> " 280 + version 281 + "</div>"; 282 } 194 283 } 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 */ 196 289 public GettingStarted() { 197 290 super(new BorderLayout()); 198 291 final LinkGeneral lg = new LinkGeneral( … … 211 304 // Asynchronously get MOTD to speed-up JOSM startup 212 305 Thread t = new Thread(new Runnable() { 213 306 public void run() { 214 assignContent(); 307 if (content.length() == 0 && Main.pref.getBoolean("help.displaymotd", true)) 308 content = new assignContent().updateIfRequiredString(); 309 215 310 EventQueue.invokeLater(new Runnable() { 216 311 public void run() { 217 312 lg.setText(content); -
src/org/openstreetmap/josm/io/CacheCustomContent.java
1 // License: GPL. For details, see LICENSE file. 2 package org.openstreetmap.josm.io; 3 4 import java.io.BufferedInputStream; 5 import java.io.BufferedOutputStream; 6 import java.io.File; 7 import java.io.FileInputStream; 8 import java.io.FileOutputStream; 9 import java.util.Date; 10 11 import org.openstreetmap.josm.Main; 12 13 public 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 }