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.chooser.adde;
030
031 import static javax.swing.GroupLayout.DEFAULT_SIZE;
032 import static javax.swing.GroupLayout.PREFERRED_SIZE;
033 import static javax.swing.GroupLayout.Alignment.BASELINE;
034 import static javax.swing.GroupLayout.Alignment.LEADING;
035 import static javax.swing.LayoutStyle.ComponentPlacement.RELATED;
036
037 import java.awt.IllegalComponentStateException;
038 import java.awt.Point;
039 import java.awt.event.ActionEvent;
040 import java.awt.event.ActionListener;
041 import java.text.SimpleDateFormat;
042 import java.util.ArrayList;
043 import java.util.Arrays;
044 import java.util.Collections;
045 import java.util.Date;
046 import java.util.Enumeration;
047 import java.util.Hashtable;
048 import java.util.List;
049 import java.util.SortedSet;
050 import java.util.TreeSet;
051
052 import javax.swing.GroupLayout;
053 import javax.swing.JButton;
054 import javax.swing.JComboBox;
055 import javax.swing.JComponent;
056 import javax.swing.JDialog;
057 import javax.swing.JLabel;
058 import javax.swing.JPanel;
059 import javax.swing.ListSelectionModel;
060
061 import org.slf4j.Logger;
062 import org.slf4j.LoggerFactory;
063 import org.w3c.dom.Element;
064
065 import edu.wisc.ssec.mcidas.McIDASUtil;
066 import edu.wisc.ssec.mcidas.adde.AddePointDataReader;
067 import edu.wisc.ssec.mcidas.adde.DataSetInfo;
068
069 import visad.DateTime;
070 import visad.VisADException;
071
072 import ucar.unidata.data.AddeUtil;
073 import ucar.unidata.data.point.AddePointDataSource;
074 import ucar.unidata.idv.chooser.IdvChooserManager;
075 import ucar.unidata.idv.chooser.adde.AddeServer;
076 import ucar.unidata.ui.DateTimePicker;
077 import ucar.unidata.ui.symbol.StationModel;
078 import ucar.unidata.ui.symbol.StationModelManager;
079 import ucar.unidata.util.GuiUtils;
080 import ucar.unidata.util.Misc;
081 import ucar.unidata.util.TwoFacedObject;
082 import ucar.visad.UtcDate;
083
084 import edu.wisc.ssec.mcidasv.util.McVGuiUtils;
085 import edu.wisc.ssec.mcidasv.util.McVGuiUtils.Width;
086
087 /**
088 * Selection widget for ADDE point data
089 *
090 * @version $Revision$ $Date$
091 */
092 public class AddePointDataChooser extends AddeChooser {
093
094 /** Logging object. Use it! */
095 private static final Logger logger = LoggerFactory.getLogger(AddePointDataChooser.class);
096
097 /**
098 * Property for the dataset name key.
099 * @see edu.wisc.ssec.mcidasv.chooser.adde.AddeChooser#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.0f;
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 @Override 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 @Override 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 @Override protected void readDescriptors() {
188 try {
189 StringBuffer buff = getGroupUrl(REQ_DATASETINFO, getGroup());
190 buff.append("&type=").append(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.isEmpty() && 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 @Override 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.contains(AddeUtil.RELATIVE_TIME)) {
236 ht.put(AddeUtil.NUM_RELATIVE_TIMES, getRelativeTimeIndices());
237 ht.put(AddeUtil.RELATIVE_TIME_INCREMENT, getRelativeTimeIncrement());
238 }
239
240 if (getDoAbsoluteTimes()) {
241 ht.put(AddeUtil.ABSOLUTE_TIMES, getSelectedAbsoluteTimes());
242 }
243
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 logger.error("problem while setting archive day label", e);
289 }
290 // System.out.println("archiveDay = " + archiveDay);
291 setDoAbsoluteTimes(true);
292 descriptorChanged();
293 }
294 dialog.dispose();
295 }
296 };
297
298 JPanel buttons = GuiUtils.makeButtons(listener, new String[] {
299 GuiUtils.CMD_OK, GuiUtils.CMD_REMOVE, GuiUtils.CMD_CANCEL });
300
301 JComponent contents = GuiUtils.topCenterBottom(GuiUtils.inset(GuiUtils
302 .lLabel("Please select a day for this dataset:"), 10), GuiUtils
303 .inset(dtp, 10), buttons);
304 Point p = new Point(200, 200);
305 if (archiveDayBtn != null) {
306 try {
307 p = archiveDayBtn.getLocationOnScreen();
308 } catch (IllegalComponentStateException ice) {
309 logger.error("archive day button in illegal state", ice);
310 }
311 }
312 dialog.setLocation(p);
313 dialog.getContentPane().add(contents);
314 dialog.pack();
315 dialog.setVisible(true);
316 }
317
318 /**
319 * Get the selected station model.
320 *
321 * @return StationModel to use: defined by defaultModels list in ctor
322 */
323 public StationModel getSelectedStationModel() {
324 StationModel returnModel = null;
325 if (isUpperAir()) {
326 returnModel = this.stationModelManager.getStationModel("Observations>Upper Air");
327 } else if (isSynoptic()) {
328 returnModel = this.stationModelManager.getStationModel("Observations>SYNOP");
329 } else {
330 returnModel = this.stationModelManager.getStationModel("Observations>METAR");
331 }
332 return returnModel;
333 }
334
335 /**
336 * Add the interval selector to the component.
337 * @return superclass component with extra stuff
338 */
339 @Override protected JPanel makeTimesPanel() {
340 JComponent extra1 = getExtraTimeComponentRelative();
341 GuiUtils.enableTree(extra1, false);
342 JComponent extra2 = getExtraTimeComponentAbsolute();
343 JPanel timesPanel = super.makeTimesPanel(extra1, extra2);
344 return timesPanel;
345 }
346
347 /**
348 * Get the extra time widget, but built in a different way.
349 * Designed to be put into a GroupLayout
350 *
351 * @return Extra time widget
352 */
353 protected JComponent getExtraTimeComponentRelative() {
354 TwoFacedObject[] intervals = {
355 new TwoFacedObject("30 minute", .5f),
356 new TwoFacedObject("Hourly", 1f),
357 new TwoFacedObject("Three hourly", 3f),
358 new TwoFacedObject("Six hourly", 6f),
359 new TwoFacedObject("12 hourly", 12f),
360 new TwoFacedObject("24 hourly", 24f)
361 };
362
363 GuiUtils.setListData(relTimeIncBox, intervals);
364 if (relTimeIncBox.getItemCount()>=2) relTimeIncBox.setSelectedIndex(1);
365
366 return McVGuiUtils.makeLabeledComponent(relTimeIncLabel, relTimeIncBox, McVGuiUtils.Position.LEFT);
367 }
368
369 /**
370 * Get the time popup widget
371 *
372 * @return a widget for selecing the day
373 */
374 protected JComponent getExtraTimeComponentAbsolute() {
375 return null;
376 // return McVGuiUtils.makeLabeledComponent(archiveDayLabel, archiveDayBtn);
377 }
378
379 /**
380 * Get the value from the relative increment box
381 *
382 * @return the selected value or a default
383 */
384 protected float getRelBoxValue() {
385 float value = relativeTimeIncrement;
386 if (relTimeIncBox != null) {
387 Object o = relTimeIncBox.getSelectedItem();
388 if (o != null) {
389 String val = TwoFacedObject.getIdString(o);
390 value = (float) Misc.parseNumber(val);
391 }
392 }
393 return value;
394 }
395
396 /**
397 * Get the string from the relative increment box
398 *
399 * @return the selected string or a default
400 */
401 public String getRelBoxString() {
402 String value = "";
403 if (relTimeIncBox != null) {
404 Object o = relTimeIncBox.getSelectedItem();
405 if (o != null) {
406 value = TwoFacedObject.getIdString(o);
407 }
408 }
409 return value;
410 }
411
412 /**
413 * Get the request URL
414 *
415 * @return the request URL
416 */
417 public String getRequestUrl() {
418 StringBuffer request = getGroupUrl(REQ_POINTDATA, getGroup());
419 appendKeyValue(request, PROP_USER, getLastAddedUser());
420 appendKeyValue(request, PROP_PROJ, getLastAddedProj());
421 appendKeyValue(request, PROP_DESCR, getDescriptor());
422 appendRequestSelectClause(request);
423 appendKeyValue(request, PROP_NUM, "ALL");
424 appendKeyValue(request, PROP_POS, getDoRelativeTimes() ? "ALL" : "0");
425 return request.toString();
426 }
427
428 /**
429 * Get the select clause for the adde request specific to this
430 * type of data.
431 *
432 * @param buf The buffer to append to
433 */
434 protected void appendRequestSelectClause(StringBuffer buf) {
435 StringBuilder selectValue = new StringBuilder(1024);
436 selectValue.append('\'');
437 selectValue.append(getDayTimeSelectString());
438 //TODO: why is SFCHOURLY explicit here? better way to do it?
439 if ("SFCHOURLY".equalsIgnoreCase(getDescriptor())) {
440 selectValue.append(";type 0");
441 }
442 selectValue.append(';');
443
444 if (isUpperAir()){
445 selectValue.append(AddeUtil.LEVEL);
446 selectValue.append(';');
447 }
448 selectValue.append(AddeUtil.LATLON_BOX);
449 selectValue.append('\'');
450 appendKeyValue(buf, PROP_SELECT, selectValue.toString());
451 }
452
453 /**
454 * Check if we are ready to read times
455 *
456 * @return true if times can be read
457 */
458 protected boolean canReadTimes() {
459 return haveDescriptorSelected();
460 }
461
462 /**
463 * Enable or disable the GUI widgets based on what has been
464 * selected.
465 */
466 @Override protected void enableWidgets() {
467 boolean descriptorState = ((getState() == STATE_CONNECTED)
468 && canReadTimes());
469
470 for (int i = 0; i < compsThatNeedDescriptor.size(); i++) {
471 JComponent comp = (JComponent) compsThatNeedDescriptor.get(i);
472 GuiUtils.enableTree(comp, descriptorState);
473 }
474
475 boolean timesOk = timesOk();
476
477 // Require times to be selected
478 GuiUtils.enableTree(loadButton, descriptorState && timesOk);
479
480 checkTimesLists();
481
482 enableAbsoluteTimesList(getDoAbsoluteTimes() && descriptorState);
483
484 getRelativeTimesChooser().setEnabled( !getDoAbsoluteTimes()
485 && descriptorState);
486
487 revalidate();
488 }
489
490 /**
491 * Do we have times selected. Either we are doing absolute
492 * times and there are some selected in the list. Or we
493 * are doing relative times and we have done a connect to the
494 * server
495 *
496 * @return Do we have times
497 */
498 public boolean timesOk() {
499 if (getDoAbsoluteTimes() && !haveTimeSelected()) {
500 return false;
501 }
502 return true;
503 }
504
505 /**
506 * Return {@code true} if selected descriptor is for SYNOPTIC data.
507 *
508 * @return {@code true} iff {@link edu.wisc.ssec.mcidasv.chooser.adde.AddePointDataChooser#getDescriptor()}
509 * is {@literal "SYNOP"}.
510 */
511 protected boolean isSynoptic() {
512 return "SYNOP".equals(getDescriptor());
513 }
514
515 /**
516 * Return {@code true} if selected descriptor is for upper air.
517 *
518 * @return {@code true} iff {@link edu.wisc.ssec.mcidasv.chooser.adde.AddePointDataChooser#getDescriptor()}
519 * is {@literal "UPPERMAND"}.
520 */
521 protected boolean isUpperAir() {
522 return "UPPERMAND".equals(getDescriptor());
523
524 }
525
526 /**
527 * Return {@code true} if selected descriptor is for profiler.
528 *
529 * @return {@code true} iff {@link edu.wisc.ssec.mcidasv.chooser.adde.AddePointDataChooser#getDescriptor()}
530 * is {@literal "PROF"}.
531 */
532 protected boolean isProfiler() {
533 return "PROF".equals(getDescriptor());
534 }
535
536 /**
537 * Update the widget with the latest data.
538 *
539 * @throws Exception On badness
540 */
541 @Override public void handleUpdate() throws Exception {
542 if (getState() != STATE_CONNECTED) {
543 //If not connected then update the server list
544 updateServerList();
545 } else {
546 //If we are already connected then update the rest of the chooser
547 descriptorChanged();
548 }
549 updateStatus();
550 }
551
552 /**
553 * Get the request string for times particular to this chooser
554 *
555 * @return request string
556 */
557 protected String getTimesRequest() {
558 StringBuffer buf = getGroupUrl(REQ_POINTDATA, getGroup());
559 appendKeyValue(buf, PROP_USER, getLastAddedUser());
560 appendKeyValue(buf, PROP_PROJ, getLastAddedProj());
561 appendKeyValue(buf, PROP_DESCR, getDescriptor());
562 if (!isUpperAir() && !tryWithoutSampling) {
563 // appendKeyValue(buf, PROP_POS, "0");
564 appendKeyValue(buf, PROP_POS, "ALL");
565 appendKeyValue(buf, PROP_SELECT, "'DAY "+getJulianDay()+";LAT 38 42;LON 70 75'");
566 }
567 else {
568 appendKeyValue(buf, PROP_SELECT, "'DAY "+getJulianDay()+"'");
569 appendKeyValue(buf, PROP_POS, "ALL");
570 }
571 if (getDoAbsoluteTimes()) {
572 appendKeyValue(buf, PROP_NUM, "ALL");
573 }
574 appendKeyValue(buf, PROP_PARAM, "DAY TIME");
575 return buf.toString();
576 }
577
578 /**
579 * Get the current Julian day as a String
580 *
581 * @return the current day as a string (yyyyDDD)
582 */
583 // TODO: This should really be more accessible
584 private String getJulianDay() {
585 String retString = "";
586 try {
587 DateTime dt = new DateTime();
588 retString = UtcDate.formatUtcDate(dt, "yyyyDDD");
589 } catch (VisADException ve) {
590 logger.error("error building julian date", ve);
591 }
592 return retString;
593 }
594
595 /**
596 * This allows derived classes to provide their own name for labeling, etc.
597 *
598 * @return the dataset name
599 */
600 @Override public String getDataName() {
601 return "Point Data";
602 }
603
604 /**
605 * _more_
606 */
607 @Override public void doCancel() {
608 readTimesTask = null;
609 setState(STATE_UNCONNECTED);
610 super.doCancel();
611 }
612
613 /** locking mutex */
614 private final Object MUTEX = new Object();
615
616 /**
617 * Read the set of image times available for the current server/group/type
618 * This method is a wrapper, setting the wait cursor and wrapping the
619 * call to {@link #readTimesInner()}; in a try/catch block
620 */
621 @Override public void readTimes() {
622 clearTimesList();
623 if (!canReadTimes()) {
624 return;
625 }
626 Misc.run(new Runnable() {
627 @Override public void run() {
628 updateStatus();
629 showWaitCursor();
630 try {
631 gotObs = false;
632 tryWithoutSampling = false;
633 readTimesInner();
634 // Try again, this time not sampling by LAT/LON
635 if (!gotObs) {
636 tryWithoutSampling = true;
637 readTimesInner();
638 }
639 } catch (Exception e) {
640 handleConnectionError(e);
641 }
642 showNormalCursor();
643 updateStatus();
644 }
645 });
646 }
647
648 /**
649 * Set the list of dates/times based on the image selection
650 */
651 protected void readTimesInner() {
652 SortedSet<DateTime> uniqueTimes = Collections.synchronizedSortedSet(new TreeSet<DateTime>());
653
654 readTimesTask = startTask();
655 updateStatus();
656 Object task = readTimesTask;
657 try {
658 AddePointDataReader apr = new AddePointDataReader(getTimesRequest());
659 //Make sure no other loads are occurred
660 boolean ok = stopTaskAndIsOk(task);
661 if (!Misc.equals(readTimesTask, task) || !ok) {
662 return;
663 }
664 readTimesTask = null;
665
666 synchronized (MUTEX) {
667 int[][] data = apr.getData();
668 String[] units = apr.getUnits();
669 if ( !"CYD".equals(units[0]) || !"HMS".equals(units[1])) {
670 throw new Exception("can't handle date/time units");
671 }
672 int numObs = data[0].length;
673 //System.out.println("found " + numObs + " obs");
674 // loop through and find all the unique times
675 try {
676 for (int i = 0; i < numObs; i++) {
677 DateTime dt =
678 new DateTime(McIDASUtil.mcDayTimeToSecs(data[0][i],
679 data[1][i]));
680 uniqueTimes.add(dt);
681 }
682 } catch (Exception e) {
683 logger.error("problem building list of unique times", e);
684 }
685 //System.out.println(
686 // "found " + uniqueTimes.size() + " unique times");
687 if (getDoAbsoluteTimes()) {
688 if (!uniqueTimes.isEmpty()) {
689 setAbsoluteTimes(new ArrayList<DateTime>(uniqueTimes));
690 getTimesList().setSelectionMode(ListSelectionModel.SINGLE_INTERVAL_SELECTION);
691 }
692
693 // Select the last n hours
694 int selectedIndex = getAbsoluteTimes().size() - 1;
695 int firstIndex = Math.max(0, selectedIndex
696 - getDefaultRelativeTimeIndex());
697 if (selectedIndex >= 0)
698 setSelectedAbsoluteTime(selectedIndex, firstIndex);
699 }
700 if (numObs>0) {
701 gotObs = true;
702 }
703 }
704 setState(STATE_CONNECTED);
705 } catch (Exception excp) {
706 stopTask(task);
707 readTimesTask = null;
708 handleConnectionError(excp);
709 if (!retry) {
710 return;
711 }
712 try {
713 handleUpdate();
714 } catch (Exception e) {
715 logger.error("problem handling update", e);
716 }
717 }
718 }
719
720 /**
721 * Show the given error to the user. If it was an Adde exception
722 * that was a bad server error then print out a nice message.
723 *
724 * @param e The exception
725 */
726 @Override protected void handleConnectionError(Exception e) {
727 retry = false;
728 super.handleConnectionError(e);
729 }
730
731 /**
732 * Are there any times selected.
733 *
734 * @return Any times selected.
735 */
736 @Override protected boolean haveTimeSelected() {
737 return !getDoAbsoluteTimes() || getHaveAbsoluteTimesSelected();
738 }
739
740 /**
741 * Create the date time selection string for the "select" clause
742 * of the ADDE URL.
743 *
744 * @return the select day and time strings
745 */
746 protected String getDayTimeSelectString() {
747 StringBuilder buf = new StringBuilder(1024);
748 if (getDoAbsoluteTimes()) {
749 List times = getSelectedAbsoluteTimes();
750
751 // no time selection is permitted as a valid choice -
752 // will then use all times today by default.
753 if (times.isEmpty()) {
754 return "";
755 }
756
757 //check for the "no times available" message
758 if (times.get(0) instanceof String) {
759 return "";
760 }
761
762 if (archiveDay!=null) {
763 logger.trace("archiveDay: {}", archiveDay);
764 try {
765 Date d = archiveDayFormatter.parse(archiveDay);
766 DateTime dt = new DateTime(d);
767 logger.trace("parsed to: {}", dt.toString());
768 buf.append("day ").append(UtcDate.getIYD(dt)).append(';');
769 } catch (Exception e) {
770 logger.error("archiveDay parse error", e);
771 }
772 }
773 else {
774 logger.trace("archiveDay is null!");
775 }
776
777 buf.append("time ");
778 for (int i = 0; i < times.size(); i++) {
779 DateTime dt = (DateTime) times.get(i);
780 buf.append(UtcDate.getHMS(dt));
781 if (i != times.size() - 1) {
782 buf.append(',');
783 }
784 }
785 } else {
786 buf.append(AddeUtil.RELATIVE_TIME);
787 }
788 return buf.toString();
789 }
790
791 /**
792 * Get the data type for this chooser
793 *
794 * @return the type
795 */
796 @Override public String getDataType() {
797 return "POINT";
798 }
799
800 /**
801 * Get the increment between times for relative time requests
802 *
803 * @return time increment (hours)
804 */
805 @Override public float getRelativeTimeIncrement() {
806 return relativeTimeIncrement;
807 }
808
809 /**
810 * Set the increment between times for relative time requests
811 *
812 * @param increment time increment (hours)
813 */
814 public void setRelativeTimeIncrement(float increment) {
815 relativeTimeIncrement = increment;
816 if (relTimeIncBox != null) {
817 relTimeIncBox.setSelectedItem(relativeTimeIncrement);
818 }
819 }
820
821 /**
822 * Update labels, enable widgets, etc.
823 */
824 @Override protected void updateStatus() {
825 super.updateStatus();
826 if (readTimesTask != null) {
827 if (taskOk(readTimesTask)) {
828 setStatus("Reading available times from server");
829 }
830 } else if (getDoAbsoluteTimes() && !haveTimeSelected()) {
831 setStatus(MSG_TIMES);
832 }
833 enableWidgets();
834 }
835
836
837 /**
838 * Get the descriptor widget label.
839 *
840 * @return label for the descriptor widget
841 */
842 @Override public String getDescriptorLabel() {
843 return "Point Type";
844 }
845
846 /**
847 * get the adde server grup type to use
848 *
849 * @return group type
850 */
851 @Override protected String getGroupType() {
852 return AddeServer.TYPE_POINT;
853 }
854
855 /**
856 * Make the UI for this selector.
857 *
858 * @return The gui
859 */
860 @Override public JComponent doMakeContents() {
861 JPanel myPanel = new JPanel();
862
863 McVGuiUtils.setComponentWidth(descriptorComboBox, Width.DOUBLEDOUBLE);
864
865 JLabel stationLabel = McVGuiUtils.makeLabelRight("Station:");
866 addServerComp(stationLabel);
867
868 JLabel timesLabel = McVGuiUtils.makeLabelRight("Times:");
869 addDescComp(timesLabel);
870
871 JPanel timesPanel = makeTimesPanel();
872 timesPanel.setBorder(javax.swing.BorderFactory.createEtchedBorder());
873 addDescComp(timesPanel);
874
875 GroupLayout layout = new GroupLayout(myPanel);
876 myPanel.setLayout(layout);
877 layout.setHorizontalGroup(
878 layout.createParallelGroup(LEADING)
879 .addGroup(layout.createSequentialGroup()
880 .addGroup(layout.createParallelGroup(LEADING)
881 .addGroup(layout.createSequentialGroup()
882 .addComponent(descriptorLabel)
883 .addGap(GAP_RELATED)
884 .addComponent(descriptorComboBox))
885 .addGroup(layout.createSequentialGroup()
886 .addComponent(timesLabel)
887 .addGap(GAP_RELATED)
888 .addComponent(timesPanel, PREFERRED_SIZE, DEFAULT_SIZE, Short.MAX_VALUE))))
889 );
890 layout.setVerticalGroup(
891 layout.createParallelGroup(LEADING)
892 .addGroup(layout.createSequentialGroup()
893 .addGroup(layout.createParallelGroup(BASELINE)
894 .addComponent(descriptorLabel)
895 .addComponent(descriptorComboBox))
896 .addPreferredGap(RELATED)
897 .addGroup(layout.createParallelGroup(LEADING)
898 .addComponent(timesLabel)
899 .addComponent(timesPanel, PREFERRED_SIZE, DEFAULT_SIZE, Short.MAX_VALUE)))
900 );
901
902 setInnerPanel(myPanel);
903 return super.doMakeContents();
904 }
905
906 public JComponent doMakeContents(boolean doesOverride) {
907 if (doesOverride) {
908 return super.doMakeContents();
909 } else {
910 return doMakeContents();
911 }
912 }
913 }