1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License, Version 1.0 only
   6  * (the "License").  You may not use this file except in compliance
   7  * with the License.
   8  *
   9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
  10  * or http://www.opensolaris.org/os/licensing.
  11  * See the License for the specific language governing permissions
  12  * and limitations under the License.
  13  *
  14  * When distributing Covered Code, include this CDDL HEADER in each
  15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  16  * If applicable, add the following below this CDDL HEADER, with the
  17  * fields enclosed by brackets "[]" replaced with your own identifying
  18  * information: Portions Copyright [yyyy] [name of copyright owner]
  19  *
  20  * CDDL HEADER END
  21  */
  22 /*
  23  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
  24  * Use is subject to license terms.
  25  */
  26 package com.sun.dhcpmgr.client;
  27 
  28 import com.sun.dhcpmgr.ui.*;
  29 import com.sun.dhcpmgr.data.*;
  30 import com.sun.dhcpmgr.bridge.BridgeException;
  31 import com.sun.dhcpmgr.bridge.NoEntryException;
  32 import com.sun.dhcpmgr.bridge.ExistsException;
  33 import com.sun.dhcpmgr.bridge.HostExistsException;
  34 import com.sun.dhcpmgr.bridge.NoTableException;
  35 import com.sun.dhcpmgr.server.DhcpNetMgr;
  36 
  37 import javax.swing.*;
  38 import javax.swing.table.*;
  39 import javax.swing.event.*;
  40 import javax.swing.border.*;
  41 
  42 import java.awt.*;
  43 import java.awt.event.*;
  44 import java.util.*;
  45 import java.text.MessageFormat;
  46 import java.net.*;
  47 
  48 /**
  49  * A wizard to configure a group of addresses.
  50  */
  51 public class AddressWizard extends Wizard {
  52     private Network network;
  53     private int number = 10;
  54     private String comment = "";
  55     private String server = DataManager.get().getShortServerName();
  56     private IPAddress serverIP;
  57     private IPAddress startAddress;
  58     private String macro = DataManager.get().getShortServerName();
  59     private boolean unusable = false;
  60     private boolean dynamic = true;
  61     private WizardTableModel addressTableModel;
  62     private Macro noMacro;
  63 
  64     class Address {
  65         IPAddress addr;
  66         String name;
  67 
  68         public Address() {
  69             addr = null;
  70             name = "";
  71         }
  72 
  73         public Address(int a, String n) {
  74             name = n;
  75             setAddr(a);
  76         }
  77 
  78         public Address(String a, String n) {
  79             name = n;
  80             setAddr(a);
  81         }
  82 
  83         public void setAddr(int a) {
  84             addr = new IPAddress(a);
  85         }
  86 
  87         public void setAddr(String a) {
  88             try {
  89                 addr = new IPAddress(a);
  90             } catch (ValidationException e) {
  91                 // Do nothing
  92             }
  93         }
  94 
  95         public String toString() {
  96             return addr.getHostAddress();
  97         }
  98     }
  99 
 100     class WizardTableModel extends AbstractTableModel {
 101         private Vector addrs = new Vector();
 102 
 103         public int getRowCount() {
 104             return addrs.size();
 105         }
 106 
 107         public int getColumnCount() {
 108             return 2;
 109         }
 110 
 111         public Object getValueAt(int row, int column) {
 112             if (column == 0) {
 113                 return ((Address)addrs.elementAt(row)).addr;
 114             } else {
 115                 return ((Address)addrs.elementAt(row)).name;
 116             }
 117         }
 118 
 119         public Class getColumnClass(int column) {
 120             if (column == 0) {
 121                 return IPAddress.class;
 122             } else {
 123                 return String.class;
 124             }
 125         }
 126 
 127         public String getColumnName(int column) {
 128             if (column == 0) {
 129                 return ResourceStrings.getString("address_column");
 130             } else {
 131                 return ResourceStrings.getString("client_name_column");
 132             }
 133         }
 134 
 135         public long generateAddresses() {
 136             if (!network.containsAddress(startAddress)) {
 137                 return 0;
 138             }
 139 
 140             int net = network.getAddress().intValue();
 141             int mask = network.getMask().intValue();
 142             int start = startAddress.intValue();
 143 
 144             addrs.removeAllElements();
 145             long max = (long)(net + ~mask) & 0xffffffffL;
 146             int count = 0;
 147             int index = start - net;
 148             if (index == 0) {
 149                 // Don't try allocating the network address as a client address
 150                 ++index;
 151             }
 152             DhcpClientRecord [] clients = null;
 153             try {
 154                 /*
 155                  * Sort the data so we can generate the list of addresses
 156                  * with a minimal number of comparisons here.  First, though,
 157                  * clone the array so that sorting won't affect the original
 158                  * data set and throw off the main display.
 159                  */
 160                 clients = (DhcpClientRecord [])DataManager.get().getClients(
 161                     network.getAddress().toString(), false).clone();
 162                 Arrays.sort(clients);
 163             } catch (Throwable e) {
 164                 // XXX What to do here???
 165                 e.printStackTrace();
 166             }
 167             int base = 0;
 168             long searchAddress = 0;
 169             while (count < number) {
 170                 long address = (long)(net + index) & 0xffffffffL;
 171                 if (address == max) {
 172                     // We finished searching before satisfying the request
 173                     break;
 174                 }
 175                 /*
 176                  * If clients == null then this is an empty network,
 177                  * so searching for holes is unnecessary
 178                  */
 179                 if (clients != null) {
 180                     // Advance search pointer past lower-numbered addresses
 181                     while ((base < clients.length)
 182                             && ((searchAddress =
 183                             clients[base].getBinaryAddress()) < address)) {
 184                         ++base;
 185                     }
 186                 }
 187                 if (searchAddress != address) {
 188                     // found an empty slot; create the address
 189                     addrs.addElement(new Address((int)address, ""));
 190                     ++count;
 191                 }
 192                 ++index;
 193             }
 194 
 195             // Inform UI that the data is ready.
 196             fireTableDataChanged();
 197             return count;
 198         }
 199 
 200         public Address getAddressAt(int index) {
 201             return (Address)addrs.elementAt(index);
 202         }
 203     }
 204 
 205     // This step selects the number of addresses and a comment
 206     class NumberStep implements WizardStep {
 207         private Box stepBox;
 208         private IntegerField addressCount;
 209         private JTextField commentField;
 210 
 211         public NumberStep() {
 212             stepBox = Box.createVerticalBox();
 213 
 214             // Explanatory text at the top
 215             stepBox.add(Wizard.createTextArea(
 216                 ResourceStrings.getString("add_wiz_explain"), 4, 45));
 217             stepBox.add(Box.createVerticalStrut(10));
 218             stepBox.add(Box.createVerticalGlue());
 219 
 220             // Get the number of addresses to create
 221             stepBox.add(Wizard.createTextArea(
 222                 ResourceStrings.getString("add_wiz_count_explain"), 1, 45));
 223 
 224             Mnemonic mnCount =
 225                 new Mnemonic(ResourceStrings.getString("add_wiz_count_label"));
 226             JLabel label = new JLabel(mnCount.getString());
 227             addressCount = new IntegerField(); // Ensure numeric input
 228             addressCount.setMaximumSize(addressCount.getPreferredSize());
 229 
 230             label.setLabelFor(addressCount);
 231             label.setToolTipText(mnCount.getString());
 232             label.setDisplayedMnemonic(mnCount.getMnemonic());
 233 
 234             Box box = Box.createHorizontalBox();
 235             box.add(Box.createHorizontalStrut(10));
 236             box.add(label);
 237             box.add(Box.createHorizontalStrut(5));
 238             box.add(addressCount);
 239             box.add(Box.createHorizontalGlue());
 240             stepBox.add(box);
 241 
 242             stepBox.add(Box.createVerticalStrut(10));
 243             stepBox.add(Box.createVerticalGlue());
 244             stepBox.add(Wizard.createTextArea(
 245                 ResourceStrings.getString("add_wiz_comment_explain"), 2, 45));
 246 
 247             // Let user supply a comment
 248             Mnemonic mnComm =
 249                 new Mnemonic(ResourceStrings.getString(
 250                     "add_wiz_comment_label"));
 251             label = new JLabel(mnComm.getString());
 252             commentField = new JTextField("", 20);
 253 
 254             label.setLabelFor(commentField);
 255             label.setToolTipText(mnComm.getString());
 256             label.setDisplayedMnemonic(mnComm.getMnemonic());
 257 
 258             commentField.setMaximumSize(commentField.getPreferredSize());
 259             box = Box.createHorizontalBox();
 260             box.add(Box.createHorizontalStrut(10));
 261             box.add(label);
 262             box.add(Box.createHorizontalStrut(5));
 263             box.add(commentField);
 264             stepBox.add(box);
 265             stepBox.add(Box.createVerticalGlue());
 266 
 267             /*
 268              * This listener ensures that the forward button is enabled only
 269              * when there is a count of addresses in the addressCount field.
 270              */
 271             addressCount.getDocument().addDocumentListener(
 272                     new DocumentListener() {
 273                 public void insertUpdate(DocumentEvent e) {
 274                     setForwardEnabled(e.getDocument().getLength() != 0);
 275                 }
 276                 public void changedUpdate(DocumentEvent e) {
 277                     insertUpdate(e);
 278                 }
 279                 public void removeUpdate(DocumentEvent e) {
 280                     insertUpdate(e);
 281                 }
 282             });
 283         }
 284 
 285         public String getDescription() {
 286             return ResourceStrings.getString("add_wiz_number_desc");
 287         }
 288 
 289         public Component getComponent() {
 290             return stepBox;
 291         }
 292 
 293         public void setActive(int direction) {
 294             addressCount.setValue(number);
 295             commentField.setText(comment);
 296             setForwardEnabled(addressCount.getValue() != 0);
 297         }
 298 
 299         public boolean setInactive(int direction) {
 300             number = addressCount.getValue();
 301             if (number == 0) {
 302                 /*
 303                  *  Going forward with 0 addresses makes no sense,
 304                  * display error and veto the move.
 305                  */
 306                 JOptionPane.showMessageDialog(AddressWizard.this,
 307                     ResourceStrings.getString("add_wiz_count_error"),
 308                     ResourceStrings.getString("error_message"),
 309                     JOptionPane.ERROR_MESSAGE);
 310                 return false;
 311             }
 312             comment = commentField.getText();
 313             return true;
 314         }
 315     }
 316 
 317     // This step selects the server and starting address
 318     class ServerStep implements WizardStep {
 319         private Box stepBox;
 320         private IPAddressField startField;
 321         private HostnameField serverField;
 322 
 323         public ServerStep() {
 324             stepBox = Box.createVerticalBox();
 325 
 326             // Explanatory text at the top
 327             stepBox.add(Wizard.createTextArea(
 328                 ResourceStrings.getString("add_wiz_server_explain"), 1, 45));
 329 
 330             // Server to own these addresses
 331             Mnemonic mnMan =
 332                 new Mnemonic(ResourceStrings.getString("add_wiz_server_label"));
 333             JLabel jl = new JLabel(mnMan.getString());
 334             Box box = Box.createHorizontalBox();
 335             box.add(jl);
 336             box.add(Box.createHorizontalStrut(5));
 337             serverField = new HostnameField("", 15);
 338             jl.setLabelFor(serverField);
 339             jl.setToolTipText(mnMan.getString());
 340             jl.setDisplayedMnemonic(mnMan.getMnemonic());
 341 
 342             serverField.setMaximumSize(serverField.getPreferredSize());
 343             box.add(serverField);
 344             box.add(Box.createHorizontalGlue());
 345             stepBox.add(box);
 346 
 347             // Add some spacing
 348             stepBox.add(Box.createVerticalStrut(5));
 349             stepBox.add(Box.createVerticalGlue());
 350 
 351             // Starting address
 352             stepBox.add(Wizard.createTextArea(
 353                 ResourceStrings.getString("add_wiz_start_explain"), 2, 45));
 354             box = Box.createHorizontalBox();
 355 
 356             Mnemonic mnStart =
 357                 new Mnemonic(ResourceStrings.getString("add_wiz_start_label"));
 358             JLabel startLbl = new JLabel(mnStart.getString());
 359             box.add(startLbl);
 360             box.add(Box.createHorizontalStrut(5));
 361             startField = new IPAddressField();  // Ensure it's an IP address
 362             startLbl.setLabelFor(startField);
 363             startLbl.setToolTipText(mnStart.getString());
 364             startLbl.setDisplayedMnemonic(mnStart.getMnemonic());
 365 
 366             startField.setMaximumSize(startField.getPreferredSize());
 367             box.add(startField);
 368             stepBox.add(box);
 369 
 370             DocumentListener docListener = new DocumentListener() {
 371                 public void insertUpdate(DocumentEvent e) {
 372                     setForwardEnabled((startField.getText().length() != 0)
 373                         && (serverField.getText().length() != 0));
 374                 }
 375                 public void changedUpdate(DocumentEvent e) {
 376                     insertUpdate(e);
 377                 }
 378                 public void removeUpdate(DocumentEvent e) {
 379                     insertUpdate(e);
 380                 }
 381             };
 382 
 383             startField.getDocument().addDocumentListener(docListener);
 384             serverField.getDocument().addDocumentListener(docListener);
 385         }
 386 
 387         public String getDescription() {
 388             return ResourceStrings.getString("add_wiz_server_desc");
 389         }
 390 
 391         public Component getComponent() {
 392             return stepBox;
 393         }
 394 
 395         public void setActive(int direction) {
 396             serverField.setText(server);
 397             startField.setValue(startAddress);
 398             setForwardEnabled(true);
 399         }
 400 
 401         public boolean setInactive(int direction) {
 402             if (direction == FORWARD) {
 403                 // Validate that address is on the network we're working on
 404                 IPAddress a = startField.getValue();
 405                 if (a == null) {
 406                     // Not a valid address at all
 407                     MessageFormat form = new MessageFormat(
 408                         ResourceStrings.getString("add_wiz_invalid_address"));
 409                     Object [] args = new Object[1];
 410                     args[0] = startField.getText();
 411                     JOptionPane.showMessageDialog(AddressWizard.this,
 412                         form.format(args),
 413                         ResourceStrings.getString("input_error"),
 414                         JOptionPane.ERROR_MESSAGE);
 415                     return false;
 416                 } else if (!network.containsAddress(a)) {
 417                     // Address is not on network
 418                     MessageFormat form = new MessageFormat(
 419                         ResourceStrings.getString("bad_network_address"));
 420                     Object [] args = new Object[2];
 421                     args[0] = startField.getText();
 422                     args[1] = network.getAddress();
 423                     JOptionPane.showMessageDialog(AddressWizard.this,
 424                         form.format(args),
 425                         ResourceStrings.getString("input_error"),
 426                         JOptionPane.ERROR_MESSAGE);
 427                     return false;
 428                 }
 429                 try {
 430                     serverIP = new IPAddress(serverField.getText());
 431                 } catch (Throwable e) {
 432                     /*
 433                      * Unknown hostname, probably, so put up the message and
 434                      * decline to continue
 435                      */
 436                     MessageFormat form = new MessageFormat(
 437                         ResourceStrings.getString("bad_server_name"));
 438                     Object [] args = new Object[1];
 439                     args[0] = serverField.getText();
 440                     JOptionPane.showMessageDialog(AddressWizard.this,
 441                         form.format(args),
 442                         ResourceStrings.getString("error_message"),
 443                         JOptionPane.ERROR_MESSAGE);
 444                     return false;
 445                 }
 446             }
 447             server = serverField.getText();
 448             startAddress = startField.getValue();
 449             return true;
 450         }
 451     }
 452 
 453     // This step confirms the list of addresses to be generated
 454     class ConfirmStep implements WizardStep {
 455         private JPanel stepPanel;
 456         private JTable addressTable;
 457 
 458         public ConfirmStep() {
 459             stepPanel = new JPanel(new BorderLayout(10, 10));
 460 
 461             // Explanatory text at the top
 462             stepPanel.add(Wizard.createTextArea(
 463                 ResourceStrings.getString("add_wiz_confirm_explain"), 3, 45),
 464                 BorderLayout.NORTH);
 465 
 466             // Label the table
 467             JPanel panel = new JPanel(new BorderLayout());
 468             panel.setBorder(BorderFactory.createEmptyBorder(0, 15, 0, 15));
 469             Mnemonic mnIP =
 470                 new Mnemonic(ResourceStrings.getString(
 471                     "add_wiz_confirm_label"));
 472             JLabel label = new JLabel(mnIP.getString());
 473             panel.add(label, BorderLayout.NORTH);
 474 
 475             // Display the addresses in a table
 476             addressTable = new JTable(addressTableModel);
 477 
 478             label.setLabelFor(addressTable);
 479             label.setToolTipText(mnIP.getString());
 480             label.setDisplayedMnemonic(mnIP.getMnemonic());
 481 
 482             addressTable.setDefaultRenderer(IPAddress.class,
 483                 new ExtendedCellRenderer());
 484 
 485             // Table is not selectable in any way
 486             addressTable.setRowSelectionAllowed(false);
 487             addressTable.setColumnSelectionAllowed(false);
 488             addressTable.setCellSelectionEnabled(false);
 489 
 490             // Wrap in a scroll pane so column headings display
 491             JScrollPane scrollPane = new JScrollPane(addressTable);
 492             panel.add(scrollPane, BorderLayout.CENTER);
 493             stepPanel.add(panel, BorderLayout.CENTER);
 494         }
 495 
 496         public String getDescription() {
 497             return ResourceStrings.getString("add_wiz_confirm_desc");
 498         }
 499 
 500         public Component getComponent() {
 501             return stepPanel;
 502         }
 503 
 504         public void setActive(int direction) {
 505             /*
 506              * If we're activating coming from the previous step,
 507              * generate the address list
 508              */
 509             if (direction == FORWARD) {
 510                 long count = addressTableModel.generateAddresses();
 511                 // Error if no addresses could be generated
 512                 if (count == 0) {
 513                     JOptionPane.showMessageDialog(AddressWizard.this,
 514                         ResourceStrings.getString("add_wiz_none_available"),
 515                         ResourceStrings.getString("error_message"),
 516                         JOptionPane.ERROR_MESSAGE);
 517                     setForwardEnabled(false);
 518                 } else {
 519                     if (count != number) {
 520                         /*
 521                          * Warn if we couldn't generate the number of addresses
 522                          * requested
 523                          */
 524                         MessageFormat form = new MessageFormat(
 525                             ResourceStrings.getString(
 526                             "generate_addresses_warning"));
 527                         Object [] args = new Object[2];
 528                         args[0] = new Long(count);
 529                         args[1] = new Long(number);
 530                         JOptionPane.showMessageDialog(AddressWizard.this,
 531                             form.format(args),
 532                             ResourceStrings.getString("warning"),
 533                             JOptionPane.WARNING_MESSAGE);
 534                     }
 535                     setForwardEnabled(true);
 536                 }
 537             } else {
 538                 setForwardEnabled(true);
 539             }
 540         }
 541 
 542         public boolean setInactive(int direction) {
 543             return true; // Nothing to do when leaving
 544         }
 545     }
 546 
 547     // This step selects the macro and flags
 548     class ConfigureStep implements WizardStep {
 549 
 550         // Model class for the macro list
 551         class MacroListModel extends AbstractListModel
 552                 implements ComboBoxModel {
 553             private Object currentValue;
 554             private Macro data[] = null;
 555 
 556             public int getSize() {
 557                 if (data == null) {
 558                     try {
 559                         // If we don't have data yet, grab currently cached list
 560                         data = DataManager.get().getMacros(false);
 561                     } catch (NoTableException e) {
 562                         // can function without table
 563                     } catch (Throwable e) {
 564                         e.printStackTrace();
 565                     }
 566                 }
 567 
 568                 if (data == null || data.length == 0) {
 569                     return 1;
 570                 } else {
 571                     return data.length+1;
 572                 }
 573             }
 574 
 575             public Object getElementAt(int index) {
 576                 if (data == null) {
 577                     try {
 578                         // If we don't have data yet, grab currently cached list
 579                         data = DataManager.get().getMacros(false);
 580                     } catch (NoTableException e) {
 581                         // can function without table
 582                     } catch (Throwable e) {
 583                         e.printStackTrace();
 584                     }
 585                 }
 586                 if (index == 0) {
 587                     return noMacro.getKey();
 588                 } else {
 589                     return data[index-1].getKey();
 590                 }
 591             }
 592 
 593             public void setSelectedItem(Object anItem) {
 594                 currentValue = noMacro.getKey();
 595                 for (int i = 0; data != null && i < data.length; i++) {
 596                     if (((String)(anItem)).equals(((Macro)data[i]).getKey())) {
 597                         currentValue = anItem;
 598                     }
 599                 }
 600                 fireContentsChanged(this, -1, -1);
 601             }
 602 
 603             public Object getSelectedItem() {
 604                 return currentValue;
 605             }
 606 
 607             public Macro getMacroAt(int index) {
 608                 if (index == 0) {
 609                     return noMacro;
 610                 } else {
 611                     return data[index-1];
 612                 }
 613             }
 614         }
 615 
 616         private Box stepBox;
 617         private JComboBox macroBox;
 618         private MacroListModel macroBoxModel;
 619         private JButton viewButton;
 620         private JCheckBox unusableBox;
 621 
 622         public ConfigureStep() {
 623             stepBox = Box.createVerticalBox();
 624 
 625             // Start with some explanatory text
 626             JComponent component = Wizard.createTextArea(
 627                 ResourceStrings.getString("add_wiz_macro_explain"), 3, 45);
 628             component.setAlignmentX(Component.LEFT_ALIGNMENT);
 629             stepBox.add(component);
 630             // Add some spacing
 631             stepBox.add(Box.createVerticalStrut(10));
 632 
 633             // Let 'em select the macro to use
 634             Mnemonic mnConf =
 635                 new Mnemonic(ResourceStrings.getString("add_wiz_macro_label"));
 636             JLabel label = new JLabel(mnConf.getString());
 637             label.setAlignmentX(Component.LEFT_ALIGNMENT);
 638             stepBox.add(label);
 639             JPanel panel = new JPanel(new FlowLayout(FlowLayout.LEFT));
 640             macroBoxModel = new MacroListModel();
 641             macroBox = new JComboBox(macroBoxModel);
 642 
 643             label.setLabelFor(macroBox);
 644             label.setToolTipText(mnConf.getString());
 645             label.setDisplayedMnemonic(mnConf.getMnemonic());
 646 
 647             panel.add(macroBox);
 648             // Button to view the contents of the selected macro
 649 
 650             Mnemonic mnView =
 651                 new Mnemonic(ResourceStrings.getString("add_wiz_view_button"));
 652             viewButton = new JButton(mnView.getString());
 653             viewButton.setToolTipText(mnView.getString());
 654             viewButton.setMnemonic(mnView.getMnemonic());
 655 
 656             panel.add(viewButton);
 657             panel.setAlignmentX(Component.LEFT_ALIGNMENT);
 658             stepBox.add(panel);
 659 
 660             // Give the option to mark them unusable for now
 661             component = Wizard.createTextArea(
 662                 ResourceStrings.getString("add_wiz_flag_explain"), 2, 45);
 663             component.setAlignmentX(Component.LEFT_ALIGNMENT);
 664             stepBox.add(component);
 665             unusableBox = new JCheckBox(
 666                 ResourceStrings.getString("add_wiz_unusable_label"));
 667             unusableBox.setAlignmentX(Component.LEFT_ALIGNMENT);
 668             stepBox.add(unusableBox);
 669             stepBox.add(Box.createVerticalGlue());
 670 
 671             // When user presses View, show the macro's contents
 672             viewButton.addActionListener(new ActionListener() {
 673                 public void actionPerformed(ActionEvent e) {
 674                     /*
 675                      * viewButton is passed as component relative to which the
 676                      * dialog should be displayed, keeping user more in
 677                      * context with the task.
 678                      */
 679                     ViewMacroDialog d = new ViewMacroDialog(
 680                         AddressWizard.this, viewButton,
 681                         macroBoxModel.getMacroAt(macroBox.getSelectedIndex()));
 682                     d.pack();
 683                     d.setVisible(true);
 684                 }
 685             });
 686         }
 687 
 688         public String getDescription() {
 689             return ResourceStrings.getString("add_wiz_configure_desc");
 690         }
 691 
 692         public Component getComponent() {
 693             return stepBox;
 694         }
 695 
 696         public void setActive(int direction) {
 697             macroBox.setSelectedItem(macro);
 698             unusableBox.setSelected(unusable);
 699             setForwardEnabled(true);
 700         }
 701 
 702         public boolean setInactive(int direction) {
 703             macro = (String)macroBox.getSelectedItem();
 704             unusable = unusableBox.isSelected();
 705             return true;
 706         }
 707     }
 708 
 709     // This step selects the lease type
 710     class LeaseStep implements WizardStep {
 711         private Box stepBox;
 712         private JRadioButton dynamicButton, permanentButton;
 713         private ButtonGroup buttonGroup;
 714 
 715         public LeaseStep() {
 716             stepBox = Box.createVerticalBox();
 717 
 718             // Start with explanatory text
 719             JComponent component = Wizard.createTextArea(
 720                 ResourceStrings.getString("add_wiz_lease_explain"), 0, 45);
 721             component.setAlignmentX(Component.LEFT_ALIGNMENT);
 722             stepBox.add(component);
 723             stepBox.add(Box.createVerticalStrut(10));
 724 
 725             // User has choice of dynamic or permanent leases
 726             Mnemonic mnLease =
 727                 new Mnemonic(ResourceStrings.getString("add_wiz_lease_label"));
 728             JLabel label = new JLabel(mnLease.getString());
 729             JPanel panel = new JPanel(new FieldLayout(10, 0));
 730             label.setToolTipText(mnLease.getString());
 731             label.setDisplayedMnemonic(mnLease.getMnemonic());
 732 
 733             panel.add(FieldLayout.LABEL, label);
 734             buttonGroup = new ButtonGroup();
 735             dynamicButton = new JRadioButton(
 736                 ResourceStrings.getString("dynamic"), true);
 737             buttonGroup.add(dynamicButton);
 738             permanentButton = new JRadioButton(
 739                 ResourceStrings.getString("permanent"), false);
 740             buttonGroup.add(permanentButton);
 741             label.setLabelFor(dynamicButton);
 742             panel.add(FieldLayout.FIELD, dynamicButton);
 743             panel.add(FieldLayout.LABEL, new JLabel(""));
 744             panel.add(FieldLayout.FIELD, permanentButton);
 745             panel.setAlignmentX(Component.LEFT_ALIGNMENT);
 746             stepBox.add(panel);
 747             stepBox.add(Box.createVerticalGlue());
 748         }
 749 
 750         public String getDescription() {
 751             return ResourceStrings.getString("add_wiz_lease_desc");
 752         }
 753 
 754         public Component getComponent() {
 755             return stepBox;
 756         }
 757 
 758         public void setActive(int direction) {
 759             dynamicButton.setSelected(dynamic);
 760             setForwardEnabled(true);
 761         }
 762 
 763         public boolean setInactive(int direction) {
 764             dynamic = dynamicButton.isSelected();
 765             return true;
 766         }
 767     }
 768 
 769     // Last chance to check work before committing to it
 770     class ReviewStep implements WizardStep {
 771         private Box stepBox;
 772         private JPanel panel;
 773         private JTable addressTable;
 774         private JLabel numberLabel;
 775         private JLabel commentLabel;
 776         private JLabel serverLabel;
 777         private JLabel macroLabel;
 778         private JLabel flagLabel;
 779         private JLabel leaseLabel;
 780 
 781         public ReviewStep() {
 782             stepBox = Box.createVerticalBox();
 783             stepBox.add(Wizard.createTextArea(
 784                 ResourceStrings.getString("add_wiz_review_explain"), 4, 45));
 785 
 786             panel = new JPanel(new FieldLayout());
 787             JLabel tmpL;
 788 
 789             tmpL = addLabelMnemonic("add_wiz_count_label");
 790             numberLabel = addField("20");
 791             tmpL.setLabelFor(numberLabel);
 792 
 793             tmpL = addLabelMnemonic("add_wiz_comment_label");
 794             commentLabel = addField("Marketing");
 795             tmpL.setLabelFor(commentLabel);
 796 
 797             tmpL = addLabelMnemonic("add_wiz_server_label");
 798             serverLabel = addField("atlantic");
 799             tmpL.setLabelFor(serverLabel);
 800 
 801             tmpL = addLabelMnemonic("add_wiz_macro_label");
 802             macroLabel = addField("atlantic");
 803             tmpL.setLabelFor(macroLabel);
 804 
 805             tmpL = addLabel("add_wiz_review_unusable");
 806             flagLabel = addField("Yes");
 807             tmpL.setLabelFor(flagLabel);
 808             tmpL.setToolTipText(
 809                 ResourceStrings.getString("add_wiz_review_unusable"));
 810 
 811             tmpL = addLabelMnemonic("add_wiz_lease_label");
 812             leaseLabel = addField(ResourceStrings.getString("dynamic"));
 813             tmpL.setLabelFor(leaseLabel);
 814 
 815             panel.setAlignmentX(Component.LEFT_ALIGNMENT);
 816             stepBox.add(panel);
 817 
 818             stepBox.add(Box.createVerticalStrut(5));
 819 
 820             Mnemonic mnAdd =
 821                 new Mnemonic(ResourceStrings.getString(
 822                     "add_wiz_confirm_label"));
 823             JLabel label = new JLabel(mnAdd.getString());
 824             stepBox.add(label);
 825             stepBox.add(Box.createVerticalStrut(2));
 826             addressTable = new JTable(addressTableModel);
 827             label.setLabelFor(addressTable);
 828             label.setToolTipText(mnAdd.getString());
 829             label.setDisplayedMnemonic(mnAdd.getMnemonic());
 830 
 831             addressTable.setDefaultRenderer(IPAddress.class,
 832                 new ExtendedCellRenderer());
 833 
 834             // Table should not be selectable in any way
 835             addressTable.setRowSelectionAllowed(false);
 836             addressTable.setColumnSelectionAllowed(false);
 837             addressTable.setCellSelectionEnabled(false);
 838             JScrollPane scrollPane = new JScrollPane(addressTable);
 839             Dimension d = addressTable.getPreferredScrollableViewportSize();
 840             d.height = 50;
 841             addressTable.setPreferredScrollableViewportSize(d);
 842             stepBox.add(scrollPane);
 843             stepBox.add(Box.createVerticalGlue());
 844         }
 845 
 846         private JLabel addLabel(String s) {
 847             JLabel l = new JLabel(ResourceStrings.getString(s));
 848             panel.add(FieldLayout.LABEL, l);
 849             return l;
 850         }
 851 
 852         private JLabel addLabelMnemonic(String s) {
 853             Mnemonic mnStr =
 854                 new Mnemonic(ResourceStrings.getString(s));
 855             JLabel l = new JLabel(mnStr.getString());
 856             l.setToolTipText(mnStr.getString());
 857             panel.add(FieldLayout.LABEL, l);
 858             return l;
 859         }
 860 
 861         private JLabel addField(String s) {
 862             JLabel l = new JLabel(s);
 863             l.setForeground(Color.black);
 864             panel.add(FieldLayout.FIELD, l);
 865             return l;
 866         }
 867 
 868         public String getDescription() {
 869             return ResourceStrings.getString("add_wiz_review_desc");
 870         }
 871 
 872         public Component getComponent() {
 873             return stepBox;
 874         }
 875 
 876         public void setActive(int direction) {
 877             // Use number of addresses actually generated, not requested
 878             numberLabel.setText(
 879                 String.valueOf(addressTableModel.getRowCount()));
 880             commentLabel.setText(comment);
 881             serverLabel.setText(server);
 882             macroLabel.setText(macro);
 883             if (unusable) {
 884                 flagLabel.setText(ResourceStrings.getString("yes"));
 885             } else {
 886                 flagLabel.setText(ResourceStrings.getString("no"));
 887             }
 888             if (dynamic) {
 889                 leaseLabel.setText(ResourceStrings.getString("dynamic"));
 890             } else {
 891                 leaseLabel.setText(ResourceStrings.getString("permanent"));
 892             }
 893             setFinishEnabled(true);
 894         }
 895 
 896         public boolean setInactive(int direction) {
 897             // Nothing to do
 898             return true;
 899         }
 900     }
 901 
 902     public AddressWizard(Frame owner, Network net) {
 903         super(owner, "");
 904         setTitle(MessageFormat.format(
 905             ResourceStrings.getString("address_wizard_title"), net.toString()));
 906 
 907         network = net;
 908         startAddress = network.getAddress();
 909 
 910         try {
 911             noMacro = new Macro(ResourceStrings.getString("no_macro_item"));
 912         } catch (ValidationException e) {
 913             // this should never happen!
 914             System.err.println(e.getMessage());
 915         }
 916         addressTableModel = new WizardTableModel();
 917 
 918         // Create steps in order of appearance
 919         addStep(new NumberStep());
 920         addStep(new ServerStep());
 921         addStep(new ConfirmStep());
 922         addStep(new ConfigureStep());
 923         addStep(new LeaseStep());
 924         addStep(new ReviewStep());
 925         showFirstStep();
 926     }
 927 
 928     public void doFinish() {
 929         /*
 930          * Method here is as follows:
 931          * 1. Create a ProgressManager which will apprise user of our progress
 932          * 2. Create a background thread to execute the add operations
 933          * 3. Within the background thread, update the progress monitor
 934          *    as each address is created.
 935          * 4. At completion, the background thread displays the error
 936          *    output, if any, before it invokes one last runnable which pops
 937          *    down and cleans up.
 938          */
 939         // final so that ProgressUpdater can access it
 940         final ProgressManager progress = new ProgressManager(this,
 941             ResourceStrings.getString("add_wiz_progress"), "", 0,
 942             addressTableModel.getRowCount());
 943         final Runnable finisher = new Runnable() {
 944             public void run() {
 945                 reallyFinish();
 946             }
 947         };
 948 
 949         // Here's the thread which does the adds
 950         Thread addThread = new Thread() {
 951             public void run() {
 952                 DhcpNetMgr server = DataManager.get().getDhcpNetMgr();
 953                 // Create a template object which we'll use for all the adds
 954                 DhcpClientRecord rec = new DhcpClientRecord();
 955                 rec.setExpiration(new Date(0));
 956                 rec.setUnusable(unusable);
 957                 rec.setPermanent(!dynamic);
 958                 try {
 959                     rec.setServerIP(serverIP);
 960                 } catch (ValidationException e) {
 961                     // Should never happen as we have a valid IP already
 962                 }
 963                 if (macro.equals(noMacro.getKey())) {
 964                         rec.setMacro("");
 965                 } else {
 966                         rec.setMacro(macro);
 967                 }
 968                 rec.setComment(comment);
 969 
 970                 // This is final so it can be used in the errorDisplay Runnable
 971                 final ErrorTable failedTable = new ErrorTable(
 972                     ResourceStrings.getString("address_column"),
 973                     IPAddress.class);
 974 
 975                 /*
 976                  * For each address, create a client record and possibly a
 977                  * hosts record, log any errors for later consumption.
 978                  */
 979                 for (int i = 0; i < addressTableModel.getRowCount(); ++i) {
 980                     Address addr = addressTableModel.getAddressAt(i);
 981                     try {
 982                         rec.setClientIP(addr.addr);
 983                         rec.setClientName(addr.name);
 984                         server.addClient(rec, network.toString());
 985                         progress.update(i+1, addr.addr.toString());
 986                     } catch (InterruptedException e) {
 987                         SwingUtilities.invokeLater(finisher);
 988                         return;
 989                     } catch (Throwable e) {
 990                         // Pick the best message for the exception thrown
 991                         String msg;
 992                         if (e instanceof ExistsException) {
 993                             msg = ResourceStrings.getString("address_exists");
 994                         } else if (e instanceof HostExistsException) {
 995                             msg = ResourceStrings.getString("host_exists");
 996                         } else {
 997                             msg = e.getMessage();
 998                         }
 999                         failedTable.addError(addr.addr, msg);
1000                     }
1001                 }
1002 
1003                 // If any errors occurred, display them all at once.
1004                 if (!failedTable.isEmpty()) {
1005                     Runnable errorDisplay = new Runnable() {
1006                         public void run() {
1007                             Object [] objs = new Object[2];
1008                             objs[0] =
1009                                 ResourceStrings.getString("add_wiz_error");
1010                             JScrollPane scrollPane =
1011                                 new JScrollPane(failedTable);
1012                             // Resize the table to something kind of small
1013                             Dimension d =
1014                                 failedTable.
1015                                 getPreferredScrollableViewportSize();
1016                             d.height = 80;
1017                             failedTable.setPreferredScrollableViewportSize(d);
1018                             objs[1] = scrollPane;
1019                             JOptionPane.showMessageDialog(AddressWizard.this,
1020                                 objs,
1021                                 ResourceStrings.getString("server_error_title"),
1022                                 JOptionPane.ERROR_MESSAGE);
1023                         }
1024                     };
1025                     try {
1026                         SwingUtilities.invokeAndWait(errorDisplay);
1027                     } catch (Throwable e) {
1028                         e.printStackTrace();
1029                     }
1030                 }
1031                 SwingUtilities.invokeLater(finisher);
1032             }
1033         };
1034         addThread.start();
1035     }
1036 
1037     protected void reallyFinish() {
1038         super.doFinish();
1039     }
1040 
1041     public void doHelp() {
1042         DhcpmgrApplet.showHelp("address_wizard");
1043     }
1044 }