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