001/*
002 * $Id: AddePointDataChooser.java,v 1.30 2011/03/24 16:06:32 davep Exp $
003 *
004 * This file is part of McIDAS-V
005 *
006 * Copyright 2007-2011
007 * Space Science and Engineering Center (SSEC)
008 * University of Wisconsin - Madison
009 * 1225 W. Dayton Street, Madison, WI 53706, USA
010 * https://www.ssec.wisc.edu/mcidas
011 * 
012 * All Rights Reserved
013 * 
014 * McIDAS-V is built on Unidata's IDV and SSEC's VisAD libraries, and
015 * some McIDAS-V source code is based on IDV and VisAD source code.  
016 * 
017 * McIDAS-V is free software; you can redistribute it and/or modify
018 * it under the terms of the GNU Lesser Public License as published by
019 * the Free Software Foundation; either version 3 of the License, or
020 * (at your option) any later version.
021 * 
022 * McIDAS-V is distributed in the hope that it will be useful,
023 * but WITHOUT ANY WARRANTY; without even the implied warranty of
024 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
025 * GNU Lesser Public License for more details.
026 * 
027 * You should have received a copy of the GNU Lesser Public License
028 * along with this program.  If not, see http://www.gnu.org/licenses.
029 */
030
031package edu.wisc.ssec.mcidasv.chooser.adde;
032
033
034import static javax.swing.GroupLayout.DEFAULT_SIZE;
035import static javax.swing.GroupLayout.PREFERRED_SIZE;
036import static javax.swing.GroupLayout.Alignment.BASELINE;
037import static javax.swing.GroupLayout.Alignment.LEADING;
038import static javax.swing.LayoutStyle.ComponentPlacement.RELATED;
039
040import java.awt.IllegalComponentStateException;
041import java.awt.Point;
042import java.awt.event.ActionEvent;
043import java.awt.event.ActionListener;
044import java.text.SimpleDateFormat;
045import java.util.ArrayList;
046import java.util.Arrays;
047import java.util.Collections;
048import java.util.Date;
049import java.util.Enumeration;
050import java.util.Hashtable;
051import java.util.List;
052import java.util.SortedSet;
053import java.util.TreeSet;
054
055import javax.swing.GroupLayout;
056import javax.swing.JButton;
057import javax.swing.JComboBox;
058import javax.swing.JComponent;
059import javax.swing.JDialog;
060import javax.swing.JLabel;
061import javax.swing.JPanel;
062import javax.swing.ListSelectionModel;
063
064import org.w3c.dom.Element;
065
066import edu.wisc.ssec.mcidas.McIDASUtil;
067import edu.wisc.ssec.mcidas.adde.AddePointDataReader;
068import edu.wisc.ssec.mcidas.adde.DataSetInfo;
069
070import visad.DateTime;
071import visad.VisADException;
072
073import ucar.unidata.data.AddeUtil;
074import ucar.unidata.data.point.AddePointDataSource;
075import ucar.unidata.idv.chooser.IdvChooserManager;
076import ucar.unidata.idv.chooser.adde.AddeServer;
077import ucar.unidata.ui.DateTimePicker;
078import ucar.unidata.ui.symbol.StationModel;
079import ucar.unidata.ui.symbol.StationModelManager;
080import ucar.unidata.util.GuiUtils;
081import ucar.unidata.util.Misc;
082import ucar.unidata.util.TwoFacedObject;
083import ucar.visad.UtcDate;
084
085import edu.wisc.ssec.mcidasv.util.McVGuiUtils;
086import edu.wisc.ssec.mcidasv.util.McVGuiUtils.Width;
087
088
089/**
090 * Selection widget for ADDE point data
091 *
092 * @author MetApps Development Team
093 * @version $Revision: 1.30 $ $Date: 2011/03/24 16:06:32 $
094 */
095public class AddePointDataChooser extends AddeChooser {
096    
097    /**
098     * Property for the dataset name key.
099     * @see #getDatasetName()
100     */
101    public static String DATASET_NAME_KEY = "name";
102
103    /** Property for the data type. */
104    public static String DATA_TYPE = "ADDE.POINT.V";
105
106    /** Are we currently reading times */
107    private Object readTimesTask;
108    
109    /** box and label for the relative time */
110    protected JLabel relTimeIncLabel;
111    protected JComboBox relTimeIncBox;
112
113    /** the relative time increment */
114    private float relativeTimeIncrement = 1;
115    
116    /** archive date */
117    protected String archiveDay = null;
118
119    /** archive day button and label */
120    protected JLabel archiveDayLabel;
121    protected JButton archiveDayBtn;
122
123    /** archive date formatter */
124    private SimpleDateFormat archiveDayFormatter;
125
126    /** station model manager */
127    private StationModelManager stationModelManager;
128    
129    /** allowed descriptor prefix */
130    protected String descriptorsAllowPrefix = "";
131        
132    protected boolean firstTime = true;
133    protected boolean retry = true;
134    
135    /** Possibly ask for times a second time if the first sampling doesn't get any */
136    private boolean gotObs = false;
137    protected boolean tryWithoutSampling = false;
138        
139    /**
140     * Create a chooser for Adde POINT data
141     *
142     * @param mgr The chooser manager
143     * @param root The chooser.xml node
144     */
145    public AddePointDataChooser(IdvChooserManager mgr, Element root) {
146        super(mgr, root);
147                                
148        this.stationModelManager = getIdv().getStationModelManager();
149
150        relTimeIncLabel = new JLabel(" Interval:");
151        relTimeIncBox = new JComboBox();
152        relTimeIncBox.setToolTipText("Set the increment between relative times");
153        relTimeIncBox.addActionListener(new ActionListener() {
154            public void actionPerformed(ActionEvent ae) {
155                JComboBox box = (JComboBox) ae.getSource();
156                if (GuiUtils.anySelected(box)) {
157                    setRelativeTimeIncrement(getRelBoxValue());
158                }
159            }
160        });
161        McVGuiUtils.setComponentWidth(relTimeIncBox, Width.ONEHALF);
162        
163        descriptorsAllowPrefix = "";
164        
165        archiveDayBtn = GuiUtils.makeImageButton(
166                "/auxdata/ui/icons/calendar_edit.png", this, "getArchiveDay", null,
167                true);
168        archiveDayBtn.setToolTipText("Select a day for archive datasets");
169        archiveDayLabel = new JLabel("Select day:");
170        archiveDayFormatter = new SimpleDateFormat(UtcDate.YMD_FORMAT);
171    }
172    
173    /**
174     * Do server connection stuff... override this with type-specific methods
175     */
176    protected void readFromServer() {
177        archiveDay = null;
178        if (archiveDayLabel != null) {
179            archiveDayLabel.setText("Select day:");
180        }
181        super.readFromServer();
182    }
183
184    /**
185     *  Generate a list of image descriptors for the descriptor list.
186     */
187    protected void readDescriptors() {
188        try {
189            StringBuffer buff   = getGroupUrl(REQ_DATASETINFO, getGroup());
190            buff.append("&type=" + getDataType());
191            DataSetInfo  dsinfo = new DataSetInfo(buff.toString());
192            descriptorTable = dsinfo.getDescriptionTable();
193            
194            // Only show descriptorsAllowPrefix if set
195            for (Enumeration e = descriptorTable.keys(); e.hasMoreElements();) {
196                Object key = e.nextElement();
197                String str = (String) descriptorTable.get(key);
198                if (!descriptorsAllowPrefix.equals("") && str.indexOf(descriptorsAllowPrefix) != 0)
199                    descriptorTable.remove(key);
200            }
201            
202            String[]    names       = new String[descriptorTable.size()];
203            Enumeration enumeration = descriptorTable.keys();
204            for (int i = 0; enumeration.hasMoreElements(); i++) {
205                Object thisElement = enumeration.nextElement();
206                names[i] = descriptorTable.get(thisElement).toString() + " - " + thisElement.toString();
207            }
208            Arrays.sort(names);
209            setDescriptors(names);
210            setState(STATE_CONNECTED);
211        } catch (Exception e) {
212            handleConnectionError(e);
213        }
214    }
215    
216    /**
217     * Load in an ADDE point data set based on the
218     * <code>PropertyChangeEvent<code>.
219     *
220     */
221    public void doLoadInThread() {
222        showWaitCursor();
223        try {
224            StationModel selectedStationModel = getSelectedStationModel();
225            String       source               = getRequestUrl();
226                        
227            // make properties Hashtable to hand the station name
228            // to the AddeProfilerDataSource
229            Hashtable ht = new Hashtable();
230            getDataSourceProperties(ht);
231            ht.put(AddePointDataSource.PROP_STATIONMODELNAME,
232                   selectedStationModel.getName());
233            ht.put(DATASET_NAME_KEY, getDescriptor());
234            ht.put(DATA_NAME_KEY, getDataName());
235            if (source.indexOf(AddeUtil.RELATIVE_TIME) >= 0) {
236                ht.put(AddeUtil.NUM_RELATIVE_TIMES, getRelativeTimeIndices());
237                ht.put(AddeUtil.RELATIVE_TIME_INCREMENT,
238                       new Float(getRelativeTimeIncrement()));
239            }
240
241//            System.out.println("makeDataSource: source=" + source);
242//            System.out.println("    DATA_TYPE=" + DATA_TYPE);
243//            System.out.println("    ht=" + ht);
244            makeDataSource(source, DATA_TYPE, ht);
245            saveServerState();
246        } catch (Exception excp) {
247            logException("Unable to open ADDE point dataset", excp);
248        }
249        showNormalCursor();
250    }
251        
252    /**
253     * Show the archive dialog. This method is not meant to be called but is
254     * public by reason of implementation (or insanity).
255     */
256    public void getArchiveDay() {
257        final JDialog dialog = GuiUtils.createDialog("Set Archive Day", true);
258        final DateTimePicker dtp = new DateTimePicker((Date) null, false);
259        if (archiveDay != null) {
260            if (archiveDayFormatter == null) {
261                archiveDayFormatter = new SimpleDateFormat(UtcDate.YMD_FORMAT);
262            }
263            Date d = null;
264            try {
265                d = archiveDayFormatter.parse(archiveDay);
266                dtp.setDate(d);
267            } catch (Exception e) {
268                logException("parsing archive day " + archiveDay, e);
269            }
270        }
271
272        ActionListener listener = new ActionListener() {
273            public void actionPerformed(ActionEvent ae) {
274                String cmd = ae.getActionCommand();
275                if (cmd.equals(GuiUtils.CMD_REMOVE)) {
276                    archiveDay = null;
277                    archiveDayLabel.setText("Select day:");
278                    setDoAbsoluteTimes(true);
279                    descriptorChanged();
280                } else if (cmd.equals(GuiUtils.CMD_OK)) {
281                    try {
282                        DateTime dt = new DateTime(dtp.getDate());
283                        String myDay = UtcDate.getYMD(dt);
284                        // archiveDayLabel.setText(UtcDate.formatUtcDate(dt,
285                        // "MMM dd, yyyy"));
286                        archiveDayLabel.setText(myDay);
287                    } catch (Exception e) {
288                    }
289                    // System.out.println("archiveDay = " + archiveDay);
290                    setDoAbsoluteTimes(true);
291                    descriptorChanged();
292                }
293                dialog.dispose();
294            }
295        };
296
297        JPanel buttons = GuiUtils.makeButtons(listener, new String[] {
298                GuiUtils.CMD_OK, GuiUtils.CMD_REMOVE, GuiUtils.CMD_CANCEL });
299
300        JComponent contents = GuiUtils.topCenterBottom(GuiUtils.inset(GuiUtils
301                .lLabel("Please select a day for this dataset:"), 10), GuiUtils
302                .inset(dtp, 10), buttons);
303        Point p = new Point(200, 200);
304        if (archiveDayBtn != null) {
305            try {
306                p = archiveDayBtn.getLocationOnScreen();
307            } catch (IllegalComponentStateException ice) {
308            }
309        }
310        dialog.setLocation(p);
311        dialog.getContentPane().add(contents);
312        dialog.pack();
313        dialog.setVisible(true);
314    }
315
316    /**
317     * Get the selected station model.
318     *
319     * @return StationModel to use: defined by defaultModels list in ctor
320     */
321    public StationModel getSelectedStationModel() {
322        StationModel returnModel = null;
323        if (isUpperAir())
324            returnModel = this.stationModelManager.getStationModel("Observations>Upper Air");
325        else if (isSynoptic())
326            returnModel = this.stationModelManager.getStationModel("Observations>SYNOP");
327        else
328            returnModel = this.stationModelManager.getStationModel("Observations>METAR");
329        return returnModel;
330    }
331    
332    /**
333     * Add the interval selector to the component.
334     * @return superclass component with extra stuff
335     */
336    protected JPanel makeTimesPanel() {
337        JComponent extra1 = getExtraTimeComponentRelative();
338        GuiUtils.enableTree(extra1, false);
339        JComponent extra2 = getExtraTimeComponentAbsolute();
340        JPanel timesPanel = super.makeTimesPanel(extra1, extra2);
341        return timesPanel;
342    }
343    
344    /**
345     * Get the extra time widget, but built in a different way.
346     * Designed to be put into a GroupLayout
347     */
348    protected JComponent getExtraTimeComponentRelative() {
349        TwoFacedObject[] intervals = { 
350                new TwoFacedObject("30 minute", .5f),
351                new TwoFacedObject("Hourly", 1f),
352                new TwoFacedObject("Three hourly", 3f),
353                new TwoFacedObject("Six hourly", 6f),
354                new TwoFacedObject("12 hourly", 12f),
355                new TwoFacedObject("24 hourly", 24f)
356        };
357
358        GuiUtils.setListData(relTimeIncBox, intervals);
359        if (relTimeIncBox.getItemCount()>=2) relTimeIncBox.setSelectedIndex(1);
360        
361        return McVGuiUtils.makeLabeledComponent(relTimeIncLabel, relTimeIncBox, McVGuiUtils.Position.LEFT);
362    }
363    
364    /**
365     * Get the time popup widget
366     * 
367     * @return a widget for selecing the day
368     */
369    protected JComponent getExtraTimeComponentAbsolute() {
370        return null;
371//      return McVGuiUtils.makeLabeledComponent(archiveDayLabel, archiveDayBtn);
372    }
373        
374    /**
375     * Get the value from the relative increment box
376     *
377     * @return the selected value or a default
378     */
379    protected float getRelBoxValue() {
380        float value = relativeTimeIncrement;
381        if (relTimeIncBox != null) {
382            Object o = relTimeIncBox.getSelectedItem();
383            if (o != null) {
384                String val = TwoFacedObject.getIdString(o);
385                value = (float) Misc.parseNumber(val);
386            }
387        }
388        return value;
389    }
390    
391    /**
392     * Get the string from the relative increment box
393     *
394     * @return the selected string or a default
395     */
396    public String getRelBoxString() {
397        String value = "";
398        if (relTimeIncBox != null) {
399            Object o = relTimeIncBox.getSelectedItem();
400            if (o != null) {
401                value = TwoFacedObject.getIdString(o);
402            }
403        }
404        return value;
405    }
406
407    /**
408     * Get the request URL
409     *
410     * @return  the request URL
411     */
412    public String getRequestUrl() {
413        StringBuffer request = getGroupUrl(REQ_POINTDATA, getGroup());
414        appendKeyValue(request, PROP_USER, getLastAddedUser());
415        appendKeyValue(request, PROP_PROJ, getLastAddedProj());
416        appendKeyValue(request, PROP_DESCR, getDescriptor());
417        appendRequestSelectClause(request);
418        appendKeyValue(request, PROP_NUM, "ALL");
419        appendKeyValue(request, PROP_POS, getDoRelativeTimes() ? "ALL" : "0");
420        return request.toString();
421    }
422        
423    /**
424     * Get the select clause for the adde request specific to this
425     * type of data.
426     *
427     * @param buf The buffer to append to
428     */
429    protected void appendRequestSelectClause(StringBuffer buf) {
430        StringBuffer selectValue = new StringBuffer();
431        selectValue.append("'");
432        selectValue.append(getDayTimeSelectString());
433        //TODO: why is SFCHOURLY explicit here?  better way to do it?
434        if (getDescriptor().equalsIgnoreCase("SFCHOURLY")) {
435            selectValue.append(";type 0");
436        }
437        selectValue.append(";");
438        if (isUpperAir()){
439            selectValue.append(AddeUtil.LEVEL);
440            selectValue.append(";");
441        }
442        selectValue.append(AddeUtil.LATLON_BOX);
443        selectValue.append("'");
444        appendKeyValue(buf, PROP_SELECT, selectValue.toString());
445    }
446    
447    /**
448     * Check if we are ready to read times
449     *
450     * @return  true if times can be read
451     */
452    protected boolean canReadTimes() {
453        return haveDescriptorSelected();
454    }
455        
456    /**
457     * Enable or disable the GUI widgets based on what has been
458     * selected.
459     */
460    protected void enableWidgets() {
461        boolean descriptorState = ((getState() == STATE_CONNECTED)
462                                   && canReadTimes());
463
464        for (int i = 0; i < compsThatNeedDescriptor.size(); i++) {
465            JComponent comp = (JComponent) compsThatNeedDescriptor.get(i);
466            GuiUtils.enableTree(comp, descriptorState);
467        }
468
469        boolean timesOk = timesOk();
470        
471        // Require times to be selected
472        GuiUtils.enableTree(loadButton, descriptorState && timesOk);
473
474        checkTimesLists();
475        
476        enableAbsoluteTimesList(getDoAbsoluteTimes() && descriptorState);
477
478        getRelativeTimesChooser().setEnabled( !getDoAbsoluteTimes()
479                && descriptorState);
480
481        revalidate();
482    }
483    
484    /**
485     * Do we have times selected. Either we are doing absolute
486     * times and there are some selected in the list. Or we
487     * are doing relative times and we have done a connect to the
488     * server
489     *
490     * @return Do we have times
491     */
492    public boolean timesOk() {
493        if (getDoAbsoluteTimes() && !haveTimeSelected()) {
494            return false;
495        }
496        return true;
497    }
498    
499    /**
500     * Return true if selected descriptor is for SYNOPTIC data
501     */
502    protected boolean isSynoptic() {
503        if (getDescriptor().indexOf("SYNOP") == 0) return true;
504        return false;
505    }
506    
507    /**
508     * Return true if selected descriptor is for upper air
509     */
510    protected boolean isUpperAir() {
511        if (getDescriptor().indexOf("UPPER") == 0) return true;
512        return false;
513    }
514    
515    /**
516     * Return true if selected descriptor is for profiler
517     */
518    protected boolean isProfiler() {
519        if (getDescriptor().indexOf("PROF") == 0) return true;
520        return false;
521    }
522        
523    /**
524     * Update the widget with the latest data.
525     *
526     * @throws Exception On badness
527     */
528    public void handleUpdate() throws Exception {
529        if (getState() != STATE_CONNECTED) {
530            //If not connected then update the server list
531            updateServerList();
532        } else {
533            //If we are already connected then update the rest of the chooser
534            descriptorChanged();
535        }
536        updateStatus();
537    }
538        
539    /**
540     * Get the request string for times particular to this chooser
541     *
542     * @return request string
543     */
544    protected String getTimesRequest() {
545        StringBuffer buf = getGroupUrl(REQ_POINTDATA, getGroup());
546        appendKeyValue(buf, PROP_USER, getLastAddedUser());
547        appendKeyValue(buf, PROP_PROJ, getLastAddedProj());
548        appendKeyValue(buf, PROP_DESCR, getDescriptor());
549        if (!isUpperAir() && !tryWithoutSampling) {
550//            appendKeyValue(buf, PROP_POS, "0");
551            appendKeyValue(buf, PROP_POS, "ALL");
552            appendKeyValue(buf, PROP_SELECT, "'DAY "+getJulianDay()+";LAT 38 42;LON 70 75'");
553        }
554        else {
555            appendKeyValue(buf, PROP_SELECT, "'DAY "+getJulianDay()+"'");
556            appendKeyValue(buf, PROP_POS, "ALL");
557        }
558        if (getDoAbsoluteTimes()) {
559            appendKeyValue(buf, PROP_NUM, "ALL");
560        }
561        appendKeyValue(buf, PROP_PARAM, "DAY TIME");
562        return buf.toString();
563    }
564    
565    /**
566     * Get the current  Julian day as a String
567     *
568     * @return the current day as a string (yyyyDDD)
569     */
570    // TODO: This should really be more accessible
571    private String getJulianDay() {
572        String retString = "";
573        try {
574            DateTime dt = new DateTime();
575            retString = UtcDate.formatUtcDate(dt, "yyyyDDD");
576        } catch (VisADException ve) {}
577        return retString;
578
579    }
580    
581    /**
582     * This allows derived classes to provide their own name for labeling, etc.
583     *
584     * @return  the dataset name
585     */
586    public String getDataName() {
587        return "Point Data";
588    }
589    
590    /**
591     * _more_
592     */
593    public void doCancel() {
594        readTimesTask = null;
595        setState(STATE_UNCONNECTED);
596        super.doCancel();
597    }
598
599    /** locking mutex */
600    private Object MUTEX = new Object();
601    
602    /**
603     *  Read the set of image times available for the current server/group/type
604     *  This method is a wrapper, setting the wait cursor and wrapping the
605     *  call to {@link #readTimesInner()}; in a try/catch block
606     */
607    public void readTimes() {
608        clearTimesList();
609        if ( !canReadTimes()) {
610            return;
611        }
612        Misc.run(new Runnable() {
613            public void run() {
614                updateStatus();
615                showWaitCursor();
616                try {
617                    gotObs = false;
618                    tryWithoutSampling = false;
619                    readTimesInner();
620                    // Try again, this time not sampling by LAT/LON
621                    if (!gotObs) {
622                        tryWithoutSampling = true;
623                        readTimesInner();
624                    }
625                } catch (Exception e) {
626                    handleConnectionError(e);
627                }
628                showNormalCursor();
629                updateStatus();
630            }
631        });
632    }
633
634    /**
635     * Set the list of dates/times based on the image selection
636     *
637     */
638    protected void readTimesInner() {
639        SortedSet uniqueTimes = Collections.synchronizedSortedSet(new TreeSet());
640
641        readTimesTask = startTask();
642        updateStatus();
643        Object task = readTimesTask;
644        try {
645            AddePointDataReader apr = new AddePointDataReader(getTimesRequest());
646            //Make sure no other loads are  occurred
647            boolean ok = stopTaskAndIsOk(task);
648            if ( !Misc.equals(readTimesTask, task) || !ok) {
649                return;
650            }
651            readTimesTask = null;
652
653            synchronized (MUTEX) {
654                int[][]  data  = apr.getData();
655                String[] units = apr.getUnits();
656                if ( !units[0].equals("CYD") || !units[1].equals("HMS")) {
657                    throw new Exception("can't handle date/time units");
658                }
659                int numObs = data[0].length;
660                //System.out.println("found " + numObs + " obs");
661                // loop through and find all the unique times
662                for (int i = 0; i < numObs; i++) {
663                    try {
664                        DateTime dt =
665                            new DateTime(McIDASUtil.mcDayTimeToSecs(data[0][i],
666                                data[1][i]));
667                        uniqueTimes.add(dt);
668                    } catch (Exception e) {}
669                }
670                //System.out.println(
671                //      "found " + uniqueTimes.size() + " unique times");
672                if (getDoAbsoluteTimes()) {
673                    if ( !uniqueTimes.isEmpty()) {
674                        setAbsoluteTimes(new ArrayList(uniqueTimes));
675                        getTimesList().setSelectionMode(
676                                ListSelectionModel.SINGLE_INTERVAL_SELECTION);
677                    }
678
679                    //   Select the last n hours 
680                    int selectedIndex = getAbsoluteTimes().size() - 1;
681                    int firstIndex = Math.max(0, selectedIndex
682                            - getDefaultRelativeTimeIndex());
683                    if (selectedIndex >= 0)
684                        setSelectedAbsoluteTime(selectedIndex, firstIndex);
685                }
686                if (numObs>0) gotObs=true;
687            }
688            setState(STATE_CONNECTED);
689        } catch (Exception excp) {
690            stopTask(task);
691            readTimesTask = null;
692            handleConnectionError(excp);
693            if (retry == false) return;
694            try {
695                handleUpdate();
696            } catch (Exception e) {}
697
698        }
699    }
700        
701    /**
702     * Show the given error to the user. If it was an Adde exception
703     * that was a bad server error then print out a nice message.
704     *
705     * @param excp The exception
706     */
707    protected void handleConnectionError(Exception e) {
708        retry = false;
709        super.handleConnectionError(e);
710    }
711
712    /**
713     * Are there any times selected.
714     *
715     * @return Any times selected.
716     */
717    protected boolean haveTimeSelected() {
718        return !getDoAbsoluteTimes() || getHaveAbsoluteTimesSelected();
719    }
720
721    /**
722     * Create the date time selection string for the "select" clause
723     * of the ADDE URL.
724     *
725     * @return the select day and time strings
726     */
727    protected String getDayTimeSelectString() {
728        StringBuffer buf = new StringBuffer();
729        if (getDoAbsoluteTimes()) {
730            List times = getSelectedAbsoluteTimes();
731
732            // no time selection is permitted as a valid choice -
733            // will then use all times today by default.
734            if (times.size() == 0) {
735                return "";
736            }
737
738            //check for the "no times available" message
739            if (times.get(0) instanceof String) {
740                return "";
741            }
742
743            if (archiveDay!=null) {
744                System.out.println("archiveDay: " + archiveDay);
745                try {
746                    Date d = archiveDayFormatter.parse(archiveDay);
747                    DateTime dt = new DateTime(d);
748                    System.out.println("parsed to: " + dt.toString());
749                    buf.append("day " + UtcDate.getIYD(dt) + ";");
750                } catch (Exception e) {
751                    System.out.println("archiveDay parse error");
752                }
753            }
754            else { System.out.println("archiveDay is null!"); }
755            
756            buf.append("time ");
757            for (int i = 0; i < times.size(); i++) {
758                DateTime dt = (DateTime) times.get(i);
759                buf.append(UtcDate.getHMS(dt));
760                if (i != times.size() - 1) {
761                    buf.append(",");
762                }
763            }
764        } else {
765            buf.append(AddeUtil.RELATIVE_TIME);
766        }
767        return buf.toString();
768    }
769        
770//    /**
771//     * Get the name of the dataset.
772//     *
773//     * @return descriptive name of the dataset.
774//     */
775//    public String getDatasetName() {
776//        return dataTypes.getSelectedItem().toString();
777//    }
778
779    /**
780     * Get the data type for this chooser
781     *
782     * @return  the type
783     */
784    public String getDataType() {
785        return "POINT";
786    }
787
788    /**
789     * Get the increment between times for relative time requests
790     *
791     * @return time increment (hours)
792     */
793    public float getRelativeTimeIncrement() {
794        return relativeTimeIncrement;
795    }
796
797    /**
798     * Set the increment between times for relative time requests
799     *
800     * @param increment time increment (hours)
801     */
802    public void setRelativeTimeIncrement(float increment) {
803        relativeTimeIncrement = increment;
804        if (relTimeIncBox != null) {
805            relTimeIncBox.setSelectedItem(new Float(relativeTimeIncrement));
806        }
807    }
808
809    /**
810     * Update labels, enable widgets, etc.
811     */
812    protected void updateStatus() {
813        super.updateStatus();
814        if (readTimesTask != null) {
815            if (taskOk(readTimesTask)) {
816                setStatus("Reading available times from server");
817            }
818        } else if (getDoAbsoluteTimes() && !haveTimeSelected()) {
819            setStatus(MSG_TIMES);
820        }
821        enableWidgets();
822    }
823
824                
825    /**
826     * Get the descriptor widget label.
827     *
828     * @return  label for the descriptor  widget
829     */
830    public String getDescriptorLabel() { 
831        return "Point Type"; 
832    }
833    
834    /**
835     * get the adde server grup type to use
836     *
837     * @return group type
838     */
839    @Override
840    protected String getGroupType() {
841        return AddeServer.TYPE_POINT;
842    }
843    
844    /**
845     * Make the UI for this selector.
846     * 
847     * @return The gui
848     */   
849    public JComponent doMakeContents() {
850        JPanel myPanel = new JPanel();
851        
852        McVGuiUtils.setComponentWidth(descriptorComboBox, Width.DOUBLEDOUBLE);
853                
854        JLabel stationLabel = McVGuiUtils.makeLabelRight("Station:");
855        addServerComp(stationLabel);
856        
857        JLabel timesLabel = McVGuiUtils.makeLabelRight("Times:");
858        addDescComp(timesLabel);
859        
860        JPanel timesPanel = makeTimesPanel();
861        timesPanel.setBorder(javax.swing.BorderFactory.createEtchedBorder());
862        addDescComp(timesPanel);
863                
864        GroupLayout layout = new GroupLayout(myPanel);
865        myPanel.setLayout(layout);
866        layout.setHorizontalGroup(
867            layout.createParallelGroup(LEADING)
868            .addGroup(layout.createSequentialGroup()
869                .addGroup(layout.createParallelGroup(LEADING)
870                    .addGroup(layout.createSequentialGroup()
871                        .addComponent(descriptorLabel)
872                        .addGap(GAP_RELATED)
873                        .addComponent(descriptorComboBox))
874                    .addGroup(layout.createSequentialGroup()
875                        .addComponent(timesLabel)
876                        .addGap(GAP_RELATED)
877                        .addComponent(timesPanel, PREFERRED_SIZE, DEFAULT_SIZE, Short.MAX_VALUE))))
878        );
879        layout.setVerticalGroup(
880            layout.createParallelGroup(LEADING)
881            .addGroup(layout.createSequentialGroup()
882                .addGroup(layout.createParallelGroup(BASELINE)
883                    .addComponent(descriptorLabel)
884                    .addComponent(descriptorComboBox))
885                .addPreferredGap(RELATED)
886                .addGroup(layout.createParallelGroup(LEADING)
887                    .addComponent(timesLabel)
888                    .addComponent(timesPanel, PREFERRED_SIZE, DEFAULT_SIZE, Short.MAX_VALUE)))
889        );
890        
891        setInnerPanel(myPanel);
892        return super.doMakeContents();
893    }
894
895    public JComponent doMakeContents(boolean doesOverride) {
896        if (doesOverride)
897            return super.doMakeContents();
898        else
899            return doMakeContents();
900    }
901    
902}