001    /*
002     * This file is part of McIDAS-V
003     *
004     * Copyright 2007-2013
005     * Space Science and Engineering Center (SSEC)
006     * University of Wisconsin - Madison
007     * 1225 W. Dayton Street, Madison, WI 53706, USA
008     * https://www.ssec.wisc.edu/mcidas
009     * 
010     * All Rights Reserved
011     * 
012     * McIDAS-V is built on Unidata's IDV and SSEC's VisAD libraries, and
013     * some McIDAS-V source code is based on IDV and VisAD source code.  
014     * 
015     * McIDAS-V is free software; you can redistribute it and/or modify
016     * it under the terms of the GNU Lesser Public License as published by
017     * the Free Software Foundation; either version 3 of the License, or
018     * (at your option) any later version.
019     * 
020     * McIDAS-V is distributed in the hope that it will be useful,
021     * but WITHOUT ANY WARRANTY; without even the implied warranty of
022     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
023     * GNU Lesser Public License for more details.
024     * 
025     * You should have received a copy of the GNU Lesser Public License
026     * along with this program.  If not, see http://www.gnu.org/licenses.
027     */
028    
029    package edu.wisc.ssec.mcidasv;
030    
031    import java.awt.event.ActionEvent;
032    import java.io.BufferedReader;
033    import java.io.BufferedWriter;
034    import java.io.FileReader;
035    import java.io.FileWriter;
036    import java.util.Hashtable;
037    import java.util.Map;
038    import java.util.Properties;
039    
040    import javax.swing.JEditorPane;
041    import javax.swing.JLabel;
042    import javax.swing.JOptionPane;
043    import javax.swing.JPanel;
044    import javax.swing.event.HyperlinkEvent;
045    import javax.swing.event.HyperlinkListener;
046    
047    import ucar.unidata.idv.IdvObjectStore;
048    import ucar.unidata.idv.IntegratedDataViewer;
049    import ucar.unidata.util.IOUtil;
050    import ucar.unidata.util.Misc;
051    import ucar.unidata.util.StringUtil;
052    import edu.wisc.ssec.mcidasv.startupmanager.StartupManager;
053    import edu.wisc.ssec.mcidasv.util.SystemState;
054    
055    public class StateManager extends ucar.unidata.idv.StateManager implements Constants, HyperlinkListener {
056    
057        /** Lazily-loaded VisAD build date. */
058        private String visadDate;
059    
060        /** Lazily-loaded VisAD SVN revision number. */
061        private String visadVersion;
062    
063        private String version;
064        private String versionAbout;
065    
066        public StateManager(IntegratedDataViewer idv) {
067            super(idv);
068        }
069    
070        /**
071         * Override to set the right user directory
072         */
073        protected IdvObjectStore doMakeObjectStore() {
074            IdvObjectStore store = new IdvObjectStore(getIdv(),
075                                       getStoreSystemName(), getStoreName(),
076                                       getIdv().getEncoderForRead(),
077                                       StartupManager.getInstance().getPlatform().getUserDirectory());
078            initObjectStore(store);
079            return store;
080        }
081    
082        /**
083         * Handle a change to a link
084         *
085         * @param e  the link's event
086         */
087        public void hyperlinkUpdate(HyperlinkEvent e) {
088            if (e.getEventType() == HyperlinkEvent.EventType.ACTIVATED) {
089                if (e.getURL() == null) {
090                    click(e.getDescription());
091                } else {
092                    click(e.getURL().toString());
093                }
094            }
095        }
096    
097        /**
098         * Handle a click on a link
099         *
100         * @param url  the link definition
101         */
102        public void click(String url) {
103            getIdv().actionPerformed(new ActionEvent(this, 0, url));
104        }
105    
106        public String getOSName() {
107            String os = System.getProperty("os.name");
108            os = os.replaceAll(" ", "_");
109            return os;
110        }
111    
112        public String getMcIdasVersionAbout() {
113            getMcIdasVersion();
114    
115            versionAbout = IOUtil.readContents((String) getProperty(Constants.PROP_ABOUTTEXT), "");
116            versionAbout = StringUtil.replace(versionAbout, MACRO_VERSION, version);
117            Properties props = Misc.readProperties(
118                (String) getProperty(Constants.PROP_VERSIONFILE), 
119                null, 
120                getClass()
121            );
122    
123            String value = getIdvVersion();
124            versionAbout = StringUtil.replace(versionAbout, Constants.MACRO_IDV_VERSION, value);
125            value = props.getProperty(PROP_COPYRIGHT_YEAR, "");
126            versionAbout = StringUtil.replace(versionAbout, Constants.MACRO_COPYRIGHT_YEAR, value);
127            value = props.getProperty(PROP_BUILD_DATE, "Unknown");
128            versionAbout = StringUtil.replace(versionAbout, Constants.MACRO_BUILDDATE, value);
129            versionAbout = StringUtil.replace(versionAbout, Constants.MACRO_VISAD_VERSION, getVisadVersion());
130            
131            return versionAbout;
132        }
133        
134        public String getMcIdasVersion() {
135            if (version != null) {
136                return version;
137            }
138            
139            Properties props = new Properties();
140            props = Misc.readProperties((String) getProperty(Constants.PROP_VERSIONFILE), null, getClass());
141            String maj = props.getProperty(PROP_VERSION_MAJOR, "0");
142            String min = props.getProperty(PROP_VERSION_MINOR, "0");
143            String rel = props.getProperty(PROP_VERSION_RELEASE, "");
144            
145            version = maj.concat(".").concat(min).concat(rel);
146            
147            return version;
148        }
149        
150        /**
151         * Returns the current Jython version.
152         * 
153         * @return Jython's version information.
154         */
155        @Override public String getJythonVersion() {
156            return org.python.Version.PY_VERSION;
157        }
158        
159        /**
160         * Get a property
161         *
162         * @param name name of the property
163         *
164         * @return  the property or null
165         */
166        @Override public Object getProperty(final String name) {
167            Object value = null;
168            if (McIDASV.isMac()) {
169                value = getProperties().get("mac."+name);
170            }
171            if (value == null) {
172                value = getProperties().get(name);
173            }
174            if (value == null) {
175                String fixedName = StateManager.fixIds(name);
176                if (!name.equals(fixedName)) {
177                    return getProperties().get(fixedName);
178                }
179            }
180            return value;
181        }
182        
183        /**
184         * Returns information about the current version of McIDAS-V and the IDV,
185         * along with their respective build dates.
186         * 
187         * @return Hashtable containing versioning information.
188         */
189        public Hashtable<String, String> getVersionInfo() {
190            Properties props = new Properties();
191            props = Misc.readProperties((String) getProperty(Constants.PROP_VERSIONFILE), null, getClass());
192    
193            String mcvBuild = props.getProperty(PROP_BUILD_DATE, "Unknown");
194    
195            Hashtable<String, String> table = new Hashtable<String, String>();
196            table.put("mcv.version.general", getMcIdasVersion());
197            table.put("mcv.version.build", mcvBuild);
198            table.put("idv.version.general", getVersion());
199            table.put("idv.version.build", getBuildDate());
200            table.put("visad.version.general", getVisadVersion());
201            table.put("visad.version.build", getVisadDate());
202            return table;
203        }
204    
205        /**
206         * Return the timestamp from visad.jar was created.
207         * 
208         * @return {@code String} representation of the creation timestamp. Likely to change formatting over time.
209         */
210        public String getVisadDate() {
211            if (visadDate == null) {
212                Map<String, String> props = SystemState.queryVisadBuildProperties();
213                visadDate = props.get(Constants.PROP_VISAD_DATE);
214                visadVersion = props.get(Constants.PROP_VISAD_REVISION);
215            }
216            return visadDate;
217        }
218    
219        /**
220         * Return the {@literal "version"} of VisAD.
221         * 
222         * @return Currently returns whatever the SVN revision number was when
223         * visad.jar was built. 
224         */
225        public String getVisadVersion() {
226            if (visadVersion== null) {
227                Map<String, String> props = SystemState.queryVisadBuildProperties();
228                visadDate = props.get(Constants.PROP_VISAD_DATE);
229                visadVersion = props.get(Constants.PROP_VISAD_REVISION);
230            }
231            return visadVersion;
232        }
233    
234        public String getIdvVersion() {
235            return getVersion();
236        }
237    
238        /**
239         * Overridden to set default of McIDAS-V
240         */
241        public String getStoreSystemName() {
242            return StartupManager.getInstance().getPlatform().getUserDirectory();
243        }
244    
245        /**
246         * Overridden to get dir of the unnecessary second level directory.
247         * 
248         * @see ucar.unidata.idv.StateManager#getStoreName()
249         */
250        public String getStoreName() {
251            return "";
252        }
253            
254            /**
255             * Connect to McIDAS website and look for latest stable version
256             */
257            public String getMcIdasVersionStable() {
258                    String offscreen = "0";
259                    if (super.getIdv().getArgsManager().getIsOffScreen()) {
260                            offscreen = "1";
261                    }
262    
263                    String version = "";
264                    try {
265                            version = IOUtil.readContents(Constants.HOMEPAGE_URL+"/"+Constants.VERSION_HANDLER_URL+"?v="+getMcIdasVersion()+"&os="+getOSName()+"&off="+offscreen, "");
266                    } catch (Exception e) {}
267                    return version.trim();
268            }
269    
270            /**
271             * Connect to McIDAS website and look for latest prerelease version
272             */
273            public String getMcIdasVersionPrerelease() {
274                    String version = "";
275                    try {
276                            String htmlList = IOUtil.readContents(Constants.HOMEPAGE_URL+'/'+Constants.PRERELEASE_URL, "");
277                            String lines[] = htmlList.split("\n");
278                            for (int i=0; i<lines.length; i++) {
279                                    String line = lines[i].trim();
280                                    if (line.matches(".*McIDAS-V_\\d+\\.\\d+.*")) {
281                                            line = line.substring(line.indexOf("McIDAS-V_")+9);
282                                            String aVersion = line.substring(0, line.indexOf("_"));
283                                            if (version == "") {
284                                                    version = aVersion;
285                                            }
286                                            else {
287                                                    int comp = compareVersions(version, aVersion);
288                                                    if (comp > 0) {
289                                                            version = aVersion;
290                                                    }
291                                            }
292                                    }
293                            }
294                    } catch (Exception e) {}
295                    return version.trim();
296            }
297    
298            /**
299             * Connect to McIDAS website and look for latest notice
300             */
301            public String getNoticeLatest() {
302                    String notice = "";
303                    try {
304                            notice = IOUtil.readContents(Constants.HOMEPAGE_URL+"/"+Constants.NOTICE_URL+"?requesting="+getMcIdasVersion()+"&os="+getOSName(), "");
305                    } catch (Exception e) {}
306                    if (notice.indexOf("<notice>")<0) notice="";
307                    notice = notice.replaceAll("<[/?]notice>","");
308                    return notice.trim();
309            }
310    
311            /**
312             * Compare version strings
313             *  0: equal
314             * <0: this version is greater
315             * >0: that version is greater
316             */
317            private int compareVersions(String thisVersion, String thatVersion) {
318                    int thisInt = versionToInteger(thisVersion);            
319                    int thatInt = versionToInteger(thatVersion);
320                    return (thatInt - thisInt);
321            }
322            
323            /**
324             * Turn version strings of the form #.#(a#)
325             *  where # is one or two digits, a is one of alpha or beta, and () is optional
326             * Into an integer... (empty) > beta > alpha
327             */
328            private int versionToInteger(String version) {
329                    int value = 0;
330                    int p;
331                    String part;
332                    Character one = null;
333                    
334                    try {
335                            
336                            // Major version
337                            p = version.indexOf('.');
338                            if (p > 0) {
339                                    part = version.substring(0,p);
340                                    value += Integer.parseInt(part) * 1000000;
341                                    version = version.substring(p+1);
342                            }
343                            
344                            // Minor version
345                            int minor = 0;
346                            int i=0;
347                            for (i=0; i<2 && i<version.length(); i++) {
348                                    one = version.charAt(i);
349                                    if (Character.isDigit(one)) {
350                                            if (i>0) minor *= 10;
351                                            minor += Character.digit(one, 10) * 10000;
352                                    }
353                                    else {
354                                            break;
355                                    }
356                            }
357                            value += minor;
358                            if (one!=null) version = version.substring(i);
359            
360                            // Alpha/beta/update/release status
361                            if (version.length() == 0) value += 300;
362                            else if (version.charAt(0) == 'b') value += 200;
363                            else if (version.charAt(0) == 'a') value += 100;
364                            else if (version.charAt(0) == 'u') value += 400;
365                            else if (version.charAt(0) == 'r') value += 400;
366                            for (i=0; i<version.length(); i++) {
367                                    one = version.charAt(i);
368                                    if (Character.isDigit(one)) break;
369                            }
370                            if (one!=null) version = version.substring(i);
371            
372                            // Alpha/beta version
373                            if (version.length() > 0)
374                                    value += Integer.parseInt(version);
375                            
376                    } catch (Exception e) {}
377                    
378                    return value;
379            }
380            
381            public boolean getIsPrerelease() {
382                    boolean isPrerelease = false;
383                    String version = getMcIdasVersion();
384                    if (version.indexOf("a") >= 0 || version.indexOf("b") >= 0) {
385                            isPrerelease = true;
386                    }
387                    return isPrerelease;
388            }
389            
390            public void checkForNewerVersion(boolean notifyDialog) {
391                    checkForNewerVersionStable(notifyDialog);
392            if (getStore().get(Constants.PREF_PRERELEASE_CHECK, getIsPrerelease())) {
393                    checkForNewerVersionPrerelease(notifyDialog);
394            }
395            }
396            
397            public void checkForNewerVersionStable(boolean notifyDialog) {
398    
399                    /** Get the stable version from the website (for statistics recording) */
400                    String thatVersion = getMcIdasVersionStable();
401    
402                    /** Shortcut the rest of the process if we are processing offscreen */
403                    if (super.getIdv().getArgsManager().getIsOffScreen()) {
404                            return;
405                    }
406    
407                    String thisVersion = getMcIdasVersion();
408                    String titleText = "Version Check";
409                    
410                    if (thisVersion.equals("") || thatVersion.equals("")) {
411                            if (notifyDialog) {
412                                    JOptionPane.showMessageDialog(null, "Version check failed", titleText, 
413                                                    JOptionPane.WARNING_MESSAGE);
414                            }
415                    }
416                    else if (compareVersions(thisVersion, thatVersion) > 0) {
417                            String labelText = "<html>Version <b>" + thatVersion + "</b> is available<br><br>";
418                            labelText += "Visit <a href=\"" + Constants.HOMEPAGE_URL + "\">";
419                            labelText += Constants.HOMEPAGE_URL + "</a> to download</html>";
420                            
421                            JPanel backgroundColorGetterPanel = new JPanel();
422                            JEditorPane messageText = new JEditorPane("text/html", labelText);
423                            messageText.setBackground(backgroundColorGetterPanel.getBackground());
424                            messageText.setEditable(false);
425                            messageText.addHyperlinkListener(this);
426    
427    //                      JLabel message = new JLabel(labelText, JLabel.CENTER);
428                            JOptionPane.showMessageDialog(null, messageText, titleText, 
429                                            JOptionPane.INFORMATION_MESSAGE);
430                    }
431                    else {
432                            if (notifyDialog) {
433                                    String labelText = "<html>This version (<b>" + thisVersion + "</b>) is up to date</html>";
434                                    JLabel message = new JLabel(labelText, JLabel.CENTER);
435                                    JOptionPane.showMessageDialog(null, message, titleText, 
436                                                    JOptionPane.INFORMATION_MESSAGE);
437                            }
438                    }
439                    
440            }
441            
442            public void checkForNewerVersionPrerelease(boolean notifyDialog) {
443                    
444                    /** Shortcut the rest of the process if we are processing offscreen */
445                    if (super.getIdv().getArgsManager().getIsOffScreen()) {
446                            return;
447                    }
448    
449                    String thisVersion = getMcIdasVersion();
450                    String thatVersion = getMcIdasVersionPrerelease();
451                    String titleText = "Prerelease Check";
452                    
453                    if (thisVersion.equals("") || thatVersion.equals("")) {
454                            if (notifyDialog) {
455                                    JOptionPane.showMessageDialog(null, "No prerelease version available", titleText, 
456                                                    JOptionPane.WARNING_MESSAGE);
457                            }
458                    }
459                    else if (compareVersions(thisVersion, thatVersion) > 0) {
460                            String labelText = "<html>Prerelease <b>" + thatVersion + "</b> is available<br><br>";
461                            labelText += "Visit <a href=\"" + Constants.HOMEPAGE_URL+'/'+Constants.PRERELEASE_URL + "\">";
462                            labelText += Constants.HOMEPAGE_URL+'/'+Constants.PRERELEASE_URL + "</a> to download</html>";
463                            
464                            JPanel backgroundColorGetterPanel = new JPanel();
465                            JEditorPane messageText = new JEditorPane("text/html", labelText);
466                            messageText.setBackground(backgroundColorGetterPanel.getBackground());
467                            messageText.setEditable(false);
468                            messageText.addHyperlinkListener(this);
469    
470    //                      JLabel message = new JLabel(labelText, JLabel.CENTER);
471                            JOptionPane.showMessageDialog(null, messageText, titleText, 
472                                            JOptionPane.INFORMATION_MESSAGE);
473                    }
474                    else {
475                            if (notifyDialog) {
476                                    String labelText = "<html>This version (<b>" + thisVersion + "</b>) is up to date</html>";
477                                    JLabel message = new JLabel(labelText, JLabel.CENTER);
478                                    JOptionPane.showMessageDialog(null, message, titleText, 
479                                                    JOptionPane.INFORMATION_MESSAGE);
480                            }
481                    }
482                    
483            }
484            
485            public void checkForNotice(boolean notifyDialog) {
486                    
487                    /** Shortcut this whole process if we are processing offscreen */
488                    if (super.getIdv().getArgsManager().getIsOffScreen())
489                            return;
490    
491                    String thisNotice = getNoticeCached().trim();
492                    String thatNotice = getNoticeLatest().trim();
493                    String titleText = "New Notice";
494                    String labelText = thatNotice;
495                    
496                    if (thatNotice.equals("")) {
497                            setNoticeCached(thatNotice);
498                            if (notifyDialog) {
499                                    titleText = "No Notice";
500                                    JLabel message = new JLabel("There is no current notice", JLabel.CENTER);
501                                    JOptionPane.showMessageDialog(null, message, titleText, 
502                                                    JOptionPane.INFORMATION_MESSAGE);
503                            }
504                            return;
505                    }
506                    else if (!thisNotice.equals(thatNotice)) {
507                            setNoticeCached(thatNotice);
508                            
509                            JPanel backgroundColorGetterPanel = new JPanel();
510                            JEditorPane messageText = new JEditorPane("text/html", labelText);
511                            messageText.setBackground(backgroundColorGetterPanel.getBackground());
512                            messageText.setEditable(false);
513                            messageText.addHyperlinkListener(this);
514    
515    //                      JLabel message = new JLabel(labelText, JLabel.CENTER);
516                            JOptionPane.showMessageDialog(null, messageText, titleText, 
517                                            JOptionPane.INFORMATION_MESSAGE);
518                    }
519                    else {
520                            if (notifyDialog) {
521                                    titleText = "Previous Notice";
522                                    JLabel message = new JLabel(labelText, JLabel.CENTER);
523                                    JOptionPane.showMessageDialog(null, message, titleText, 
524                                                    JOptionPane.INFORMATION_MESSAGE);
525                            }
526                    }
527                    
528            }
529            
530            private String getNoticePath() {
531            return StartupManager.getInstance().getPlatform().getUserFile("notice.txt");
532            }
533    
534            private String getNoticeCached() {
535                String notice = "";
536                    try{
537                            FileReader fstream = new FileReader(getNoticePath());
538                            BufferedReader in = new BufferedReader(fstream);
539                        String line;
540                        while ((line = in.readLine()) != null) {
541                            notice += line + '\n';
542                        }
543                            in.close();
544                    } catch (Exception e){
545                            System.err.println("Error: " + e.getMessage());
546                    }
547                    return notice;
548            }
549            
550            private void setNoticeCached(String notice) {
551                    try{
552                            FileWriter fstream = new FileWriter(getNoticePath());
553                            BufferedWriter out = new BufferedWriter(fstream);
554                            out.write(notice);
555                            out.close();
556                    } catch (Exception e){
557                            System.err.println("Error: " + e.getMessage());
558                    }
559            }
560            
561    }