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 }