001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.data; 003 004import static org.openstreetmap.josm.tools.I18n.tr; 005 006import java.io.BufferedReader; 007import java.io.IOException; 008import java.net.URL; 009import java.util.HashMap; 010import java.util.Map; 011import java.util.Map.Entry; 012import java.util.regex.Matcher; 013import java.util.regex.Pattern; 014 015import org.openstreetmap.josm.Main; 016import org.openstreetmap.josm.tools.LanguageInfo; 017import org.openstreetmap.josm.tools.Utils; 018 019/** 020 * Provides basic information about the currently used JOSM build. 021 * 022 */ 023public class Version { 024 /** constant to indicate that the current build isn't assigned a JOSM version number */ 025 public static final int JOSM_UNKNOWN_VERSION = 0; 026 027 /** the unique instance */ 028 private static Version instance; 029 030 /** 031 * Load the specified resource as string. 032 * 033 * @param resource the resource url to load 034 * @return the content of the resource file; null, if an error occurred 035 */ 036 public static String loadResourceFile(URL resource) { 037 if (resource == null) return null; 038 String s = null; 039 try { 040 StringBuilder sb = new StringBuilder(); 041 try (BufferedReader in = Utils.openURLReader(resource)) { 042 for (String line = in.readLine(); line != null; line = in.readLine()) { 043 sb.append(line).append("\n"); 044 } 045 } 046 s = sb.toString(); 047 } catch (IOException e) { 048 Main.error(tr("Failed to load resource ''{0}'', error is {1}.", resource.toString(), e.toString())); 049 Main.error(e); 050 } 051 return s; 052 } 053 054 /** 055 * Replies the unique instance of the version information 056 * 057 * @return the unique instance of the version information 058 */ 059 public static Version getInstance() { 060 if (instance == null) { 061 instance = new Version(); 062 instance.init(); 063 } 064 return instance; 065 } 066 067 private int version; 068 private String releaseDescription; 069 private String time; 070 private String buildName; 071 private boolean isLocalBuild; 072 073 protected Map<String, String> parseManifestStyleFormattedString(String content) { 074 Map<String, String> properties = new HashMap<>(); 075 if (content == null) return properties; 076 Pattern p = Pattern.compile("^([^:]+):(.*)$"); 077 for (String line: content.split("\n")) { 078 if (line == null || line.trim().isEmpty()) { 079 continue; 080 } 081 if (line.matches("^\\s*#.*$")) { 082 continue; 083 } 084 Matcher m = p.matcher(line); 085 if (m.matches()) { 086 properties.put(m.group(1), m.group(2)); 087 } 088 } 089 return properties; 090 } 091 092 /** 093 * Initializes the version infos from the revision resource file 094 * 095 * @param revisionInfo the revision info loaded from a revision resource file 096 */ 097 protected void initFromRevisionInfo(String revisionInfo) { 098 if (revisionInfo == null) { 099 this.releaseDescription = tr("UNKNOWN"); 100 this.version = JOSM_UNKNOWN_VERSION; 101 this.time = null; 102 return; 103 } 104 105 Map<String, String> properties = parseManifestStyleFormattedString(revisionInfo); 106 String value = properties.get("Revision"); 107 if (value != null) { 108 value = value.trim(); 109 try { 110 version = Integer.parseInt(value); 111 } catch(NumberFormatException e) { 112 version = 0; 113 Main.warn(tr("Unexpected JOSM version number in revision file, value is ''{0}''", value)); 114 } 115 } else { 116 version = JOSM_UNKNOWN_VERSION; 117 } 118 119 // the last changed data 120 // 121 time = properties.get("Last Changed Date"); 122 if (time == null) { 123 time = properties.get("Build-Date"); 124 } 125 126 // is this a local build ? 127 // 128 isLocalBuild = false; 129 value = properties.get("Is-Local-Build"); 130 if (value != null && "true".equalsIgnoreCase(value.trim())) { 131 isLocalBuild = true; 132 } 133 134 // is this a specific build ? 135 // 136 buildName = null; 137 value = properties.get("Build-Name"); 138 if (value != null && !value.trim().isEmpty()) { 139 buildName = value.trim(); 140 } 141 142 // the revision info 143 // 144 StringBuilder sb = new StringBuilder(); 145 for(Entry<String,String> property: properties.entrySet()) { 146 sb.append(property.getKey()).append(":").append(property.getValue()).append("\n"); 147 } 148 releaseDescription = sb.toString(); 149 } 150 151 /** 152 * Initializes version info 153 */ 154 public void init() { 155 URL u = Main.class.getResource("/REVISION"); 156 if (u == null) { 157 Main.warn(tr("The revision file ''/REVISION'' is missing.")); 158 version = 0; 159 releaseDescription = ""; 160 return; 161 } 162 initFromRevisionInfo(loadResourceFile(u)); 163 } 164 165 /** 166 * Replies the version string. Either the SVN revision "1234" (as string) or the 167 * the I18n equivalent of "UNKNOWN". 168 * 169 * @return the JOSM version 170 */ 171 public String getVersionString() { 172 return version == 0 ? tr("UNKNOWN") : Integer.toString(version); 173 } 174 175 /** 176 * Replies a text with the release attributes 177 * 178 * @return a text with the release attributes 179 */ 180 public String getReleaseAttributes() { 181 return releaseDescription; 182 } 183 184 /** 185 * Replies the build date as string 186 * 187 * @return the build date as string 188 */ 189 public String getTime() { 190 return time; 191 } 192 193 /** 194 * Replies the JOSM version. Replies {@link #JOSM_UNKNOWN_VERSION} if the version isn't known. 195 * @return the JOSM version 196 */ 197 public int getVersion() { 198 return version; 199 } 200 201 /** 202 * Replies true if this is a local build, i.e. an inofficial development build. 203 * 204 * @return true if this is a local build, i.e. an inofficial development build. 205 */ 206 public boolean isLocalBuild() { 207 return isLocalBuild; 208 } 209 210 /** 211 * Returns the User-Agent string 212 * @return The User-Agent 213 */ 214 public String getAgentString() { 215 return getAgentString(true); 216 } 217 218 /** 219 * Returns the User-Agent string, with or without OS details 220 * @param includeOsDetails Append Operating System details at the end of the User-Agent 221 * @return The User-Agent 222 * @since 5956 223 */ 224 public String getAgentString(boolean includeOsDetails) { 225 int v = getVersion(); 226 String s = (v == JOSM_UNKNOWN_VERSION) ? "UNKNOWN" : Integer.toString(v); 227 if (buildName != null) { 228 s += " " + buildName; 229 } 230 if (isLocalBuild() && v != JOSM_UNKNOWN_VERSION) { 231 s += " SVN"; 232 } 233 String result = "JOSM/1.5 ("+ s+" "+LanguageInfo.getJOSMLocaleCode()+")"; 234 if (includeOsDetails && Main.platform != null) { 235 result += " " + Main.platform.getOSDescription(); 236 } 237 return result; 238 } 239 240 /** 241 * Returns the full User-Agent string 242 * @return The User-Agent 243 * @since 5868 244 */ 245 public String getFullAgentString() { 246 return getAgentString() + " Java/"+System.getProperty("java.version"); 247 } 248}