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