001/*
002 * $Id: AddeChooser.java,v 1.85 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
033import static edu.wisc.ssec.mcidasv.servermanager.AddeEntry.DEFAULT_ACCOUNT;
034import static edu.wisc.ssec.mcidasv.util.CollectionHelpers.arrList;
035import static edu.wisc.ssec.mcidasv.McIDASV.isLoopback;
036
037import static javax.swing.GroupLayout.DEFAULT_SIZE;
038import static javax.swing.GroupLayout.Alignment.BASELINE;
039import static javax.swing.GroupLayout.Alignment.LEADING;
040import static javax.swing.GroupLayout.Alignment.TRAILING;
041import static javax.swing.LayoutStyle.ComponentPlacement.RELATED;
042import static javax.swing.LayoutStyle.ComponentPlacement.UNRELATED;
043
044import java.awt.Component;
045import java.awt.event.ActionEvent;
046import java.awt.event.ActionListener;
047import java.awt.event.ItemEvent;
048import java.awt.event.ItemListener;
049import java.awt.event.KeyEvent;
050import java.awt.event.KeyListener;
051import java.awt.event.MouseAdapter;
052import java.awt.event.MouseEvent;
053import java.io.EOFException;
054import java.io.InputStream;
055import java.net.ConnectException;
056import java.net.URL;
057import java.net.URLConnection;
058import java.util.ArrayList;
059import java.util.Arrays;
060import java.util.Collections;
061import java.util.Comparator;
062import java.util.Enumeration;
063import java.util.HashMap;
064import java.util.Hashtable;
065import java.util.LinkedHashMap;
066import java.util.List;
067import java.util.Map;
068import java.util.Vector;
069
070import javax.swing.GroupLayout;
071import javax.swing.JButton;
072import javax.swing.JCheckBox;
073import javax.swing.JComboBox;
074import javax.swing.JComponent;
075import javax.swing.JLabel;
076import javax.swing.JMenu;
077import javax.swing.JMenuItem;
078import javax.swing.JPanel;
079import javax.swing.JPopupMenu;
080import javax.swing.JTabbedPane;
081import javax.swing.JTextField;
082import javax.swing.SwingUtilities;
083
084import org.bushe.swing.event.annotation.AnnotationProcessor;
085import org.bushe.swing.event.annotation.EventSubscriber;
086import org.slf4j.Logger;
087import org.slf4j.LoggerFactory;
088import org.w3c.dom.Element;
089
090import edu.wisc.ssec.mcidas.adde.AddeURLException;
091import edu.wisc.ssec.mcidas.adde.DataSetInfo;
092
093import visad.DateTime;
094
095import ucar.unidata.idv.chooser.IdvChooser;
096import ucar.unidata.idv.chooser.IdvChooserManager;
097import ucar.unidata.idv.chooser.adde.AddeServer;
098import ucar.unidata.idv.chooser.adde.AddeServer.Group;
099import ucar.unidata.util.DatedThing;
100import ucar.unidata.util.GuiUtils;
101import ucar.unidata.util.LogUtil;
102import ucar.unidata.util.Misc;
103import ucar.unidata.util.PreferenceList;
104import ucar.unidata.util.StringUtil;
105import ucar.unidata.xml.XmlObjectStore;
106
107import edu.wisc.ssec.mcidasv.Constants;
108import edu.wisc.ssec.mcidasv.McIDASV;
109import edu.wisc.ssec.mcidasv.ParameterSet;
110import edu.wisc.ssec.mcidasv.PersistenceManager;
111import edu.wisc.ssec.mcidasv.servermanager.AddeAccount;
112import edu.wisc.ssec.mcidasv.servermanager.AddeEntry;
113import edu.wisc.ssec.mcidasv.servermanager.AddeEntry.EditorAction;
114import edu.wisc.ssec.mcidasv.servermanager.AddeEntry.EntryType;
115import edu.wisc.ssec.mcidasv.servermanager.EntryStore;
116import edu.wisc.ssec.mcidasv.servermanager.EntryTransforms;
117import edu.wisc.ssec.mcidasv.servermanager.LocalEntryEditor;
118import edu.wisc.ssec.mcidasv.servermanager.RemoteAddeEntry;
119import edu.wisc.ssec.mcidasv.servermanager.RemoteEntryEditor;
120import edu.wisc.ssec.mcidasv.servermanager.TabbedAddeManager;
121import edu.wisc.ssec.mcidasv.ui.ParameterTree;
122import edu.wisc.ssec.mcidasv.ui.UIManager;
123import edu.wisc.ssec.mcidasv.util.CollectionHelpers;
124import edu.wisc.ssec.mcidasv.util.McVGuiUtils;
125import edu.wisc.ssec.mcidasv.util.McVGuiUtils.Position;
126import edu.wisc.ssec.mcidasv.util.McVGuiUtils.TextColor;
127import edu.wisc.ssec.mcidasv.util.McVGuiUtils.Width;
128
129/**
130 *
131 * @version $Revision: 1.85 $
132 */
133public class AddeChooser extends ucar.unidata.idv.chooser.adde.AddeChooser implements Constants {
134
135    private static final Logger logger = LoggerFactory.getLogger(AddeChooser.class);
136
137    private JComboBox serverSelector;
138
139    /** List of descriptors */
140    private PreferenceList descList;
141
142    /** Descriptor/name hashtable */
143    protected Hashtable descriptorTable;
144
145    /** Property for the descriptor table */
146    public static final String DESCRIPTOR_TABLE = "DESCRIPTOR_TABLE";
147
148    /** Connect button--we need to be able to disable this */
149    JButton connectButton = McVGuiUtils.makeImageTextButton(ICON_CONNECT_SMALL, "Connect");
150
151    /** Parameter button--we need to be able to disable this */
152    JButton parameterButton =
153        McVGuiUtils.makeImageButton("/edu/wisc/ssec/mcidasv/resources/icons/toolbar/document-open22.png",
154            this, "doParameters", null, "Load parameter set");
155
156    /** Manage button */
157    JButton manageButton =
158        McVGuiUtils.makeImageButton("/edu/wisc/ssec/mcidasv/resources/icons/toolbar/preferences-system22.png",
159            this, "doManager", null, "Manage servers");
160
161    /** Public button--we need to draw a menu from this */
162    JButton publicButton =
163        McVGuiUtils.makeImageButton("/edu/wisc/ssec/mcidasv/resources/icons/toolbar/show-layer-controls22.png",
164            this, "showGroups", null, "List public datasets");
165
166    /** descriptor label */
167    protected JLabel descriptorLabel = new JLabel(getDescriptorLabel()+":");
168
169    /** A widget for the list of dataset descriptors */
170    protected JComboBox descriptorComboBox = new JComboBox();
171
172    /** The descriptor names */
173    protected String[] descriptorNames;
174
175    /** Flag to keep from infinite looping */
176    protected boolean ignoreDescriptorChange = false;
177
178    /**
179     * List of JComponent-s that depend on a descriptor being selected
180     * to be enabled
181     */
182    protected ArrayList compsThatNeedDescriptor = new ArrayList();
183
184    /** Selection label text */
185    protected String LABEL_SELECT = " -- Select -- ";
186
187    /** Separator string */
188    protected static String separator = "----------------";
189
190    /** Name separator string */
191    protected static String nameSeparator = " - ";
192
193    /** Reference back to the server manager */
194    protected EntryStore serverManager;
195
196    public boolean allServersFlag;
197
198    /** Command for opening up the server manager */
199    protected static final String CMD_MANAGER = "cmd.manager";
200
201    private String lastBadServer = "";
202    private String lastBadGroup = "";
203
204    private String lastServerName = "";
205    private String lastServerGroup = "";
206    private String lastServerUser = "";
207    private String lastServerProj = "";
208    private AddeServer lastServer = new AddeServer("");
209
210    private List<AddeServer> addeServers;
211
212    /** Used for parameter set restore */
213    private static final String TAG_FOLDER = "folder";
214    private static final String TAG_DEFAULT = "default";
215    private static final String ATTR_NAME = "name";
216    private static final String ATTR_SERVER = "server";
217    private static final String ATTR_GROUP = "GROUP";
218    private static final String ATTR_DESCRIPTOR = "DESCRIPTOR";
219    private static final String ATTR_POS = "POS";
220    private static final String ATTR_DAY = "DAY";
221    private static final String ATTR_TIME = "TIME";
222    private List restoreTimes = new ArrayList();
223    public Element restoreElement;
224    private boolean shouldAddSource = false;
225    final JCheckBox cb = new JCheckBox("Add source",shouldAddSource);
226
227    /** Maps favorite type to the BundleTree that shows the Manage window for the type */
228    private Hashtable parameterTrees = new Hashtable();
229
230    /**
231     * Create an AddeChooser associated with an IdvChooser
232     *
233     * @param mgr The chooser manager
234     * @param root The chooser.xml node
235     */
236    public AddeChooser(IdvChooserManager mgr, Element root) {
237        super(mgr, root);
238        AnnotationProcessor.process(this);
239        simpleMode = !getProperty(IdvChooser.ATTR_SHOWDETAILS, true);
240
241        loadButton = McVGuiUtils.makeImageTextButton(ICON_ACCEPT_SMALL, getLoadCommandName());
242        loadButton.setActionCommand(getLoadCommandName());
243        loadButton.addActionListener(this);
244
245        cancelButton = McVGuiUtils.makeImageButton(ICON_CANCEL, "Cancel");
246        cancelButton.setActionCommand(GuiUtils.CMD_CANCEL);
247        cancelButton.addActionListener(this);
248        cancelButton.setEnabled(false);
249
250        serverSelector = getServerSelector();
251
252        serverSelector.setToolTipText("Right click to manage servers");
253        serverSelector.getEditor().getEditorComponent().addMouseListener(
254            new MouseAdapter() {
255                public void mouseReleased(MouseEvent e) {
256                    if (!SwingUtilities.isRightMouseButton(e)) {
257                        return;
258                    }
259
260                    AddeServer server = getAddeServer();
261                    if (server == null) {
262                        return;
263                    }
264                    List<JMenuItem> items = new ArrayList<JMenuItem>();
265
266                    // Set the right-click behavior
267                    if (isLocalServer()) {
268                        items.add(GuiUtils.makeMenuItem("Manage local ADDE data",
269                            AddeChooser.this,
270                            "doManager", null));
271                    }
272                    else {
273                        items.add(GuiUtils.makeMenuItem("Manage ADDE servers",
274                            AddeChooser.this,
275                            "doManager", null));
276                    }
277                    JPopupMenu popup = GuiUtils.makePopupMenu(items);
278                    popup.show(serverSelector, e.getX(), e.getY());
279                }
280            });
281        serverSelector.setMaximumRowCount(16);
282
283        groupSelector.setToolTipText("Right click to manage servers");
284        groupSelector.getEditor().getEditorComponent().addMouseListener(
285            new MouseAdapter() {
286                public void mouseReleased(MouseEvent e) {
287                    if (!SwingUtilities.isRightMouseButton(e)) {
288                        return;
289                    }
290
291                    AddeServer server = getAddeServer();
292                    if (server == null) {
293                        return;
294                    }
295                    List<JMenuItem> items = new ArrayList<JMenuItem>();
296
297                    // Set the right-click behavior
298                    if (isLocalServer()) {
299                        items.add(GuiUtils.makeMenuItem("Manage local ADDE data",
300                            AddeChooser.this, "doManager", null));
301                    }
302                    else {
303                        items.add(GuiUtils.makeMenuItem("Manage ADDE servers",
304                            AddeChooser.this, "doManager", null));
305                    }
306                    JPopupMenu popup = GuiUtils.makePopupMenu(items);
307                    popup.show(groupSelector, e.getX(), e.getY());
308                }
309            });
310        groupSelector.setMaximumRowCount(16);
311
312        //        serverManager = ((McIDASV)getIdv()).getServerManager();
313        //        serverManager.addManagedChooser(this);
314        addServerComp(descriptorLabel);
315        //        addServerComp(descriptorComboBox);
316
317        descriptorComboBox.addItemListener(new ItemListener() {
318            public void itemStateChanged(ItemEvent e) {
319                if ( !ignoreDescriptorChange
320                    && (e.getStateChange() == e.SELECTED)) {
321                    descriptorChanged();
322                }
323            }
324        });
325
326        // Update the server list and load the saved state
327        updateServerList();
328        loadServerState();
329
330        // Default to no parameter button unless the overriding class wants one
331        hideParameterButton();
332    }
333
334    /**
335     * Force a reload of the available servers and groups.
336     */
337    public void updateServerList() {
338        updateServers();
339        updateGroups();
340    }
341
342    /**
343     * Returns a {@link java.util.Map Map} containing {@code user} and {@code proj}
344     * keys for the given {@code server/group} combination.
345     * 
346     * <p>The values are either the specific ADDE account details for 
347     * {@code server/group} or {@link edu.wisc.ssec.mcidasv.servermanager.AddeEntry#DEFAULT_ACCOUNT DEFAULT_ACCOUNT}
348     * values.
349     * 
350     * @param server Server name. Should not be {@code null}.
351     * @param group Group name on {@code name}. Should not be {@code null}.
352     * 
353     * @return {@code Map} containing the accounting details for {@code server/group}.
354     */
355    protected Map<String, String> getAccounting(final String server, final String group) {
356        Map<String, String> acctInfo = new HashMap<String, String>();
357        EntryStore entryStore = ((McIDASV)getIdv()).getServerManager();
358        String strType = this.getDataType();
359        EntryType type = EntryTransforms.strToEntryType(strType);
360        AddeAccount acct = entryStore.getAccountingFor(server, group, type);
361        acctInfo.put("user", acct.getUsername());
362        acctInfo.put("proj", acct.getProject());
363        return acctInfo;
364    }
365
366    /**
367     * Returns a {@link java.util.Map Map} containing {@code user} and {@code proj}
368     * keys for the given {@code server/group} combination.
369     * 
370     * <p>The values are either the specific ADDE account details for 
371     * {@code server/group} or {@link edu.wisc.ssec.mcidasv.servermanager.AddeEntry#DEFAULT_ACCOUNT DEFAULT_ACCOUNT}
372     * values.
373     * 
374     * @param server Server name. Should not be {@code null}.
375     * @param group Group name on {@code name}. Should not be {@code null}.
376     * 
377     * @return {@code Map} containing the accounting details for {@code server/group}.
378     */
379    protected Map<String, String> getAccounting(final AddeServer server, final String group) {
380        return getAccounting(server.getName(), group);
381    }
382
383    private List<AddeServer> getManagedServers(final String type) {
384        EntryStore entryStore = ((McIDASV)getIdv()).getServerManager();
385        return arrList(entryStore.getIdvStyleEntries(type));
386    }
387
388    public void updateServers() {
389        Object selected = serverSelector.getSelectedItem();
390
391        String type = getGroupType();
392        List<AddeServer> managedServers = getManagedServers(type);
393        List<AddeServer> localList = arrList();
394        List<AddeServer> remoteList = arrList();
395        addeServers = CollectionHelpers.arrList();
396        for (AddeServer server : managedServers) {
397            if (server.getIsLocal())
398                localList.add(server);
399            else
400                remoteList.add(server);
401        }
402
403        logger.debug("{}: updateServers: local size={} contents={}", new Object[] { getDataType(), localList.size(), localList });
404        logger.debug("{}: updateServers: remote size={} contents={}", new Object[] { getDataType(), remoteList.size(), remoteList });
405
406        // server list doesn't need a separator if there's only remote servers
407        if (!localList.isEmpty()) {
408            addeServers.addAll(localList);
409            addeServers.add(new AddeServer(separator));
410        }
411        Comparator<AddeServer> byServer = new ServerComparator();
412        Collections.sort(remoteList, byServer);
413        addeServers.addAll(remoteList);
414
415        // always making this call helps to ensure the chooser stays up to date
416        // with the server manager.
417        GuiUtils.setListData(serverSelector, addeServers);
418        if (!addeServers.isEmpty()) {
419            if (selected == null || !containsServerName(addeServers, selected)) {
420                selected = serverSelector.getItemAt(0);
421                logger.debug("updateServers: selecting item at idx=0, item={} chooser={}", selected, this.getDataType());
422            }
423            
424            int index = getSelectorIndex(selected, serverSelector);
425            serverSelector.setSelectedIndex(index);
426        }
427    }
428
429    /**
430     * Searches the given {@link java.util.List List} of {@link ucar.unidata.idv.chooser.AddeServer AddeServers}
431     * for {@code server}.
432     * 
433     * @param servers Servers to search. {@code null} is permitted.
434     * @param server Server to search for within {@code servers}. {@code null} is permitted.
435     * 
436     * @return {@code true} if {@code servers} contains {@code server} or {@code false} otherwise.
437     */
438    protected static boolean containsServerName(final List<AddeServer> servers, final Object server) {
439        if (servers == null || server == null) {
440            return false;
441        }
442        String serverName = (server instanceof AddeServer) ? ((AddeServer)server).getName() : server.toString();
443        for (AddeServer tmp : servers) {
444            if (tmp.getName().equals(serverName)) {
445                return true;
446            }
447        }
448        return false;
449    }
450
451    /**
452     * Searches the given {@link java.util.List List} of {@link ucar.unidata.idv.chooser.AddeServer.Group Groups}
453     * for {@code group}.
454     * 
455     * @param groups Groups to search. {@code null} is permitted.
456     * @param group Group to search for within {@code group}. {@code null} is permitted.
457     * 
458     * @return {@code true} if {@code groups} contains {@code group} or {@code false} otherwise.
459     */
460    protected static boolean containsGroupName(final List<Group> groups, final Object group) {
461        if (groups == null || group == null) {
462            return false;
463        }
464        String groupName = (group instanceof Group) ? ((Group)group).getName() : group.toString();
465        for (Group tmp : groups) {
466            if (tmp.getName().equals(groupName)) {
467                return true;
468            }
469        }
470        return false;
471    }
472
473    /**
474     * Sort the groups alphabetically
475     */
476    public void updateGroups() {
477        if (addingServer || groupSelector == null || getAddeServer() == null)
478            return;
479
480        Object selected = groupSelector.getSelectedItem();
481
482        EntryStore servManager = ((McIDASV)getIdv()).getServerManager();
483
484        List<Group> groups = CollectionHelpers.arrList();
485        if (isLocalServer()) {
486            groups.addAll(servManager.getIdvStyleLocalGroups());
487        } else {
488            String sel = null;
489            Object obj = serverSelector.getSelectedItem();
490            if (obj instanceof String) {
491                sel = (String)obj;
492                logger.debug("updateGroups: string={} chooser={}", sel, this.getDataType());
493            } else if (obj instanceof AddeServer) {
494                sel = ((AddeServer)obj).getName();
495                logger.debug("updateGroups: server selection={} chooser={}", sel, this.getDataType());
496            } else {
497                sel = obj.toString();
498                logger.debug("updateGroups: unknown type={}; toString={}", sel.getClass().getName(), sel);
499            }
500
501            EntryType selType = EntryTransforms.strToEntryType(getGroupType());
502            groups.addAll(servManager.getIdvStyleRemoteGroups(sel, selType));
503        }
504        logger.info("updateGroups: selected={} (type={}) chooser={} contents={}", new Object[] { serverSelector.getSelectedItem(), serverSelector.getSelectedItem().getClass().getName(), this.getDataType(), groups});
505        Comparator<Group> byGroup = new GroupComparator();
506        Collections.sort(groups, byGroup);
507        GuiUtils.setListData(groupSelector, groups);
508        if (!groups.isEmpty()) {
509            if (selected == null || !containsGroupName(groups, selected)) {
510                selected = groupSelector.getItemAt(0);
511            }
512            groupSelector.setSelectedItem(selected);
513        }
514    }
515
516    /**
517     * Load any saved server state
518     */
519    //TODO: Make loadServerState protected in IDV, remove from here
520    private void loadServerState() {
521        if (addeServers == null) {
522            logger.debug("loadServerState: addeServers == null chooser={}", this.getDataType());
523            return;
524        }
525        String id = getId();
526        String[] serverState =
527            (String[]) getIdv().getStore().get(Constants.PREF_SERVERSTATE + '.' + id);
528        if (serverState == null) {
529//            serverState = Constants.DEFAULT_SERVERSTATE;
530            logger.debug("loadServerState: serverState == null chooser={}",this.getDataType());
531            return;
532        }
533        AddeServer server = AddeServer.findServer(addeServers, serverState[0]);
534        if (server == null) {
535            logger.debug("loadServerState: server == null chooser={}",this.getDataType());
536            return;
537        }
538        logger.debug("loadServerState: selecting server={} chooser={}", server, this.getDataType());
539        serverSelector.setSelectedItem(server);
540        setGroups();
541        updateGroups();
542        if (serverState[1] != null) {
543            Group group = new Group(getDataType(), serverState[1], serverState[1]);
544            int index = getSelectorIndex(group, groupSelector);
545            if (index >= 0) {
546                logger.debug("loadServerState: selecting index={} group={} chooser={}", new Object[] { index, group, this.getDataType() });
547                groupSelector.setSelectedIndex(index);
548            } else {
549                logger.debug("loadServerState: group == null chooser={}", this.getDataType());
550            }
551        } else {
552            logger.debug("loadServerState: serverState[1] == null chooser={}", this.getDataType());
553        }
554    }
555
556    /**
557     * Decide if the server you're asking about is actually a separator
558     */
559    protected static boolean isSeparator(AddeServer checkServer) {
560        if (checkServer != null) {
561            if (checkServer.getName().equals(separator)) {
562                return true;
563            }
564        }
565        return false;
566    }
567
568    /**
569     * Decide if the server you're asking about is local
570     */
571    protected boolean isLocalServer() {
572        return isLocalServer(getAddeServer());
573    }
574
575    protected static boolean isLocalServer(AddeServer checkServer) {
576        if (checkServer != null) {
577            return checkServer.getIsLocal();
578        }
579        return false;
580    }
581
582    private void setBadServer(String name, String group) {
583        if (name == null) {
584            name = "";
585        }
586        if (group == null) {
587            group = "";
588        }
589
590        lastBadServer = name;
591        lastBadGroup = group;
592    }
593
594    private boolean isBadServer(String name, String group) {
595        assert lastBadServer != null;
596        assert lastBadGroup != null;
597        return lastBadServer.equals(name) && lastBadGroup.equals(group);
598    }
599
600    private void setLastServer(String name, String group, AddeServer server) {
601        logger.trace("name='{}' group='{}' server='{}' old: name='{}' group='{}' server='{}'", new Object[] { name, group, server, lastServerName, lastServerGroup, lastServer });
602        if (name == null) {
603            name = "";
604        }
605        if (group == null) {
606            group = "";
607        }
608        if (server == null) {
609            server = new AddeServer(name);
610            Group addeGroup = new Group(getDataType(), group, group);
611            server.addGroup(addeGroup);
612        }
613        lastServerName = name;
614        lastServerGroup = group;
615        lastServer = server;
616    }
617
618    private boolean isLastServer(String name, String group) {
619        assert lastServer != null;
620        assert lastServerName != null;
621        assert lastServerGroup != null;
622        return lastServerName.equals(name) && lastServerGroup.equals(group);
623    }
624
625    @EventSubscriber(eventClass=EntryStore.Event.class)
626    public void onServerManagerDataEvent(EntryStore.Event evt) {
627        EntryStore servManager = ((McIDASV)getIdv()).getServerManager();
628        logger.debug("onServerManagerDataEvent: evt={} server={}", evt, servManager.getLastAdded());
629        this.updateServerList();
630    }
631
632    @EventSubscriber(eventClass=TabbedAddeManager.Event.class)
633    public void onServerManagerWindowEvent(TabbedAddeManager.Event evt) {
634        logger.debug("onServerManagerWindowEvent: caught event bus obj");
635    }
636
637    private boolean addingServer = false;
638
639    /**
640     * Search a given {@link JComboBox} for the index of a given object. Mostly
641     * useful for searching {@link #serverSelector} or {@link #groupSelector}.
642     * 
643     * @param needle An object. {@code null} values are permitted.
644     * @param haystack {@code JComboBox} to search. {@code null} values are 
645     * permitted, but return {@code -1}.
646     * 
647     * @return Either the index of {@code needle} within {@code haystack}, or
648     * {@code -1} if {@code needle} could not be found (or {@code haystack} is 
649     * {@code null}).
650     */
651    protected static int getSelectorIndex(final Object needle, 
652        final JComboBox haystack) 
653    {
654        if (haystack == null) {
655            return -1;
656        }
657
658        String name = null;
659        if (needle instanceof AddeServer) {
660            name = ((AddeServer)needle).getName();
661        } else if (needle instanceof Group) {
662            name = ((Group)needle).getName();
663        } else if (needle instanceof AddeEntry) {
664            name = ((AddeEntry)needle).getAddress();
665        } else {
666            name = needle.toString();
667        }
668
669        if (isLoopback(name)) {
670            return 0;
671        }
672
673        for (int i = 0; i < haystack.getItemCount(); i++) {
674            Object item = haystack.getItemAt(i);
675            String tmpName;
676            if (item instanceof AddeServer) {
677                tmpName = ((AddeServer)item).getName();
678            } else {
679                tmpName = item.toString();
680            }
681
682            if (name.equals(tmpName)) {
683                return i;
684            }
685        }
686        return -1;
687    }
688
689    /**
690     * Get the selected AddeServer
691     *
692     * @return the server or null
693     */
694    protected AddeServer getAddeServer() {
695        if (lastServerName != null && lastServerName.equals("unset")) {
696            return null;
697        }
698
699        Object selected = serverSelector.getSelectedItem();
700        if ((selected != null) && (selected instanceof AddeServer)) {
701            AddeServer server = (AddeServer)selected;
702            String group = getGroup(true);
703            Map<String, String> accounting = getAccounting(server, group);
704            logger.trace("accounting: new: u='{}' p='{}' old: u='{}' p='{}'", new Object[] { accounting.get("user"), accounting.get("proj"), lastServerUser, lastServerProj });
705            lastServerUser = accounting.get("user");
706            lastServerProj = accounting.get("proj");
707            setLastServer(server.getName(), group, server);
708            return (AddeServer)selected;
709        } else if ((selected != null) && (selected instanceof String)) {
710
711            EntryStore servManager = ((McIDASV)getIdv()).getServerManager();
712            String server = (String)selected;
713            String group = getGroup(true);
714
715            if (isBadServer(server, group)) {
716                logger.trace("getAddeServer: returning null; known bad server; server={} group={}", server, group);
717                return null;
718            }
719
720            if (isLastServer(server, group)) {
721                logger.trace("getAddeServer: returning last server name; server={} group={}", server, group);
722                return lastServer;
723            }
724
725            EditorAction editorAction = EditorAction.INVALID;
726            if (!isLoopback(server)) {
727                RemoteEntryEditor editor = new RemoteEntryEditor(servManager, server, "");
728                editor.setVisible(true);
729                editorAction = editor.getEditorAction();
730            } else {
731                LocalEntryEditor editor = new LocalEntryEditor(servManager, group);
732                editor.setVisible(true);
733                editorAction = editor.getEditorAction();
734            }
735
736            int servIndex = 0;
737            int groupIndex = 0;
738
739            if (editorAction != EditorAction.CANCELLED && editorAction != EditorAction.INVALID) {
740
741                List<AddeServer> added = arrList(EntryTransforms.convertMcvServers(servManager.getLastAddedByType(EntryTransforms.strToEntryType(getDataType()))));
742                AddeServer first = null;
743                if (!added.isEmpty()) {
744                    first = added.get(0);
745                    servIndex = getSelectorIndex(first, serverSelector);
746                    setLastServer(server, group, first);
747                } 
748
749                serverSelector.setSelectedIndex(servIndex);
750                groupSelector.setSelectedIndex(groupIndex);
751                logger.trace("getAddeServer: serverIdx={} groupIdx={}", servIndex, groupIndex);
752
753                return first;
754            } else {
755                logger.trace("getAddeServer: returning null due to cancel request");
756                setBadServer(server, group);
757                return null;
758            }
759
760            
761            
762        } else if (selected == null) {
763            logger.trace("getAddeServer: null object in selector; returning null");
764        } else {
765            logger.debug("getAddeServer: unknown obj type={}; toString={}", selected.getClass().getName(), selected.toString());
766        }
767        return null;
768    }
769
770    /**
771     * A utility to add a component to the list of components that
772     * need the descriptor
773     *
774     * @param comp The component
775     * @return The component
776     */
777    protected JComponent addDescComp(JComponent comp) {
778        compsThatNeedDescriptor.add(comp);
779        return comp;
780    }
781    
782    /**
783     * Set LABEL_SELECT from elsewhere
784     */
785    protected void setSelectString(String string) {
786        LABEL_SELECT = string;
787    }
788    
789    /**
790     * Reset the descriptor stuff
791     */
792    protected void resetDescriptorBox() {
793        ignoreDescriptorChange = true;
794        descriptorComboBox.setSelectedItem(LABEL_SELECT);
795        ignoreDescriptorChange = false;
796    }
797    
798    /**
799     * Handle when the user presses the connect button
800     *
801     * @throws Exception On badness
802     */
803    public void handleConnect() throws Exception {
804        AddeServer server = getAddeServer();
805        if (server == null) {
806            return;
807        }
808        setState(STATE_CONNECTING);
809        connectToServer();
810        handleUpdate();
811    }
812    
813    @Override protected void handleConnectionError(Exception e) {
814        if (e != null && e.getMessage() != null) {
815            String msg = e.getMessage();
816            int msgPos = msg.indexOf("AddeURLException:");
817            if (msgPos >= 0 && msg.length() > 18) {
818                msg = msg.substring(msgPos + 18);
819                setState(STATE_UNCONNECTED);
820                setHaveData(false);
821                resetDescriptorBox();
822                GuiUtils.showDialog("ADDE Error", new JLabel(msg));
823                return;
824            }
825            if (msg.indexOf("Connecting to server:localhost:") >= 0) {
826                setState(STATE_UNCONNECTED);
827                setHaveData(false);
828                resetDescriptorBox();
829                GuiUtils.showDialog("ADDE Error", new JLabel("Local server is not responding"));
830                return;
831            }
832        }
833        super.handleConnectionError(e);
834    }
835
836    /**
837     * Handle unknown data set error
838     */
839    @Override protected void handleUnknownDataSetError() {
840        String server = getServer();
841        String group = getGroup();
842        Map<String, String> acct = getAccounting(server, group);
843        String user = acct.get("user");
844        String proj = acct.get("proj");
845
846        StringBuilder msg = new StringBuilder("Could not connect to dataset \"");
847        msg.append(getGroup()).append("\" on server \"").append(getServer()).append("\".");
848        if (DEFAULT_ACCOUNT.getUsername().equals(user) && DEFAULT_ACCOUNT.getProject().equals(proj)) {
849            msg.append("\n\nDataset may require ADDE accounting information.");
850        } else {
851            msg.append("\n\nAccounting information:\nusername: \"")
852            .append(user).append("\"\nproject: \"").append(proj).append('"');
853        }
854        LogUtil.userErrorMessage(msg.toString());
855        setState(STATE_UNCONNECTED);
856    }
857
858    /**
859     * Handle the event
860     *
861     * @param ae The event
862     */
863    public void actionPerformed(ActionEvent ae) {
864        String cmd = ae.getActionCommand();
865        if (cmd.equals(CMD_MANAGER)) {
866            doManager();
867        }
868        else {
869            super.actionPerformed(ae);
870        }
871    }
872
873    /**
874     * Go directly to the Server Manager
875     */
876    public void doManager() {
877//      if (isLocalServer()) {
878//          ((McIDASV)getIdv()).showAddeManager();
879//          return;
880//      }
881        getIdv().getPreferenceManager().showTab(Constants.PREF_LIST_ADDE_SERVERS);
882    }
883    
884    /**
885     * Show the parameter restore tree
886     */
887    public void doParameters() {
888        JPopupMenu popup = new JPopupMenu();
889        JMenuItem mi = new JMenuItem("Manage...");
890        mi.addActionListener(new ActionListener() {
891            public void actionPerformed(ActionEvent ae) {
892                System.out.println(ae);
893                showParameterSetDialog(getParameterSetType());
894            }
895        });
896        popup.add(mi);
897        
898        // Add the checkbox to automatically create a data source
899        cb.addActionListener(new ActionListener() {
900            public void actionPerformed(ActionEvent ae) {
901                shouldAddSource = cb.isSelected();
902            }
903        });
904        popup.addSeparator();
905        popup.add(cb);
906
907        final PersistenceManager pm = (PersistenceManager)getIdv().getPersistenceManager();
908        List<ParameterSet> parameterSets = pm.getAllParameterSets(getParameterSetType());
909
910        for (int i=0; i<parameterSets.size(); i++) {
911            if (i==0) popup.addSeparator();
912            final ParameterSet ps = parameterSets.get(i);
913            
914            // Parameter set at root
915            if (ps.getCategories().size() == 0) {
916                mi = new JMenuItem(ps.getName());
917                mi.addActionListener(new ActionListener() {
918                    public void actionPerformed(ActionEvent ae) {
919                        restoreParameterSet(ps.getElement());
920                    }
921                });
922                popup.add(mi);
923            }
924            
925            // Recurse into folders
926            else {
927                // Find or make the menu for the given parameter set
928                JMenu m = getPopupSubMenuForParameterSet(popup, ps);
929                // Create parameter set entry
930                mi = new JMenuItem(ps.getName());
931                mi.addActionListener(new ActionListener() {
932                    public void actionPerformed(ActionEvent ae) {
933                        restoreParameterSet(ps.getElement());
934                    }
935                });
936                m.add(mi);
937            }
938            
939        }
940
941        popup.show(parameterButton, 0, (int) parameterButton.getBounds().getHeight());
942    }
943    
944    private JMenu getPopupSubMenuForParameterSet(JPopupMenu popup, final ParameterSet ps) {
945        List<String> menuNames = ps.getCategories();
946        if (menuNames.size() < 1) return null;
947
948        // Build the complete menu
949        String menuName = menuNames.get(0);
950        menuNames.remove(0);
951        JMenu theMenu = new JMenu();
952        
953        // Look for the menu in popup
954        boolean found = false;
955        for (int i=0; i<popup.getComponentCount(); i++) {
956            Component thisComponent = popup.getComponent(i);
957            if (thisComponent instanceof JMenu && ((JMenu)thisComponent).getText().equals(menuName)) {
958                theMenu = mergeMenuNames((JMenu)thisComponent, menuNames);
959                found = true;
960            }
961        }
962        
963        // Make a new menu, add the root, return the leaf
964        if (!found) {
965            JMenu theRoot = new JMenu(menuName);
966            theMenu = makeMenuRecursive(theRoot, menuNames);
967            popup.add(theRoot);
968        }
969        
970        return theMenu;
971    }
972    
973    /**
974     * Make a new recursive menu
975     * 
976     * @param rootMenu The root menu to add items to
977     * @param menuNames List of string names for submenus
978     * @return A new JMenu representing the leaf
979     */
980    private JMenu makeMenuRecursive(JMenu rootMenu, List<String> menuNames) {
981        if (menuNames.size() < 1) return rootMenu;
982        JMenu newMenu = new JMenu(menuNames.get(0));
983        rootMenu.add(newMenu);
984        menuNames.remove(0);
985        return makeMenuRecursive(newMenu, menuNames);
986    }
987    
988    /**
989     * Recurse into a menu, returning either a pointer to the designated names path
990     *  or a pointer to the leaf menu added by merging new names
991     * 
992     * @param thisMenu The root menu to merge
993     * @param menuNames List of string names to look for
994     * @return A new JMenu representing the leaf matched by menuNames
995     */
996    private JMenu mergeMenuNames(JMenu thisMenu, List<String> menuNames) {
997        if (menuNames.size() < 1) return thisMenu;
998        boolean found = false;
999        String menuName = menuNames.get(0);
1000        for (int i=0; i<thisMenu.getItemCount(); i++) {
1001            JMenuItem mi = thisMenu.getItem(i);
1002            if (!(mi instanceof JMenu)) continue;
1003            if (mi.getText().equals(menuName)) {
1004                menuNames.remove(0);
1005                thisMenu = mergeMenuNames((JMenu)mi, menuNames);
1006                found = true;
1007            }
1008        }
1009        if (!found) {
1010            thisMenu = makeMenuRecursive(thisMenu, menuNames);
1011        }
1012        return thisMenu;
1013    }
1014    
1015    /**
1016     * Return the parameter type associated with this chooser.  Override!
1017     */
1018    protected String getParameterSetType() {
1019        return "adde";
1020    }
1021    
1022    /**
1023     * Show the parameter set manager
1024     */
1025    private void showParameterSetDialog(final String parameterSetType) {
1026        ParameterTree tree = (ParameterTree) parameterTrees.get(parameterSetType);
1027        if (tree == null) {
1028            tree = new ParameterTree((UIManager)getIdv().getIdvUIManager() , parameterSetType);
1029            parameterTrees.put(parameterSetType, tree);
1030        }
1031        else {
1032            //DAVEP
1033            System.out.println("Should refresh the parameter tree here");
1034        }
1035        tree.setVisible(true);
1036    }
1037
1038    /**
1039     * Clear the selected parameter set
1040     */
1041    protected void clearParameterSet() {
1042        restoreElement = null;
1043        restoreTimes = new ArrayList(); 
1044        shouldAddSource = false;
1045    }
1046
1047    /**
1048     * Restore the selected parameter set using element attributes
1049     * 
1050     * @param restoreElement
1051     * @return
1052     */
1053    protected boolean restoreParameterSet(Element restoreElement) {
1054        if (restoreElement == null) return false;
1055        if (!restoreElement.getTagName().equals("default")) return false;
1056
1057        this.restoreElement = restoreElement;
1058
1059        boolean oldISCE = ignoreStateChangedEvents;
1060        ignoreStateChangedEvents = true;
1061
1062        // Restore server
1063        String server = restoreElement.getAttribute(ATTR_SERVER);
1064        if (server != null) serverSelector.setSelectedItem(new AddeServer(server));
1065
1066        // Restore group
1067        String group = restoreElement.getAttribute(ATTR_GROUP);
1068        if (group != null) groupSelector.setSelectedItem(group);
1069
1070        // Act as though the user hit "connect"
1071        readFromServer();
1072
1073        // Restore descriptor
1074        String descriptor = restoreElement.getAttribute(ATTR_DESCRIPTOR);
1075        if (descriptor != null) {
1076            Enumeration enumeration = descriptorTable.keys();
1077            for (int i = 0; enumeration.hasMoreElements(); i++) {
1078                String key = enumeration.nextElement().toString();
1079                Object val = descriptorTable.get(key);
1080                if (descriptor.equals(val)) {
1081                    descriptorComboBox.setSelectedItem(val + nameSeparator + key);
1082                    descriptorChanged();
1083                    break;
1084                }
1085            } 
1086        }
1087
1088        // Restore date/time
1089        if (restoreElement.hasAttribute(ATTR_POS)) {
1090            setDoAbsoluteTimes(false);
1091            Integer pos = new Integer(restoreElement.getAttribute(ATTR_POS));
1092            if (pos.intValue() >= 0) {
1093                getRelativeTimesList().setSelectedIndex(pos);
1094            }
1095            restoreTimes = new ArrayList(); 
1096        }
1097        else if ((restoreElement.hasAttribute(ATTR_DAY)) && (restoreElement.hasAttribute(ATTR_TIME))) {
1098            setDoAbsoluteTimes(true);
1099            String dateStr = restoreElement.getAttribute(ATTR_DAY);
1100            String timeStr = restoreElement.getAttribute(ATTR_TIME);
1101            List dateS = StringUtil.split(dateStr, ",");
1102            List timeS = StringUtil.split(timeStr, ",");
1103            int numImages = timeS.size();
1104            restoreTimes = new ArrayList(); 
1105            try {
1106                DateTime dt = new DateTime();
1107                dt.resetFormat();
1108                String dtformat = dt.getFormatPattern();
1109                for (int ix=0; ix<numImages; ix++) {
1110                    DateTime restoreTime = dt.createDateTime((String)dateS.get(ix) + " " + (String)timeS.get(ix));
1111                    restoreTimes.add(restoreTime);
1112                }
1113            } catch (Exception e) {
1114                System.out.println("Exception e=" + e);
1115                return false;
1116            }
1117        }
1118
1119        System.out.println("Returning from AddeChooser.restoreParameterSet()");
1120
1121        ignoreStateChangedEvents = oldISCE;
1122        return true;
1123    }
1124
1125    /**
1126     * Set the absolute times list. The times list can contain any of the object types
1127     * that makeDatedObjects knows how to handle, i.e., Date, visad.DateTime, DatedThing, AddeImageDescriptor, etc.
1128     *
1129     * @param times List of thinggs to put into absolute times list
1130     */
1131    protected void setAbsoluteTimes(List times) {
1132        super.setAbsoluteTimes(times);
1133        restoreAbsoluteTimes();
1134    }
1135
1136    protected void restoreAbsoluteTimes() {
1137        List allTimes = makeDatedObjects(super.getAbsoluteTimes());
1138        if (restoreTimes.size() > 0 && allTimes.size() > 0) {
1139            int[] indices  = new int[restoreTimes.size()];
1140            try {
1141                DateTime rtdt;
1142                DateTime atdt;
1143                DatedThing at;
1144                for (int i = 0; i < restoreTimes.size(); i++) {
1145                    rtdt = (DateTime)restoreTimes.get(i);
1146                    for (int j = 0; j < allTimes.size(); j++) {
1147                        at = (DatedThing)allTimes.get(j);
1148                        atdt = new DateTime(at.getDate());
1149                        if (atdt.equals(rtdt)) {
1150                            indices[i] = j;
1151                        }
1152                    }
1153                }
1154            } catch (Exception e) {
1155                System.out.println("Exception e=" + e);
1156            }
1157            setSelectedAbsoluteTimes(indices);
1158        }
1159    }
1160
1161    /**
1162     * show/hide the parameter restore button
1163     */
1164    public void showParameterButton() {
1165        parameterButton.setVisible(true);
1166    }
1167
1168    public void hideParameterButton() {
1169        parameterButton.setVisible(false);
1170    }
1171
1172    /**
1173     * Override and simulate clicking Add Source if requested
1174     */
1175    public void setHaveData(boolean have) {
1176        super.setHaveData(have);
1177        if (have && shouldAddSource) {
1178            System.out.println("Adding source at setHaveData");
1179            // Even though setHaveData should mean we can go, we can't... wait a few jiffies
1180            Misc.runInABit(100, AddeChooser.this, "doClickLoad", null);
1181        }
1182    }
1183
1184    public void doClickLoad() {
1185        loadButton.doClick();
1186    }
1187
1188    public void showServers() {
1189        allServersFlag = !allServersFlag;
1190        XmlObjectStore store = getIdv().getStore();
1191        store.put(Constants.PREF_SYSTEMSERVERSIMG, allServersFlag);
1192        store.save();
1193        updateServers();
1194        updateGroups();
1195    }
1196
1197    protected String getStateString() {
1198        int state = getState();
1199        switch (state) {
1200            case STATE_CONNECTED: return "Connected to server";
1201            case STATE_UNCONNECTED: return "Not connected to server";
1202            case STATE_CONNECTING: return "Connecting to server";
1203            default: return "Unknown state: " + state;
1204        }
1205    }
1206
1207    /**
1208     * Disable/enable any components that depend on the server.
1209     * Try to update the status label with what we know here.
1210     */
1211    protected void updateStatus() {
1212        super.updateStatus();
1213        if (getState() == STATE_CONNECTED) {
1214            lastServer = new AddeServer("");
1215            lastServerGroup = "";
1216            lastServerName = "";
1217            lastServerProj = "";
1218            lastServerUser = "";
1219
1220            if (!haveDescriptorSelected()) {
1221                if (!usingStations() || haveStationSelected()) {
1222                    //                String name = getDataName().toLowerCase();
1223                    String name = getDescriptorLabel().toLowerCase();
1224                    if (StringUtil.startsWithVowel(name)) {
1225                        setStatus("Please select an " + name);
1226                    } else {
1227                        setStatus("Please select a " + name);
1228                    }
1229                }
1230            }
1231        }
1232
1233        GuiUtils.enableTree(connectButton, getState() != STATE_CONNECTING);
1234    }
1235    
1236    /**
1237     * Get the data type ID
1238     *
1239     * @return  the data type
1240     */
1241    public String getDataType() {
1242        return "ANY";
1243    }
1244
1245    /**
1246     * Check if the server is ok
1247     *
1248     * @return status code
1249     */
1250    protected int checkIfServerIsOk() {
1251        try {
1252            StringBuffer buff = getUrl(REQ_TEXT);
1253            appendKeyValue(buff, PROP_FILE, FILE_PUBLICSRV);
1254            URL           url  = new URL(buff.toString());
1255            URLConnection urlc = url.openConnection();
1256            InputStream   is   = urlc.getInputStream();
1257            is.close();
1258            return STATUS_OK;
1259        } catch (AddeURLException ae) {
1260            String aes = ae.toString();
1261            if (aes.indexOf("Invalid project number") >= 0) {
1262                LogUtil.userErrorMessage("Invalid project number");
1263                return STATUS_NEEDSLOGIN;
1264            }
1265            if (aes.indexOf("Invalid user id") >= 0) {
1266                LogUtil.userErrorMessage("Invalid user ID");
1267                return STATUS_NEEDSLOGIN;
1268            }
1269            if (aes.indexOf("Accounting data") >= 0) {
1270                return STATUS_NEEDSLOGIN;
1271            }
1272            if (aes.indexOf("cannot run server 'txtgserv'") >= 0) {
1273                return STATUS_OK;
1274            }
1275            LogUtil.userErrorMessage("Error connecting to server " + getServer() + ":\n"
1276                                     + ae.getMessage());
1277            return STATUS_ERROR;
1278        } catch (ConnectException exc) {
1279            setState(STATE_UNCONNECTED);
1280            setHaveData(false);
1281            resetDescriptorBox();
1282            String message = "Error connecting to server " + getServer();
1283            if (isLocalServer())
1284                message += "\n\nLocal servers can be restarted from the\n'Local ADDE Data Manager' in the 'Tools' menu";
1285            LogUtil.userErrorMessage(message);
1286            return STATUS_ERROR;
1287        } catch (EOFException exc) {
1288            setState(STATE_UNCONNECTED);
1289            setHaveData(false);
1290            resetDescriptorBox();
1291            LogUtil.userErrorMessage("Server " + getServer() + " is not responding");
1292            return STATUS_ERROR;
1293        } catch (Exception exc) {
1294            logException("Connecting to server: " + getServer(), exc);
1295            return STATUS_ERROR;
1296        }
1297    }
1298
1299    public boolean canAccessServer() {
1300//        Set<Types> defaultTypes = EnumSet.of(ServerPropertyDialog.convertDataType(getDataType()));
1301//        while (true) {
1302//            int status = checkIfServerIsOk();
1303//            if (status == STATUS_OK) {
1304//                break;
1305//            }
1306//            if (status == STATUS_ERROR) {
1307//                setState(STATE_UNCONNECTED);
1308//                return false;
1309//            }
1310//
1311////            AddeServer server = getAddeServer();
1312//            AddeServer server = getAddeServer2(serverSelector, groupSelector);
1313//            Map<String, String> accounting = serverManager.getAccountingFor(server, type)
1314//
1315//            String name = server.getName();
1316//            String group = getGroup();
1317//            String user = accounting.get("user");
1318//            String proj = accounting.get("proj");
1319//
1320//            ServerPropertyDialog dialog = new ServerPropertyDialog(null, true, serverManager);
1321//            dialog.setTitle("Edit Server Information");
1322//            dialog.showDialog(name, group, user, proj, defaultTypes);
1323//
1324//            if (!dialog.getAddedDatasetDescriptors().isEmpty()) {
1325//                System.err.println("verified info: " + dialog.getAddedDatasetDescriptors());
1326//                break;
1327//            }
1328//        }
1329        return true;
1330    }
1331
1332    public Map<String, String> getAccountingInfo() {
1333        AddeServer server = getAddeServer();
1334        Map<String, String> map = new LinkedHashMap<String, String>();
1335        if (server != null) {
1336            List<AddeServer.Group> groups = server.getGroups();
1337            Map<String, String>acctInfo = getAccounting(server, groups.get(0).toString());
1338            map.put("user", acctInfo.get("user"));
1339            map.put("proj", acctInfo.get("proj"));
1340            map.put("server", server.getName());
1341            map.put("group", getGroup());
1342        } else {
1343            map.put("user", RemoteAddeEntry.DEFAULT_ACCOUNT.getUsername());
1344            map.put("proj", RemoteAddeEntry.DEFAULT_ACCOUNT.getUsername());
1345            map.put("server", "");
1346            map.put("group", "");
1347        }
1348        return map;
1349    }
1350
1351    /**
1352     * Saves the currently selected server and group to a chooser-specific 
1353     * preference. Preference ID is {@code PREF_SERVERSTATE+'.'+getId()}.
1354     */
1355    @Override public void saveServerState() {
1356        String[] serverState = { getServer(), getGroup() };
1357        getIdv().getStore().put(PREF_SERVERSTATE+'.'+getId(), serverState);
1358        getIdv().getStore().save();
1359    }
1360
1361    /**
1362     * Connect to the server.
1363     */
1364    protected void connectToServer() {
1365        clearParameterSet();
1366        setDescriptors(null);
1367        setDoAbsoluteTimes(false);
1368        if (!canAccessServer()) {
1369            logger.debug("couldn't connect! shucks! golly!");
1370            return;
1371        } else {
1372            logger.debug("you have successfully used the server manager! it is a miracle!");
1373        }
1374        readFromServer();
1375        saveServerState();
1376        ignoreStateChangedEvents = true;
1377        if (descList != null) {
1378            descList.saveState(groupSelector);
1379        }
1380        ignoreStateChangedEvents = false;
1381    }
1382
1383    /**
1384     * Do server connection stuff... override this with type-specific methods
1385     */
1386    protected void readFromServer() {
1387        readDescriptors();
1388        readTimes();
1389    }
1390
1391//    what the request needs to look like:
1392//    adde://localhost:8112/imagedata?&PORT=112&COMPRES S=gzip&USER=idv&PROJ=0
1393//        &VERSION=1&DEBUG=false&TRAC E=0&GROUP=MYDATA&DESCRIPTOR=ENTRY4&BAND=1
1394//        &LATLON= 30.37139 71.74912&PLACE=CENTER&SIZE=1000 1000&UNI T=BRIT
1395//        &MAG=1 1&SPAC=1&NAV=X&AUX=YES&DOC=X&POS=0
1396
1397    /**
1398     *  Generate a list of image descriptors for the descriptor list.
1399     */
1400    protected void readDescriptors() {
1401        try {
1402            StringBuffer buff = getGroupUrl(REQ_DATASETINFO, getGroup());
1403            buff.append("&type=").append(getDataType());
1404            logger.debug("readDesc: buff={}", buff.toString());
1405            DataSetInfo  dsinfo = new DataSetInfo(buff.toString());
1406            descriptorTable = dsinfo.getDescriptionTable();
1407            String[] names = new String[descriptorTable.size()];
1408            Enumeration enumeration = descriptorTable.keys();
1409            for (int i = 0; enumeration.hasMoreElements(); i++) {
1410                Object thisElement = enumeration.nextElement();
1411                if (!isLocalServer())
1412                    names[i] = descriptorTable.get(thisElement).toString() + nameSeparator + thisElement.toString();
1413                else
1414                    names[i] = thisElement.toString();
1415            }
1416            logger.debug("readDesc: names={}", names);
1417            Arrays.sort(names);
1418            setDescriptors(names);
1419            setState(STATE_CONNECTED);
1420        } catch (Exception e) {
1421            handleConnectionError(e);
1422        }
1423    }
1424
1425    /**
1426     * Initialize the descriptor list from a list of names
1427     *
1428     * @param names  list of names
1429     */
1430    protected void setDescriptors(String[] names) {
1431        synchronized (WIDGET_MUTEX) {
1432            ignoreDescriptorChange = true;
1433            descriptorComboBox.removeAllItems();
1434            descriptorNames = names;
1435            if ((names == null) || (names.length == 0)) {
1436                return;
1437            }
1438            descriptorComboBox.addItem(LABEL_SELECT);
1439            for (int j = 0; j < names.length; j++) {
1440                descriptorComboBox.addItem(names[j]);
1441            }
1442            ignoreDescriptorChange = false;
1443        }
1444    }
1445
1446    /**
1447     * Respond to a change in the descriptor list.
1448     */
1449    protected void descriptorChanged() {
1450        readTimes();
1451        updateStatus();
1452    }
1453
1454    /**
1455     * Check if a descriptor (image type) has been chosen
1456     *
1457     * @return  true if an image type has been chosen
1458     */
1459    protected boolean haveDescriptorSelected() {
1460        if ( !GuiUtils.anySelected(descriptorComboBox)) {
1461            return false;
1462        }
1463        return (getDescriptor() != null);
1464    }
1465
1466    /**
1467     * Get the selected descriptor.
1468     *
1469     * @return  the currently selected descriptor.
1470     */
1471    protected String getDescriptor() {
1472        return getDescriptorFromSelection(getSelectedDescriptor());
1473    }
1474
1475    /**
1476     * Get the descriptor relating to the selection.
1477     *
1478     * @param selection   String name from the widget
1479     *
1480     * @return  the descriptor
1481     */
1482    protected String getDescriptorFromSelection(String selection) {
1483        if (descriptorTable == null) {
1484            return null;
1485        }
1486        if (selection == null) {
1487            return null;
1488        }
1489
1490        if (!selection.contains(nameSeparator)) {
1491            return (String)descriptorTable.get(selection);
1492        }
1493        else {
1494            String[] toks = selection.split(nameSeparator, 2);
1495            String key = toks[1].trim();
1496            return (String)descriptorTable.get(key);
1497        }
1498    }
1499
1500    /**
1501     * Get the selected descriptor.
1502     *
1503     * @return the selected descriptor
1504     */
1505    public String getSelectedDescriptor() {
1506        String selection = (String) descriptorComboBox.getSelectedItem();
1507        if (selection == null) {
1508            return null;
1509        }
1510        if (selection.equals(LABEL_SELECT)) {
1511            return null;
1512        }
1513        return selection;
1514    }
1515
1516    /**
1517     * Get the descriptor table for this chooser
1518     *
1519     * @return a Hashtable of descriptors and names
1520     */
1521    public Hashtable getDescriptorTable() {
1522        return descriptorTable;
1523    }
1524
1525    /**
1526     * Get any extra key=value pairs that are appended to all requests.
1527     *
1528     * @param buff The buffer to append onto
1529     */
1530    protected void appendMiscKeyValues(StringBuffer buff) {
1531        appendKeyValue(buff, PROP_COMPRESS, DEFAULT_COMPRESS);
1532        appendKeyValue(buff, PROP_PORT, DEFAULT_PORT);
1533        // appendKeyValue(buff, PROP_DEBUG, DEFAULT_DEBUG);
1534        appendKeyValue(buff, PROP_DEBUG, Boolean.toString(EntryStore.isAddeDebugEnabled(false)));
1535        appendKeyValue(buff, PROP_VERSION, DEFAULT_VERSION);
1536        appendKeyValue(buff, PROP_USER, getLastAddedUser());
1537        appendKeyValue(buff, PROP_PROJ, getLastAddedProj());
1538    }
1539
1540    public String getLastAddedUser() {
1541        if (lastServerUser != null && lastServerUser.length() > 0) {
1542            logger.debug("getLastAddedUser: using non-default {}", lastServerUser);
1543            return lastServerUser;
1544        }
1545        else {
1546            logger.debug("getLastAddedUser: using default {}", DEFAULT_USER);
1547            return DEFAULT_USER;
1548        }
1549    }
1550
1551    public String getLastAddedProj() {
1552       if (lastServerProj != null && lastServerProj.length() > 0) {
1553           logger.debug("getLastAddedProj: using non-default {}", lastServerProj);
1554            return lastServerProj;
1555        }
1556        else {
1557            logger.debug("getLastAddedProj: using default {}", DEFAULT_PROJ);
1558            return DEFAULT_PROJ;
1559        }
1560    }
1561
1562    /**
1563     * Show the groups dialog.  This method is not meant to be called
1564     * but is public by reason of implementation (or insanity).
1565     */
1566    public void showGroups() {
1567        JPopupMenu popup = new JPopupMenu();
1568        popup.add(new JMenuItem("Reading public datasets..."));
1569        popup.show(publicButton, 0, (int) publicButton.getBounds().getHeight());
1570
1571        List groups = readGroups();
1572        popup.removeAll();
1573        if ((groups == null) || (groups.size() == 0)) {
1574            popup.add(new JMenuItem("No public datasets available"));
1575            popup.setVisible(false);
1576            popup.setVisible(true);
1577            return;
1578        }
1579
1580        JMenuItem mi;
1581        for (int i = 0; i < groups.size(); i++) {
1582            final String group = groups.get(i).toString();
1583            mi = new JMenuItem(group);
1584            mi.addActionListener(new ActionListener() {
1585                public void actionPerformed(ActionEvent ae) {
1586                    groupSelector.setSelectedItem(group);
1587                    doConnect();
1588                }
1589            });
1590            popup.add(mi);
1591        }
1592        popup.setVisible(false);
1593        popup.setVisible(true);
1594    }
1595
1596    /**
1597     * return the String id of the chosen server name
1598     *
1599     * @return  the server name
1600     */
1601    public String getServer() {
1602        AddeServer server = getAddeServer();
1603        if (server!=null)
1604            return server.getName();
1605        else
1606            return "";
1607    }
1608
1609    protected String getGroup() {
1610        return getGroup(false);
1611    }
1612
1613    /**
1614     * Is the group selector editable?  Override if ya want.
1615     * @return
1616     */
1617    protected boolean isGroupEditable() {
1618        return true;
1619    }
1620
1621    /**
1622     * Get the image group from the GUI.
1623     *
1624     * @return The image group.
1625     */
1626    protected String getGroup(final boolean fromGetServer) {
1627        Object selected = groupSelector.getSelectedItem();
1628        if (selected == null) {
1629            return null;
1630        }
1631
1632        if (selected instanceof AddeServer.Group) {
1633            AddeServer.Group group = (AddeServer.Group) selected;
1634        return group.getName();
1635        }
1636
1637        if (selected instanceof String) {
1638            return (String)selected;
1639        }
1640
1641        String groupName = selected.toString().trim();
1642        if (!fromGetServer && (groupName.length() > 0)) {
1643            //Force the get in case they typed a server name
1644            getServer();
1645
1646            AddeServer server = getAddeServer();
1647            if (server != null) {
1648                AddeServer.Group group =
1649                    getIdv().getIdvChooserManager().addAddeServerGroup(
1650                        server, groupName, getGroupType());
1651                if (!group.getActive()) {
1652                    getIdv().getIdvChooserManager().activateAddeServerGroup(
1653                        server, group);
1654                }
1655                //Now put the list of groups back in to the selector
1656                setGroups();
1657                groupSelector.setSelectedItem(group);
1658            }
1659        }
1660        return groupName;
1661    }
1662
1663    /**
1664     * Get the server selector
1665     * @return The server selector
1666     */
1667    public JComboBox getServerSelector() {
1668        if (serverSelector == null)
1669            serverSelector = super.getServerSelector();
1670
1671        ItemListener[] ell = serverSelector.getItemListeners();
1672        for (int i=0; i<ell.length; i++) {
1673            serverSelector.removeItemListener((ItemListener)ell[i]);
1674        }
1675        updateServers();
1676        updateGroups();
1677        serverSelector.addItemListener(new ItemListener() {
1678            public void itemStateChanged(ItemEvent e) {
1679                if ( !ignoreStateChangedEvents) {
1680                    Object selected = serverSelector.getSelectedItem();
1681                    if (selected instanceof AddeServer) {
1682                        AddeServer selectedServer = (AddeServer)selected;
1683                        if (selectedServer != null) {
1684                            if (isSeparator(selectedServer)) {
1685                                connectButton.setEnabled(false);
1686                                return;
1687                            }
1688                        }
1689                    }
1690                    setState(STATE_UNCONNECTED);
1691                    connectButton.setEnabled(true);
1692//                    setGroups();
1693                    resetDescriptorBox();
1694                    updateGroups();
1695//                    System.err.println("itemStateChanged");
1696                }
1697//                else {
1698//                  System.out.println("Ignoring state change here...");
1699//                }
1700            }
1701        });
1702
1703        serverSelector.getEditor().getEditorComponent().addKeyListener(new KeyListener() {
1704            public void keyTyped(final KeyEvent e) {}
1705            public void keyPressed(final KeyEvent e) {}
1706            public void keyReleased(final KeyEvent e) {
1707                JTextField field = (JTextField)serverSelector.getEditor().getEditorComponent();
1708                boolean partialMatch = false;
1709                for (int i = 0; i < serverSelector.getItemCount(); i++) {
1710                    String entry = serverSelector.getItemAt(i).toString();
1711                    if (entry.toLowerCase().startsWith(field.getText().toLowerCase()))
1712                        partialMatch = true;
1713                }
1714
1715                if (!partialMatch && groupSelector != null) {
1716                    logger.debug("aha! chooser=", getDataType());
1717                    ((JTextField)groupSelector.getEditor().getEditorComponent()).setText("");
1718                }
1719            }
1720        });
1721
1722        return serverSelector;
1723    }
1724
1725    /**
1726     * Enable or disable the GUI widgets based on what has been
1727     * selected.
1728     */
1729    protected void enableWidgets() {
1730        synchronized (WIDGET_MUTEX) {
1731            boolean newEnabledState = (getState() == STATE_CONNECTED);
1732            for (int i = 0; i < compsThatNeedDescriptor.size(); i++) {
1733                JComponent comp = (JComponent) compsThatNeedDescriptor.get(i);
1734                if (comp.isEnabled() != newEnabledState) {
1735                    GuiUtils.enableTree(comp, newEnabledState);
1736                }
1737            }
1738        }
1739    }
1740    
1741    /**
1742     * Add a listener to the given combobox that will set the
1743     * state to unconnected
1744     *
1745     * @param box The box to listen to.
1746     */
1747    protected void clearOnChange(final JComboBox box) {
1748        box.addItemListener(new ItemListener() {
1749            public void itemStateChanged(ItemEvent e) {
1750                if ( !ignoreStateChangedEvents) {
1751                    setState(STATE_UNCONNECTED);
1752                    GuiUtils.setListData(descriptorComboBox, new Vector());
1753//                    System.err.println("clearOnChange");
1754                }
1755//                else {
1756//                  System.out.println("Ignoring state change in clearOnChange for: " + box.toString());
1757//                }
1758            }
1759        });
1760    }
1761
1762    /**
1763     * Get the descriptor widget label
1764     *
1765     * @return  label for the descriptor  widget
1766     */
1767    public String getDescriptorLabel() {
1768        return "Descriptor";
1769    }
1770
1771    protected int getNumTimesToSelect() {
1772        return 5;
1773    }
1774
1775    /**
1776     * Get the default selected index for the relative times list.
1777     *
1778     * @return default index
1779     */
1780    protected int getDefaultRelativeTimeIndex() {
1781        return 4;
1782    }
1783
1784    /**
1785     * Check the times lists
1786     */
1787    protected void checkTimesLists() {
1788        super.checkTimesLists();
1789        if (timesCardPanelExtra == null) {
1790            return;
1791        }
1792        if (getDoAbsoluteTimes()) {
1793            timesCardPanelExtra.show("absolute");
1794        } else {
1795            timesCardPanelExtra.show("relative");
1796        }
1797    }
1798
1799    /** Card panel to hold extra relative and absolute time components */
1800    private GuiUtils.CardLayoutPanel timesCardPanelExtra;
1801
1802    /**
1803     * Set the relative and absolute extra components
1804     */
1805    protected JPanel makeTimesPanel(JComponent relativeCard, JComponent absoluteCard) {
1806        JPanel timesPanel = super.makeTimesPanel(false,true);
1807
1808        // Make a new timesPanel that has extra components tacked on the bottom, inside the tabs
1809        Component[] comps = timesPanel.getComponents();
1810
1811        if (comps.length==1 && comps[0] instanceof JTabbedPane) {
1812            timesCardPanelExtra = new GuiUtils.CardLayoutPanel();
1813            if (relativeCard == null) relativeCard = new JPanel();
1814            if (absoluteCard == null) absoluteCard = new JPanel();
1815            timesCardPanelExtra.add(relativeCard, "relative");
1816            timesCardPanelExtra.add(absoluteCard, "absolute");
1817            timesPanel = GuiUtils.centerBottom(comps[0], timesCardPanelExtra);
1818        }
1819
1820        return timesPanel;
1821    }
1822
1823    /**
1824     * Make the UI for this selector.
1825     * 
1826     * Thank you NetBeans for helping with the layout!
1827     * 
1828     * @return The gui
1829     */ 
1830    private JPanel innerPanel = new JPanel();
1831
1832    private JLabel statusLabel = new JLabel("Status");
1833
1834    /**
1835     * Super setStatus() takes a second string to enable "simple" mode
1836     * which highlights the required component.  We don't really care
1837     * about that feature, and we don't want getStatusLabel() to
1838     * change the label background color.
1839     */
1840    @Override
1841    public void setStatus(String statusString, String foo) {
1842        if (statusString == null)
1843            statusString = "";
1844        statusLabel.setText(statusString);
1845    }
1846
1847    protected void setInnerPanel(JPanel newInnerPanel) {
1848        innerPanel = newInnerPanel;
1849    }
1850
1851    /**
1852     * Create the basic layout
1853     */
1854    protected JComponent doMakeContents() {
1855        JPanel outerPanel = new JPanel();
1856
1857        JLabel serverLabelInner = new JLabel("Server:");    
1858        McVGuiUtils.setLabelPosition(serverLabelInner, Position.RIGHT);
1859        JPanel serverLabel = GuiUtils.leftRight(parameterButton, serverLabelInner);
1860        McVGuiUtils.setComponentWidth(serverLabel);
1861
1862        clearOnChange(serverSelector);
1863        McVGuiUtils.setComponentWidth(serverSelector, Width.DOUBLE);
1864
1865        JLabel groupLabel = McVGuiUtils.makeLabelRight("Dataset:");
1866
1867        groupSelector.setEditable(isGroupEditable());
1868        clearOnChange(groupSelector);
1869        McVGuiUtils.setComponentWidth(groupSelector, Width.DOUBLE);
1870
1871        McVGuiUtils.setComponentWidth(connectButton, Width.DOUBLE);
1872        connectButton.setActionCommand(CMD_CONNECT);
1873        connectButton.addActionListener(this);
1874
1875        /** Set the attributes for the descriptor label and combo box, even though
1876         * they are not used here.  Extending classes can add them to the panel if
1877         * necessary.
1878         */
1879        McVGuiUtils.setComponentWidth(descriptorLabel);
1880        McVGuiUtils.setLabelPosition(descriptorLabel, Position.RIGHT);
1881
1882        McVGuiUtils.setComponentWidth(descriptorComboBox, Width.DOUBLEDOUBLE);
1883
1884        if (descriptorComboBox.getMinimumSize().getWidth() < ELEMENT_DOUBLE_WIDTH) {
1885            McVGuiUtils.setComponentWidth(descriptorComboBox, Width.DOUBLE);
1886        }
1887
1888        JLabel statusLabelLabel = McVGuiUtils.makeLabelRight("");
1889
1890        statusLabel.setText("Status");
1891        McVGuiUtils.setLabelPosition(statusLabel, Position.RIGHT);
1892        McVGuiUtils.setComponentColor(statusLabel, TextColor.STATUS);
1893
1894        JButton helpButton = McVGuiUtils.makeImageButton(ICON_HELP, "Show help");
1895        helpButton.setActionCommand(GuiUtils.CMD_HELP);
1896        helpButton.addActionListener(this);
1897
1898        JButton refreshButton = McVGuiUtils.makeImageButton(ICON_REFRESH, "Refresh");
1899        refreshButton.setActionCommand(GuiUtils.CMD_UPDATE);
1900        refreshButton.addActionListener(this);
1901
1902        McVGuiUtils.setComponentWidth(loadButton, Width.DOUBLE);
1903
1904        GroupLayout layout = new GroupLayout(outerPanel);
1905        outerPanel.setLayout(layout);
1906        layout.setHorizontalGroup(
1907            layout.createParallelGroup(LEADING)
1908            .addGroup(TRAILING, layout.createSequentialGroup()
1909                .addGroup(layout.createParallelGroup(TRAILING)
1910                    .addGroup(layout.createSequentialGroup()
1911                        .addContainerGap()
1912                        .addComponent(helpButton)
1913                        .addGap(GAP_RELATED)
1914                        .addComponent(refreshButton)
1915                        .addGap(GAP_RELATED)
1916                        .addComponent(cancelButton)
1917                        .addPreferredGap(RELATED)
1918                        .addComponent(loadButton))
1919                        .addGroup(LEADING, layout.createSequentialGroup()
1920                        .addContainerGap()
1921                        .addGroup(layout.createParallelGroup(LEADING)
1922                            .addComponent(innerPanel, DEFAULT_SIZE, DEFAULT_SIZE, Short.MAX_VALUE)
1923                            .addGroup(layout.createSequentialGroup()
1924                                .addComponent(serverLabel)
1925                                .addGap(GAP_RELATED)
1926                                .addComponent(serverSelector)
1927                                .addGap(GAP_RELATED)
1928                                .addComponent(manageButton)
1929                                .addGap(GAP_RELATED)
1930                                .addComponent(groupLabel)
1931                                .addGap(GAP_RELATED)
1932                                .addComponent(groupSelector)
1933                                .addGap(GAP_RELATED)
1934                                .addComponent(publicButton)
1935                                .addPreferredGap(RELATED, DEFAULT_SIZE, Short.MAX_VALUE)
1936                                .addComponent(connectButton))
1937                            .addGroup(layout.createSequentialGroup()
1938                                .addComponent(statusLabelLabel)
1939                                .addGap(GAP_RELATED)
1940                                .addComponent(statusLabel, DEFAULT_SIZE, DEFAULT_SIZE, Short.MAX_VALUE)))))
1941                .addContainerGap())
1942        );
1943        layout.setVerticalGroup(
1944            layout.createParallelGroup(LEADING)
1945            .addGroup(layout.createSequentialGroup()
1946                .addContainerGap()
1947                .addGroup(layout.createParallelGroup(BASELINE)
1948                    .addComponent(serverLabel)
1949                    .addComponent(serverSelector)
1950                    .addComponent(manageButton)
1951                    .addComponent(groupLabel)
1952                    .addComponent(groupSelector)
1953                    .addComponent(publicButton)
1954                    .addComponent(connectButton))
1955                .addPreferredGap(UNRELATED)
1956                .addComponent(innerPanel, DEFAULT_SIZE, DEFAULT_SIZE, Short.MAX_VALUE)
1957                .addPreferredGap(UNRELATED)
1958                .addGroup(layout.createParallelGroup(BASELINE)
1959                    .addComponent(statusLabelLabel)
1960                    .addComponent(statusLabel))
1961                .addPreferredGap(UNRELATED)
1962                .addGroup(layout.createParallelGroup(BASELINE)
1963                    .addComponent(loadButton)
1964                    .addComponent(cancelButton)
1965                    .addComponent(refreshButton)
1966                    .addComponent(helpButton))
1967                .addContainerGap())
1968        );
1969    
1970        return outerPanel;
1971
1972    }
1973
1974    public class ServerComparator implements Comparator<AddeServer> {
1975        public int compare(AddeServer server1, AddeServer server2) {
1976            return server1.getName().compareTo(server2.getName());
1977        }
1978    }
1979
1980    public class GroupComparator implements Comparator<Group> {
1981        public int compare(Group group1, Group group2) {
1982            return group1.getName().compareTo(group2.getName());
1983        }
1984    }
1985}
1986