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    package edu.wisc.ssec.mcidasv.servermanager;
029    
030    import static javax.swing.GroupLayout.DEFAULT_SIZE;
031    import static javax.swing.GroupLayout.PREFERRED_SIZE;
032    import static javax.swing.GroupLayout.Alignment.BASELINE;
033    import static javax.swing.GroupLayout.Alignment.LEADING;
034    import static javax.swing.GroupLayout.Alignment.TRAILING;
035    import static javax.swing.LayoutStyle.ComponentPlacement.RELATED;
036    import static javax.swing.LayoutStyle.ComponentPlacement.UNRELATED;
037    
038    import static edu.wisc.ssec.mcidasv.util.Contract.notNull;
039    import static edu.wisc.ssec.mcidasv.util.CollectionHelpers.newLinkedHashSet;
040    import static edu.wisc.ssec.mcidasv.util.CollectionHelpers.newMap;
041    import static edu.wisc.ssec.mcidasv.util.CollectionHelpers.set;
042    import static edu.wisc.ssec.mcidasv.util.McVGuiUtils.runOnEDT;
043    
044    import java.awt.Color;
045    import java.util.Collection;
046    import java.util.Collections;
047    import java.util.EnumSet;
048    import java.util.LinkedHashSet;
049    import java.util.LinkedHashMap;
050    import java.util.List;
051    import java.util.Map;
052    import java.util.Set;
053    import java.util.StringTokenizer;
054    import java.util.concurrent.Callable;
055    import java.util.concurrent.CompletionService;
056    import java.util.concurrent.ExecutionException;
057    import java.util.concurrent.ExecutorCompletionService;
058    import java.util.concurrent.ExecutorService;
059    import java.util.concurrent.Executors;
060    
061    import javax.swing.SwingUtilities;
062    
063    import org.slf4j.Logger;
064    import org.slf4j.LoggerFactory;
065    
066    import ucar.unidata.util.LogUtil;
067    
068    import edu.wisc.ssec.mcidasv.servermanager.AddeEntry.EditorAction;
069    import edu.wisc.ssec.mcidasv.servermanager.AddeEntry.EntrySource;
070    import edu.wisc.ssec.mcidasv.servermanager.AddeEntry.EntryType;
071    import edu.wisc.ssec.mcidasv.servermanager.AddeEntry.EntryValidity;
072    import edu.wisc.ssec.mcidasv.util.CollectionHelpers;
073    import edu.wisc.ssec.mcidasv.util.Contract;
074    import edu.wisc.ssec.mcidasv.util.McVTextField;
075    
076    /**
077     * Simple dialog that allows the user to define or modify {@link RemoteAddeEntry}s.
078     */
079    @SuppressWarnings("serial")
080    public class RemoteEntryEditor extends javax.swing.JDialog {
081    
082        /** Logger object. */
083        private static final Logger logger = LoggerFactory.getLogger(RemoteEntryEditor.class);
084    
085        /** Possible entry verification states. */
086        public enum AddeStatus { PREFLIGHT, BAD_SERVER, BAD_ACCOUNTING, NO_METADATA, OK, BAD_GROUP };
087    
088        /** Number of threads in the thread pool. */
089        private static final int POOL = 5;
090    
091        /** Whether or not to input in the dataset, username, and project fields should be uppercased. */
092        private static final String PREF_FORCE_CAPS = "mcv.servers.forcecaps";
093    
094        /** Background {@link java.awt.Color Color} of an {@literal "invalid"} {@link javax.swing.JTextField JTextField}. */
095        private static final Color ERROR_FIELD_COLOR = Color.PINK;
096    
097        /** Text {@link java.awt.Color Color} of an {@literal "invalid"} {@link javax.swing.JTextField JTextField}. */
098        private static final Color ERROR_TEXT_COLOR = Color.WHITE;
099    
100        /** Background {@link java.awt.Color Color} of a {@literal "valid"} {@link javax.swing.JTextField JTextField}. */
101        private static final Color NORMAL_FIELD_COLOR = Color.WHITE;
102    
103        /** Text {@link java.awt.Color Color} of a {@literal "valid"} {@link javax.swing.JTextField JTextField}. */
104        private static final Color NORMAL_TEXT_COLOR = Color.BLACK;
105    
106        /**
107         * Contains any {@code JTextField}s that may be in an invalid
108         * (to McIDAS-V) state.
109         */
110        private final Set<javax.swing.JTextField> badFields = newLinkedHashSet();
111    
112        /** Reference back to the server manager. */
113        private final EntryStore entryStore;
114    
115    //    private final TabbedAddeManager manager;
116    
117        /** Current contents of the editor. */
118        private final Set<RemoteAddeEntry> currentEntries = newLinkedHashSet();
119    
120        /** The last dialog action performed by the user. */
121        private EditorAction editorAction = EditorAction.INVALID;
122    
123        /** Initial contents of {@link #serverField}. Be aware that {@code null} is allowed. */
124        private final String serverText;
125    
126        /** Initial contents of {@link #datasetField}. Be aware that {@code null} is allowed. */
127        private final String datasetText;
128    
129        /** Whether or not the editor is prompting the user to adjust input. */
130        private boolean inErrorState = false;
131    
132        // if we decide to restore error overlays for known "bad" values.
133    //    private Set<RemoteAddeEntry> invalidEntries = CollectionHelpers.newLinkedHashSet();
134    
135        /**
136         * Populates the server and dataset text fields with given {@link String}s.
137         * This only works if the dialog <b>is not yet visible</b>.
138         * 
139         * <p>This is mostly useful when adding an entry from a chooser.
140         * 
141         * @param address Should be the address of a server, but empty and 
142         * {@code null} values are allowed.
143         * @param group Should be the name of a group/dataset on {@code server}, 
144         * but empty and {@code null} values are allowed.
145         */
146        public RemoteEntryEditor(EntryStore entryStore, String address, String group) {
147            super((javax.swing.JDialog)null, true);
148            this.entryStore = entryStore;
149    //        this.manager = null;
150            this.serverText = address;
151            this.datasetText = group;
152            initComponents(RemoteAddeEntry.INVALID_ENTRIES);
153        }
154    
155        // TODO(jon): hold back on javadocs, this is likely to change
156        public RemoteEntryEditor(java.awt.Frame parent, boolean modal, final TabbedAddeManager manager, final EntryStore store) {
157            this(parent, modal, manager, store, RemoteAddeEntry.INVALID_ENTRIES);
158        }
159    
160        public RemoteEntryEditor(java.awt.Frame parent, boolean modal, final TabbedAddeManager manager, final EntryStore store, final RemoteAddeEntry entry) {
161            this(parent, modal, manager, store, CollectionHelpers.list(entry));
162        }
163    
164        // TODO(jon): hold back on javadocs, this is likely to change
165        public RemoteEntryEditor(java.awt.Frame parent, boolean modal, final TabbedAddeManager manager, final EntryStore store, final List<RemoteAddeEntry> entries) {
166            super(manager, modal);
167            this.entryStore = store;
168    //        this.manager = manager;
169            this.serverText = null;
170            this.datasetText = null;
171            if (entries != RemoteAddeEntry.INVALID_ENTRIES) {
172                currentEntries.addAll(entries);
173            }
174            initComponents(entries);
175        }
176    
177        /**
178         * Poll the various UI components and attempt to construct valid ADDE
179         * entries based upon the information provided by the user.
180         *
181         * @param ignoreCheckboxes Whether or not the {@literal "type"} checkboxes
182         * should get ignored. Setting this to {@code true} means that <i>all</i>
183         * types are considered valid--which is useful when attempting to verify
184         * the user's input.
185         *
186         * @return {@link Set} of entries that represent the user's input, or an
187         * empty {@code Set} if the input was invalid somehow.
188         */
189        private Set<RemoteAddeEntry> pollWidgets(final boolean ignoreCheckboxes) {
190            String host = serverField.getText().trim();
191            String dataset = datasetField.getText().trim();
192            String username = RemoteAddeEntry.DEFAULT_ACCOUNT.getUsername();
193            String project = RemoteAddeEntry.DEFAULT_ACCOUNT.getProject();
194            if (acctBox.isSelected()) {
195                username = userField.getText().trim();
196                project = projField.getText().trim();
197            }
198    
199            // determine the "valid" types
200            Set<EntryType> selectedTypes = newLinkedHashSet();
201            if (!ignoreCheckboxes) {
202                if (imageBox.isSelected()) {
203                    selectedTypes.add(EntryType.IMAGE);
204                }
205                if (pointBox.isSelected()) {
206                    selectedTypes.add(EntryType.POINT);
207                }
208                if (gridBox.isSelected()) {
209                    selectedTypes.add(EntryType.GRID);
210                }
211                if (textBox.isSelected()) {
212                    selectedTypes.add(EntryType.TEXT);
213                }
214                if (navBox.isSelected()) {
215                    selectedTypes.add(EntryType.NAV);
216                }
217                if (radarBox.isSelected()) {
218                    selectedTypes.add(EntryType.RADAR);
219                }
220            } else {
221                selectedTypes.addAll(set(EntryType.IMAGE, EntryType.POINT, EntryType.GRID, EntryType.TEXT, EntryType.NAV, EntryType.RADAR));
222            }
223    
224            if (selectedTypes.isEmpty()) {
225                selectedTypes.add(EntryType.UNKNOWN);
226            }
227    
228            // deal with the user trying to add multiple groups at once (even though this UI doesn't work right with it)
229            StringTokenizer tok = new StringTokenizer(dataset, ",");
230            Set<String> newDatasets = newLinkedHashSet();
231            while (tok.hasMoreTokens()) {
232                newDatasets.add(tok.nextToken().trim());
233            }
234    
235            // create a new entry for each group and its valid types.
236            Set<RemoteAddeEntry> entries = newLinkedHashSet();
237            for (String newGroup : newDatasets) {
238                for (EntryType type : selectedTypes) {
239                    RemoteAddeEntry.Builder builder = new RemoteAddeEntry.Builder(host, newGroup).type(type).validity(EntryValidity.VERIFIED).source(EntrySource.USER);
240                    if (acctBox.isSelected()) {
241                        builder = builder.account(username, project);
242                    }
243                    RemoteAddeEntry newEntry = builder.build();
244                    List<AddeEntry> matches = entryStore.searchWithPrefix(newEntry.asStringId());
245                    if (matches.isEmpty()) {
246                        entries.add(newEntry);
247                    } else if (matches.size() == 1) {
248                        AddeEntry matchedEntry = matches.get(0);
249                        if (matchedEntry.getEntrySource() != EntrySource.SYSTEM) {
250                            entries.add(newEntry);
251                        } else {
252                            entries.add((RemoteAddeEntry)matchedEntry);
253                        }
254                    } else {
255                        // results should only be empty or a single entry
256                        logger.warn("server manager returned unexpected results={}", matches);
257                    }
258                }
259            }
260            return entries;
261        }
262    
263        private void disposeDisplayable(final boolean refreshManager) {
264            if (isDisplayable()) {
265                dispose();
266            }
267            TabbedAddeManager tmpController = TabbedAddeManager.getTabbedManager();
268            if (refreshManager && tmpController != null) {
269                tmpController.refreshDisplay();
270            }
271        }
272    
273        /**
274         * Creates new {@link RemoteAddeEntry}s based upon the contents of the dialog
275         * and adds {@literal "them"} to the managed servers. If the dialog is
276         * displayed, we call {@link #dispose()} and attempt to refresh the
277         * server manager GUI if it is available.
278         */
279        private void addEntry() {
280            Set<RemoteAddeEntry> addedEntries = pollWidgets(false);
281            entryStore.addEntries(addedEntries);
282    //        if (manager != null) {
283    //            manager.addEntries(addedEntries);
284    //        }
285            disposeDisplayable(true);
286        }
287    
288        /**
289         * Replaces the entries within {@link #currentEntries} with new entries 
290         * from {@link #pollWidgets(boolean)}. If the dialog is displayed, we call 
291         * {@link #dispose()} and attempt to refresh the server manager GUI if it's 
292         * available.
293         */
294        private void editEntry() {
295            Set<RemoteAddeEntry> newEntries = pollWidgets(false);
296            entryStore.replaceEntries(currentEntries, newEntries);
297    //        if (manager != null) {
298    //            manager.replaceEntries(currentEntries, newEntries);
299    //        }
300            logger.trace("currentEntries={}", currentEntries);
301            disposeDisplayable(true);
302        }
303    
304        /**
305         * Attempts to verify that the current contents of the GUI are
306         * {@literal "valid"}.
307         */
308        private void verifyInput() {
309            resetBadFields();
310            Set<RemoteAddeEntry> unverifiedEntries = pollWidgets(true);
311    
312            // the editor GUI only works with one server address at a time. so 
313            // although there may be several RemoteAddeEntry objs, they'll all have
314            // the same address and the follow *isn't* as dumb as it looks!
315            if (!unverifiedEntries.isEmpty()) {
316                if (!RemoteAddeEntry.checkHost(unverifiedEntries.toArray(new RemoteAddeEntry[0])[0])) {
317                    setStatus("Could not connect to the given server.");
318                    setBadField(serverField, true);
319                    return;
320                }
321            } else {
322                setStatus("Please specify ");
323                setBadField(serverField, true);
324                return;
325            }
326    
327            setStatus("Contacting server...");
328            Set<RemoteAddeEntry> verifiedEntries = checkGroups(unverifiedEntries);
329            EnumSet<EntryType> presentTypes = EnumSet.noneOf(EntryType.class);
330            if (!verifiedEntries.isEmpty()) {
331                for (RemoteAddeEntry verifiedEntry : verifiedEntries) {
332                    presentTypes.add(verifiedEntry.getEntryType());
333                }
334                imageBox.setSelected(presentTypes.contains(EntryType.IMAGE));
335                pointBox.setSelected(presentTypes.contains(EntryType.POINT));
336                gridBox.setSelected(presentTypes.contains(EntryType.GRID));
337                textBox.setSelected(presentTypes.contains(EntryType.TEXT));
338                navBox.setSelected(presentTypes.contains(EntryType.NAV));
339                radarBox.setSelected(presentTypes.contains(EntryType.RADAR));
340            }
341        }
342    
343        /**
344         * Displays a short status message in {@link #statusLabel}.
345         *
346         * @param msg Status message. Shouldn't be {@code null}.
347         */
348        private void setStatus(final String msg) {
349            assert msg != null;
350            logger.debug("msg={}", msg);
351            runOnEDT(new Runnable() {
352                public void run() {
353                    statusLabel.setText(msg);
354                }
355            });
356            statusLabel.revalidate();
357        }
358    
359        /**
360         * Marks a {@code JTextField} as {@literal "valid"} or {@literal "invalid"}.
361         * Mostly this just means that the field is highlighted in order to provide
362         * to the user a sense of {@literal "what do I fix"} when something goes
363         * wrong.
364         *
365         * @param field {@code JTextField} to mark.
366         * @param isBad {@code true} means that the field is {@literal "invalid"},
367         * {@code false} means that the field is {@literal "valid"}.
368         */
369        private void setBadField(final javax.swing.JTextField field, final boolean isBad) {
370            assert field != null;
371            assert field == serverField || field == datasetField || field == userField || field == projField;
372    
373            if (isBad) {
374                badFields.add(field);
375            } else {
376                badFields.remove(field);
377            }
378    
379            runOnEDT(new Runnable() {
380                public void run() {
381                    if (isBad) {
382                        field.setForeground(ERROR_TEXT_COLOR);
383                        field.setBackground(ERROR_FIELD_COLOR);
384                    } else {
385                        field.setForeground(NORMAL_TEXT_COLOR);
386                        field.setBackground(NORMAL_FIELD_COLOR);
387                    }
388                }
389            });
390            field.revalidate();
391        }
392    
393       /**
394         * Determines whether or not any fields are in an invalid state. Useful
395         * for disallowing the user to add invalid entries to the server manager.
396         *
397         * @return Whether or not any fields are invalid.
398         */
399        private boolean anyBadFields() {
400            assert badFields != null;
401            return !badFields.isEmpty();
402        }
403    
404        /**
405         * Clear out {@link #badFields} and {@literal "set"} the field's status to
406         * valid.
407         */
408        private void resetBadFields() {
409            Set<javax.swing.JTextField> fields = new LinkedHashSet<javax.swing.JTextField>(badFields);
410            for (javax.swing.JTextField field : fields) {
411                setBadField(field, false);
412            }
413        }
414    
415        /**
416         * @see #editorAction
417         */
418        public EditorAction getEditorAction() {
419            return editorAction;
420        }
421    
422        /**
423         * @see #editorAction
424         */
425        private void setEditorAction(final EditorAction editorAction) {
426            this.editorAction = editorAction;
427        }
428    
429        /**
430         * Controls the value associated with the {@link #PREF_FORCE_CAPS} preference.
431         * 
432         * @param value {@code true} causes user input into the dataset, username, 
433         * and project fields to be capitalized.
434         * 
435         * @see #getForceMcxCaps()
436         */
437        private void setForceMcxCaps(final boolean value) {
438            entryStore.getIdvStore().put(PREF_FORCE_CAPS, value);
439        }
440    
441        /**
442         * Returns the value associated with the {@link #PREF_FORCE_CAPS} preference.
443         * 
444         * @see #setForceMcxCaps(boolean)
445         */
446        private boolean getForceMcxCaps() {
447            return entryStore.getIdvStore().get(PREF_FORCE_CAPS, true);
448        }
449    
450        // TODO(jon): oh man clean this junk up
451        /** This method is called from within the constructor to
452         * initialize the form.
453         * WARNING: Do NOT modify this code. The content of this method is
454         * always regenerated by the Form Editor.
455         */
456        @SuppressWarnings("unchecked")
457        // <editor-fold defaultstate="collapsed" desc="Generated Code">
458        private void initComponents(final List<RemoteAddeEntry> initEntries) {
459            assert SwingUtilities.isEventDispatchThread();
460            entryPanel = new javax.swing.JPanel();
461            serverLabel = new javax.swing.JLabel();
462            serverField = new javax.swing.JTextField();
463            datasetLabel = new javax.swing.JLabel();
464            datasetField = new McVTextField();
465            acctBox = new javax.swing.JCheckBox();
466            userLabel = new javax.swing.JLabel();
467            userField = new McVTextField();
468            projLabel = new javax.swing.JLabel();
469            projField = new javax.swing.JTextField();
470            capBox = new javax.swing.JCheckBox();
471            typePanel = new javax.swing.JPanel();
472            imageBox = new javax.swing.JCheckBox();
473            pointBox = new javax.swing.JCheckBox();
474            gridBox = new javax.swing.JCheckBox();
475            textBox = new javax.swing.JCheckBox();
476            navBox = new javax.swing.JCheckBox();
477            radarBox = new javax.swing.JCheckBox();
478            statusPanel = new javax.swing.JPanel();
479            statusLabel = new javax.swing.JLabel();
480            verifyAddButton = new javax.swing.JButton();
481            verifyServer = new javax.swing.JButton();
482            addServer = new javax.swing.JButton();
483            cancelButton = new javax.swing.JButton();
484    
485            boolean forceCaps = getForceMcxCaps();
486            datasetField.setUppercase(forceCaps);
487            userField.setUppercase(forceCaps);
488    
489            if (initEntries == RemoteAddeEntry.INVALID_ENTRIES) {
490                setTitle("Add Remote Dataset");
491            } else {
492                setTitle("Edit Remote Dataset");
493            }
494            setResizable(false);
495            setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);
496            addWindowListener(new java.awt.event.WindowAdapter() {
497                public void windowClosed(java.awt.event.WindowEvent evt) {
498                    formWindowClosed(evt);
499                }
500            });
501    
502            serverLabel.setText("Server:");
503            if (serverText != null) {
504                serverField.setText(serverText);
505            }
506    
507            datasetLabel.setText("Dataset:");
508            if (datasetText != null) {
509                datasetField.setText(datasetText);
510            }
511    
512            acctBox.setText("Specify accounting information:");
513            acctBox.addActionListener(new java.awt.event.ActionListener() {
514                public void actionPerformed(java.awt.event.ActionEvent evt) {
515                    acctBoxActionPerformed(evt);
516                }
517            });
518    
519            userLabel.setText("Username:");
520            userField.setEnabled(acctBox.isSelected());
521    
522            projLabel.setText("Project #:");
523            projField.setEnabled(acctBox.isSelected());
524    
525            capBox.setText("Automatically capitalize dataset and username?");
526            capBox.setSelected(forceCaps);
527            capBox.addActionListener(new java.awt.event.ActionListener() {
528                public void actionPerformed(java.awt.event.ActionEvent evt) {
529                    capBoxActionPerformed(evt);
530                }
531            });
532    
533            javax.swing.event.DocumentListener inputListener = new javax.swing.event.DocumentListener() {
534                public void changedUpdate(javax.swing.event.DocumentEvent evt) {
535                    reactToValueChanges();
536                }
537                public void insertUpdate(javax.swing.event.DocumentEvent evt) {
538                    if (inErrorState) {
539                        verifyAddButton.setEnabled(true);
540                        verifyServer.setEnabled(true);
541                        inErrorState = false;
542                        resetBadFields();
543                    }
544                }
545                public void removeUpdate(javax.swing.event.DocumentEvent evt) {
546                    if (inErrorState) {
547                        verifyAddButton.setEnabled(true);
548                        verifyServer.setEnabled(true);
549                        inErrorState = false;
550                        resetBadFields();
551                    }
552                }
553            };
554    
555            serverField.getDocument().addDocumentListener(inputListener);
556            datasetField.getDocument().addDocumentListener(inputListener);
557            userField.getDocument().addDocumentListener(inputListener);
558            projField.getDocument().addDocumentListener(inputListener);
559    
560            javax.swing.GroupLayout entryPanelLayout = new javax.swing.GroupLayout(entryPanel);
561            entryPanel.setLayout(entryPanelLayout);
562            entryPanelLayout.setHorizontalGroup(
563                entryPanelLayout.createParallelGroup(LEADING)
564                .addGroup(entryPanelLayout.createSequentialGroup()
565                    .addGroup(entryPanelLayout.createParallelGroup(LEADING)
566                        .addComponent(serverLabel, TRAILING)
567                        .addComponent(datasetLabel, TRAILING)
568                        .addComponent(userLabel, TRAILING)
569                        .addComponent(projLabel, TRAILING))
570                    .addPreferredGap(RELATED)
571                    .addGroup(entryPanelLayout.createParallelGroup(LEADING)
572                        .addComponent(serverField, DEFAULT_SIZE, 419, Short.MAX_VALUE)
573                        .addComponent(capBox)
574                        .addComponent(acctBox)
575                        .addComponent(datasetField, DEFAULT_SIZE, 419, Short.MAX_VALUE)
576                        .addComponent(userField, DEFAULT_SIZE, 419, Short.MAX_VALUE)
577                        .addComponent(projField, DEFAULT_SIZE, 419, Short.MAX_VALUE))
578                    .addContainerGap())
579            );
580            entryPanelLayout.setVerticalGroup(
581                entryPanelLayout.createParallelGroup(LEADING)
582                .addGroup(entryPanelLayout.createSequentialGroup()
583                    .addGroup(entryPanelLayout.createParallelGroup(BASELINE)
584                        .addComponent(serverLabel)
585                        .addComponent(serverField, PREFERRED_SIZE, DEFAULT_SIZE, PREFERRED_SIZE))
586                    .addPreferredGap(RELATED)
587                    .addGroup(entryPanelLayout.createParallelGroup(BASELINE)
588                        .addComponent(datasetLabel)
589                        .addComponent(datasetField, PREFERRED_SIZE, DEFAULT_SIZE, PREFERRED_SIZE))
590                    .addGap(16, 16, 16)
591                    .addComponent(acctBox)
592                    .addPreferredGap(RELATED)
593                    .addGroup(entryPanelLayout.createParallelGroup(BASELINE)
594                        .addComponent(userLabel)
595                        .addComponent(userField, PREFERRED_SIZE, DEFAULT_SIZE, PREFERRED_SIZE))
596                    .addPreferredGap(RELATED)
597                    .addGroup(entryPanelLayout.createParallelGroup(BASELINE)
598                        .addComponent(projLabel)
599                        .addComponent(projField, PREFERRED_SIZE, DEFAULT_SIZE, PREFERRED_SIZE))
600                    .addPreferredGap(RELATED)
601                    .addComponent(capBox)
602                    .addGap(0, 0, Short.MAX_VALUE))
603            );
604    
605            typePanel.setBorder(javax.swing.BorderFactory.createTitledBorder("Dataset Types"));
606    
607            java.awt.event.ActionListener typeInputListener = new java.awt.event.ActionListener() {
608                public void actionPerformed(java.awt.event.ActionEvent evt) {
609                    if (inErrorState) {
610                        verifyAddButton.setEnabled(true);
611                        verifyServer.setEnabled(true);
612                        inErrorState = false;
613                        resetBadFields();
614                    }
615                }
616            };
617    
618            imageBox.setText("Image");
619            imageBox.addActionListener(typeInputListener);
620            typePanel.add(imageBox);
621    
622            pointBox.setText("Point");
623            pointBox.addActionListener(typeInputListener);
624            typePanel.add(pointBox);
625    
626            gridBox.setText("Grid");
627            gridBox.addActionListener(typeInputListener);
628            typePanel.add(gridBox);
629    
630            textBox.setText("Text");
631            textBox.addActionListener(typeInputListener);
632            typePanel.add(textBox);
633    
634            navBox.setText("Navigation");
635            navBox.addActionListener(typeInputListener);
636            typePanel.add(navBox);
637    
638            radarBox.setText("Radar");
639            radarBox.addActionListener(typeInputListener);
640            typePanel.add(radarBox);
641    
642            statusPanel.setBorder(javax.swing.BorderFactory.createTitledBorder("Status"));
643    
644            statusLabel.setText("Please provide the address of a remote ADDE server.");
645    
646            javax.swing.GroupLayout statusPanelLayout = new javax.swing.GroupLayout(statusPanel);
647            statusPanel.setLayout(statusPanelLayout);
648            statusPanelLayout.setHorizontalGroup(
649                statusPanelLayout.createParallelGroup(LEADING)
650                .addGroup(statusPanelLayout.createSequentialGroup()
651                    .addContainerGap()
652                    .addComponent(statusLabel)
653                    .addContainerGap(154, Short.MAX_VALUE))
654            );
655            statusPanelLayout.setVerticalGroup(
656                statusPanelLayout.createParallelGroup(LEADING)
657                .addGroup(statusPanelLayout.createSequentialGroup()
658                    .addComponent(statusLabel)
659                    .addContainerGap(DEFAULT_SIZE, Short.MAX_VALUE))
660            );
661    
662            if (initEntries == RemoteAddeEntry.INVALID_ENTRIES) {
663                verifyAddButton.setText("Verify and Add Server");
664            } else {
665                verifyAddButton.setText("Verify and Save Changes");
666            }
667            verifyAddButton.addActionListener(new java.awt.event.ActionListener() {
668                public void actionPerformed(java.awt.event.ActionEvent evt) {
669                    if (initEntries == RemoteAddeEntry.INVALID_ENTRIES)
670                        verifyAddButtonActionPerformed(evt);
671                    else
672                        verifyEditButtonActionPerformed(evt);
673                }
674            });
675    
676            if (initEntries == RemoteAddeEntry.INVALID_ENTRIES) {
677                verifyServer.setText("Verify Server");
678            } else {
679                verifyServer.setText("Verify Changes");
680            }
681            verifyServer.addActionListener(new java.awt.event.ActionListener() {
682                public void actionPerformed(java.awt.event.ActionEvent evt) {
683                    verifyServerActionPerformed(evt);
684                }
685            });
686    
687            if (initEntries == RemoteAddeEntry.INVALID_ENTRIES) {
688                addServer.setText("Add Server");
689            } else {
690                addServer.setText("Save Changes");
691            }
692            addServer.addActionListener(new java.awt.event.ActionListener() {
693                public void actionPerformed(java.awt.event.ActionEvent evt) {
694                    if (initEntries == RemoteAddeEntry.INVALID_ENTRIES) {
695                        addServerActionPerformed(evt);
696                    } else {
697                        editServerActionPerformed(evt);
698                    }
699                }
700            });
701    
702            cancelButton.setText("Cancel");
703            cancelButton.addActionListener(new java.awt.event.ActionListener() {
704                public void actionPerformed(java.awt.event.ActionEvent evt) {
705                    cancelButtonActionPerformed(evt);
706                }
707            });
708    
709            javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
710            getContentPane().setLayout(layout);
711            layout.setHorizontalGroup(
712                layout.createParallelGroup(LEADING)
713                .addGroup(layout.createSequentialGroup()
714                    .addContainerGap()
715                    .addGroup(layout.createParallelGroup(LEADING)
716                        .addComponent(statusPanel, DEFAULT_SIZE, DEFAULT_SIZE, Short.MAX_VALUE)
717                        .addComponent(typePanel, 0, 0, Short.MAX_VALUE)
718                        .addComponent(entryPanel, DEFAULT_SIZE, DEFAULT_SIZE, Short.MAX_VALUE)
719                        .addGroup(layout.createSequentialGroup()
720                            .addComponent(verifyAddButton)
721                            .addPreferredGap(RELATED)
722                            .addComponent(verifyServer)
723                            .addPreferredGap(RELATED)
724                            .addComponent(addServer)
725                            .addPreferredGap(RELATED)
726                            .addComponent(cancelButton)))
727                    .addContainerGap())
728            );
729            layout.setVerticalGroup(
730                layout.createParallelGroup(LEADING)
731                .addGroup(layout.createSequentialGroup()
732                    .addContainerGap()
733                    .addComponent(entryPanel, PREFERRED_SIZE, DEFAULT_SIZE, PREFERRED_SIZE)
734                    .addPreferredGap(UNRELATED)
735                    .addComponent(typePanel, PREFERRED_SIZE, 57, PREFERRED_SIZE)
736                    .addGap(18, 18, 18)
737                    .addComponent(statusPanel, PREFERRED_SIZE, DEFAULT_SIZE, PREFERRED_SIZE)
738                    .addGap(18, 18, 18)
739                    .addGroup(layout.createParallelGroup(BASELINE)
740                        .addComponent(verifyServer)
741                        .addComponent(addServer)
742                        .addComponent(cancelButton)
743                        .addComponent(verifyAddButton))
744                    .addContainerGap(17, Short.MAX_VALUE))
745            );
746    
747            if (initEntries != null && !RemoteAddeEntry.INVALID_ENTRIES.equals(initEntries)) {
748                RemoteAddeEntry initEntry = initEntries.get(0);
749                boolean hasSystemEntry = false;
750                for (RemoteAddeEntry entry : initEntries) {
751                    if (entry.getEntrySource() == EntrySource.SYSTEM) {
752                        initEntry = entry;
753                        hasSystemEntry = true;
754                        break;
755                    }
756                }
757                serverField.setText(initEntry.getAddress());
758                datasetField.setText(initEntry.getGroup());
759    
760                if (!RemoteAddeEntry.DEFAULT_ACCOUNT.equals(initEntry.getAccount())) {
761                    acctBox.setSelected(true);
762                    userField.setEnabled(true);
763                    userField.setText(initEntry.getAccount().getUsername());
764                    projField.setEnabled(true);
765                    projField.setText(initEntry.getAccount().getProject());
766                }
767    
768                if (hasSystemEntry) {
769                    serverField.setEnabled(false);
770                    datasetField.setEnabled(false);
771                    acctBox.setEnabled(false);
772                    userField.setEnabled(false);
773                    projField.setEnabled(false);
774                    capBox.setEnabled(false);
775                }
776    
777                for (RemoteAddeEntry entry : initEntries) {
778                    boolean nonDefaultSource = entry.getEntrySource() != EntrySource.SYSTEM;
779                    if (entry.getEntryType() == EntryType.IMAGE) {
780                        imageBox.setSelected(true);
781                        imageBox.setEnabled(nonDefaultSource);
782                    } else if (entry.getEntryType() == EntryType.POINT) {
783                        pointBox.setSelected(true);
784                        pointBox.setEnabled(nonDefaultSource);
785                    } else if (entry.getEntryType() == EntryType.GRID) {
786                        gridBox.setSelected(true);
787                        gridBox.setEnabled(nonDefaultSource);
788                    } else if (entry.getEntryType() == EntryType.TEXT) {
789                        textBox.setSelected(true);
790                        textBox.setEnabled(nonDefaultSource);
791                    } else if (entry.getEntryType() == EntryType.NAV) {
792                        navBox.setSelected(true);
793                        navBox.setEnabled(nonDefaultSource);
794                    } else if (entry.getEntryType() == EntryType.RADAR) {
795                        radarBox.setSelected(true);
796                        radarBox.setEnabled(nonDefaultSource);
797                    }
798                }
799            }
800            pack();
801        }// </editor-fold>
802    
803        private void acctBoxActionPerformed(java.awt.event.ActionEvent evt) {
804            assert SwingUtilities.isEventDispatchThread();
805            resetBadFields();
806            boolean enabled = acctBox.isSelected();
807            userField.setEnabled(enabled);
808            projField.setEnabled(enabled);
809            verifyAddButton.setEnabled(true);
810            verifyServer.setEnabled(true);
811        }
812    
813        private void capBoxActionPerformed(java.awt.event.ActionEvent evt) {
814            assert SwingUtilities.isEventDispatchThread();
815            boolean forceCaps = capBox.isSelected();
816            datasetField.setUppercase(forceCaps);
817            userField.setUppercase(forceCaps);
818            setForceMcxCaps(forceCaps);
819            if (!forceCaps) {
820                return;
821            }
822            datasetField.setText(datasetField.getText().toUpperCase());
823            userField.setText(userField.getText().toUpperCase());
824        }
825    
826        private void verifyAddButtonActionPerformed(java.awt.event.ActionEvent evt) {
827            verifyInput();
828            if (!anyBadFields()) {
829                setEditorAction(EditorAction.ADDED_VERIFIED);
830                addEntry();
831            } else {
832                inErrorState = true;
833                verifyAddButton.setEnabled(false);
834                verifyServer.setEnabled(false);
835            }
836        }
837    
838        private void verifyEditButtonActionPerformed(java.awt.event.ActionEvent evt) {
839            verifyInput();
840            if (!anyBadFields()) {
841                setEditorAction(EditorAction.EDITED_VERIFIED);
842                editEntry();
843            } else {
844                inErrorState = true;
845                verifyAddButton.setEnabled(false);
846                verifyServer.setEnabled(false);
847            }
848        }
849    
850        private void cancelButtonActionPerformed(java.awt.event.ActionEvent evt) {
851            setEditorAction(EditorAction.CANCELLED);
852            disposeDisplayable(false);
853        }
854    
855        private void formWindowClosed(java.awt.event.WindowEvent evt) {
856            setEditorAction(EditorAction.CANCELLED);
857            disposeDisplayable(false);
858        }
859    
860        private void verifyServerActionPerformed(java.awt.event.ActionEvent evt) {
861            verifyInput();
862            if (anyBadFields()) {
863                // save poll widget state
864                // toggle a "listen for *any* input event" switch to on
865    //            invalidEntries.clear();
866    //            invalidEntries.addAll(pollWidgets(false));
867                inErrorState = true;
868                verifyAddButton.setEnabled(false);
869                verifyServer.setEnabled(false);
870            }
871        }
872    
873        private void addServerActionPerformed(java.awt.event.ActionEvent evt) {
874            setEditorAction(EditorAction.ADDED);
875            addEntry();
876        }
877    
878        private void editServerActionPerformed(java.awt.event.ActionEvent evt) {
879            setEditorAction(EditorAction.EDITED);
880            editEntry();
881        }
882    
883        private void reactToValueChanges() {
884            assert SwingUtilities.isEventDispatchThread();
885            if (inErrorState) {
886                verifyAddButton.setEnabled(true);
887                verifyServer.setEnabled(true);
888                inErrorState = false;
889                resetBadFields();
890            }
891        }
892    
893        /**
894         * Attempt to verify a {@link Set} of {@link RemoteAddeEntry}s. Useful for
895         * checking a {@literal "MCTABLE.TXT"} after importing.
896         * 
897         * @param entries {@code Set} of remote ADDE entries to validate. Cannot 
898         * be {@code null}.
899         * 
900         * @return {@code Set} of {@code RemoteAddeEntry}s that McIDAS-V was able
901         * to connect to. 
902         * 
903         * @throws NullPointerException if {@code entries} is {@code null}.
904         */
905        public Set<RemoteAddeEntry> checkHosts(final Set<RemoteAddeEntry> entries) {
906            Contract.notNull(entries, "entries cannot be null");
907            Set<RemoteAddeEntry> goodEntries = newLinkedHashSet();
908            Set<String> checkedHosts = newLinkedHashSet();
909            Map<String, Boolean> hostStatus = newMap();
910            for (RemoteAddeEntry entry : entries) {
911                String host = entry.getAddress();
912                if (hostStatus.get(host) == Boolean.FALSE) {
913                    continue;
914                } else if (hostStatus.get(host) == Boolean.TRUE) {
915                    goodEntries.add(entry);
916                } else {
917                    checkedHosts.add(host);
918                    if (RemoteAddeEntry.checkHost(entry)) {
919                        goodEntries.add(entry);
920                        hostStatus.put(host, Boolean.TRUE);
921                    } else {
922                        hostStatus.put(host, Boolean.FALSE);
923                    }
924                }
925            }
926            return goodEntries;
927        }
928    
929        public Set<RemoteAddeEntry> checkHosts2(final Set<RemoteAddeEntry> entries) {
930          Contract.notNull(entries, "entries cannot be null");
931          if (entries.isEmpty()) {
932              return Collections.emptySet();
933          }
934          
935          Set<RemoteAddeEntry> verified = newLinkedHashSet(entries.size());
936          Set<String> hosts = newLinkedHashSet(entries.size());
937    //      Set<String> validHosts = newLinkedHashSet(entries.size());
938          
939          ExecutorService exec = Executors.newFixedThreadPool(POOL);
940          CompletionService<StatusWrapper> ecs = new ExecutorCompletionService<StatusWrapper>(exec);
941          for (RemoteAddeEntry entry : entries) {
942              ecs.submit(new VerifyHostTask(new StatusWrapper(entry)));
943          }
944    
945          try {
946              for (int i = 0; i < entries.size(); i++) {
947                  StatusWrapper pairing = ecs.take().get();
948                  RemoteAddeEntry entry = pairing.getEntry();
949                  AddeStatus status = pairing.getStatus();
950    //              setStatus(entry.getAddress()+": attempting to connect...");
951    //              statuses.add(status);
952    //              entry2Status.put(entry, status);
953                  if (status == AddeStatus.OK) {
954                      verified.add(entry);
955    //                  setStatus("Found host name "+entry.getAddress());
956                  }
957              }
958          } catch (InterruptedException e) {
959              LogUtil.logException("interrupted while checking ADDE entries", e);
960          } catch (ExecutionException e) {
961              LogUtil.logException("ADDE validation execution error", e);
962          } finally {
963              exec.shutdown();
964          }
965          return verified;
966        }
967    //    public Set<RemoteAddeEntry> checkHosts2(final Set<RemoteAddeEntry> entries) {
968    //        Contract.notNull(entries, "entries cannot be null");
969    //        if (entries.isEmpty()) {
970    //            return Collections.emptySet();
971    //        }
972    //        
973    //        Set<RemoteAddeEntry> verified = newLinkedHashSet(entries.size());
974    //        ExecutorService exec = Executors.newFixedThreadPool(POOL);
975    //        CompletionService<StatusWrapper> ecs = new ExecutorCompletionService<StatusWrapper>(exec);
976    ////        Map<RemoteAddeEntry, AddeStatus> entry2Status = new LinkedHashMap<RemoteAddeEntry, AddeStatus>(entries.size());
977    //        
978    //        for (RemoteAddeEntry entry : entries) {
979    //            StatusWrapper check = new StatusWrapper(entry);
980    //            ecs.submit(new VerifyHostTask(check));
981    //        }
982    //        
983    //        try {
984    //            for (int i = 0; i < entries.size(); i++) {
985    //                StatusWrapper pairing = ecs.take().get();
986    //                RemoteAddeEntry entry = pairing.getEntry();
987    //                AddeStatus status = pairing.getStatus();
988    //                setStatus(entry.getAddress()+": attempting to connect...");
989    //                statuses.add(status);
990    ////                entry2Status.put(entry, status);
991    //                if (status == AddeStatus.OK) {
992    //                    verified.add(entry);
993    ////                    setStatus("Found host name "+entry.getAddress());
994    //                }
995    //            }
996    //        } catch (InterruptedException e) {
997    //            
998    //        } catch (ExecutionException e) {
999    //            
1000    //        } finally {
1001    //            exec.shutdown();
1002    //        }
1003    //
1004    //        if (statuses.contains(AddeStatus.BAD_SERVER)) {
1005    //            setStatus("Could not connect to the server.");
1006    //            setBadField(serverField, true);
1007    //        } else {
1008    //            setStatus("Connected to server.");
1009    //        }
1010    ////        
1011    ////        if (!statuses.contains(AddeStatus.OK)) {
1012    ////            if (statuses.contains(AddeStatus.BAD_ACCOUNTING)) {
1013    ////                setStatus("Incorrect accounting information.");
1014    ////                setBadField(userField, true);
1015    ////                setBadField(projField, true);
1016    ////            } else if (statuses.contains(AddeStatus.BAD_GROUP)) {
1017    ////                setStatus("Dataset does not appear to be valid.");
1018    ////                setBadField(datasetField, true);
1019    ////            } else if (statuses.contains(AddeStatus.BAD_SERVER)) {
1020    ////                setStatus("Could not connect to the ADDE server.");
1021    ////                setBadField(serverField, true);
1022    ////            } else {
1023    ////                logger.warn("guru meditation error: statuses={}", statuses);
1024    ////            }
1025    ////        } else {
1026    ////            setStatus("Finished verifying.");
1027    ////        }
1028    //
1029    //        return verified;
1030    //    }
1031        
1032        public Set<RemoteAddeEntry> checkGroups(final Set<RemoteAddeEntry> entries) {
1033            Contract.notNull(entries, "entries cannot be null");
1034            if (entries.isEmpty()) {
1035                return Collections.emptySet();
1036            }
1037    
1038            Set<RemoteAddeEntry> verified = newLinkedHashSet(entries.size());
1039            Collection<AddeStatus> statuses = EnumSet.noneOf(AddeStatus.class);
1040            ExecutorService exec = Executors.newFixedThreadPool(POOL);
1041            CompletionService<StatusWrapper> ecs = new ExecutorCompletionService<StatusWrapper>(exec);
1042            Map<RemoteAddeEntry, AddeStatus> entry2Status = new LinkedHashMap<RemoteAddeEntry, AddeStatus>(entries.size());
1043    
1044            // submit new verification tasks to the pool's queue ... (apologies for the pun?)
1045            for (RemoteAddeEntry entry : entries) {
1046                StatusWrapper pairing = new StatusWrapper(entry);
1047                ecs.submit(new VerifyEntryTask(pairing));
1048            }
1049    
1050            // use completion service magic to only deal with finished verification tasks
1051            try {
1052                for (int i = 0; i < entries.size(); i++) {
1053                    StatusWrapper pairing = ecs.take().get();
1054                    RemoteAddeEntry entry = pairing.getEntry();
1055                    AddeStatus status = pairing.getStatus();
1056                    setStatus(entry.getEntryText()+": attempting verification...");
1057                    statuses.add(status);
1058                    entry2Status.put(entry, status);
1059                    if (status == AddeStatus.OK) {
1060                        verified.add(entry);
1061                        setStatus("Found accessible "+entry.getEntryType().toString().toLowerCase()+" data.");
1062                    }
1063                }
1064            } catch (InterruptedException e) {
1065                LogUtil.logException("interrupted while checking ADDE entries", e);
1066            } catch (ExecutionException e) {
1067                LogUtil.logException("ADDE validation execution error", e);
1068            } finally {
1069                exec.shutdown();
1070            }
1071    
1072            if (!statuses.contains(AddeStatus.OK)) {
1073                if (statuses.contains(AddeStatus.BAD_ACCOUNTING)) {
1074                    setStatus("Incorrect accounting information.");
1075                    setBadField(userField, true);
1076                    setBadField(projField, true);
1077                } else if (statuses.contains(AddeStatus.BAD_GROUP)) {
1078                    setStatus("Dataset does not appear to be valid.");
1079                    setBadField(datasetField, true);
1080                } else if (statuses.contains(AddeStatus.BAD_SERVER)) {
1081                    setStatus("Could not connect to the ADDE server.");
1082                    setBadField(serverField, true);
1083                } else {
1084                    logger.warn("guru meditation error: statuses={}", statuses);
1085                }
1086            } else {
1087                setStatus("Finished verifying.");
1088            }
1089            return verified;
1090        }
1091    
1092        private static Map<RemoteAddeEntry, AddeStatus> bulkPut(final Collection<RemoteAddeEntry> entries, final AddeStatus status) {
1093            Map<RemoteAddeEntry, AddeStatus> map = new LinkedHashMap<RemoteAddeEntry, AddeStatus>(entries.size());
1094            for (RemoteAddeEntry entry : entries) {
1095                map.put(entry, status);
1096            }
1097            return map;
1098        }
1099    
1100        /**
1101         * Associates a {@link RemoteAddeEntry} with one of the states from 
1102         * {@link AddeStatus}.
1103         */
1104        private static class StatusWrapper {
1105            /** */
1106            private final RemoteAddeEntry entry;
1107    
1108            /** Current {@literal "status"} of {@link #entry}. */
1109            private AddeStatus status;
1110    
1111            /**
1112             * Builds an entry/status pairing.
1113             * 
1114             * @param entry The {@code RemoteAddeEntry} to wrap up.
1115             * 
1116             * @throws NullPointerException if {@code entry} is {@code null}.
1117             */
1118            public StatusWrapper(final RemoteAddeEntry entry) {
1119                notNull(entry, "cannot create a entry/status pair with a null descriptor");
1120                this.entry = entry;
1121            }
1122    
1123            /**
1124             * Set the {@literal "status"} of this {@link #entry} to a given 
1125             * {@link AddeStatus}.
1126             * 
1127             * @param status New status of {@code entry}.
1128             */
1129            public void setStatus(AddeStatus status) {
1130                this.status = status;
1131            }
1132    
1133            /**
1134             * Returns the current {@literal "status"} of {@link #entry}.
1135             * 
1136             * @return One of {@link AddeStatus}.
1137             */
1138            public AddeStatus getStatus() {
1139                return status;
1140            }
1141    
1142            /**
1143             * Returns the {@link RemoteAddeEntry} stored in this wrapper.
1144             * 
1145             * @return {@link #entry}
1146             */
1147            public RemoteAddeEntry getEntry() {
1148                return entry;
1149            }
1150        }
1151    
1152        /**
1153         * Represents an ADDE entry verification task. These are executed asynchronously 
1154         * by the completion service within {@link RemoteEntryEditor#checkGroups(Set)}.
1155         */
1156        private class VerifyEntryTask implements Callable<StatusWrapper> {
1157            private final StatusWrapper entryStatus;
1158            public VerifyEntryTask(final StatusWrapper descStatus) {
1159                notNull(descStatus, "cannot verify or set status of a null descriptor/status pair");
1160                this.entryStatus = descStatus;
1161            }
1162    
1163            public StatusWrapper call() throws Exception {
1164                entryStatus.setStatus(RemoteAddeEntry.checkEntry(entryStatus.getEntry()));
1165                return entryStatus;
1166            }
1167        }
1168        
1169        private class VerifyHostTask implements Callable<StatusWrapper> {
1170            private final StatusWrapper entryStatus;
1171            public VerifyHostTask(final StatusWrapper descStatus) {
1172                entryStatus = notNull(descStatus, "cannot verify or set status of a null descriptor/status pair");
1173            }
1174            public StatusWrapper call() throws Exception {
1175                boolean validHost = RemoteAddeEntry.checkHost(entryStatus.getEntry());
1176                if (validHost) {
1177                    entryStatus.setStatus(AddeStatus.OK);
1178                } else {
1179                    entryStatus.setStatus(AddeStatus.BAD_SERVER);
1180                }
1181                return entryStatus;
1182            }
1183        }
1184    
1185        // Variables declaration - do not modify
1186        private javax.swing.JCheckBox acctBox;
1187        private javax.swing.JButton addServer;
1188        private javax.swing.JButton cancelButton;
1189        private javax.swing.JCheckBox capBox;
1190        private McVTextField datasetField;
1191        private javax.swing.JLabel datasetLabel;
1192        private javax.swing.JPanel entryPanel;
1193        private javax.swing.JCheckBox gridBox;
1194        private javax.swing.JCheckBox imageBox;
1195        private javax.swing.JCheckBox navBox;
1196        private javax.swing.JCheckBox pointBox;
1197        private javax.swing.JTextField projField;
1198        private javax.swing.JLabel projLabel;
1199        private javax.swing.JCheckBox radarBox;
1200        private javax.swing.JTextField serverField;
1201        private javax.swing.JLabel serverLabel;
1202        private javax.swing.JLabel statusLabel;
1203        private javax.swing.JPanel statusPanel;
1204        private javax.swing.JCheckBox textBox;
1205        private javax.swing.JPanel typePanel;
1206        private McVTextField userField;
1207        private javax.swing.JLabel userLabel;
1208        private javax.swing.JButton verifyAddButton;
1209        private javax.swing.JButton verifyServer;
1210        // End of variables declaration
1211    }