001    /*
002     * $Id: AddeChooser.java,v 1.87 2012/02/19 17:35:35 davep Exp $
003     *
004     * This file is part of McIDAS-V
005     *
006     * Copyright 2007-2012
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    
031    package edu.wisc.ssec.mcidasv.chooser.adde;
032    
033    import static edu.wisc.ssec.mcidasv.servermanager.AddeEntry.DEFAULT_ACCOUNT;
034    import static edu.wisc.ssec.mcidasv.util.CollectionHelpers.arrList;
035    import static edu.wisc.ssec.mcidasv.McIDASV.isLoopback;
036    
037    import static javax.swing.GroupLayout.DEFAULT_SIZE;
038    import static javax.swing.GroupLayout.Alignment.BASELINE;
039    import static javax.swing.GroupLayout.Alignment.LEADING;
040    import static javax.swing.GroupLayout.Alignment.TRAILING;
041    import static javax.swing.LayoutStyle.ComponentPlacement.RELATED;
042    import static javax.swing.LayoutStyle.ComponentPlacement.UNRELATED;
043    
044    import java.awt.Component;
045    import java.awt.event.ActionEvent;
046    import java.awt.event.ActionListener;
047    import java.awt.event.ItemEvent;
048    import java.awt.event.ItemListener;
049    import java.awt.event.KeyEvent;
050    import java.awt.event.KeyListener;
051    import java.awt.event.MouseAdapter;
052    import java.awt.event.MouseEvent;
053    import java.io.EOFException;
054    import java.io.InputStream;
055    import java.net.ConnectException;
056    import java.net.URL;
057    import java.net.URLConnection;
058    import java.util.ArrayList;
059    import java.util.Arrays;
060    import java.util.Collections;
061    import java.util.Comparator;
062    import java.util.Enumeration;
063    import java.util.HashMap;
064    import java.util.Hashtable;
065    import java.util.LinkedHashMap;
066    import java.util.List;
067    import java.util.Map;
068    import java.util.Vector;
069    
070    import javax.swing.GroupLayout;
071    import javax.swing.JButton;
072    import javax.swing.JCheckBox;
073    import javax.swing.JComboBox;
074    import javax.swing.JComponent;
075    import javax.swing.JLabel;
076    import javax.swing.JMenu;
077    import javax.swing.JMenuItem;
078    import javax.swing.JPanel;
079    import javax.swing.JPopupMenu;
080    import javax.swing.JTabbedPane;
081    import javax.swing.JTextField;
082    import javax.swing.SwingUtilities;
083    
084    import org.bushe.swing.event.annotation.AnnotationProcessor;
085    import org.bushe.swing.event.annotation.EventSubscriber;
086    import org.slf4j.Logger;
087    import org.slf4j.LoggerFactory;
088    import org.w3c.dom.Element;
089    
090    import edu.wisc.ssec.mcidas.adde.AddeURLException;
091    import edu.wisc.ssec.mcidas.adde.DataSetInfo;
092    
093    import visad.DateTime;
094    
095    import ucar.unidata.idv.chooser.IdvChooser;
096    import ucar.unidata.idv.chooser.IdvChooserManager;
097    import ucar.unidata.idv.chooser.adde.AddeServer;
098    import ucar.unidata.idv.chooser.adde.AddeServer.Group;
099    import ucar.unidata.util.DatedThing;
100    import ucar.unidata.util.GuiUtils;
101    import ucar.unidata.util.LogUtil;
102    import ucar.unidata.util.Misc;
103    import ucar.unidata.util.PreferenceList;
104    import ucar.unidata.util.StringUtil;
105    import ucar.unidata.xml.XmlObjectStore;
106    
107    import edu.wisc.ssec.mcidasv.Constants;
108    import edu.wisc.ssec.mcidasv.McIDASV;
109    import edu.wisc.ssec.mcidasv.ParameterSet;
110    import edu.wisc.ssec.mcidasv.PersistenceManager;
111    import edu.wisc.ssec.mcidasv.servermanager.AddeAccount;
112    import edu.wisc.ssec.mcidasv.servermanager.AddeEntry;
113    import edu.wisc.ssec.mcidasv.servermanager.AddeEntry.EditorAction;
114    import edu.wisc.ssec.mcidasv.servermanager.AddeEntry.EntryType;
115    import edu.wisc.ssec.mcidasv.servermanager.EntryStore;
116    import edu.wisc.ssec.mcidasv.servermanager.EntryTransforms;
117    import edu.wisc.ssec.mcidasv.servermanager.LocalEntryEditor;
118    import edu.wisc.ssec.mcidasv.servermanager.RemoteAddeEntry;
119    import edu.wisc.ssec.mcidasv.servermanager.RemoteEntryEditor;
120    import edu.wisc.ssec.mcidasv.servermanager.TabbedAddeManager;
121    import edu.wisc.ssec.mcidasv.ui.ParameterTree;
122    import edu.wisc.ssec.mcidasv.ui.UIManager;
123    import edu.wisc.ssec.mcidasv.util.CollectionHelpers;
124    import edu.wisc.ssec.mcidasv.util.McVGuiUtils;
125    import edu.wisc.ssec.mcidasv.util.McVGuiUtils.Position;
126    import edu.wisc.ssec.mcidasv.util.McVGuiUtils.TextColor;
127    import edu.wisc.ssec.mcidasv.util.McVGuiUtils.Width;
128    
129    /**
130     *
131     * @version $Revision: 1.87 $
132     */
133    public 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.trace("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