View Javadoc

1   /*
2    * Copyright (c) 1998-2004 The Jgroup Team.
3    *
4    * This program is free software; you can redistribute it and/or modify
5    * it under the terms of the GNU Lesser General Public License version 2 as
6    * published by the Free Software Foundation.
7    *
8    * This program is distributed in the hope that it will be useful,
9    * but WITHOUT ANY WARRANTY; without even the implied warranty of
10   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11   * GNU Lesser General Public License for more details.
12   *
13   * You should have received a copy of the GNU Lesser General Public License
14   * along with this program; if not, write to the Free Software
15   * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
16   *
17   */
18  
19  package jgroup.experiment.gui.views.helpers;
20  
21  import java.awt.Color;
22  import java.awt.GridBagConstraints;
23  import java.awt.GridBagLayout;
24  import java.awt.event.ActionEvent;
25  import java.awt.event.ActionListener;
26  import java.text.ParseException;
27  import java.util.Enumeration;
28  import java.util.Iterator;
29  import java.util.Map;
30  import java.util.TreeMap;
31  import java.util.TreeSet;
32  
33  import javax.swing.BorderFactory;
34  import javax.swing.Box;
35  import javax.swing.BoxLayout;
36  import javax.swing.JButton;
37  import javax.swing.JFormattedTextField;
38  import javax.swing.JLabel;
39  import javax.swing.JOptionPane;
40  import javax.swing.JPanel;
41  import javax.swing.JScrollPane;
42  import javax.swing.JTabbedPane;
43  import javax.swing.JTextField;
44  import javax.swing.JTree;
45  import javax.swing.ScrollPaneConstants;
46  import javax.swing.event.TreeSelectionEvent;
47  import javax.swing.event.TreeSelectionListener;
48  import javax.swing.text.MaskFormatter;
49  import javax.swing.text.NumberFormatter;
50  import javax.swing.tree.DefaultMutableTreeNode;
51  import javax.swing.tree.DefaultTreeModel;
52  import javax.swing.tree.TreePath;
53  import javax.swing.tree.TreeSelectionModel;
54  
55  import jgroup.relacs.config.Domain;
56  import jgroup.relacs.config.ExperimentConfig;
57  import jgroup.relacs.config.Host;
58  
59  import org.w3c.dom.Document;
60  import org.w3c.dom.Element;
61  
62  /**
63   * @author Bjarte Svaeren
64   */
65  public class HostPanelManager
66    implements PanelManager
67  {
68    ////////////////////////////////////////////////////////////////////////////////////////////
69    // Constants
70    ////////////////////////////////////////////////////////////////////////////////////////////
71  
72    private static final String SERVER_TYPE = "Servers";
73    private static final String CLIENT_TYPE = "Clients";
74    
75    
76    ////////////////////////////////////////////////////////////////////////////////////////////
77    // GUI fields
78    ////////////////////////////////////////////////////////////////////////////////////////////
79  
80    // The panel to managed:
81    private JPanel                 hostPanel = null;
82    
83    private JLabel                 domainNameLabel;
84    private JLabel                 domainAddressLabel;
85    private JLabel                 domainPortLabel;
86    private JLabel                 hostNameLabel;
87    private JLabel                 hostPortLabel;
88    private JTextField             domainNameField;
89    private JFormattedTextField    domainAddressField;
90    private JFormattedTextField    domainPortField;
91    private JTextField             hostNameField;
92    private JFormattedTextField    hostPortField;
93    private JPanel                 settingsPanel;
94    
95    private JTree                  hostsTree;
96    private DefaultMutableTreeNode rootNode;
97    private DefaultTreeModel       treeModel;
98    private JButton                changeButton;
99    private JButton                addButton;
100   private JButton                removeButton;
101   private JButton                clearButton;
102   private JPanel                 buttonPanel;
103   
104   
105   ////////////////////////////////////////////////////////////////////////////////////////////
106   // Data fields
107   ////////////////////////////////////////////////////////////////////////////////////////////
108 
109   private String hostType;
110   
111 
112   ////////////////////////////////////////////////////////////////////////////////////////////
113   // Constructor
114   ////////////////////////////////////////////////////////////////////////////////////////////
115 
116   public HostPanelManager(JPanel parentPanel)
117   { 
118     hostPanel = parentPanel;
119   }
120 
121 
122   ////////////////////////////////////////////////////////////////////////////////////////////
123   // Implemented methods from interface PanelManager
124   ////////////////////////////////////////////////////////////////////////////////////////////
125 
126   public void makePanel()
127   {
128     // Get the parent tabbed pane, and decide if this is
129     // the server panel or the client panel  
130     JTabbedPane tabs = (JTabbedPane) hostPanel.getParent();
131     int index = tabs.indexOfComponent(hostPanel);
132     hostType = tabs.getTitleAt(index);
133 
134 
135     // Create labels and textfields for the settings panel
136     domainNameLabel    = new JLabel("Domain name:");
137     domainAddressLabel = new JLabel("Domain address:");
138     domainPortLabel    = new JLabel("Domain port:");
139     hostNameLabel      = new JLabel(hostType + " name:");
140     hostPortLabel      = new JLabel(hostType + " port:");
141         
142     domainNameField    = new JTextField();
143     // Complicated formatting...make it simple!
144     // TO DO: Create class(es) for custom formatting.
145     try {
146       domainAddressField = new JFormattedTextField(new MaskFormatter("###.###.###.###"));
147     } catch (ParseException e) {
148       e.printStackTrace();
149     }
150     NumberFormatter domainPortFormatter = new NumberFormatter();
151     NumberFormatter hostPortFormatter = new NumberFormatter();
152     domainPortFormatter.setMinimum(new Integer(0));
153     domainPortFormatter.setMaximum(new Integer(65535));
154     hostPortFormatter.setMinimum(new Integer(0));
155     hostPortFormatter.setMaximum(new Integer(65535));
156     domainPortField    = new JFormattedTextField(domainPortFormatter);
157     hostNameField    = new JTextField();
158     hostPortField    = new JFormattedTextField(hostPortFormatter);
159     
160     domainNameField.setColumns(40);
161     domainPortField.setColumns(40);
162     hostNameField.setColumns(40);
163     hostPortField.setColumns(40);
164     
165     // Add action listeners
166     ChangeTracker.addListener(domainNameField, domainNameLabel, hostPanel);
167     ChangeTracker.addListener(domainAddressField, domainAddressLabel, hostPanel);
168     ChangeTracker.addListener(domainPortField, domainPortLabel, hostPanel);
169     ChangeTracker.addListener(hostNameField, hostNameLabel, hostPanel);
170     ChangeTracker.addListener(hostPortField, hostPortLabel, hostPanel);
171     
172     settingsPanel = new JPanel(new GridBagLayout());
173     settingsPanel.setBorder(BorderFactory.createCompoundBorder(
174                             BorderFactory.createEmptyBorder(5,5,5,5),
175                             BorderFactory.createEmptyBorder(5,5,5,5)));
176                         
177 
178     // Create the buttons and button panel
179     changeButton  = new JButton("Change");  
180     addButton     = new JButton("Add");
181     removeButton  = new JButton("Remove");
182     clearButton   = new JButton("Clear");
183     buttonPanel   = new JPanel(new GridBagLayout());
184 
185     // Do the button panel layout     
186     GridBagConstraints gbc = new GridBagConstraints();
187     gbc.fill      = GridBagConstraints.BOTH;
188     gbc.gridwidth = GridBagConstraints.REMAINDER;
189     buttonPanel.add(changeButton, gbc);
190     buttonPanel.add(removeButton, gbc);
191     buttonPanel.add(clearButton, gbc);
192     buttonPanel.add(addButton, gbc);
193     buttonPanel.setBorder(BorderFactory.createCompoundBorder(
194                           BorderFactory.createEmptyBorder(5,5,5,5),
195                           BorderFactory.createEmptyBorder(5,5,5,5)));
196     addButtonListeners();
197     
198 
199     // Create the hosts panel and tree
200     rootNode   = new DefaultMutableTreeNode("Domains");
201     treeModel  = new DefaultTreeModel(rootNode);
202     treeModel.setAsksAllowsChildren(true);
203     hostsTree  = new JTree(treeModel);
204     hostsTree.getSelectionModel().setSelectionMode
205                     (TreeSelectionModel.SINGLE_TREE_SELECTION);
206     addTreeListener();
207     // Select the root node by default
208     hostsTree.setSelectionPath(new TreePath(rootNode));     
209     JScrollPane hostsScroller = new JScrollPane(hostsTree);
210     hostsScroller.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
211     hostsScroller.setBorder(BorderFactory.createCompoundBorder(
212                             BorderFactory.createTitledBorder(
213                             BorderFactory.createEmptyBorder(),
214                             hostType + " list:"),
215                             BorderFactory.createCompoundBorder(
216                             BorderFactory.createLineBorder(Color.BLACK,2),
217                             BorderFactory.createEtchedBorder())));
218     Box hostsBox = new Box(BoxLayout.Y_AXIS);
219     hostsBox.add(hostsScroller);
220     
221     
222     // Do the hostPanel layout        
223     gbc.fill       = GridBagConstraints.BOTH;
224     gbc.gridx      = 0;
225     gbc.gridy      = 0;
226     gbc.gridheight = 3;
227     gbc.gridwidth  = 1;
228     gbc.weightx    = 0.1;
229     hostPanel.add(settingsPanel, gbc);
230 
231     gbc.fill       = GridBagConstraints.NONE;
232     gbc.gridx      = 1;
233     gbc.gridy      = 2;
234     gbc.gridheight = 1;
235     hostPanel.add(buttonPanel, gbc);
236 
237     gbc.fill       = GridBagConstraints.BOTH;
238     gbc.gridx      = 2;
239     gbc.gridy      = 0;
240     gbc.gridheight = 3;
241     gbc.weighty    = 0.1;
242     gbc.weightx    = 0.9;
243     hostPanel.add(hostsBox, gbc);
244     
245     hostPanel.setBorder(BorderFactory.createCompoundBorder(
246                            BorderFactory.createEtchedBorder(),
247                            BorderFactory.createEmptyBorder(5,5,5,5)));
248   }
249 
250 
251   /* (non-Javadoc)
252    * @see jgroup.experiment.gui.views.helpers.PanelManager#updatePanel()
253    */
254   public void updatePanel(ExperimentConfig expConfig)
255   {
256     if( (hostPanel.getParent() instanceof JTabbedPane) == false )
257       throw new IllegalArgumentException("The parent of hostPanel must be a JTabbedPane");
258 
259     JTabbedPane tabs = (JTabbedPane) hostPanel.getParent();
260     int index = tabs.indexOfComponent(hostPanel);
261 
262     // Determine the type of hosts and get the respective HostSet
263     Host[] hosts = null;
264     if(hostType.equals(SERVER_TYPE))
265       hosts = expConfig.getServerConfig().getAllHosts().toArray();
266     else if(hostType.equals(CLIENT_TYPE))
267       hosts = expConfig.getClientConfig().getAllHosts().toArray();
268 
269 
270     // Put the hosts into a map with domain name as key
271     TreeMap domainMap = sortHosts(hosts);
272 
273     // Iterate through the set and add the domains and hosts to the tree
274     int domainIndex = 0;
275     for (Iterator domainIter = domainMap.entrySet().iterator(); domainIter.hasNext();) {
276       Map.Entry domainEntry = (Map.Entry) domainIter.next();
277       DefaultMutableTreeNode domainNode 
278                            = new DefaultMutableTreeNode(domainEntry.getKey(), true);
279       treeModel.insertNodeInto(domainNode, rootNode, domainIndex++);
280 
281       TreeSet hostSet = (TreeSet) domainEntry.getValue();      
282       int hostIndex = 0;
283       for (Iterator hostIter = hostSet.iterator(); hostIter.hasNext();) {
284         DefaultMutableTreeNode hostNode = new DefaultMutableTreeNode(hostIter.next(), false);
285         treeModel.insertNodeInto(hostNode, domainNode, hostIndex++);        
286       }
287     }
288     
289     hostsTree.expandRow(0);
290     hostsTree.setSelectionPath(new TreePath(rootNode));     
291   }
292 
293 
294   /* (non-Javadoc)
295    * @see jgroup.experiment.gui.views.helpers.PanelManager#setParentPanel(javax.swing.JPanel)
296    */
297   public void setParentPanel(JPanel parentPanel)
298   {
299     if(hostPanel == null)
300       throw new IllegalArgumentException("JPanel hostPanel has already been set");
301       
302     hostPanel = parentPanel;
303   }
304   
305 
306   /* (non-Javadoc)
307    * @see jgroup.experiment.gui.views.helpers.PanelManager#clear()
308    */
309   public void clear() {
310     domainNameField.setText("");
311     domainAddressField.setValue(null);
312     domainPortField.setValue(null);
313     hostNameField.setText("");
314     hostPortField.setValue(null);
315     rootNode.removeAllChildren();
316     treeModel.reload();
317   }
318   
319   
320   public void save(Element parentElement, Document document)
321   {
322     Element hostTypeElement = document.createElement(hostType);
323     parentElement.appendChild(hostTypeElement);
324 
325     DefaultMutableTreeNode rootNode 
326                          = (DefaultMutableTreeNode) treeModel.getRoot();
327 
328     // Iterate through the domains in the tree, and append them to the DOM
329     for (Enumeration e = rootNode.children(); e.hasMoreElements();) {
330       DefaultMutableTreeNode currentNode
331                            = (DefaultMutableTreeNode) e.nextElement();
332       appendDomain(currentNode, hostTypeElement, document);
333     }
334   }
335 
336   
337   ////////////////////////////////////////////////////////////////////////////////////////////
338   // Private methods
339   ////////////////////////////////////////////////////////////////////////////////////////////
340   
341   private void addButtonListeners()
342   {
343     addChangeButtonListener();  
344     addAddButtonListener();        
345     addRemoveButtonListener();
346     addClearButtonListener();
347   }
348 
349     
350   private void addTreeListener()
351   {
352     hostsTree.addTreeSelectionListener(new TreeSelectionListener() {
353       public void valueChanged(TreeSelectionEvent e) {
354         DefaultMutableTreeNode selectedNode = (DefaultMutableTreeNode)
355                                hostsTree.getLastSelectedPathComponent();
356         displayNodeInfo(selectedNode);        
357       }
358     });
359   }
360   
361   
362   /*
363    * Put the hosts in a map with domain name as key.
364    * The value for every domain is a list of hosts.
365    */
366   private TreeMap sortHosts(Host[] hosts)
367   {
368     TreeMap domainMap = new TreeMap();
369     
370     for (int i = 0; i < hosts.length; i++) {
371       Domain domain = hosts[i].getDomain();
372       String domainNodeText = domain.getName() + " : " + domain.getPort();
373 
374       // The nodetext is stored in a TreeSet, and automatically sorted
375       TreeSet hostSet = new TreeSet();
376 
377       if(!domainMap.containsKey(domainNodeText)){
378         String hostNodeText = hosts[i].getHostName() + " : " + hosts[i].getPort();
379         hostSet.add(hostNodeText);
380 
381         for (int j = i+1; j < hosts.length; j++) {
382           if(hosts[j].getDomainName().equalsIgnoreCase(domain.getName()))
383             hostNodeText = hosts[j].getHostName() + " : " + hosts[j].getPort();
384             hostSet.add(hostNodeText);
385         }
386         
387         domainMap.put(domainNodeText, hostSet);
388       }      
389     }    
390     
391     return domainMap; 
392   }
393   
394   
395   private void displayNodeInfo(DefaultMutableTreeNode selectedNode)
396   {
397     if(selectedNode == null) {
398 //      settingsPanel.removeAll();
399 //      settingsPanel.revalidate();
400       changeButton.setEnabled(false);
401       addButton.setEnabled(false);
402       removeButton.setEnabled(false);
403       clearButton.setEnabled(false);
404       return;
405     }
406       
407     String nodeValue = (String) selectedNode.getUserObject();
408     GridBagConstraints gbc = new GridBagConstraints();
409     gbc.gridx   = 0;
410     gbc.fill    = GridBagConstraints.BOTH;
411     // Clear the settingsPanel
412     settingsPanel.removeAll();
413 
414     if(selectedNode.getChildCount() == 0)
415       clearButton.setEnabled(false);
416     else
417       clearButton.setEnabled(true);
418 
419     switch(selectedNode.getLevel()) {
420       // CASE 0: root node is selected
421       case 0:
422         changeButton.setEnabled(false);
423         addButton.setEnabled(true);
424         removeButton.setEnabled(false);
425         domainNameField.setText(""); 
426         domainPortField.setText("");
427         
428         settingsPanel.add(domainNameLabel, gbc);
429         settingsPanel.add(domainNameField, gbc);
430 //        settingsBox.add(domainAddressLabel);
431 //        settingsBox.add(domainAddressField);
432         settingsPanel.add(domainPortLabel, gbc);
433         settingsPanel.add(domainPortField, gbc);
434         break;
435         
436       // CASE 1: A domain node is selected  
437       case 1:
438         changeButton.setEnabled(true);
439         addButton.setEnabled(true);
440         removeButton.setEnabled(true);
441         int domainIndex = nodeValue.indexOf(':');
442         domainNameField.setText(nodeValue.substring(0, domainIndex));
443         domainPortField.setText(nodeValue.substring(domainIndex + 1, nodeValue.length()));
444         
445         settingsPanel.add(domainNameLabel, gbc);
446         settingsPanel.add(domainNameField, gbc);
447 //        settingsBox.add(domainAddressLabel);
448 //        settingsBox.add(domainAddressField);
449         settingsPanel.add(domainPortLabel, gbc);
450         settingsPanel.add(domainPortField, gbc);
451         settingsPanel.add(hostNameLabel, gbc);
452         settingsPanel.add(hostNameField, gbc);
453         settingsPanel.add(hostPortLabel, gbc);
454         settingsPanel.add(hostPortField, gbc);
455         break;
456         
457       // CASE 2: A host node is selected  
458       case 2:
459         changeButton.setEnabled(true);
460         addButton.setEnabled(false);
461         removeButton.setEnabled(true);
462         int hostIndex = nodeValue.indexOf(':');
463         hostNameField.setText(nodeValue.substring(0, hostIndex));
464         hostPortField.setText(nodeValue.substring(hostIndex + 1, nodeValue.length()));
465         settingsPanel.add(hostNameLabel, gbc);
466         settingsPanel.add(hostNameField, gbc);
467         settingsPanel.add(hostPortLabel, gbc);
468         settingsPanel.add(hostPortField, gbc);
469         break;        
470     }
471 
472     settingsPanel.revalidate();
473 }
474 
475 
476   private void addChangeButtonListener() {
477     changeButton.addActionListener(new ActionListener() {
478       public void actionPerformed(ActionEvent e) {
479         DefaultMutableTreeNode selectedNode = (DefaultMutableTreeNode)
480                                hostsTree.getLastSelectedPathComponent();
481         String nodeText = null;
482 
483         switch(selectedNode.getLevel()) {
484           /* CASE 1: */
485           case 1:
486             String domainName = domainNameField.getText();
487             String domainPort = domainPortField.getText();
488               
489             if(domainName.length() == 0) {
490               JOptionPane.showMessageDialog(clearButton.getTopLevelAncestor(),
491                                             "You must provide a\n"
492                                            +"name for the domain.",
493                                             "Missing parameter",
494                                             JOptionPane.INFORMATION_MESSAGE);
495               return;
496             } else if(domainPort.length() == 0) {
497               JOptionPane.showMessageDialog(clearButton.getTopLevelAncestor(),
498                                             "You must provide a port\n"
499                                            +"number for the domain.",
500                                             "Missing parameter",
501                                             JOptionPane.INFORMATION_MESSAGE);
502               return;
503             }
504             
505             nodeText = domainName + " : " + domainPort;
506             selectedNode.setUserObject(nodeText);
507             treeModel.reload(selectedNode);
508             ChangeTracker.performedChange(selectedNode);
509             break;
510            
511           /* CASE 2: */
512           case 2:
513             String hostName = hostNameField.getText();
514             String hostPort = hostPortField.getText();
515               
516             if(hostName.length() == 0) {
517               JOptionPane.showMessageDialog(clearButton.getTopLevelAncestor(),
518                                             "You must provide a\n"
519                                            +"name for the host.",
520                                             "Missing parameter",
521                                             JOptionPane.INFORMATION_MESSAGE);
522               return;
523             } else if(hostPort.length() == 0) {
524               JOptionPane.showMessageDialog(clearButton.getTopLevelAncestor(),
525                                             "You must provide a port\n"
526                                            +"number for the host.",
527                                             "Missing parameter",
528                                             JOptionPane.INFORMATION_MESSAGE);
529               return;
530             }
531               
532             nodeText = hostName + " : " + hostPort;
533             selectedNode.setUserObject(nodeText);
534             treeModel.reload(selectedNode);
535             ChangeTracker.performedChange(selectedNode);            
536             break;
537         }
538       }
539     });
540   }
541 
542   private void addAddButtonListener() {
543     addButton.addActionListener(new ActionListener() {
544       public void actionPerformed(ActionEvent e) {
545         DefaultMutableTreeNode selectedNode = (DefaultMutableTreeNode)
546                                hostsTree.getLastSelectedPathComponent();
547 
548         String nodeText = null;
549         DefaultMutableTreeNode newNode = null;        
550 
551         switch(selectedNode.getLevel()) {
552           // CASE 0: root node is selected
553           case 0:
554             String domainName = domainNameField.getText();
555             String domainPort = domainPortField.getText();
556             
557             if(domainName.length() == 0) {
558               JOptionPane.showMessageDialog(clearButton.getTopLevelAncestor(),
559                                             "You must provide a\n"
560                                            +"name for the domain.",
561                                             "Missing parameter",
562                                             JOptionPane.INFORMATION_MESSAGE);
563               return;
564             } else if(domainPort.length() == 0) {
565               JOptionPane.showMessageDialog(clearButton.getTopLevelAncestor(),
566                                             "You must provide a port\n"
567                                            +"number for the domain.",
568                                             "Missing parameter",
569                                             JOptionPane.INFORMATION_MESSAGE);
570               return;
571             }
572           
573             nodeText = domainName + " : " + domainPort;
574             newNode = new DefaultMutableTreeNode(nodeText);
575             selectedNode.add(newNode);
576             treeModel.reload();
577             ChangeTracker.performedChange(newNode.getParent());
578             break;
579             
580     
581           // CASE 1: A domain is selected
582           case 1:
583             String hostName = hostNameField.getText();
584             String hostPort = hostPortField.getText();
585             
586             if(hostName.length() == 0) {
587               JOptionPane.showMessageDialog(clearButton.getTopLevelAncestor(),
588                                             "You must provide a\n"
589                                            +"name for the host.",
590                                             "Missing parameter",
591                                             JOptionPane.INFORMATION_MESSAGE);
592               return;
593             } else if(hostPort.length() == 0) {
594               JOptionPane.showMessageDialog(clearButton.getTopLevelAncestor(),
595                                             "You must provide a port\n"
596                                            +"number for the host.",
597                                             "Missing parameter",
598                                             JOptionPane.INFORMATION_MESSAGE);
599               return;
600             }
601             
602             nodeText = hostName + " : " + hostPort;
603             newNode = new DefaultMutableTreeNode(nodeText);
604             newNode.setAllowsChildren(false);
605             selectedNode.add(newNode);
606             treeModel.reload();
607             ChangeTracker.performedChange(newNode.getParent());            
608             break;
609         }
610         
611         hostsTree.expandPath(new TreePath(newNode.getPath()));
612       }
613     });
614   }
615 
616 
617   private void addRemoveButtonListener() {
618     removeButton.addActionListener(new ActionListener() {
619       public void actionPerformed(ActionEvent e) {
620         DefaultMutableTreeNode selectedNode = (DefaultMutableTreeNode)
621                                hostsTree.getLastSelectedPathComponent();
622         DefaultMutableTreeNode parentNode   = (DefaultMutableTreeNode)
623                                selectedNode.getParent();
624 
625         if(selectedNode.getChildCount() > 0)                               
626           JOptionPane.showMessageDialog(clearButton.getTopLevelAncestor(),
627                                         "You have unsaved changes,\n"
628                                        +"but SAVE is not yet implemented :(",
629                                         "Would you like to save?",
630                                         JOptionPane.WARNING_MESSAGE);
631 
632         ChangeTracker.performedChange(selectedNode.getParent());    
633         selectedNode.removeFromParent();
634         treeModel.reload();
635         
636         if(parentNode.getChildCount() > 0) {
637           hostsTree.expandPath(new TreePath(parentNode.getPath()));
638         }
639       }
640     });
641   }
642 
643 
644   private void addClearButtonListener() {
645     clearButton.addActionListener(new ActionListener() {
646       public void actionPerformed(ActionEvent e) {
647         DefaultMutableTreeNode selectedNode = (DefaultMutableTreeNode)
648                                hostsTree.getLastSelectedPathComponent();
649     
650         if(ChangeTracker.hasNodeChanges())
651           JOptionPane.showMessageDialog(clearButton.getTopLevelAncestor(),
652                                         "You have unsaved changes,\n"
653                                        +"but SAVE is not yet implemented :(",
654                                         "Would you like to save?",
655                                         JOptionPane.WARNING_MESSAGE);
656     
657         if(selectedNode == null)
658           rootNode.removeAllChildren();
659         else
660           selectedNode.removeAllChildren();
661     
662         treeModel.reload(selectedNode);
663       }
664     });
665   }
666   
667   
668   private void appendDomain(DefaultMutableTreeNode domainNode, Element parentElement, Document document)
669   {
670     String nodeTitle   = (String) domainNode.getUserObject();
671     int    index       = nodeTitle.indexOf(" : ");
672     String nodeName    = "";
673     String port        = "";
674 
675     if(index != -1) {
676       nodeName = nodeTitle.substring(0, index);
677       port     = nodeTitle.substring(index + 3, nodeTitle.length());
678     } else {
679       nodeName = nodeTitle;
680     }
681 
682     // Append the domain element, and set name and port
683     Element domainElement = document.createElement("Domain");
684     domainElement.setAttribute("name", nodeName);
685     if(port != null && port.length() > 0)
686       domainElement.setAttribute("port", port);
687     parentElement.appendChild(domainElement);
688 
689     // Iterate through the hosts of the domain, and append them to the DOM    
690     for (Enumeration e = domainNode.children(); e.hasMoreElements();) {
691       DefaultMutableTreeNode hostNode
692                            = (DefaultMutableTreeNode) e.nextElement();
693       appendHost(hostNode, domainElement, document);
694     } 
695   }
696   
697   
698   private void appendHost(DefaultMutableTreeNode hostNode, Element domainElement, Document document)
699   {
700     String nodeTitle   = (String) hostNode.getUserObject();
701     int    index       = nodeTitle.indexOf(" : ");
702     String nodeName    = "";
703     String port        = "";
704     
705     if(index != -1) {
706       nodeName = nodeTitle.substring(0, index);
707       port     = nodeTitle.substring(index + 3, nodeTitle.length());
708     } else {
709       nodeName = nodeTitle;
710     }
711 
712     // Append the domain element, and set name and port
713     Element hostElement = document.createElement("Host");
714     hostElement.setAttribute("name", nodeName);
715     if(port != null && port.length() > 0)
716       hostElement.setAttribute("port", port);
717     domainElement.appendChild(hostElement);
718   }
719 }